diff --git a/pkg/models/events.go b/pkg/models/events.go index 909591f5a..820aba0c6 100644 --- a/pkg/models/events.go +++ b/pkg/models/events.go @@ -80,6 +80,18 @@ func (t *TaskAssigneeCreatedEvent) Name() string { return "task.assignee.created" } +// TaskAssigneeDeletedEvent represents a TaskAssigneeDeletedEvent event +type TaskAssigneeDeletedEvent struct { + Task *Task + Assignee *user.User + Doer *user.User +} + +// Name defines the name for TaskAssigneeDeletedEvent +func (t *TaskAssigneeDeletedEvent) Name() string { + return "task.assignee.deleted" +} + // TaskCommentCreatedEvent represents an event where a task comment has been created type TaskCommentCreatedEvent struct { Task *Task @@ -104,6 +116,66 @@ func (t *TaskCommentUpdatedEvent) Name() string { return "task.comment.edited" } +// TaskCommentDeletedEvent represents a TaskCommentDeletedEvent event +type TaskCommentDeletedEvent struct { + Task *Task + Comment *TaskComment + Doer *user.User +} + +// Name defines the name for TaskCommentDeletedEvent +func (t *TaskCommentDeletedEvent) Name() string { + return "task.comment.deleted" +} + +// TaskAttachmentCreatedEvent represents a TaskAttachmentCreatedEvent event +type TaskAttachmentCreatedEvent struct { + Task *Task + Attachment *TaskAttachment + Doer *user.User +} + +// Name defines the name for TaskAttachmentCreatedEvent +func (t *TaskAttachmentCreatedEvent) Name() string { + return "task.attachment.created" +} + +// TaskAttachmentDeletedEvent represents a TaskAttachmentDeletedEvent event +type TaskAttachmentDeletedEvent struct { + Task *Task + Attachment *TaskAttachment + Doer *user.User +} + +// Name defines the name for TaskAttachmentDeletedEvent +func (t *TaskAttachmentDeletedEvent) Name() string { + return "task.attachment.deleted" +} + +// TaskRelationCreatedEvent represents a TaskRelationCreatedEvent event +type TaskRelationCreatedEvent struct { + Task *Task + Relation *TaskRelation + Doer *user.User +} + +// Name defines the name for TaskRelationCreatedEvent +func (t *TaskRelationCreatedEvent) Name() string { + return "task.relation.created" +} + +// TaskRelationDeletedEvent represents a TaskRelationDeletedEvent event +type TaskRelationDeletedEvent struct { + Task *Task + Relation *TaskRelation + Doer *user.User +} + +// Name defines the name for TaskRelationDeletedEvent +func (t *TaskRelationDeletedEvent) Name() string { + return "task.relation.deleted" +} + ////////////////////// // Namespace Events // ////////////////////// diff --git a/pkg/models/listeners.go b/pkg/models/listeners.go index 0e0eb13cc..b4f90fc12 100644 --- a/pkg/models/listeners.go +++ b/pkg/models/listeners.go @@ -51,6 +51,15 @@ func RegisterListeners() { events.RegisterListener((&TaskCreatedEvent{}).Name(), &HandleTaskCreateMentions{}) events.RegisterListener((&TaskUpdatedEvent{}).Name(), &HandleTaskUpdatedMentions{}) events.RegisterListener((&UserDataExportRequestedEvent{}).Name(), &HandleUserDataExport{}) + events.RegisterListener((&TaskCommentCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskCommentUpdatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskCommentDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskAssigneeCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskAssigneeDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskAttachmentCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskAttachmentDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskRelationCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) + events.RegisterListener((&TaskRelationDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) } ////// @@ -403,9 +412,62 @@ func (s *HandleTaskUpdatedMentions) Handle(msg *message.Message) (err error) { Doer: event.Doer, IsNew: false, } + _, err = notifyMentionedUsers(sess, event.Task, event.Task.Description, n) return err +} +// HandleTaskUpdateLastUpdated represents a listener +type HandleTaskUpdateLastUpdated struct { +} + +// Name defines the name for the HandleTaskUpdateLastUpdated listener +func (s *HandleTaskUpdateLastUpdated) Name() string { + return "handle.task.update.last.updated" +} + +// Handle is executed when the event HandleTaskUpdateLastUpdated listens on is fired +func (s *HandleTaskUpdateLastUpdated) Handle(msg *message.Message) (err error) { + // Using a map here allows us to plug this listener to all kinds of task events + event := map[string]interface{}{} + err = json.Unmarshal(msg.Payload, &event) + if err != nil { + return err + } + + task, is := event["Task"].(map[string]interface{}) + if !is { + log.Errorf("Event payload does not contain task ID") + return + } + + taskID, is := task["id"] + if !is { + log.Errorf("Event payload does not contain a valid task ID") + return + } + + var taskIDInt int64 + switch taskID.(type) { + case int64: + taskIDInt = taskID.(int64) + case int: + taskIDInt = int64(taskID.(int)) + case int32: + taskIDInt = int64(taskID.(int32)) + case float64: + taskIDInt = int64(taskID.(float64)) + case float32: + taskIDInt = int64(taskID.(float32)) + default: + log.Errorf("Event payload does not contain a valid task ID") + return + } + + sess := db.NewSession() + defer sess.Close() + + return updateTaskLastUpdated(sess, &Task{ID: taskIDInt}) } /////// diff --git a/pkg/models/task_assignees.go b/pkg/models/task_assignees.go index d2dc996c7..3993a5da8 100644 --- a/pkg/models/task_assignees.go +++ b/pkg/models/task_assignees.go @@ -179,7 +179,16 @@ func (la *TaskAssginee) Delete(s *xorm.Session, a web.Auth) (err error) { } err = updateListByTaskID(s, la.TaskID) - return + if err != nil { + return err + } + + doer, _ := user.GetFromAuth(a) + return events.Dispatch(&TaskAssigneeDeletedEvent{ + Task: &Task{ID: la.TaskID}, + Assignee: &user.User{ID: la.UserID}, + Doer: doer, + }) } // Create adds a new assignee to a task diff --git a/pkg/models/task_attachment.go b/pkg/models/task_attachment.go index 70708feb4..41041129c 100644 --- a/pkg/models/task_attachment.go +++ b/pkg/models/task_attachment.go @@ -17,6 +17,7 @@ package models import ( + "code.vikunja.io/api/pkg/events" "io" "time" @@ -44,7 +45,7 @@ type TaskAttachment struct { } // TableName returns the table name for task attachments -func (TaskAttachment) TableName() string { +func (*TaskAttachment) TableName() string { return "task_attachments" } @@ -84,7 +85,11 @@ func (ta *TaskAttachment) NewAttachment(s *xorm.Session, f io.ReadCloser, realna return err } - return nil + return events.Dispatch(&TaskAttachmentCreatedEvent{ + Task: &Task{ID: ta.TaskID}, + Attachment: ta, + Doer: ta.CreatedBy, + }) } // ReadOne returns a task attachment @@ -209,7 +214,16 @@ func (ta *TaskAttachment) Delete(s *xorm.Session, a web.Auth) error { if err != nil && files.IsErrFileDoesNotExist(err) { return nil } - return err + if err != nil { + return err + } + + doer, _ := user.GetFromAuth(a) + return events.Dispatch(&TaskAttachmentDeletedEvent{ + Task: &Task{ID: ta.TaskID}, + Attachment: ta, + Doer: doer, + }) } func getTaskAttachmentsByTaskIDs(s *xorm.Session, taskIDs []int64) (attachments []*TaskAttachment, err error) { diff --git a/pkg/models/task_comments.go b/pkg/models/task_comments.go index 062313e3d..0c0514dd3 100644 --- a/pkg/models/task_comments.go +++ b/pkg/models/task_comments.go @@ -109,7 +109,16 @@ func (tc *TaskComment) Delete(s *xorm.Session, a web.Auth) error { if deleted == 0 { return ErrTaskCommentDoesNotExist{ID: tc.ID} } - return err + + if err != nil { + return err + } + + return events.Dispatch(&TaskCommentDeletedEvent{ + Task: &Task{ID: tc.TaskID}, + Comment: tc, + Doer: tc.Author, + }) } // Update updates a task text by its ID diff --git a/pkg/models/task_relation.go b/pkg/models/task_relation.go index 4b626c33b..4f587dcd1 100644 --- a/pkg/models/task_relation.go +++ b/pkg/models/task_relation.go @@ -17,6 +17,7 @@ package models import ( + "code.vikunja.io/api/pkg/events" "time" "xorm.io/builder" @@ -98,7 +99,7 @@ type TaskRelation struct { } // TableName holds the table name for the task relation table -func (TaskRelation) TableName() string { +func (*TaskRelation) TableName() string { return "task_relations" } @@ -190,7 +191,16 @@ func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error { rel, otherRelation, }) - return err + if err != nil { + return err + } + + doer, _ := user.GetFromAuth(a) + return events.Dispatch(&TaskRelationCreatedEvent{ + Task: &Task{ID: rel.TaskID}, + Relation: rel, + Doer: doer, + }) } // Delete removes a task relation @@ -241,5 +251,14 @@ func (rel *TaskRelation) Delete(s *xorm.Session, a web.Auth) error { _, err = s. Where(cond). Delete(&TaskRelation{}) - return err + if err != nil { + return err + } + + doer, _ := user.GetFromAuth(a) + return events.Dispatch(&TaskRelationDeletedEvent{ + Task: &Task{ID: rel.TaskID}, + Relation: rel, + Doer: doer, + }) } diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index fbb9bd42b..065382575 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -1441,6 +1441,11 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro return } +func updateTaskLastUpdated(s *xorm.Session, task *Task) error { + _, err := s.ID(task.ID).Cols("updated").Update(task) + return err +} + // Delete implements the delete method for listTask // @Summary Delete a task // @Description Deletes a task from a list. This does not mean "mark it done".