Compare commits
2 Commits
main
...
feature/nu
| Author | SHA1 | Date |
|---|---|---|
|
|
21a32853da | |
|
|
b38c2a202f |
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Make date objects from timestamps
|
* Make date objects from timestamps
|
||||||
*/
|
*/
|
||||||
export function parseDateOrNull(date: string | Date) {
|
export function parseDateOrNull(date: string | Date | null) {
|
||||||
if (date instanceof Date) {
|
if (date instanceof Date) {
|
||||||
return date
|
return date
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package caldav
|
package caldav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -190,7 +191,6 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||||
var caldavtodos []*Todo
|
var caldavtodos []*Todo
|
||||||
for _, t := range projectTasks {
|
for _, t := range projectTasks {
|
||||||
|
|
||||||
duration := t.EndDate.Sub(t.StartDate)
|
|
||||||
var categories []string
|
var categories []string
|
||||||
for _, label := range t.Labels {
|
for _, label := range t.Labels {
|
||||||
categories = append(categories, label.Title)
|
categories = append(categories, label.Title)
|
||||||
|
|
@ -198,7 +198,7 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||||
var alarms []Alarm
|
var alarms []Alarm
|
||||||
for _, reminder := range t.Reminders {
|
for _, reminder := range t.Reminders {
|
||||||
alarms = append(alarms, Alarm{
|
alarms = append(alarms, Alarm{
|
||||||
Time: reminder.Reminder,
|
Time: reminder.Reminder.Time(),
|
||||||
Duration: time.Duration(reminder.RelativePeriod) * time.Second,
|
Duration: time.Duration(reminder.RelativePeriod) * time.Second,
|
||||||
RelativeTo: reminder.RelativeTo,
|
RelativeTo: reminder.RelativeTo,
|
||||||
})
|
})
|
||||||
|
|
@ -214,20 +214,32 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var duration time.Duration
|
||||||
|
if t.StartDate != nil && t.EndDate != nil {
|
||||||
|
duration = t.EndDate.Sub(t.StartDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeIfNotNil := func(t *modules.Time) time.Time {
|
||||||
|
if t == nil {
|
||||||
|
return time.Time{} // Return zero value of time.Time if t is nil
|
||||||
|
}
|
||||||
|
return t.Time() // Dereference the pointer and return the value
|
||||||
|
}
|
||||||
|
|
||||||
caldavtodos = append(caldavtodos, &Todo{
|
caldavtodos = append(caldavtodos, &Todo{
|
||||||
Timestamp: t.Updated,
|
Timestamp: t.Updated.Time(),
|
||||||
UID: t.UID,
|
UID: t.UID,
|
||||||
Summary: t.Title,
|
Summary: t.Title,
|
||||||
Description: t.Description,
|
Description: t.Description,
|
||||||
Completed: t.DoneAt,
|
Completed: getTimeIfNotNil(t.DoneAt),
|
||||||
// Organizer: &t.CreatedBy, // Disabled until we figure out how this works
|
// Organizer: &t.CreatedBy, // Disabled until we figure out how this works
|
||||||
Categories: categories,
|
Categories: categories,
|
||||||
Priority: t.Priority,
|
Priority: t.Priority,
|
||||||
Start: t.StartDate,
|
Start: getTimeIfNotNil(t.StartDate),
|
||||||
End: t.EndDate,
|
End: getTimeIfNotNil(t.EndDate),
|
||||||
Created: t.Created,
|
Created: getTimeIfNotNil(t.Created),
|
||||||
Updated: t.Updated,
|
Updated: getTimeIfNotNil(t.Updated),
|
||||||
DueDate: t.DueDate,
|
DueDate: getTimeIfNotNil(t.DueDate),
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
RepeatAfter: t.RepeatAfter,
|
RepeatAfter: t.RepeatAfter,
|
||||||
RepeatMode: t.RepeatMode,
|
RepeatMode: t.RepeatMode,
|
||||||
|
|
@ -406,7 +418,7 @@ func parseVAlarm(vAlarm *ics.VAlarm, vTask *models.Task) *models.Task {
|
||||||
|
|
||||||
if contains(property.ICalParameters["RELATED"], "END") {
|
if contains(property.ICalParameters["RELATED"], "END") {
|
||||||
// Example: TRIGGER;RELATED=END:-P2D
|
// Example: TRIGGER;RELATED=END:-P2D
|
||||||
if vTask.EndDate.IsZero() {
|
if vTask.EndDate == nil || (vTask.EndDate != nil && vTask.EndDate.IsZero()) {
|
||||||
vTask.Reminders = append(vTask.Reminders, &models.TaskReminder{
|
vTask.Reminders = append(vTask.Reminders, &models.TaskReminder{
|
||||||
RelativePeriod: int64(duration.Seconds()),
|
RelativePeriod: int64(duration.Seconds()),
|
||||||
RelativeTo: models.ReminderRelationDueDate})
|
RelativeTo: models.ReminderRelationDueDate})
|
||||||
|
|
@ -437,10 +449,10 @@ func contains(array []string, str string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc5545#section-3.3.5
|
// https://tools.ietf.org/html/rfc5545#section-3.3.5
|
||||||
func caldavTimeToTimestamp(ianaProperty ics.IANAProperty) time.Time {
|
func caldavTimeToTimestamp(ianaProperty ics.IANAProperty) *modules.Time {
|
||||||
tstring := ianaProperty.Value
|
tstring := ianaProperty.Value
|
||||||
if tstring == "" {
|
if tstring == "" {
|
||||||
return time.Time{}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
format := DateFormat
|
format := DateFormat
|
||||||
|
|
@ -453,7 +465,6 @@ func caldavTimeToTimestamp(ianaProperty ics.IANAProperty) time.Time {
|
||||||
format = `20060102`
|
format = `20060102`
|
||||||
}
|
}
|
||||||
|
|
||||||
var t time.Time
|
|
||||||
var err error
|
var err error
|
||||||
tzParameter := ianaProperty.ICalParameters["TZID"]
|
tzParameter := ianaProperty.ICalParameters["TZID"]
|
||||||
if len(tzParameter) > 0 {
|
if len(tzParameter) > 0 {
|
||||||
|
|
@ -461,19 +472,18 @@ func caldavTimeToTimestamp(ianaProperty ics.IANAProperty) time.Time {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Error while parsing caldav timezone %s: %s", tzParameter[0], err)
|
log.Warningf("Error while parsing caldav timezone %s: %s", tzParameter[0], err)
|
||||||
} else {
|
} else {
|
||||||
t, err = time.ParseInLocation(format, tstring, loc)
|
tt, err := time.ParseInLocation(format, tstring, loc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s at location %s", tstring, loc, err)
|
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s at location %s", tstring, loc, err)
|
||||||
} else {
|
} else {
|
||||||
t = t.In(config.GetTimeZone())
|
return modules.TimeFromTime(tt.In(config.GetTimeZone()))
|
||||||
return t
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t, err = time.Parse(format, tstring)
|
tt, err := time.Parse(format, tstring)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s", tstring, err)
|
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s", tstring, err)
|
||||||
return time.Time{}
|
return nil
|
||||||
}
|
}
|
||||||
return t
|
return modules.TimeFromTime(tt)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package caldav
|
package caldav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ END:VCALENDAR`,
|
||||||
Title: "Todo #1",
|
Title: "Todo #1",
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -82,7 +83,7 @@ END:VCALENDAR`,
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -115,7 +116,7 @@ END:VCALENDAR`,
|
||||||
Title: "cat2",
|
Title: "cat2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -144,10 +145,10 @@ END:VCALENDAR`,
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Date(2018, 12, 1, 1, 12, 10, 0, config.GetTimeZone()),
|
Reminder: modules.TimeFromTime(time.Date(2018, 12, 1, 1, 12, 10, 0, config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -192,8 +193,8 @@ END:VCALENDAR`,
|
||||||
Title: "Todo #1",
|
Title: "Todo #1",
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
StartDate: time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone()),
|
StartDate: modules.TimeFromTime(time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone())),
|
||||||
DueDate: time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone()),
|
DueDate: modules.TimeFromTime(time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone())),
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: models.ReminderRelationStartDate,
|
RelativeTo: models.ReminderRelationStartDate,
|
||||||
|
|
@ -216,7 +217,7 @@ END:VCALENDAR`,
|
||||||
RelativePeriod: -1800,
|
RelativePeriod: -1800,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -241,7 +242,7 @@ END:VCALENDAR`,
|
||||||
Title: "SubTask #1",
|
Title: "SubTask #1",
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindParenttask: {
|
models.RelationKindParenttask: {
|
||||||
{
|
{
|
||||||
|
|
@ -273,7 +274,7 @@ END:VCALENDAR`,
|
||||||
Title: "Parent",
|
Title: "Parent",
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindSubtask: {
|
models.RelationKindSubtask: {
|
||||||
{
|
{
|
||||||
|
|
@ -331,7 +332,7 @@ END:VTIMEZONE
|
||||||
END:VCALENDAR`,
|
END:VCALENDAR`,
|
||||||
},
|
},
|
||||||
wantVTask: &models.Task{
|
wantVTask: &models.Task{
|
||||||
Updated: time.Date(2023, 4, 2, 7, 41, 58, 0, config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Date(2023, 4, 2, 7, 41, 58, 0, config.GetTimeZone())),
|
||||||
UID: "4290517349243274514",
|
UID: "4290517349243274514",
|
||||||
Title: "Test with tasks.org",
|
Title: "Test with tasks.org",
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
|
|
@ -340,15 +341,15 @@ END:VCALENDAR`,
|
||||||
Title: "Vikunja",
|
Title: "Vikunja",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DueDate: time.Date(2023, 4, 2, 15, 0, 1, 0, config.GetTimeZone()),
|
DueDate: modules.TimeFromTime(time.Date(2023, 4, 2, 15, 0, 1, 0, config.GetTimeZone())),
|
||||||
StartDate: time.Date(2023, 4, 1, 7, 0, 0, 0, config.GetTimeZone()),
|
StartDate: modules.TimeFromTime(time.Date(2023, 4, 1, 7, 0, 0, 0, config.GetTimeZone())),
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: models.ReminderRelationDueDate,
|
RelativeTo: models.ReminderRelationDueDate,
|
||||||
RelativePeriod: 0,
|
RelativePeriod: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Date(2023, 4, 2, 10, 0, 0, 0, config.GetTimeZone()),
|
Reminder: modules.TimeFromTime(time.Date(2023, 4, 2, 10, 0, 0, 0, config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -529,12 +530,12 @@ func TestGetCaldavTodosForTasks(t *testing.T) {
|
||||||
UID: "randomuid",
|
UID: "randomuid",
|
||||||
Description: "Description",
|
Description: "Description",
|
||||||
Priority: 3,
|
Priority: 3,
|
||||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626721, 0).In(config.GetTimeZone())),
|
||||||
DueDate: time.Unix(1543626722, 0).In(config.GetTimeZone()),
|
DueDate: modules.TimeFromTime(time.Unix(1543626722, 0).In(config.GetTimeZone())),
|
||||||
StartDate: time.Unix(1543626723, 0).In(config.GetTimeZone()),
|
StartDate: modules.TimeFromTime(time.Unix(1543626723, 0).In(config.GetTimeZone())),
|
||||||
EndDate: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
EndDate: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626725, 0).In(config.GetTimeZone())),
|
||||||
DoneAt: time.Unix(1543626726, 0).In(config.GetTimeZone()),
|
DoneAt: modules.TimeFromTime(time.Unix(1543626726, 0).In(config.GetTimeZone())),
|
||||||
RepeatAfter: 86400,
|
RepeatAfter: 86400,
|
||||||
Labels: []*models.Label{
|
Labels: []*models.Label{
|
||||||
{
|
{
|
||||||
|
|
@ -548,10 +549,10 @@ func TestGetCaldavTodosForTasks(t *testing.T) {
|
||||||
},
|
},
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1543626730, 0).In(config.GetTimeZone()),
|
Reminder: modules.TimeFromTime(time.Unix(1543626730, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1543626731, 0).In(config.GetTimeZone()),
|
Reminder: modules.TimeFromTime(time.Unix(1543626731, 0).In(config.GetTimeZone())),
|
||||||
RelativePeriod: -3600,
|
RelativePeriod: -3600,
|
||||||
RelativeTo: models.ReminderRelationDueDate,
|
RelativeTo: models.ReminderRelationDueDate,
|
||||||
},
|
},
|
||||||
|
|
@ -609,23 +610,23 @@ END:VCALENDAR`,
|
||||||
UID: "randomuid_parent",
|
UID: "randomuid_parent",
|
||||||
Description: "A parent task",
|
Description: "A parent task",
|
||||||
Priority: 3,
|
Priority: 3,
|
||||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626721, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626725, 0).In(config.GetTimeZone())),
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindSubtask: {
|
models.RelationKindSubtask: {
|
||||||
{
|
{
|
||||||
Title: "Subtask 1",
|
Title: "Subtask 1",
|
||||||
UID: "randomuid_child_1",
|
UID: "randomuid_child_1",
|
||||||
Description: "The first child task",
|
Description: "The first child task",
|
||||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Subtask 2",
|
Title: "Subtask 2",
|
||||||
UID: "randomuid_child_2",
|
UID: "randomuid_child_2",
|
||||||
Description: "The second child task",
|
Description: "The second child task",
|
||||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -636,8 +637,8 @@ END:VCALENDAR`,
|
||||||
Title: "Subtask 1",
|
Title: "Subtask 1",
|
||||||
UID: "randomuid_child_1",
|
UID: "randomuid_child_1",
|
||||||
Description: "The first child task",
|
Description: "The first child task",
|
||||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindParenttask: {
|
models.RelationKindParenttask: {
|
||||||
{
|
{
|
||||||
|
|
@ -645,8 +646,8 @@ END:VCALENDAR`,
|
||||||
UID: "randomuid_parent",
|
UID: "randomuid_parent",
|
||||||
Description: "A parent task",
|
Description: "A parent task",
|
||||||
Priority: 3,
|
Priority: 3,
|
||||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626721, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626725, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -657,8 +658,8 @@ END:VCALENDAR`,
|
||||||
Title: "Subtask 2",
|
Title: "Subtask 2",
|
||||||
UID: "randomuid_child_2",
|
UID: "randomuid_child_2",
|
||||||
Description: "The second child task",
|
Description: "The second child task",
|
||||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(config.GetTimeZone())),
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindParenttask: {
|
models.RelationKindParenttask: {
|
||||||
{
|
{
|
||||||
|
|
@ -666,8 +667,8 @@ END:VCALENDAR`,
|
||||||
UID: "randomuid_parent",
|
UID: "randomuid_parent",
|
||||||
Description: "A parent task",
|
Description: "A parent task",
|
||||||
Priority: 3,
|
Priority: 3,
|
||||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
Created: modules.TimeFromTime(time.Unix(1543626721, 0).In(config.GetTimeZone())),
|
||||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
Updated: modules.TimeFromTime(time.Unix(1543626725, 0).In(config.GetTimeZone())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
@ -43,8 +43,8 @@ type File struct {
|
||||||
Mime string `xorm:"text null" json:"mime"`
|
Mime string `xorm:"text null" json:"mime"`
|
||||||
Size uint64 `xorm:"bigint not null" json:"size"`
|
Size uint64 `xorm:"bigint not null" json:"size"`
|
||||||
|
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created *modules.Time `xorm:"created" json:"created"`
|
||||||
CreatedByID int64 `xorm:"bigint not null" json:"-"`
|
CreatedByID int64 `xorm:"bigint not null" json:"-"`
|
||||||
|
|
||||||
File afero.File `xorm:"-" json:"-"`
|
File afero.File `xorm:"-" json:"-"`
|
||||||
// This ReadCloser is only used for migration purposes. Use with care!
|
// This ReadCloser is only used for migration purposes. Use with care!
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// BootedEvent represents a BootedEvent event
|
// BootedEvent represents a BootedEvent event
|
||||||
type BootedEvent struct {
|
type BootedEvent struct {
|
||||||
|
|
|
||||||
|
|
@ -115,49 +115,49 @@ func TestTaskCollection(t *testing.T) {
|
||||||
t.Run("by priority", func(t *testing.T) {
|
t.Run("by priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
t.Run("by priority desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority asc", func(t *testing.T) {
|
t.Run("by priority asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// should equal duedate asc
|
||||||
t.Run("by due_date", func(t *testing.T) {
|
t.Run("by due_date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
t.Run("by duedate desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
// Due date without unix suffix
|
// Due date without unix suffix
|
||||||
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by due_date without suffix", func(t *testing.T) {
|
t.Run("by due_date without suffix", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
t.Run("by duedate asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
t.Run("invalid sort parameter", func(t *testing.T) {
|
||||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
||||||
|
|
@ -235,7 +235,7 @@ func TestTaskCollection(t *testing.T) {
|
||||||
urlParams,
|
urlParams,
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// If no start date but an end date is specified, this should be null
|
// If no start date but an end date is specified, this should benull
|
||||||
// since we don't have any tasks in the fixtures with an end date >
|
// since we don't have any tasks in the fixtures with an end date >
|
||||||
// the current date.
|
// the current date.
|
||||||
assert.Equal(t, "[]\n", rec.Body.String())
|
assert.Equal(t, "[]\n", rec.Body.String())
|
||||||
|
|
@ -358,33 +358,33 @@ func TestTaskCollection(t *testing.T) {
|
||||||
t.Run("by priority", func(t *testing.T) {
|
t.Run("by priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},{"id":5,"title":"Label #5","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},{"id":5,"title":"Label #5","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
t.Run("by priority desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority asc", func(t *testing.T) {
|
t.Run("by priority asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},{"id":5,"title":"Label #5","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},{"id":5,"title":"Label #5","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":null,"due_date":null,"reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// should equal duedate asc
|
||||||
t.Run("by due_date", func(t *testing.T) {
|
t.Run("by due_date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
t.Run("by duedate desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
t.Run("by duedate asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("invalid parameter", func(t *testing.T) {
|
t.Run("invalid parameter", func(t *testing.T) {
|
||||||
// Invalid parameter should not sort at all
|
// Invalid parameter should not sort at all
|
||||||
|
|
@ -452,7 +452,7 @@ func TestTaskCollection(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// If no start date but an end date is specified, this should be null
|
// If no start date but an end date is specified, this should benull
|
||||||
// since we don't have any tasks in the fixtures with an end date >
|
// since we don't have any tasks in the fixtures with an end date >
|
||||||
// the current date.
|
// the current date.
|
||||||
assert.Equal(t, "[]\n", rec.Body.String())
|
assert.Equal(t, "[]\n", rec.Body.String())
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,9 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `"due_date":0`)
|
assert.NotContains(t, rec.Body.String(), `"due_date":0`)
|
||||||
})
|
})
|
||||||
t.Run("Due date unset", func(t *testing.T) {
|
t.Run("Due date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "5"}, `{"due_date": null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "5"}, `{"due_date":null}`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"due_date":"0001-01-01T00:00:00Z"`)
|
assert.Contains(t, rec.Body.String(), `"due_date":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders", func(t *testing.T) {
|
t.Run("Reminders", func(t *testing.T) {
|
||||||
|
|
@ -110,8 +110,8 @@ func TestTask(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders unset to null", func(t *testing.T) {
|
t.Run("Reminders unset tonull", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders": null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders":null}`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||||
|
|
@ -146,7 +146,7 @@ func TestTask(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||||
})
|
})
|
||||||
t.Run("Removing Assignees null", func(t *testing.T) {
|
t.Run("Removing Assigneesnull", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "30"}, `{"assignees":null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "30"}, `{"assignees":null}`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||||
|
|
@ -171,9 +171,9 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `"start_date":0`)
|
assert.NotContains(t, rec.Body.String(), `"start_date":0`)
|
||||||
})
|
})
|
||||||
t.Run("Start date unset", func(t *testing.T) {
|
t.Run("Start date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "7"}, `{"start_date":"0001-01-01T00:00:00Z"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "7"}, `{"start_date":null}`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"start_date":"0001-01-01T00:00:00Z"`)
|
assert.Contains(t, rec.Body.String(), `"start_date":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("End date", func(t *testing.T) {
|
t.Run("End date", func(t *testing.T) {
|
||||||
|
|
@ -183,9 +183,9 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
||||||
})
|
})
|
||||||
t.Run("End date unset", func(t *testing.T) {
|
t.Run("End date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "8"}, `{"end_date":"0001-01-01T00:00:00Z"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "8"}, `{"end_date":null}`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"end_date":"0001-01-01T00:00:00Z"`)
|
assert.Contains(t, rec.Body.String(), `"end_date":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("Color", func(t *testing.T) {
|
t.Run("Color", func(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,13 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"time"
|
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
@ -48,10 +47,10 @@ type APIToken struct {
|
||||||
// The permissions this token has. Possible values are available via the /routes endpoint and consist of the keys of the list from that endpoint. For example, if the token should be able to read all tasks as well as update existing tasks, you should add `{"tasks":["read_all","update"]}`.
|
// The permissions this token has. Possible values are available via the /routes endpoint and consist of the keys of the list from that endpoint. For example, if the token should be able to read all tasks as well as update existing tasks, you should add `{"tasks":["read_all","update"]}`.
|
||||||
Permissions APIPermissions `xorm:"json not null" json:"permissions" valid:"required"`
|
Permissions APIPermissions `xorm:"json not null" json:"permissions" valid:"required"`
|
||||||
// The date when this key expires.
|
// The date when this key expires.
|
||||||
ExpiresAt time.Time `xorm:"not null" json:"expires_at" valid:"required"`
|
ExpiresAt *modules.Time `xorm:"not null" json:"expires_at" valid:"required"`
|
||||||
|
|
||||||
// A timestamp when this api key was created. You cannot change this value.
|
// A timestamp when this api key was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
OwnerID int64 `xorm:"bigint not null" json:"-"`
|
OwnerID int64 `xorm:"bigint not null" json:"-"`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -50,9 +51,9 @@ type Bucket struct {
|
||||||
Position float64 `xorm:"double null" json:"position"`
|
Position float64 `xorm:"double null" json:"position"`
|
||||||
|
|
||||||
// A timestamp when this bucket was created. You cannot change this value.
|
// A timestamp when this bucket was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this bucket was last updated. You cannot change this value.
|
// A timestamp when this bucket was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
// The user who initially created the bucket.
|
// The user who initially created the bucket.
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
||||||
|
|
@ -175,8 +176,8 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, projects []*Pr
|
||||||
ProjectViewID: view.ID,
|
ProjectViewID: view.ID,
|
||||||
Position: float64(id),
|
Position: float64(id),
|
||||||
CreatedByID: auth.GetID(),
|
CreatedByID: auth.GetID(),
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
|
@ -152,9 +153,9 @@ func (b *TaskBucket) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
if doneChanged {
|
if doneChanged {
|
||||||
if task.Done {
|
if task.Done {
|
||||||
task.DoneAt = time.Now()
|
task.DoneAt = modules.TimeFromTime(time.Now())
|
||||||
} else {
|
} else {
|
||||||
task.DoneAt = time.Time{}
|
task.DoneAt = &modules.Time{}
|
||||||
}
|
}
|
||||||
_, err = s.Where("id = ?", task.ID).
|
_, err = s.Where("id = ?", task.ID).
|
||||||
Cols(
|
Cols(
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
||||||
|
|
@ -42,9 +41,9 @@ type Label struct {
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
||||||
|
|
||||||
// A timestamp when this label was created. You cannot change this value.
|
// A timestamp when this label was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this label was last updated. You cannot change this value.
|
// A timestamp when this label was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
@ -38,7 +37,7 @@ type LabelTask struct {
|
||||||
// The label id you want to associate with a task.
|
// The label id you want to associate with a task.
|
||||||
LabelID int64 `xorm:"bigint INDEX not null" json:"label_id" param:"label"`
|
LabelID int64 `xorm:"bigint INDEX not null" json:"label_id" param:"label"`
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
@ -54,7 +53,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +159,7 @@ func TestLabelTask_Create(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +263,7 @@ func TestLabelTask_Delete(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -37,8 +36,8 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -173,8 +172,8 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -303,8 +302,8 @@ func TestLabel_Create(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -373,8 +372,8 @@ func TestLabel_Update(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -463,8 +462,8 @@ func TestLabel_Delete(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
@ -66,9 +65,9 @@ type LinkSharing struct {
|
||||||
SharedByID int64 `xorm:"bigint INDEX not null" json:"-"`
|
SharedByID int64 `xorm:"bigint INDEX not null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this project was shared. You cannot change this value.
|
// A timestamp when this project was shared. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this share was last updated. You cannot change this value.
|
// A timestamp when this share was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -830,9 +831,9 @@ func (wl *WebhookListener) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebhookPayload struct {
|
type WebhookPayload struct {
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Time time.Time `json:"time"`
|
Time *modules.Time `json:"time"`
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIDAsInt64(id interface{}) int64 {
|
func getIDAsInt64(id interface{}) int64 {
|
||||||
|
|
@ -964,7 +965,7 @@ func (wl *WebhookListener) Handle(msg *message.Message) (err error) {
|
||||||
for _, webhook := range matchingWebhooks {
|
for _, webhook := range matchingWebhooks {
|
||||||
err = webhook.sendWebhookPayload(&WebhookPayload{
|
err = webhook.sendWebhookPayload(&WebhookPayload{
|
||||||
EventName: wl.EventName,
|
EventName: wl.EventName,
|
||||||
Time: time.Now(),
|
Time: modules.TimeFromTime(time.Now()),
|
||||||
Data: event,
|
Data: event,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -35,13 +36,15 @@ func setupTime() {
|
||||||
fmt.Printf("Error setting up time: %s", err)
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
testCreatedTime, err = time.ParseInLocation(time.RFC3339Nano, "2018-12-01T15:13:12.0+00:00", loc)
|
testCreatedTimeRaw, err := time.ParseInLocation(time.RFC3339Nano, "2018-12-01T15:13:12.0+00:00", loc)
|
||||||
|
testCreatedTime = modules.TimeFromTime(testCreatedTimeRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error setting up time: %s", err)
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
testCreatedTime = testCreatedTime.In(loc)
|
testCreatedTime = testCreatedTime.In(loc)
|
||||||
testUpdatedTime, err = time.ParseInLocation(time.RFC3339Nano, "2018-12-02T15:13:12.0+00:00", loc)
|
testUpdatedTimeRaw, err := time.ParseInLocation(time.RFC3339Nano, "2018-12-02T15:13:12.0+00:00", loc)
|
||||||
|
testUpdatedTime = modules.TimeFromTime(testUpdatedTimeRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error setting up time: %s", err)
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
||||||
|
|
@ -17,23 +17,22 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
_ "github.com/go-sql-driver/mysql" // Because.
|
"code.vikunja.io/api/pkg/modules"
|
||||||
_ "github.com/lib/pq" // Because.
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3" // Because.
|
_ "github.com/go-sql-driver/mysql" // imported for side effects
|
||||||
|
_ "github.com/lib/pq" // imported for side effects
|
||||||
|
_ "github.com/mattn/go-sqlite3" // imported for side effects
|
||||||
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
x *xorm.Engine
|
||||||
|
|
||||||
testCreatedTime time.Time
|
testCreatedTime *modules.Time
|
||||||
testUpdatedTime time.Time
|
testUpdatedTime *modules.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTables returns all structs which are also a table.
|
// GetTables returns all structs which are also a table.
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ type UndoneTaskOverdueNotification struct {
|
||||||
|
|
||||||
// ToMail returns the mail notification for UndoneTaskOverdueNotification
|
// ToMail returns the mail notification for UndoneTaskOverdueNotification
|
||||||
func (n *UndoneTaskOverdueNotification) ToMail() *notifications.Mail {
|
func (n *UndoneTaskOverdueNotification) ToMail() *notifications.Mail {
|
||||||
until := time.Until(n.Task.DueDate).Round(1*time.Hour) * -1
|
until := time.Until(n.Task.DueDate.Time()).Round(1*time.Hour) * -1
|
||||||
return notifications.NewMail().
|
return notifications.NewMail().
|
||||||
IncludeLinkToSettings().
|
IncludeLinkToSettings().
|
||||||
Subject(`Task "`+n.Task.Title+`" (`+n.Project.Title+`) is overdue`).
|
Subject(`Task "`+n.Task.Title+`" (`+n.Project.Title+`) is overdue`).
|
||||||
|
|
@ -261,12 +261,12 @@ func (n *UndoneTasksOverdueNotification) ToMail() *notifications.Mail {
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(sortedTasks, func(i, j int) bool {
|
sort.Slice(sortedTasks, func(i, j int) bool {
|
||||||
return sortedTasks[i].DueDate.Before(sortedTasks[j].DueDate)
|
return sortedTasks[i].DueDate.Time().Before(sortedTasks[j].DueDate.Time())
|
||||||
})
|
})
|
||||||
|
|
||||||
overdueLine := ""
|
overdueLine := ""
|
||||||
for _, task := range sortedTasks {
|
for _, task := range sortedTasks {
|
||||||
until := time.Until(task.DueDate).Round(1*time.Hour) * -1
|
until := time.Until(task.DueDate.Time()).Round(1*time.Hour) * -1
|
||||||
overdueLine += `* [` + task.Title + `](` + config.ServicePublicURL.GetString() + "tasks/" + strconv.FormatInt(task.ID, 10) + `) (` + n.Projects[task.ProjectID].Title + `), ` + getOverdueSinceString(until) + "\n"
|
overdueLine += `* [` + task.Title + `](` + config.ServicePublicURL.GetString() + "tasks/" + strconv.FormatInt(task.ID, 10) + `) (` + n.Projects[task.ProjectID].Title + `), ` + getOverdueSinceString(until) + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -81,9 +82,9 @@ type Project struct {
|
||||||
MaxRight Right `xorm:"-" json:"max_right"`
|
MaxRight Right `xorm:"-" json:"max_right"`
|
||||||
|
|
||||||
// A timestamp when this project was created. You cannot change this value.
|
// A timestamp when this project was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this project was last updated. You cannot change this value.
|
// A timestamp when this project was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
@ -154,8 +155,8 @@ var FavoritesPseudoProject = Project{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAll gets all projects a user has access to
|
// ReadAll gets all projects a user has access to
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
|
||||||
|
|
@ -39,9 +38,9 @@ type TeamProject struct {
|
||||||
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -212,8 +211,8 @@ func TestTeamProject_Update(t *testing.T) {
|
||||||
TeamID int64
|
TeamID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
|
||||||
|
|
@ -42,9 +41,9 @@ type ProjectUser struct {
|
||||||
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
@ -32,8 +31,8 @@ func TestProjectUser_CanDoSomething(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
|
|
@ -37,8 +36,8 @@ func TestProjectUser_Create(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -179,8 +178,8 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -271,8 +270,8 @@ func TestProjectUser_Update(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
@ -361,8 +360,8 @@ func TestProjectUser_Delete(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
@ -145,9 +144,9 @@ type ProjectView struct {
|
||||||
DoneBucketID int64 `xorm:"bigint INDEX null" json:"done_bucket_id"`
|
DoneBucketID int64 `xorm:"bigint INDEX null" json:"done_bucket_id"`
|
||||||
|
|
||||||
// A timestamp when this view was updated. You cannot change this value.
|
// A timestamp when this view was updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
// A timestamp when this reaction was created. You cannot change this value.
|
// A timestamp when this reaction was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
@ -51,7 +50,7 @@ type Reaction struct {
|
||||||
Value string `xorm:"varchar(20) not null INDEX" json:"value" valid:"required"`
|
Value string `xorm:"varchar(20) not null INDEX" json:"value" valid:"required"`
|
||||||
|
|
||||||
// A timestamp when this reaction was created. You cannot change this value.
|
// A timestamp when this reaction was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,11 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/cron"
|
"code.vikunja.io/api/pkg/cron"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
|
|
@ -49,9 +48,9 @@ type SavedFilter struct {
|
||||||
IsFavorite bool `xorm:"default false" json:"is_favorite"`
|
IsFavorite bool `xorm:"default false" json:"is_favorite"`
|
||||||
|
|
||||||
// A timestamp when this filter was created. You cannot change this value.
|
// A timestamp when this filter was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this filter was last updated. You cannot change this value.
|
// A timestamp when this filter was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,12 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
@ -107,7 +106,7 @@ type Subscription struct {
|
||||||
UserID int64 `xorm:"bigint index not null" json:"-"`
|
UserID int64 `xorm:"bigint index not null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this subscription was created. You cannot change this value.
|
// A timestamp when this subscription was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
|
|
@ -30,10 +29,10 @@ import (
|
||||||
|
|
||||||
// TaskAssginee represents an assignment of a user to a task
|
// TaskAssginee represents an assignment of a user to a task
|
||||||
type TaskAssginee struct {
|
type TaskAssginee struct {
|
||||||
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
|
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
|
||||||
TaskID int64 `xorm:"bigint INDEX not null" json:"-" param:"projecttask"`
|
TaskID int64 `xorm:"bigint INDEX not null" json:"-" param:"projecttask"`
|
||||||
UserID int64 `xorm:"bigint INDEX not null" json:"user_id" param:"user"`
|
UserID int64 `xorm:"bigint INDEX not null" json:"user_id" param:"user"`
|
||||||
Created time.Time `xorm:"created not null"`
|
Created *modules.Time `xorm:"created not null"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,17 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
"code.vikunja.io/api/pkg/files"
|
||||||
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"image"
|
"image"
|
||||||
"image/png"
|
"image/png"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
|
||||||
"code.vikunja.io/api/pkg/files"
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
|
||||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
@ -46,7 +45,7 @@ type TaskAttachment struct {
|
||||||
|
|
||||||
File *files.File `xorm:"-" json:"file"`
|
File *files.File `xorm:"-" json:"file"`
|
||||||
|
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created *modules.Time `xorm:"created" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
@ -31,7 +32,6 @@ import (
|
||||||
"github.com/iancoleman/strcase"
|
"github.com/iancoleman/strcase"
|
||||||
"github.com/jszwedko/go-datemath"
|
"github.com/jszwedko/go-datemath"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm/schemas"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type taskFilterComparator string
|
type taskFilterComparator string
|
||||||
|
|
@ -62,13 +62,14 @@ type taskFilter struct {
|
||||||
join taskFilterConcatinator
|
join taskFilterConcatinator
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTimeFromUserInput(timeString string) (value time.Time, err error) {
|
func parseTimeFromUserInput(timeString string) (value *modules.Time, err error) {
|
||||||
value, err = time.Parse(time.RFC3339, timeString)
|
var timeValue time.Time
|
||||||
|
timeValue, err = time.Parse(time.RFC3339, timeString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
value, err = time.Parse(safariDateAndTime, timeString)
|
timeValue, err = time.Parse(safariDateAndTime, timeString)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
value, err = time.Parse(safariDate, timeString)
|
timeValue, err = time.Parse(safariDate, timeString)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Here we assume a date like 2022-11-1 and try to parse it manually
|
// Here we assume a date like 2022-11-1 and try to parse it manually
|
||||||
|
|
@ -88,10 +89,10 @@ func parseTimeFromUserInput(timeString string) (value time.Time, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
value = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
timeValue = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||||
return value.In(config.GetTimeZone()), nil
|
return modules.TimeFromTime(timeValue.In(config.GetTimeZone())), nil
|
||||||
}
|
}
|
||||||
return value.In(config.GetTimeZone()), err
|
return modules.TimeFromTime(timeValue.In(config.GetTimeZone())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFilterFromExpression(f fexpr.ExprGroup, loc *time.Location) (filter *taskFilter, err error) {
|
func parseFilterFromExpression(f fexpr.ExprGroup, loc *time.Location) (filter *taskFilter, err error) {
|
||||||
|
|
@ -286,12 +287,12 @@ func getValueForField(field reflect.StructField, rawValue string, loc *time.Loca
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
value, err = strconv.ParseBool(rawValue)
|
value, err = strconv.ParseBool(rawValue)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if field.Type == schemas.TimeType {
|
if field.Type == reflect.TypeOf((*modules.Time)(nil)).Elem() {
|
||||||
var t datemath.Expression
|
var t datemath.Expression
|
||||||
var tt time.Time
|
var tt *modules.Time
|
||||||
t, err = datemath.Parse(rawValue)
|
t, err = datemath.Parse(rawValue)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tt = t.Time(datemath.WithLocation(config.GetTimeZone())).In(loc)
|
tt = modules.TimeFromTime(t.Time(datemath.WithLocation(config.GetTimeZone())).In(loc))
|
||||||
} else {
|
} else {
|
||||||
tt, err = parseTimeFromUserInput(rawValue)
|
tt, err = parseTimeFromUserInput(rawValue)
|
||||||
}
|
}
|
||||||
|
|
@ -300,12 +301,24 @@ func getValueForField(field reflect.StructField, rawValue string, loc *time.Loca
|
||||||
}
|
}
|
||||||
// Mysql/Mariadb does not support date values where the year < 1. To make this edge-case work,
|
// Mysql/Mariadb does not support date values where the year < 1. To make this edge-case work,
|
||||||
// we're setting the year to 1 in that case.
|
// we're setting the year to 1 in that case.
|
||||||
if db.GetDialect() == builder.MYSQL && tt.Year() < 1 {
|
if db.GetDialect() == builder.MYSQL && tt.Time().Year() < 1 {
|
||||||
tt = tt.AddDate(1-tt.Year(), 0, 0)
|
tt = modules.TimeFromTime(tt.Time().AddDate(1-tt.Time().Year(), 0, 0))
|
||||||
}
|
}
|
||||||
value = tt
|
value = tt
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice, reflect.Ptr:
|
||||||
|
// There are probably better ways to do this - please let me know if you have one.
|
||||||
|
if field.Type.Elem().String() == "time.Time" || field.Type.Elem().String() == "modules.Time" {
|
||||||
|
value, err = time.Parse(time.RFC3339, rawValue)
|
||||||
|
value = value.(time.Time).In(config.GetTimeZone())
|
||||||
|
|
||||||
|
if field.Type.Elem().String() == "modules.Time" {
|
||||||
|
value, err = parseTimeFromUserInput(rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a slice of pointers we're dealing with some property which is a relation
|
// If this is a slice of pointers we're dealing with some property which is a relation
|
||||||
// In that case we don't really care about what the actual type is, we just cast the value to an
|
// In that case we don't really care about what the actual type is, we just cast the value to an
|
||||||
// int64 since we need the id - yes, this assumes we only ever have int64 IDs, but this is fine.
|
// int64 since we need the id - yes, this assumes we only ever have int64 IDs, but this is fine.
|
||||||
|
|
@ -314,15 +327,9 @@ func getValueForField(field reflect.StructField, rawValue string, loc *time.Loca
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are probably better ways to do this - please let me know if you have one.
|
|
||||||
if field.Type.Elem().String() == "time.Time" {
|
|
||||||
value, err = time.Parse(time.RFC3339, rawValue)
|
|
||||||
value = value.(time.Time).In(config.GetTimeZone())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unrecognized filter type %s for field %s, value %s", field.Type.String(), field.Name, value))
|
panic(fmt.Errorf("unrecognized filter type %s for field %s (kind %s), value %s", field.Type.String(), field.Type.Kind(), field.Name, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -115,7 +116,7 @@ func TestParseFilter(t *testing.T) {
|
||||||
require.Len(t, result, 1)
|
require.Len(t, result, 1)
|
||||||
assert.Equal(t, "due_date", result[0].field)
|
assert.Equal(t, "due_date", result[0].field)
|
||||||
in30Days := time.Now().Add(time.Hour * 24 * 30)
|
in30Days := time.Now().Add(time.Hour * 24 * 30)
|
||||||
assert.Equal(t, in30Days.Unix(), result[0].value.(time.Time).Unix())
|
assert.Equal(t, in30Days.Unix(), result[0].value.(*modules.Time).Unix())
|
||||||
})
|
})
|
||||||
t.Run("date math strings with quotes", func(t *testing.T) {
|
t.Run("date math strings with quotes", func(t *testing.T) {
|
||||||
result, err := getTaskFiltersFromFilterString("due_date < 'now+30d'", "UTC")
|
result, err := getTaskFiltersFromFilterString("due_date < 'now+30d'", "UTC")
|
||||||
|
|
@ -124,7 +125,7 @@ func TestParseFilter(t *testing.T) {
|
||||||
require.Len(t, result, 1)
|
require.Len(t, result, 1)
|
||||||
assert.Equal(t, "due_date", result[0].field)
|
assert.Equal(t, "due_date", result[0].field)
|
||||||
in30Days := time.Now().Add(time.Hour * 24 * 30)
|
in30Days := time.Now().Add(time.Hour * 24 * 30)
|
||||||
assert.Equal(t, in30Days.Unix(), result[0].value.(time.Time).Unix())
|
assert.Equal(t, in30Days.Unix(), result[0].value.(*modules.Time).Unix())
|
||||||
})
|
})
|
||||||
t.Run("string values with single quotes", func(t *testing.T) {
|
t.Run("string values with single quotes", func(t *testing.T) {
|
||||||
result, err := getTaskFiltersFromFilterString("title = 'foo bar'", "UTC")
|
result, err := getTaskFiltersFromFilterString("title = 'foo bar'", "UTC")
|
||||||
|
|
@ -246,7 +247,7 @@ func TestParseFilter(t *testing.T) {
|
||||||
assert.Equal(t, "start_date", result[0].field)
|
assert.Equal(t, "start_date", result[0].field)
|
||||||
assert.Equal(t, taskFilterComparatorEquals, result[0].comparator)
|
assert.Equal(t, taskFilterComparatorEquals, result[0].comparator)
|
||||||
expectedDate, _ := time.Parse("2006-01-02", "2023-06-15")
|
expectedDate, _ := time.Parse("2006-01-02", "2023-06-15")
|
||||||
assert.Equal(t, expectedDate, result[0].value)
|
assert.Equal(t, expectedDate.Unix(), result[0].value.(*modules.Time).Unix())
|
||||||
})
|
})
|
||||||
t.Run("in query with multiple values", func(t *testing.T) {
|
t.Run("in query with multiple values", func(t *testing.T) {
|
||||||
result, err := getTaskFiltersFromFilterString("priority in 1,3,5", "UTC")
|
result, err := getTaskFiltersFromFilterString("priority in 1,3,5", "UTC")
|
||||||
|
|
@ -268,18 +269,18 @@ func TestParseFilter(t *testing.T) {
|
||||||
assert.Equal(t, "done_at", result[0].field)
|
assert.Equal(t, "done_at", result[0].field)
|
||||||
assert.Equal(t, taskFilterComparatorGreater, result[0].comparator)
|
assert.Equal(t, taskFilterComparatorGreater, result[0].comparator)
|
||||||
sevenDaysAgo := time.Now().Add(-7 * 24 * time.Hour)
|
sevenDaysAgo := time.Now().Add(-7 * 24 * time.Hour)
|
||||||
assert.Equal(t, sevenDaysAgo.Unix(), result[0].value.(time.Time).Unix())
|
assert.Equal(t, sevenDaysAgo.Unix(), result[0].value.(*modules.Time).Unix())
|
||||||
})
|
})
|
||||||
t.Run("date filter with 0000-01-01", func(t *testing.T) {
|
t.Run("date filter with 0000-01-01", func(t *testing.T) {
|
||||||
result, err := getTaskFiltersFromFilterString("due_date > 0000-01-01", "UTC")
|
result, err := getTaskFiltersFromFilterString("due_date > 0000-01-01", "UTC")
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, result, 1)
|
require.Len(t, result, 1)
|
||||||
date := result[0].value.(time.Time)
|
date := result[0].value.(*modules.Time)
|
||||||
if db.GetDialect() == builder.MYSQL {
|
if db.GetDialect() == builder.MYSQL {
|
||||||
assert.Equal(t, 1, date.Year())
|
assert.Equal(t, 1, date.Time().Year())
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, 0, date.Year())
|
assert.Equal(t, 0, date.Time().Year())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
|
|
@ -116,8 +117,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Index: 14,
|
Index: 14,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -133,7 +134,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Size: 100,
|
Size: 100,
|
||||||
Created: time.Unix(1570998791, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1570998791, 0).In(loc)),
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -156,13 +157,13 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Size: 100,
|
Size: 100,
|
||||||
Created: time.Unix(1570998791, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1570998791, 0).In(loc)),
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
var task1WithReaction = &Task{}
|
var task1WithReaction = &Task{}
|
||||||
*task1WithReaction = *task1
|
*task1WithReaction = *task1
|
||||||
|
|
@ -186,12 +187,12 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
{
|
{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
TaskID: 2,
|
TaskID: 2,
|
||||||
Reminder: time.Unix(1543626824, 0).In(loc),
|
Reminder: modules.TimeFromTime(time.Unix(1543626824, 0).In(loc)),
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task3 := &Task{
|
task3 := &Task{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
|
|
@ -202,8 +203,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Priority: 100,
|
Priority: 100,
|
||||||
}
|
}
|
||||||
task4 := &Task{
|
task4 := &Task{
|
||||||
|
|
@ -215,8 +216,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
}
|
}
|
||||||
task5 := &Task{
|
task5 := &Task{
|
||||||
|
|
@ -228,9 +229,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
DueDate: time.Unix(1543636724, 0).In(loc),
|
DueDate: modules.TimeFromTime(time.Unix(1543636724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task6 := &Task{
|
task6 := &Task{
|
||||||
ID: 6,
|
ID: 6,
|
||||||
|
|
@ -241,9 +242,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
DueDate: time.Unix(1543616724, 0).In(loc),
|
DueDate: modules.TimeFromTime(time.Unix(1543616724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task7 := &Task{
|
task7 := &Task{
|
||||||
ID: 7,
|
ID: 7,
|
||||||
|
|
@ -254,9 +255,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
StartDate: time.Unix(1544600000, 0).In(loc),
|
StartDate: modules.TimeFromTime(time.Unix(1544600000, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task8 := &Task{
|
task8 := &Task{
|
||||||
ID: 8,
|
ID: 8,
|
||||||
|
|
@ -267,9 +268,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
EndDate: time.Unix(1544700000, 0).In(loc),
|
EndDate: modules.TimeFromTime(time.Unix(1544700000, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task9 := &Task{
|
task9 := &Task{
|
||||||
ID: 9,
|
ID: 9,
|
||||||
|
|
@ -280,10 +281,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
StartDate: time.Unix(1544600000, 0).In(loc),
|
StartDate: modules.TimeFromTime(time.Unix(1544600000, 0).In(loc)),
|
||||||
EndDate: time.Unix(1544700000, 0).In(loc),
|
EndDate: modules.TimeFromTime(time.Unix(1544700000, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task10 := &Task{
|
task10 := &Task{
|
||||||
ID: 10,
|
ID: 10,
|
||||||
|
|
@ -294,8 +295,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task11 := &Task{
|
task11 := &Task{
|
||||||
ID: 11,
|
ID: 11,
|
||||||
|
|
@ -306,8 +307,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task12 := &Task{
|
task12 := &Task{
|
||||||
ID: 12,
|
ID: 12,
|
||||||
|
|
@ -318,8 +319,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task15 := &Task{
|
task15 := &Task{
|
||||||
ID: 15,
|
ID: 15,
|
||||||
|
|
@ -331,8 +332,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ProjectID: 6,
|
ProjectID: 6,
|
||||||
IsFavorite: true,
|
IsFavorite: true,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task16 := &Task{
|
task16 := &Task{
|
||||||
ID: 16,
|
ID: 16,
|
||||||
|
|
@ -343,8 +344,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 7,
|
ProjectID: 7,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task17 := &Task{
|
task17 := &Task{
|
||||||
ID: 17,
|
ID: 17,
|
||||||
|
|
@ -355,8 +356,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 8,
|
ProjectID: 8,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task18 := &Task{
|
task18 := &Task{
|
||||||
ID: 18,
|
ID: 18,
|
||||||
|
|
@ -367,8 +368,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 9,
|
ProjectID: 9,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task19 := &Task{
|
task19 := &Task{
|
||||||
ID: 19,
|
ID: 19,
|
||||||
|
|
@ -379,8 +380,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 10,
|
ProjectID: 10,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task20 := &Task{
|
task20 := &Task{
|
||||||
ID: 20,
|
ID: 20,
|
||||||
|
|
@ -391,8 +392,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 11,
|
ProjectID: 11,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task21 := &Task{
|
task21 := &Task{
|
||||||
ID: 21,
|
ID: 21,
|
||||||
|
|
@ -403,8 +404,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 32, // parent project is shared to user 1 via direct share
|
ProjectID: 32, // parent project is shared to user 1 via direct share
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task22 := &Task{
|
task22 := &Task{
|
||||||
ID: 22,
|
ID: 22,
|
||||||
|
|
@ -415,8 +416,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 33,
|
ProjectID: 33,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task23 := &Task{
|
task23 := &Task{
|
||||||
ID: 23,
|
ID: 23,
|
||||||
|
|
@ -427,8 +428,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 34,
|
ProjectID: 34,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task24 := &Task{
|
task24 := &Task{
|
||||||
ID: 24,
|
ID: 24,
|
||||||
|
|
@ -439,8 +440,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 15, // parent project is shared to user 1 via team
|
ProjectID: 15, // parent project is shared to user 1 via team
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task25 := &Task{
|
task25 := &Task{
|
||||||
ID: 25,
|
ID: 25,
|
||||||
|
|
@ -451,8 +452,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 16,
|
ProjectID: 16,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task26 := &Task{
|
task26 := &Task{
|
||||||
ID: 26,
|
ID: 26,
|
||||||
|
|
@ -463,8 +464,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user6,
|
CreatedBy: user6,
|
||||||
ProjectID: 17,
|
ProjectID: 17,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task27 := &Task{
|
task27 := &Task{
|
||||||
ID: 27,
|
ID: 27,
|
||||||
|
|
@ -477,23 +478,23 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
{
|
{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
TaskID: 27,
|
TaskID: 27,
|
||||||
Reminder: time.Unix(1543626724, 0).In(loc),
|
Reminder: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
TaskID: 27,
|
TaskID: 27,
|
||||||
Reminder: time.Unix(1543626824, 0).In(loc),
|
Reminder: modules.TimeFromTime(time.Unix(1543626824, 0).In(loc)),
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
RelativePeriod: -3600,
|
RelativePeriod: -3600,
|
||||||
RelativeTo: "start_date",
|
RelativeTo: "start_date",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
StartDate: time.Unix(1543616724, 0).In(loc),
|
StartDate: modules.TimeFromTime(time.Unix(1543616724, 0).In(loc)),
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task28 := &Task{
|
task28 := &Task{
|
||||||
ID: 28,
|
ID: 28,
|
||||||
|
|
@ -505,8 +506,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
RepeatAfter: 3600,
|
RepeatAfter: 3600,
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task29 := &Task{
|
task29 := &Task{
|
||||||
ID: 29,
|
ID: 29,
|
||||||
|
|
@ -526,13 +527,13 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
IsFavorite: true,
|
IsFavorite: true,
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task30 := &Task{
|
task30 := &Task{
|
||||||
ID: 30,
|
ID: 30,
|
||||||
|
|
@ -547,8 +548,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
user2,
|
user2,
|
||||||
},
|
},
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task31 := &Task{
|
task31 := &Task{
|
||||||
ID: 31,
|
ID: 31,
|
||||||
|
|
@ -560,8 +561,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task32 := &Task{
|
task32 := &Task{
|
||||||
ID: 32,
|
ID: 32,
|
||||||
|
|
@ -572,8 +573,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task33 := &Task{
|
task33 := &Task{
|
||||||
ID: 33,
|
ID: 33,
|
||||||
|
|
@ -585,8 +586,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
PercentDone: 0.5,
|
PercentDone: 0.5,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task35 := &Task{
|
task35 := &Task{
|
||||||
ID: 35,
|
ID: 35,
|
||||||
|
|
@ -613,8 +614,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
IsFavorite: true,
|
IsFavorite: true,
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
|
|
@ -624,13 +625,13 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
IsFavorite: true,
|
IsFavorite: true,
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
task39 := &Task{
|
task39 := &Task{
|
||||||
ID: 39,
|
ID: 39,
|
||||||
|
|
@ -640,8 +641,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ProjectID: 25,
|
ProjectID: 25,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: modules.TimeFromTime(time.Unix(1543626724, 0).In(loc)),
|
||||||
}
|
}
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
|
|
@ -955,7 +956,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "range with nulls",
|
name: "range withnulls",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterIncludeNulls: true,
|
FilterIncludeNulls: true,
|
||||||
Filter: "start_date > '2018-12-11T03:46:40+00:00' || end_date < '2018-12-13T11:20:01+00:00'",
|
Filter: "start_date > '2018-12-11T03:46:40+00:00' || end_date < '2018-12-13T11:20:01+00:00'",
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
|
@ -39,8 +38,8 @@ type TaskComment struct {
|
||||||
|
|
||||||
Reactions ReactionMap `xorm:"-" json:"reactions"`
|
Reactions ReactionMap `xorm:"-" json:"reactions"`
|
||||||
|
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created *modules.Time `xorm:"created" json:"created"`
|
||||||
Updated time.Time `xorm:"updated" json:"updated"`
|
Updated *modules.Time `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
@ -67,8 +66,8 @@ func (tc *TaskComment) TableName() string {
|
||||||
func (tc *TaskComment) Create(s *xorm.Session, a web.Auth) (err error) {
|
func (tc *TaskComment) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
tc.ID = 0
|
tc.ID = 0
|
||||||
tc.Created = time.Time{}
|
tc.Created = &modules.Time{}
|
||||||
tc.Updated = time.Time{}
|
tc.Updated = &modules.Time{}
|
||||||
|
|
||||||
return tc.CreateWithTimestamps(s, a)
|
return tc.CreateWithTimestamps(s, a)
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +85,7 @@ func (tc *TaskComment) CreateWithTimestamps(s *xorm.Session, a web.Auth) (err er
|
||||||
}
|
}
|
||||||
tc.AuthorID = tc.Author.ID
|
tc.AuthorID = tc.Author.ID
|
||||||
|
|
||||||
if !tc.Created.IsZero() && !tc.Updated.IsZero() {
|
if !tc.Created.Time().IsZero() && !tc.Updated.Time().IsZero() {
|
||||||
_, err = s.NoAutoTime().Insert(tc)
|
_, err = s.NoAutoTime().Insert(tc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ func getUndoneOverdueTasks(s *xorm.Session, now time.Time) (usersWithTasks map[i
|
||||||
overdueMailTime := time.Date(now.Year(), now.Month(), now.Day(), tm.Hour(), tm.Minute(), 0, 0, tz)
|
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(nextMinute)
|
wasTimeForReminder := overdueMailTime.Before(nextMinute)
|
||||||
taskIsOverdueInUserTimezone := overdueMailTime.After(t.Task.DueDate.In(tz))
|
taskIsOverdueInUserTimezone := overdueMailTime.After(t.Task.DueDate.Time().In(tz))
|
||||||
if isTimeForReminder && wasTimeForReminder && taskIsOverdueInUserTimezone {
|
if isTimeForReminder && wasTimeForReminder && taskIsOverdueInUserTimezone {
|
||||||
_, exists := uts[t.User.ID]
|
_, exists := uts[t.User.ID]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
@ -93,7 +92,7 @@ type TaskRelation struct {
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
||||||
|
|
||||||
// A timestamp when this label was created. You cannot change this value.
|
// A timestamp when this label was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
@ -50,8 +51,8 @@ type TaskReminder struct {
|
||||||
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
|
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
|
||||||
TaskID int64 `xorm:"bigint not null INDEX" json:"-"`
|
TaskID int64 `xorm:"bigint not null INDEX" json:"-"`
|
||||||
// The absolute time when the user wants to be reminded of the task.
|
// The absolute time when the user wants to be reminded of the task.
|
||||||
Reminder time.Time `xorm:"DATETIME not null INDEX 'reminder'" json:"reminder"`
|
Reminder *modules.Time `xorm:"DATETIME not null INDEX 'reminder'" json:"reminder"`
|
||||||
Created time.Time `xorm:"created not null" json:"-"`
|
Created *modules.Time `xorm:"created not null" json:"-"`
|
||||||
// A period in seconds relative to another date argument. Negative values mean the reminder triggers before the date. Default: 0, tiggers when RelativeTo is due.
|
// A period in seconds relative to another date argument. Negative values mean the reminder triggers before the date. Default: 0, tiggers when RelativeTo is due.
|
||||||
RelativePeriod int64 `xorm:"bigint null" json:"relative_period"`
|
RelativePeriod int64 `xorm:"bigint null" json:"relative_period"`
|
||||||
// The name of the date field to which the relative period refers to.
|
// The name of the date field to which the relative period refers to.
|
||||||
|
|
@ -243,7 +244,7 @@ func getTasksWithRemindersDueAndTheirUsers(s *xorm.Session, now time.Time) (remi
|
||||||
tzs[u.User.Timezone] = tz
|
tzs[u.User.Timezone] = tz
|
||||||
}
|
}
|
||||||
|
|
||||||
actualReminder := r.Reminder.In(tz)
|
actualReminder := r.Reminder.Time().In(tz)
|
||||||
if (actualReminder.After(now) && actualReminder.Before(now.Add(time.Minute))) || actualReminder.Equal(now) {
|
if (actualReminder.After(now) && actualReminder.Before(now.Add(time.Minute))) || actualReminder.Equal(now) {
|
||||||
reminderNotifications = append(reminderNotifications, &ReminderDueNotification{
|
reminderNotifications = append(reminderNotifications, &ReminderDueNotification{
|
||||||
User: u.User,
|
User: u.User,
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,16 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/web"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
|
||||||
|
|
||||||
"github.com/typesense/typesense-go/v2/typesense/api"
|
"github.com/typesense/typesense-go/v2/typesense/api"
|
||||||
"github.com/typesense/typesense-go/v2/typesense/api/pointer"
|
"github.com/typesense/typesense-go/v2/typesense/api/pointer"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
|
|
@ -464,6 +464,8 @@ func convertFilterValues(value interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
return "false"
|
return "false"
|
||||||
|
case *modules.Time:
|
||||||
|
return strconv.FormatInt(v.Time().Unix(), 10)
|
||||||
case time.Time:
|
case time.Time:
|
||||||
return strconv.FormatInt(v.Unix(), 10)
|
return strconv.FormatInt(v.Unix(), 10)
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
@ -60,9 +61,9 @@ type Task struct {
|
||||||
// Whether a task is done or not.
|
// Whether a task is done or not.
|
||||||
Done bool `xorm:"INDEX null" json:"done"`
|
Done bool `xorm:"INDEX null" json:"done"`
|
||||||
// The time when a task was marked as done.
|
// The time when a task was marked as done.
|
||||||
DoneAt time.Time `xorm:"INDEX null 'done_at'" json:"done_at"`
|
DoneAt *modules.Time `xorm:"INDEX null 'done_at'" json:"done_at"`
|
||||||
// The time when the task is due.
|
// The time when the task is due.
|
||||||
DueDate time.Time `xorm:"DATETIME INDEX null 'due_date'" json:"due_date"`
|
DueDate *modules.Time `xorm:"DATETIME INDEX null 'due_date'" json:"due_date"`
|
||||||
// An array of reminders that are associated with this task.
|
// An array of reminders that are associated with this task.
|
||||||
Reminders []*TaskReminder `xorm:"-" json:"reminders"`
|
Reminders []*TaskReminder `xorm:"-" json:"reminders"`
|
||||||
// The project this task belongs to.
|
// The project this task belongs to.
|
||||||
|
|
@ -74,9 +75,9 @@ type Task struct {
|
||||||
// The task priority. Can be anything you want, it is possible to sort by this later.
|
// The task priority. Can be anything you want, it is possible to sort by this later.
|
||||||
Priority int64 `xorm:"bigint null" json:"priority"`
|
Priority int64 `xorm:"bigint null" json:"priority"`
|
||||||
// When this task starts.
|
// When this task starts.
|
||||||
StartDate time.Time `xorm:"DATETIME INDEX null 'start_date'" json:"start_date" query:"-"`
|
StartDate *modules.Time `xorm:"DATETIME INDEX null 'start_date'" json:"start_date" query:"-"`
|
||||||
// When this task ends.
|
// When this task ends.
|
||||||
EndDate time.Time `xorm:"DATETIME INDEX null 'end_date'" json:"end_date" query:"-"`
|
EndDate *modules.Time `xorm:"DATETIME INDEX null 'end_date'" json:"end_date" query:"-"`
|
||||||
// An array of users who are assigned to this task
|
// An array of users who are assigned to this task
|
||||||
Assignees []*user.User `xorm:"-" json:"assignees"`
|
Assignees []*user.User `xorm:"-" json:"assignees"`
|
||||||
// An array of labels which are associated with this task. This property is read-only, you must use the separate endpoint to add labels to a task.
|
// An array of labels which are associated with this task. This property is read-only, you must use the separate endpoint to add labels to a task.
|
||||||
|
|
@ -111,9 +112,9 @@ type Task struct {
|
||||||
Subscription *Subscription `xorm:"-" json:"subscription,omitempty"`
|
Subscription *Subscription `xorm:"-" json:"subscription,omitempty"`
|
||||||
|
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this task was last updated. You cannot change this value.
|
// A timestamp when this task was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
// The bucket id. Will only be populated when the task is accessed via a view with buckets.
|
// The bucket id. Will only be populated when the task is accessed via a view with buckets.
|
||||||
// Can be used to move a task between buckets. In that case, the new bucket must be in the same view as the old one.
|
// Can be used to move a task between buckets. In that case, the new bucket must be in the same view as the old one.
|
||||||
|
|
@ -1252,20 +1253,20 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
ot.Description = ""
|
ot.Description = ""
|
||||||
}
|
}
|
||||||
// Due date
|
// Due date
|
||||||
if t.DueDate.IsZero() {
|
if t.DueDate != nil && t.DueDate.Time().IsZero() {
|
||||||
ot.DueDate = time.Time{}
|
ot.DueDate = &modules.Time{}
|
||||||
}
|
}
|
||||||
// Repeat after
|
// Repeat after
|
||||||
if t.RepeatAfter == 0 {
|
if t.RepeatAfter == 0 {
|
||||||
ot.RepeatAfter = 0
|
ot.RepeatAfter = 0
|
||||||
}
|
}
|
||||||
// Start date
|
// Start date
|
||||||
if t.StartDate.IsZero() {
|
if t.StartDate != nil && t.StartDate.Time().IsZero() {
|
||||||
ot.StartDate = time.Time{}
|
ot.StartDate = &modules.Time{}
|
||||||
}
|
}
|
||||||
// End date
|
// End date
|
||||||
if t.EndDate.IsZero() {
|
if t.EndDate != nil && t.EndDate.Time().IsZero() {
|
||||||
ot.EndDate = time.Time{}
|
ot.EndDate = &modules.Time{}
|
||||||
}
|
}
|
||||||
// Color
|
// Color
|
||||||
if t.HexColor == "" {
|
if t.HexColor == "" {
|
||||||
|
|
@ -1317,8 +1318,9 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
return updateProjectLastUpdated(s, &Project{ID: t.ProjectID})
|
return updateProjectLastUpdated(s, &Project{ID: t.ProjectID})
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOneMonthToDate(d time.Time) time.Time {
|
func addOneMonthToDate(dd *modules.Time) *modules.Time {
|
||||||
return time.Date(d.Year(), d.Month()+1, d.Day(), d.Hour(), d.Minute(), d.Second(), d.Nanosecond(), config.GetTimeZone())
|
d := dd.Time()
|
||||||
|
return modules.TimeFromTime(time.Date(d.Year(), d.Month()+1, d.Day(), d.Hour(), d.Minute(), d.Second(), d.Nanosecond(), config.GetTimeZone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTaskDatesDefault(oldTask, newTask *Task) {
|
func setTaskDatesDefault(oldTask, newTask *Task) {
|
||||||
|
|
@ -1327,12 +1329,12 @@ func setTaskDatesDefault(oldTask, newTask *Task) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current time in an extra variable to base all calculations on the same time
|
// Current time in an extra variable to base all calculations on the same time
|
||||||
now := time.Now()
|
now := modules.TimeFromTime(time.Now())
|
||||||
|
|
||||||
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
||||||
|
|
||||||
// assuming we'll merge the new task over the old task
|
// assuming we'll merge the new task over the old task
|
||||||
if !oldTask.DueDate.IsZero() {
|
if !oldTask.DueDate.Time().IsZero() {
|
||||||
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
||||||
// but not the repeating interval
|
// but not the repeating interval
|
||||||
newTask.DueDate = oldTask.DueDate.Add(repeatDuration)
|
newTask.DueDate = oldTask.DueDate.Add(repeatDuration)
|
||||||
|
|
@ -1407,7 +1409,7 @@ func setTaskDatesFromCurrentDateRepeat(oldTask, newTask *Task) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current time in an extra variable to base all calculations on the same time
|
// Current time in an extra variable to base all calculations on the same time
|
||||||
now := time.Now()
|
now := modules.TimeFromTime(time.Now())
|
||||||
|
|
||||||
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
||||||
|
|
||||||
|
|
@ -1485,12 +1487,12 @@ func updateDone(oldTask *Task, newTask *Task) {
|
||||||
setTaskDatesDefault(oldTask, newTask)
|
setTaskDatesDefault(oldTask, newTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTask.DoneAt = time.Now()
|
newTask.DoneAt = modules.TimeFromTime(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
// When unmarking a task as done, reset the timestamp
|
// When unmarking a task as done, reset the timestamp
|
||||||
if oldTask.Done && !newTask.Done {
|
if oldTask.Done && !newTask.Done {
|
||||||
newTask.DoneAt = time.Time{}
|
newTask.DoneAt = &modules.Time{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1499,7 +1501,7 @@ func updateRelativeReminderDates(task *Task) (err error) {
|
||||||
for _, reminder := range task.Reminders {
|
for _, reminder := range task.Reminders {
|
||||||
relativeDuration := time.Duration(reminder.RelativePeriod) * time.Second
|
relativeDuration := time.Duration(reminder.RelativePeriod) * time.Second
|
||||||
if reminder.RelativeTo != "" {
|
if reminder.RelativeTo != "" {
|
||||||
reminder.Reminder = time.Time{}
|
reminder.Reminder = &modules.Time{}
|
||||||
}
|
}
|
||||||
switch reminder.RelativeTo {
|
switch reminder.RelativeTo {
|
||||||
case ReminderRelationDueDate:
|
case ReminderRelationDueDate:
|
||||||
|
|
@ -1547,7 +1549,7 @@ func (t *Task) updateReminders(s *xorm.Session, task *Task) (err error) {
|
||||||
// Resolve duplicates and sort them
|
// Resolve duplicates and sort them
|
||||||
reminderMap := make(map[int64]*TaskReminder, len(task.Reminders))
|
reminderMap := make(map[int64]*TaskReminder, len(task.Reminders))
|
||||||
for _, reminder := range task.Reminders {
|
for _, reminder := range task.Reminders {
|
||||||
reminderMap[reminder.Reminder.UTC().Unix()] = reminder
|
reminderMap[reminder.Reminder.Unix()] = reminder
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Reminders = make([]*TaskReminder, 0, len(reminderMap))
|
t.Reminders = make([]*TaskReminder, 0, len(reminderMap))
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -81,9 +82,9 @@ func TestTask_Create(t *testing.T) {
|
||||||
Title: "Lorem",
|
Title: "Lorem",
|
||||||
Description: "Lorem Ipsum Dolor",
|
Description: "Lorem Ipsum Dolor",
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local),
|
DueDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local)),
|
||||||
StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local),
|
StartDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local)),
|
||||||
EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local),
|
EndDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local)),
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: "due_date",
|
RelativeTo: "due_date",
|
||||||
|
|
@ -98,7 +99,7 @@ func TestTask_Create(t *testing.T) {
|
||||||
RelativePeriod: -1,
|
RelativePeriod: -1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local),
|
Reminder: modules.TimeFromTime(time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local)),
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
err := task.Create(s, usr)
|
err := task.Create(s, usr)
|
||||||
|
|
@ -359,9 +360,9 @@ func TestTask_Update(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Title: "test",
|
Title: "test",
|
||||||
DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local),
|
DueDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local)),
|
||||||
StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local),
|
StartDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local)),
|
||||||
EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local),
|
EndDate: modules.TimeFromTime(time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local)),
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: "due_date",
|
RelativeTo: "due_date",
|
||||||
|
|
@ -376,7 +377,7 @@ func TestTask_Update(t *testing.T) {
|
||||||
RelativePeriod: -1,
|
RelativePeriod: -1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local),
|
Reminder: modules.TimeFromTime(time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local)),
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
err := task.Update(s, u)
|
err := task.Update(s, u)
|
||||||
|
|
@ -403,10 +404,10 @@ func TestTask_Update(t *testing.T) {
|
||||||
Title: "test",
|
Title: "test",
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1674745156, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1674745156, 0)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1674745156, 223),
|
Reminder: modules.TimeFromTime(time.Unix(1674745156, 223)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
|
|
@ -426,7 +427,7 @@ func TestTask_Update(t *testing.T) {
|
||||||
taskBefore := &Task{
|
taskBefore := &Task{
|
||||||
Title: "test",
|
Title: "test",
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
StartDate: time.Date(2022, time.March, 8, 8, 5, 20, 0, time.Local),
|
StartDate: modules.TimeFromTime(time.Date(2022, time.March, 8, 8, 5, 20, 0, time.Local)),
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: "start_date",
|
RelativeTo: "start_date",
|
||||||
|
|
@ -441,7 +442,7 @@ func TestTask_Update(t *testing.T) {
|
||||||
|
|
||||||
// when start_date is modified
|
// when start_date is modified
|
||||||
task := taskBefore
|
task := taskBefore
|
||||||
task.StartDate = time.Date(2023, time.March, 8, 8, 5, 0, 0, time.Local)
|
task.StartDate = modules.TimeFromTime(time.Date(2023, time.March, 8, 8, 5, 0, 0, time.Local))
|
||||||
err = task.Update(s, u)
|
err = task.Update(s, u)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
@ -481,7 +482,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{Done: false}
|
oldTask := &Task{Done: false}
|
||||||
newTask := &Task{Done: true}
|
newTask := &Task{Done: true}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.NotEqual(t, time.Time{}, newTask.DoneAt)
|
assert.NotEqual(t, &modules.Time{}, newTask.DoneAt)
|
||||||
})
|
})
|
||||||
t.Run("unmarking a task as done", func(t *testing.T) {
|
t.Run("unmarking a task as done", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
|
|
@ -491,10 +492,10 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{Done: true}
|
oldTask := &Task{Done: true}
|
||||||
newTask := &Task{Done: false}
|
newTask := &Task{Done: false}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.Equal(t, time.Time{}, newTask.DoneAt)
|
assert.Equal(t, &modules.Time{}, newTask.DoneAt)
|
||||||
})
|
})
|
||||||
t.Run("no interval set, default repeat mode", func(t *testing.T) {
|
t.Run("no interval set, default repeat mode", func(t *testing.T) {
|
||||||
dueDate := time.Unix(1550000000, 0)
|
dueDate := modules.TimeFromTime(time.Unix(1550000000, 0))
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 0,
|
RepeatAfter: 0,
|
||||||
|
|
@ -515,7 +516,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: time.Unix(1550000000, 0),
|
DueDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -534,11 +535,11 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: time.Time{},
|
DueDate: &modules.Time{},
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: time.Unix(1543626724, 0),
|
DueDate: modules.TimeFromTime(time.Unix(1543626724, 0)),
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.Equal(t, time.Unix(1543626724, 0), newTask.DueDate)
|
assert.Equal(t, time.Unix(1543626724, 0), newTask.DueDate)
|
||||||
|
|
@ -550,10 +551,10 @@ func TestUpdateDone(t *testing.T) {
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1550000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1555000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1555000000, 0)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +581,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
StartDate: time.Unix(1550000000, 0),
|
StartDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -599,7 +600,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
EndDate: time.Unix(1550000000, 0),
|
EndDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -618,7 +619,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: time.Now().Add(time.Hour),
|
DueDate: modules.TimeFromTime(time.Now().Add(time.Hour)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -634,14 +635,14 @@ func TestUpdateDone(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
DueDate: time.Unix(1550000000, 0),
|
DueDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// Only comparing unix timestamps because Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.DueDate.Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.DueDate.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
|
|
@ -652,10 +653,10 @@ func TestUpdateDone(t *testing.T) {
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1550000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1555000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1555000000, 0)),
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
|
|
@ -666,7 +667,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
diff := oldTask.Reminders[1].Reminder.Sub(oldTask.Reminders[0].Reminder)
|
diff := oldTask.Reminders[1].Reminder.Sub(oldTask.Reminders[0].Reminder)
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, 2)
|
assert.Len(t, newTask.Reminders, 2)
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// Only comparing unix timestamps because Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[0].Reminder.Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[0].Reminder.Unix())
|
||||||
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[1].Reminder.Unix())
|
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[1].Reminder.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
|
|
@ -676,14 +677,14 @@ func TestUpdateDone(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
StartDate: time.Unix(1550000000, 0),
|
StartDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// Only comparing unix timestamps because Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
|
|
@ -692,14 +693,14 @@ func TestUpdateDone(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
EndDate: time.Unix(1560000000, 0),
|
EndDate: modules.TimeFromTime(time.Unix(1560000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// Only comparing unix timestamps because Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
|
|
@ -708,8 +709,8 @@ func TestUpdateDone(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
StartDate: time.Unix(1550000000, 0),
|
StartDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
EndDate: time.Unix(1560000000, 0),
|
EndDate: modules.TimeFromTime(time.Unix(1560000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -718,7 +719,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
|
|
||||||
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||||
|
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// Only comparing unix timestamps because Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
||||||
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
|
|
@ -729,7 +730,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
DueDate: time.Unix(1550000000, 0),
|
DueDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -748,16 +749,16 @@ func TestUpdateDone(t *testing.T) {
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
Reminders: []*TaskReminder{
|
Reminders: []*TaskReminder{
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1550000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Reminder: time.Unix(1555000000, 0),
|
Reminder: modules.TimeFromTime(time.Unix(1555000000, 0)),
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
oldReminders := make([]time.Time, len(oldTask.Reminders))
|
oldReminders := make([]*modules.Time, len(oldTask.Reminders))
|
||||||
for i, r := range newTask.Reminders {
|
for i, r := range newTask.Reminders {
|
||||||
oldReminders[i] = r.Reminder
|
oldReminders[i] = r.Reminder
|
||||||
}
|
}
|
||||||
|
|
@ -775,7 +776,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
StartDate: time.Unix(1550000000, 0),
|
StartDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -792,7 +793,7 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
EndDate: time.Unix(1560000000, 0),
|
EndDate: modules.TimeFromTime(time.Unix(1560000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
@ -809,8 +810,8 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
StartDate: time.Unix(1550000000, 0),
|
StartDate: modules.TimeFromTime(time.Unix(1550000000, 0)),
|
||||||
EndDate: time.Unix(1560000000, 0),
|
EndDate: modules.TimeFromTime(time.Unix(1560000000, 0)),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/events"
|
"code.vikunja.io/api/pkg/events"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
|
@ -50,9 +49,9 @@ type Team struct {
|
||||||
Members []*TeamUser `xorm:"-" json:"members"`
|
Members []*TeamUser `xorm:"-" json:"members"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created *modules.Time `xorm:"created" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated" json:"updated"`
|
Updated *modules.Time `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
// Defines wether the team should be publicly discoverable when sharing a project
|
// Defines wether the team should be publicly discoverable when sharing a project
|
||||||
IsPublic bool `xorm:"not null default false" json:"is_public"`
|
IsPublic bool `xorm:"not null default false" json:"is_public"`
|
||||||
|
|
@ -83,7 +82,7 @@ type TeamMember struct {
|
||||||
Admin bool `xorm:"null" json:"admin"`
|
Admin bool `xorm:"null" json:"admin"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
@ -34,8 +33,8 @@ func TestTeam_CanDoSomething(t *testing.T) {
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Members []*TeamUser
|
Members []*TeamUser
|
||||||
Created time.Time
|
Created *modules.Time
|
||||||
Updated time.Time
|
Updated *modules.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -366,10 +367,10 @@ func indexDummyTask() (err error) {
|
||||||
{
|
{
|
||||||
ID: -10,
|
ID: -10,
|
||||||
TaskID: -100,
|
TaskID: -100,
|
||||||
Reminder: time.Now(),
|
Reminder: modules.TimeFromTime(time.Now()),
|
||||||
RelativePeriod: 10,
|
RelativePeriod: 10,
|
||||||
RelativeTo: ReminderRelationDueDate,
|
RelativeTo: ReminderRelationDueDate,
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Assignees: []*user.User{
|
Assignees: []*user.User{
|
||||||
|
|
@ -378,8 +379,8 @@ func indexDummyTask() (err error) {
|
||||||
Username: "dummy",
|
Username: "dummy",
|
||||||
Name: "dummy",
|
Name: "dummy",
|
||||||
Email: "dummy@vikunja",
|
Email: "dummy@vikunja",
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Labels: []*Label{
|
Labels: []*Label{
|
||||||
|
|
@ -388,30 +389,30 @@ func indexDummyTask() (err error) {
|
||||||
Title: "dummylabel",
|
Title: "dummylabel",
|
||||||
Description: "Lorem Ipsum Dummy",
|
Description: "Lorem Ipsum Dummy",
|
||||||
HexColor: "000000",
|
HexColor: "000000",
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attachments: []*TaskAttachment{
|
Attachments: []*TaskAttachment{
|
||||||
{
|
{
|
||||||
ID: -120,
|
ID: -120,
|
||||||
TaskID: -100,
|
TaskID: -100,
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Comments: []*TaskComment{
|
Comments: []*TaskComment{
|
||||||
{
|
{
|
||||||
ID: -220,
|
ID: -220,
|
||||||
Comment: "Lorem Ipsum Dummy",
|
Comment: "Lorem Ipsum Dummy",
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
Author: &user.User{
|
Author: &user.User{
|
||||||
ID: -100,
|
ID: -100,
|
||||||
Username: "dummy",
|
Username: "dummy",
|
||||||
Name: "dummy",
|
Name: "dummy",
|
||||||
Email: "dummy@vikunja",
|
Email: "dummy@vikunja",
|
||||||
Created: time.Now(),
|
Created: modules.TimeFromTime(time.Now()),
|
||||||
Updated: time.Now(),
|
Updated: modules.TimeFromTime(time.Now()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -476,22 +477,22 @@ func convertTaskToTypesenseTask(task *Task, positions []*TaskPositionWithView, b
|
||||||
Title: task.Title,
|
Title: task.Title,
|
||||||
Description: task.Description,
|
Description: task.Description,
|
||||||
Done: task.Done,
|
Done: task.Done,
|
||||||
DoneAt: pointer.Int64(task.DoneAt.UTC().Unix()),
|
DoneAt: pointer.Int64(task.DoneAt.Unix()),
|
||||||
DueDate: pointer.Int64(task.DueDate.UTC().Unix()),
|
DueDate: pointer.Int64(task.DueDate.Unix()),
|
||||||
ProjectID: task.ProjectID,
|
ProjectID: task.ProjectID,
|
||||||
RepeatAfter: task.RepeatAfter,
|
RepeatAfter: task.RepeatAfter,
|
||||||
RepeatMode: int(task.RepeatMode),
|
RepeatMode: int(task.RepeatMode),
|
||||||
Priority: task.Priority,
|
Priority: task.Priority,
|
||||||
StartDate: pointer.Int64(task.StartDate.UTC().Unix()),
|
StartDate: pointer.Int64(task.StartDate.Unix()),
|
||||||
EndDate: pointer.Int64(task.EndDate.UTC().Unix()),
|
EndDate: pointer.Int64(task.EndDate.Unix()),
|
||||||
HexColor: task.HexColor,
|
HexColor: task.HexColor,
|
||||||
PercentDone: task.PercentDone,
|
PercentDone: task.PercentDone,
|
||||||
Identifier: task.Identifier,
|
Identifier: task.Identifier,
|
||||||
Index: task.Index,
|
Index: task.Index,
|
||||||
UID: task.UID,
|
UID: task.UID,
|
||||||
CoverImageAttachmentID: task.CoverImageAttachmentID,
|
CoverImageAttachmentID: task.CoverImageAttachmentID,
|
||||||
Created: task.Created.UTC().Unix(),
|
Created: task.Created.Unix(),
|
||||||
Updated: task.Updated.UTC().Unix(),
|
Updated: task.Updated.Unix(),
|
||||||
CreatedByID: task.CreatedByID,
|
CreatedByID: task.CreatedByID,
|
||||||
Reminders: task.Reminders,
|
Reminders: task.Reminders,
|
||||||
Assignees: task.Assignees,
|
Assignees: task.Assignees,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/cron"
|
"code.vikunja.io/api/pkg/cron"
|
||||||
|
|
@ -56,7 +57,7 @@ func deleteUsers() {
|
||||||
|
|
||||||
log.Debugf("Found %d users scheduled for deletion", len(users))
|
log.Debugf("Found %d users scheduled for deletion", len(users))
|
||||||
|
|
||||||
now := time.Now()
|
now := modules.TimeFromTime(time.Now())
|
||||||
|
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
if !u.DeletionScheduledAt.Before(now) {
|
if !u.DeletionScheduledAt.Before(now) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
|
@ -61,9 +62,9 @@ type Webhook struct {
|
||||||
CreatedByID int64 `xorm:"bigint not null" json:"-"`
|
CreatedByID int64 `xorm:"bigint not null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this webhook target was created. You cannot change this value.
|
// A timestamp when this webhook target was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this webhook target was last updated. You cannot change this value.
|
// A timestamp when this webhook target was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package microsofttodo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -54,8 +55,8 @@ type task struct {
|
||||||
IsReminderOn bool `json:"isReminderOn"`
|
IsReminderOn bool `json:"isReminderOn"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
CreatedDateTime time.Time `json:"createdDateTime"`
|
CreatedDateTime *modules.Time `json:"createdDateTime"`
|
||||||
LastModifiedDateTime time.Time `json:"lastModifiedDateTime"`
|
LastModifiedDateTime *modules.Time `json:"lastModifiedDateTime"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Body *body `json:"body"`
|
Body *body `json:"body"`
|
||||||
DueDateTime *dateTimeTimeZone `json:"dueDateTime"`
|
DueDateTime *dateTimeTimeZone `json:"dueDateTime"`
|
||||||
|
|
@ -113,13 +114,14 @@ type projectsResponse struct {
|
||||||
Value []*project `json:"value"`
|
Value []*project `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dtt *dateTimeTimeZone) toTime() (t time.Time, err error) {
|
func (dtt *dateTimeTimeZone) toTime() (t *modules.Time, err error) {
|
||||||
loc, err := time.LoadLocation(dtt.TimeZone)
|
loc, err := time.LoadLocation(dtt.TimeZone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.ParseInLocation(time.RFC3339Nano, dtt.DateTime+"Z", loc)
|
tt, err := time.ParseInLocation(time.RFC3339Nano, dtt.DateTime+"Z", loc)
|
||||||
|
return modules.TimeFromTime(tt), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthURL returns the url users need to authenticate against
|
// AuthURL returns the url users need to authenticate against
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"github.com/d4l3k/messagediff"
|
"github.com/d4l3k/messagediff"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -34,8 +35,9 @@ func TestConverting(t *testing.T) {
|
||||||
TimeZone: "UTC",
|
TimeZone: "UTC",
|
||||||
}
|
}
|
||||||
|
|
||||||
testtimeTime, err := time.Parse(time.RFC3339Nano, "2020-12-18T03:00:00.4770000Z")
|
testTime, err := time.Parse(time.RFC3339Nano, "2020-12-18T03:00:00.4770000Z")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
testtimeTime := modules.TimeFromTime(testTime)
|
||||||
|
|
||||||
microsoftTodoData := []*project{
|
microsoftTodoData := []*project{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/modules/migration"
|
"code.vikunja.io/api/pkg/modules/migration"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
@ -62,16 +63,20 @@ type tickTickTask struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type tickTickTime struct {
|
type tickTickTime struct {
|
||||||
time.Time
|
*modules.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (date *tickTickTime) UnmarshalCSV(csv string) (err error) {
|
func (date *tickTickTime) UnmarshalCSV(csv string) (err error) {
|
||||||
date.Time = time.Time{}
|
date.Time = &modules.Time{}
|
||||||
if csv == "" {
|
if csv == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
date.Time, err = time.Parse(timeISO, csv)
|
tt, err := time.Parse(timeISO, csv)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
date.Time = modules.TimeFromTime(tt)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWithTasksAndBuckets) {
|
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWithTasksAndBuckets) {
|
||||||
|
|
@ -120,7 +125,7 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWi
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.DueDate.IsZero() && t.Reminder > 0 {
|
if (t.DueDate.Time != nil && !t.DueDate.IsZero()) && t.Reminder > 0 {
|
||||||
task.Task.Reminders = []*models.TaskReminder{
|
task.Task.Reminders = []*models.TaskReminder{
|
||||||
{
|
{
|
||||||
RelativeTo: models.ReminderRelationDueDate,
|
RelativeTo: models.ReminderRelationDueDate,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package ticktick
|
package ticktick
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -28,13 +29,13 @@ import (
|
||||||
func TestConvertTicktickTasksToVikunja(t *testing.T) {
|
func TestConvertTicktickTasksToVikunja(t *testing.T) {
|
||||||
t1, err := time.Parse(time.RFC3339Nano, "2022-11-18T03:00:00.4770000Z")
|
t1, err := time.Parse(time.RFC3339Nano, "2022-11-18T03:00:00.4770000Z")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
time1 := tickTickTime{Time: t1}
|
time1 := tickTickTime{Time: modules.TimeFromTime(t1)}
|
||||||
t2, err := time.Parse(time.RFC3339Nano, "2022-12-18T03:00:00.4770000Z")
|
t2, err := time.Parse(time.RFC3339Nano, "2022-12-18T03:00:00.4770000Z")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
time2 := tickTickTime{Time: t2}
|
time2 := tickTickTime{Time: modules.TimeFromTime(t2)}
|
||||||
t3, err := time.Parse(time.RFC3339Nano, "2022-12-10T03:00:00.4770000Z")
|
t3, err := time.Parse(time.RFC3339Nano, "2022-12-10T03:00:00.4770000Z")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
time3 := tickTickTime{Time: t3}
|
time3 := tickTickTime{Time: modules.TimeFromTime(t3)}
|
||||||
duration, err := time.ParseDuration("24h")
|
duration, err := time.ParseDuration("24h")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package todoist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -77,24 +78,24 @@ type dueDate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type item struct {
|
type item struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Priority int64 `json:"priority"`
|
Priority int64 `json:"priority"`
|
||||||
Due *dueDate `json:"due"`
|
Due *dueDate `json:"due"`
|
||||||
ParentID string `json:"parent_id"`
|
ParentID string `json:"parent_id"`
|
||||||
ChildOrder int64 `json:"child_order"`
|
ChildOrder int64 `json:"child_order"`
|
||||||
SectionID string `json:"section_id"`
|
SectionID string `json:"section_id"`
|
||||||
Children interface{} `json:"children"`
|
Children interface{} `json:"children"`
|
||||||
Labels []string `json:"labels"`
|
Labels []string `json:"labels"`
|
||||||
AddedByUID string `json:"added_by_uid"`
|
AddedByUID string `json:"added_by_uid"`
|
||||||
AssignedByUID string `json:"assigned_by_uid"`
|
AssignedByUID string `json:"assigned_by_uid"`
|
||||||
ResponsibleUID string `json:"responsible_uid"`
|
ResponsibleUID string `json:"responsible_uid"`
|
||||||
Checked bool `json:"checked"`
|
Checked bool `json:"checked"`
|
||||||
DateAdded time.Time `json:"added_at"`
|
DateAdded *modules.Time `json:"added_at"`
|
||||||
HasMoreNotes bool `json:"has_more_notes"`
|
HasMoreNotes bool `json:"has_more_notes"`
|
||||||
DateCompleted time.Time `json:"completed_at"`
|
DateCompleted *modules.Time `json:"completed_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type itemWrapper struct {
|
type itemWrapper struct {
|
||||||
|
|
@ -102,11 +103,11 @@ type itemWrapper struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type doneItem struct {
|
type doneItem struct {
|
||||||
CompletedDate time.Time `json:"completed_at"`
|
CompletedDate *modules.Time `json:"completed_at"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
TaskID string `json:"task_id"`
|
TaskID string `json:"task_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type doneItemSync struct {
|
type doneItemSync struct {
|
||||||
|
|
@ -128,14 +129,14 @@ type note struct {
|
||||||
ItemID string `json:"item_id"`
|
ItemID string `json:"item_id"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
FileAttachment *fileAttachment `json:"file_attachment"`
|
FileAttachment *fileAttachment `json:"file_attachment"`
|
||||||
Posted time.Time `json:"posted_at"`
|
Posted *modules.Time `json:"posted_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type projectNote struct {
|
type projectNote struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
FileAttachment *fileAttachment `json:"file_attachment"`
|
FileAttachment *fileAttachment `json:"file_attachment"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Posted time.Time `json:"posted"`
|
Posted *modules.Time `json:"posted"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,12 +149,12 @@ type reminder struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type section struct {
|
type section struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
DateAdded time.Time `json:"added_at"`
|
DateAdded *modules.Time `json:"added_at"`
|
||||||
IsDeleted bool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ProjectID string `json:"project_id"`
|
ProjectID string `json:"project_id"`
|
||||||
SectionOrder int64 `json:"section_order"`
|
SectionOrder int64 `json:"section_order"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type sync struct {
|
type sync struct {
|
||||||
|
|
@ -224,25 +225,26 @@ func (m *Migration) AuthURL() string {
|
||||||
"&state=" + utils.MakeRandomString(32)
|
"&state=" + utils.MakeRandomString(32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDate(dateString string) (date time.Time, err error) {
|
func parseDate(dateString string) (date *modules.Time, err error) {
|
||||||
if len(dateString) == 10 {
|
if len(dateString) == 10 {
|
||||||
// We're probably dealing with a date in the form of 2021-11-23 without a time
|
// We're probably dealing with a date in the form of 2021-11-23 without a time
|
||||||
date, err = time.Parse("2006-01-02", dateString)
|
tdate, err := time.Parse("2006-01-02", dateString)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// round the day to eod
|
// round the day to eod
|
||||||
|
date = modules.TimeFromTime(tdate)
|
||||||
return date.Add(time.Hour*23 + time.Minute*59), nil
|
return date.Add(time.Hour*23 + time.Minute*59), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
date, err = time.Parse("2006-01-02T15:04:05Z", dateString)
|
tdate, err := time.Parse("2006-01-02T15:04:05Z", dateString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
date, err = time.Parse("2006-01-02T15:04:05", dateString)
|
tdate, err = time.Parse("2006-01-02T15:04:05", dateString)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
date, err = time.Parse("2006-01-02", dateString)
|
tdate, err = time.Parse("2006-01-02", dateString)
|
||||||
}
|
}
|
||||||
|
|
||||||
return date, err
|
return modules.TimeFromTime(tdate), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVikunjaHierachie []*models.ProjectWithTasksAndBuckets, err error) {
|
func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVikunjaHierachie []*models.ProjectWithTasksAndBuckets, err error) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package todoist
|
package todoist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -34,20 +35,17 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
|
|
||||||
config.InitConfig()
|
config.InitConfig()
|
||||||
|
|
||||||
time1, err := time.Parse(time.RFC3339Nano, "2014-09-26T08:25:05Z")
|
parseAndConvertToTimeZone := func(timeStr string) *modules.Time {
|
||||||
require.NoError(t, err)
|
parsedTime, err := time.Parse(time.RFC3339Nano, timeStr)
|
||||||
time1 = time1.In(config.GetTimeZone())
|
require.NoError(t, err)
|
||||||
time3, err := time.Parse(time.RFC3339Nano, "2014-10-21T08:25:05Z")
|
return modules.TimeFromTime(parsedTime.In(config.GetTimeZone()))
|
||||||
require.NoError(t, err)
|
}
|
||||||
time3 = time3.In(config.GetTimeZone())
|
|
||||||
dueTime, err := time.Parse(time.RFC3339Nano, "2020-05-31T23:59:00Z")
|
time1 := parseAndConvertToTimeZone("2014-09-26T08:25:05Z")
|
||||||
require.NoError(t, err)
|
time3 := parseAndConvertToTimeZone("2014-10-21T08:25:05Z")
|
||||||
dueTime = dueTime.In(config.GetTimeZone())
|
dueTime := parseAndConvertToTimeZone("2020-05-31T23:59:00Z")
|
||||||
dueTimeWithTime, err := time.Parse(time.RFC3339Nano, "2021-01-31T19:00:00Z")
|
dueTimeWithTime := parseAndConvertToTimeZone("2021-01-31T19:00:00Z")
|
||||||
require.NoError(t, err)
|
nilTime := parseAndConvertToTimeZone("0001-01-01T00:00:00Z")
|
||||||
dueTimeWithTime = dueTimeWithTime.In(config.GetTimeZone())
|
|
||||||
nilTime, err := time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00Z")
|
|
||||||
require.NoError(t, err)
|
|
||||||
exampleFile, err := os.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/testimage.jpg")
|
exampleFile, err := os.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/testimage.jpg")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
@ -394,8 +392,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{Reminder: time.Date(2020, time.June, 15, 23, 59, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.June, 15, 23, 59, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
{Reminder: time.Date(2020, time.June, 16, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.June, 16, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -413,7 +411,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{Reminder: time.Date(2020, time.July, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.July, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -427,7 +425,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
DoneAt: time3,
|
DoneAt: time3,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -447,7 +445,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Created: time1,
|
Created: time1,
|
||||||
DoneAt: time3,
|
DoneAt: time3,
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -539,7 +537,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []*models.TaskReminder{
|
Reminders: []*models.TaskReminder{
|
||||||
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
{Reminder: modules.TimeFromTime(time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package trello
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
|
|
@ -296,7 +297,7 @@ func convertTrelloDataToVikunja(organizationName string, trelloData []*trello.Bo
|
||||||
}
|
}
|
||||||
|
|
||||||
if card.Due != nil {
|
if card.Due != nil {
|
||||||
task.DueDate = *card.Due
|
task.DueDate = modules.TimeFromTime(*card.Due)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checklists (as markdown in description)
|
// Checklists (as markdown in description)
|
||||||
|
|
@ -405,8 +406,8 @@ func convertTrelloDataToVikunja(organizationName string, trelloData []*trello.Bo
|
||||||
|
|
||||||
comment := &models.TaskComment{
|
comment := &models.TaskComment{
|
||||||
Comment: action.Data.Text,
|
Comment: action.Data.Text,
|
||||||
Created: action.Date,
|
Created: modules.TimeFromTime(action.Date),
|
||||||
Updated: action.Date,
|
Updated: modules.TimeFromTime(action.Date),
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentMember == nil || action.IDMemberCreator != currentMember.ID {
|
if currentMember == nil || action.IDMemberCreator != currentMember.ID {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"github.com/adlio/trello"
|
"github.com/adlio/trello"
|
||||||
"github.com/d4l3k/messagediff"
|
"github.com/d4l3k/messagediff"
|
||||||
|
|
@ -32,7 +33,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getTestBoard(t *testing.T) ([]*trello.Board, time.Time) {
|
func getTestBoard(t *testing.T) ([]*trello.Board, *modules.Time) {
|
||||||
|
|
||||||
config.InitConfig()
|
config.InitConfig()
|
||||||
|
|
||||||
|
|
@ -234,7 +235,7 @@ func getTestBoard(t *testing.T) ([]*trello.Board, time.Time) {
|
||||||
}
|
}
|
||||||
trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Using an image which we are hosting, so it'll still be up
|
trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Using an image which we are hosting, so it'll still be up
|
||||||
|
|
||||||
return trelloData, time1
|
return trelloData, modules.TimeFromTime(time1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertTrelloToVikunja(t *testing.T) {
|
func TestConvertTrelloToVikunja(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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 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 modules
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Time time.Time
|
||||||
|
|
||||||
|
func (t *Time) MarshalJSON() ([]byte, error) {
|
||||||
|
if time.Time(*t).IsZero() {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return []byte(`"` + time.Time(*t).Format(time.RFC3339) + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
|
if string(data) == "null" {
|
||||||
|
*t = Time{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parsedTime, err := time.Parse(`"`+time.RFC3339+`"`, string(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = Time(parsedTime)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Time() time.Time {
|
||||||
|
return time.Time(*t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) IsZero() bool {
|
||||||
|
return t.Time().IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Add(d time.Duration) *Time {
|
||||||
|
newTime := Time(t.Time().Add(d))
|
||||||
|
return &newTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) After(u *Time) bool {
|
||||||
|
return t.Time().After(u.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Before(u *Time) bool {
|
||||||
|
return t.Time().Before(u.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Sub(u *Time) time.Duration {
|
||||||
|
return t.Time().Sub(u.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Unix() int64 {
|
||||||
|
return t.Time().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) In(loc *time.Location) *Time {
|
||||||
|
newTime := Time(t.Time().In(loc))
|
||||||
|
return &newTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Format(layout string) string {
|
||||||
|
return t.Time().Format(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Time) Month() time.Month {
|
||||||
|
return t.Time().Month()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeFromTime(time time.Time) *Time {
|
||||||
|
t := Time(time)
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,8 @@ package notifications
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,10 +39,10 @@ type DatabaseNotification struct {
|
||||||
SubjectID int64 `xorm:"bigint null" json:"-"`
|
SubjectID int64 `xorm:"bigint null" json:"-"`
|
||||||
|
|
||||||
// When this notification is marked as read, this will be updated with the current timestamp.
|
// When this notification is marked as read, this will be updated with the current timestamp.
|
||||||
ReadAt time.Time `xorm:"datetime null" json:"read_at"`
|
ReadAt *modules.Time `xorm:"datetime null" json:"read_at"`
|
||||||
|
|
||||||
// A timestamp when this notification was created. You cannot change this value.
|
// A timestamp when this notification was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName resolves to a better table name for notifications
|
// TableName resolves to a better table name for notifications
|
||||||
|
|
@ -86,9 +88,9 @@ func CanMarkNotificationAsRead(s *xorm.Session, notification *DatabaseNotificati
|
||||||
// MarkNotificationAsRead marks a notification as read. It should be called only after CanMarkNotificationAsRead has
|
// MarkNotificationAsRead marks a notification as read. It should be called only after CanMarkNotificationAsRead has
|
||||||
// been called.
|
// been called.
|
||||||
func MarkNotificationAsRead(s *xorm.Session, notification *DatabaseNotification, read bool) (err error) {
|
func MarkNotificationAsRead(s *xorm.Session, notification *DatabaseNotification, read bool) (err error) {
|
||||||
notification.ReadAt = time.Time{}
|
notification.ReadAt = &modules.Time{}
|
||||||
if read {
|
if read {
|
||||||
notification.ReadAt = time.Now()
|
notification.ReadAt = modules.TimeFromTime(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.
|
_, err = s.
|
||||||
|
|
@ -102,6 +104,8 @@ func MarkAllNotificationsAsRead(s *xorm.Session, userID int64) (err error) {
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("notifiable_id = ?", userID).
|
Where("notifiable_id = ?", userID).
|
||||||
Cols("read_at").
|
Cols("read_at").
|
||||||
Update(&DatabaseNotification{ReadAt: time.Now()})
|
Update(&DatabaseNotification{
|
||||||
|
ReadAt: modules.TimeFromTime(time.Now()),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,6 @@ func GetTaskAttachment(c echo.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(c.Response(), c.Request(), taskAttachment.File.Name, taskAttachment.File.Created, taskAttachment.File.File)
|
http.ServeContent(c.Response(), c.Request(), taskAttachment.File.Name, taskAttachment.File.Created.Time(), taskAttachment.File.File)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,6 @@ func DownloadUserDataExport(c echo.Context) error {
|
||||||
return handler.HandleHTTPError(err)
|
return handler.HandleHTTPError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(c.Response(), c.Request(), exportFile.Name, exportFile.Created, exportFile.File)
|
http.ServeContent(c.Response(), c.Request(), exportFile.Name, exportFile.Created.Time(), exportFile.File)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,22 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/modules/auth/openid"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"code.vikunja.io/api/pkg/modules/auth"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
|
"code.vikunja.io/api/pkg/modules/auth"
|
||||||
|
"code.vikunja.io/api/pkg/modules/auth/openid"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web/handler"
|
"code.vikunja.io/api/pkg/web/handler"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserWithSettings struct {
|
type UserWithSettings struct {
|
||||||
user.User
|
user.User
|
||||||
Settings *UserSettings `json:"settings"`
|
Settings *UserSettings `json:"settings"`
|
||||||
DeletionScheduledAt time.Time `json:"deletion_scheduled_at"`
|
DeletionScheduledAt *modules.Time `json:"deletion_scheduled_at"`
|
||||||
IsLocalUser bool `json:"is_local_user"`
|
IsLocalUser bool `json:"is_local_user"`
|
||||||
AuthProvider string `json:"auth_provider"`
|
AuthProvider string `json:"auth_provider"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ func checkAPITokenAndPutItInContext(tokenHeaderValue string, c echo.Context) err
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(token.ExpiresAt) {
|
if time.Now().After(token.ExpiresAt.Time()) {
|
||||||
log.Debugf("[auth] Tried authenticating with token %d but it expired on %s", token.ID, token.ExpiresAt.String())
|
log.Debugf("[auth] Tried authenticating with token %d but it expired on %s", token.ID, token.ExpiresAt.Time().String())
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
user2 "code.vikunja.io/api/pkg/user"
|
user2 "code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"github.com/samedi/caldav-go/data"
|
"github.com/samedi/caldav-go/data"
|
||||||
"github.com/samedi/caldav-go/errs"
|
"github.com/samedi/caldav-go/errs"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
@ -251,7 +252,7 @@ func (vcls *VikunjaCaldavProjectStorage) GetResource(rpath string) (*data.Resour
|
||||||
}
|
}
|
||||||
vcls.task = tasks[0]
|
vcls.task = tasks[0]
|
||||||
|
|
||||||
if updated.Unix() > 0 {
|
if updated != nil && updated.Unix() > 0 {
|
||||||
vcls.task.Updated = updated
|
vcls.task.Updated = updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -620,11 +621,11 @@ func (vlra *VikunjaProjectResourceAdapter) GetContentSize() int64 {
|
||||||
// GetModTime returns when the resource was last modified
|
// GetModTime returns when the resource was last modified
|
||||||
func (vlra *VikunjaProjectResourceAdapter) GetModTime() time.Time {
|
func (vlra *VikunjaProjectResourceAdapter) GetModTime() time.Time {
|
||||||
if vlra.task != nil {
|
if vlra.task != nil {
|
||||||
return vlra.task.Updated
|
return vlra.task.Updated.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
if vlra.project != nil {
|
if vlra.project != nil {
|
||||||
return vlra.project.Updated
|
return vlra.project.Updated.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/cron"
|
"code.vikunja.io/api/pkg/cron"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/notifications"
|
"code.vikunja.io/api/pkg/notifications"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
|
|
@ -52,15 +53,15 @@ func notifyUsersScheduledForDeletion() {
|
||||||
log.Debugf("Found %d users scheduled for deletion to notify", len(users))
|
log.Debugf("Found %d users scheduled for deletion to notify", len(users))
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
if time.Since(user.DeletionLastReminderSent) < time.Hour*24 {
|
if time.Since(user.DeletionLastReminderSent.Time()) < time.Hour*24 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var number = 2
|
var number = 2
|
||||||
if user.DeletionLastReminderSent.IsZero() {
|
if user.DeletionLastReminderSent.Time().IsZero() {
|
||||||
number = 3
|
number = 3
|
||||||
}
|
}
|
||||||
if user.DeletionScheduledAt.Sub(user.DeletionLastReminderSent) < time.Hour*24 {
|
if user.DeletionScheduledAt.Time().Sub(user.DeletionLastReminderSent.Time()) < time.Hour*24 {
|
||||||
number = 1
|
number = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +76,7 @@ func notifyUsersScheduledForDeletion() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
user.DeletionLastReminderSent = time.Now()
|
user.DeletionLastReminderSent = modules.TimeFromTime(time.Now())
|
||||||
_, err = s.Where("id = ?", user.ID).
|
_, err = s.Where("id = ?", user.ID).
|
||||||
Cols("deletion_last_reminder_sent").
|
Cols("deletion_last_reminder_sent").
|
||||||
Update(user)
|
Update(user)
|
||||||
|
|
@ -115,7 +116,7 @@ func ConfirmDeletion(s *xorm.Session, user *User, token string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.DeletionScheduledAt = time.Now().Add(3 * 24 * time.Hour)
|
user.DeletionScheduledAt = modules.TimeFromTime(time.Now().Add(3 * 24 * time.Hour))
|
||||||
_, err = s.Where("id = ?", user.ID).
|
_, err = s.Where("id = ?", user.ID).
|
||||||
Cols("deletion_scheduled_at").
|
Cols("deletion_scheduled_at").
|
||||||
Update(user)
|
Update(user)
|
||||||
|
|
@ -124,8 +125,8 @@ func ConfirmDeletion(s *xorm.Session, user *User, token string) (err error) {
|
||||||
|
|
||||||
// CancelDeletion cancels the deletion of a user
|
// CancelDeletion cancels the deletion of a user
|
||||||
func CancelDeletion(s *xorm.Session, user *User) (err error) {
|
func CancelDeletion(s *xorm.Session, user *User) (err error) {
|
||||||
user.DeletionScheduledAt = time.Time{}
|
user.DeletionScheduledAt = &modules.Time{}
|
||||||
user.DeletionLastReminderSent = time.Time{}
|
user.DeletionLastReminderSent = &modules.Time{}
|
||||||
_, err = s.Where("id = ?", user.ID).
|
_, err = s.Where("id = ?", user.ID).
|
||||||
Cols("deletion_scheduled_at", "deletion_last_reminder_sent").
|
Cols("deletion_scheduled_at", "deletion_last_reminder_sent").
|
||||||
Update(user)
|
Update(user)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/cron"
|
"code.vikunja.io/api/pkg/cron"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
@ -41,12 +42,12 @@ const (
|
||||||
|
|
||||||
// Token is a token a user can use to do things like verify their email or resetting their password
|
// Token is a token a user can use to do things like verify their email or resetting their password
|
||||||
type Token struct {
|
type Token struct {
|
||||||
ID int64 `xorm:"bigint autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"bigint autoincr not null unique pk" json:"id"`
|
||||||
UserID int64 `xorm:"not null" json:"-"`
|
UserID int64 `xorm:"not null" json:"-"`
|
||||||
Token string `xorm:"varchar(450) not null index" json:"-"`
|
Token string `xorm:"varchar(450) not null index" json:"-"`
|
||||||
ClearTextToken string `xorm:"-" json:"token"`
|
ClearTextToken string `xorm:"-" json:"token"`
|
||||||
Kind TokenKind `xorm:"not null" json:"-"`
|
Kind TokenKind `xorm:"not null" json:"-"`
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns the real table name for user tokens
|
// TableName returns the real table name for user tokens
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,11 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/api/pkg/modules"
|
||||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||||
"code.vikunja.io/api/pkg/notifications"
|
"code.vikunja.io/api/pkg/notifications"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
@ -102,17 +103,17 @@ type User struct {
|
||||||
Language string `xorm:"varchar(50) null" json:"-"`
|
Language string `xorm:"varchar(50) null" json:"-"`
|
||||||
Timezone string `xorm:"varchar(255) null" json:"-"`
|
Timezone string `xorm:"varchar(255) null" json:"-"`
|
||||||
|
|
||||||
DeletionScheduledAt time.Time `xorm:"datetime null" json:"-"`
|
DeletionScheduledAt *modules.Time `xorm:"datetime null" json:"-"`
|
||||||
DeletionLastReminderSent time.Time `xorm:"datetime null" json:"-"`
|
DeletionLastReminderSent *modules.Time `xorm:"datetime null" json:"-"`
|
||||||
|
|
||||||
FrontendSettings interface{} `xorm:"json null" json:"-"`
|
FrontendSettings interface{} `xorm:"json null" json:"-"`
|
||||||
|
|
||||||
ExportFileID int64 `xorm:"bigint null" json:"-"`
|
ExportFileID int64 `xorm:"bigint null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created *modules.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this task was last updated. You cannot change this value.
|
// A timestamp when this task was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated *modules.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.Auth `xorm:"-" json:"-"`
|
web.Auth `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue