diff --git a/pkg/db/fixtures/buckets.yml b/pkg/db/fixtures/buckets.yml
index 7f294026..c1ddcd94 100644
--- a/pkg/db/fixtures/buckets.yml
+++ b/pkg/db/fixtures/buckets.yml
@@ -209,3 +209,9 @@
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
+- id: 35
+ title: testbucket35
+ list_id: 23
+ created_by_id: -2
+ created: 2020-04-18 21:13:52
+ updated: 2020-04-18 21:13:52
diff --git a/pkg/db/fixtures/task_attachments.yml b/pkg/db/fixtures/task_attachments.yml
index 9bbbcb0e..c203d5b3 100644
--- a/pkg/db/fixtures/task_attachments.yml
+++ b/pkg/db/fixtures/task_attachments.yml
@@ -9,3 +9,8 @@
file_id: 9999
created_by_id: 1
created: 2018-12-01 15:13:12
+- id: 3
+ task_id: 1
+ file_id: 1
+ created_by_id: -2
+ created: 2018-12-01 15:13:12
diff --git a/pkg/db/fixtures/task_comments.yml b/pkg/db/fixtures/task_comments.yml
index 16ce99a7..83163ed7 100644
--- a/pkg/db/fixtures/task_comments.yml
+++ b/pkg/db/fixtures/task_comments.yml
@@ -94,3 +94,9 @@
task_id: 36
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
+- id: 17
+ comment: comment 17
+ author_id: -2
+ task_id: 35
+ created: 2020-02-19 18:07:06
+ updated: 2020-02-19 18:07:06
diff --git a/pkg/db/fixtures/tasks.yml b/pkg/db/fixtures/tasks.yml
index 0fb2a986..73d392b6 100644
--- a/pkg/db/fixtures/tasks.yml
+++ b/pkg/db/fixtures/tasks.yml
@@ -338,5 +338,11 @@
bucket_id: 20
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
-
-
+- id: 37
+ title: 'task #37'
+ done: false
+ created_by_id: -2
+ list_id: 2
+ index: 2
+ created: 2018-12-01 01:12:04
+ updated: 2018-12-01 01:12:04
diff --git a/pkg/integrations/kanban_test.go b/pkg/integrations/kanban_test.go
index ac90457c..d2291aa4 100644
--- a/pkg/integrations/kanban_test.go
+++ b/pkg/integrations/kanban_test.go
@@ -19,6 +19,8 @@ package integrations
import (
"testing"
+ "code.vikunja.io/api/pkg/db"
+
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
@@ -33,6 +35,20 @@ func TestBucket(t *testing.T) {
},
t: t,
}
+ testHandlerLinkShareWrite := webHandlerTest{
+ linkShare: &models.LinkSharing{
+ ID: 2,
+ Hash: "test2",
+ ListID: 2,
+ Right: models.RightWrite,
+ SharingType: models.SharingTypeWithoutPassword,
+ SharedByID: 1,
+ },
+ strFunc: func() handler.CObject {
+ return &models.Bucket{}
+ },
+ t: t,
+ }
t.Run("ReadAll", func(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(nil, map[string]string{"list": "1"})
@@ -297,5 +313,15 @@ func TestBucket(t *testing.T) {
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
})
})
+ t.Run("Link Share", func(t *testing.T) {
+ rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"title":"Lorem Ipsum"}`)
+ assert.NoError(t, err)
+ assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
+ db.AssertExists(t, "buckets", map[string]interface{}{
+ "list_id": 2,
+ "created_by_id": -2,
+ "title": "Lorem Ipsum",
+ }, false)
+ })
})
}
diff --git a/pkg/integrations/link_sharing_test.go b/pkg/integrations/link_sharing_test.go
index 4e01f829..97d3bd6f 100644
--- a/pkg/integrations/link_sharing_test.go
+++ b/pkg/integrations/link_sharing_test.go
@@ -553,7 +553,7 @@ func TestLinkSharing(t *testing.T) {
rec, err := testHandlerTaskWriteCollection.testReadAllWithLinkShare(nil, nil)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #2`)
- assert.NotContains(t, rec.Body.String(), `task #3`)
+ assert.NotContains(t, rec.Body.String(), `task #3"`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.NotContains(t, rec.Body.String(), `task #5`)
assert.NotContains(t, rec.Body.String(), `task #6`)
diff --git a/pkg/integrations/task_comment_test.go b/pkg/integrations/task_comment_test.go
index 59e3a92d..5aa9bb5f 100644
--- a/pkg/integrations/task_comment_test.go
+++ b/pkg/integrations/task_comment_test.go
@@ -19,6 +19,8 @@ package integrations
import (
"testing"
+ "code.vikunja.io/api/pkg/db"
+
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
@@ -33,6 +35,20 @@ func TestTaskComments(t *testing.T) {
},
t: t,
}
+ testHandlerLinkShareWrite := webHandlerTest{
+ linkShare: &models.LinkSharing{
+ ID: 2,
+ Hash: "test2",
+ ListID: 2,
+ Right: models.RightWrite,
+ SharingType: models.SharingTypeWithoutPassword,
+ SharedByID: 1,
+ },
+ strFunc: func() handler.CObject {
+ return &models.TaskComment{}
+ },
+ t: t,
+ }
// Only run specific nested tests:
// ^TestTaskComments$/^Update$/^Update_task_items$/^Removing_Assignees_null$
t.Run("Update", func(t *testing.T) {
@@ -281,5 +297,15 @@ func TestTaskComments(t *testing.T) {
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
})
})
+ t.Run("Link Share", func(t *testing.T) {
+ rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"task": "13"}, `{"comment":"Lorem Ipsum"}`)
+ assert.NoError(t, err)
+ assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
+ db.AssertExists(t, "task_comments", map[string]interface{}{
+ "task_id": 13,
+ "comment": "Lorem Ipsum",
+ "author_id": -2,
+ }, false)
+ })
})
}
diff --git a/pkg/integrations/task_test.go b/pkg/integrations/task_test.go
index bc3a71dc..1a09f92c 100644
--- a/pkg/integrations/task_test.go
+++ b/pkg/integrations/task_test.go
@@ -19,6 +19,8 @@ package integrations
import (
"testing"
+ "code.vikunja.io/api/pkg/db"
+
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
@@ -33,6 +35,20 @@ func TestTask(t *testing.T) {
},
t: t,
}
+ testHandlerLinkShareWrite := webHandlerTest{
+ linkShare: &models.LinkSharing{
+ ID: 2,
+ Hash: "test2",
+ ListID: 2,
+ Right: models.RightWrite,
+ SharingType: models.SharingTypeWithoutPassword,
+ SharedByID: 1,
+ },
+ strFunc: func() handler.CObject {
+ return &models.Task{}
+ },
+ t: t,
+ }
// Only run specific nested tests:
// ^TestTask$/^Update$/^Update_task_items$/^Removing_Assignees_null$
t.Run("Update", func(t *testing.T) {
@@ -489,5 +505,15 @@ func TestTask(t *testing.T) {
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
})
})
+ t.Run("Link Share", func(t *testing.T) {
+ rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"title":"Lorem Ipsum"}`)
+ assert.NoError(t, err)
+ assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
+ db.AssertExists(t, "tasks", map[string]interface{}{
+ "list_id": 2,
+ "title": "Lorem Ipsum",
+ "created_by_id": -2,
+ }, false)
+ })
})
}
diff --git a/pkg/migration/20210403220653.go b/pkg/migration/20210403220653.go
new file mode 100644
index 00000000..1ce91a4a
--- /dev/null
+++ b/pkg/migration/20210403220653.go
@@ -0,0 +1,43 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+package migration
+
+import (
+ "src.techknowlogick.com/xormigrate"
+ "xorm.io/xorm"
+)
+
+type linkShares20210403220653 struct {
+ Name string `xorm:"text null" json:"name"`
+}
+
+func (linkShares20210403220653) TableName() string {
+ return "link_shares"
+}
+
+func init() {
+ migrations = append(migrations, &xormigrate.Migration{
+ ID: "20210403220653",
+ Description: "Add the name column to link shares",
+ Migrate: func(tx *xorm.Engine) error {
+ return tx.Sync2(linkShares20210403220653{})
+ },
+ Rollback: func(tx *xorm.Engine) error {
+ return nil
+ },
+ })
+}
diff --git a/pkg/models/kanban.go b/pkg/models/kanban.go
index 94d28d54..1e827a51 100644
--- a/pkg/models/kanban.go
+++ b/pkg/models/kanban.go
@@ -135,12 +135,9 @@ func (b *Bucket) ReadAll(s *xorm.Session, auth web.Auth, search string, page int
}
// Get all users
- users := make(map[int64]*user.User)
- if len(userIDs) > 0 {
- err = s.In("id", userIDs).Find(&users)
- if err != nil {
- return
- }
+ users, err := getUsersOrLinkSharesFromIDs(s, userIDs)
+ if err != nil {
+ return
}
for _, bb := range buckets {
@@ -234,7 +231,11 @@ func (b *Bucket) ReadAll(s *xorm.Session, auth web.Auth, search string, page int
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/buckets [put]
func (b *Bucket) Create(s *xorm.Session, a web.Auth) (err error) {
- b.CreatedByID = a.GetID()
+ b.CreatedBy, err = getUserOrLinkShareUser(s, a)
+ if err != nil {
+ return
+ }
+ b.CreatedByID = b.CreatedBy.ID
_, err = s.Insert(b)
return
diff --git a/pkg/models/kanban_test.go b/pkg/models/kanban_test.go
index 6dc6e6ba..99b099d4 100644
--- a/pkg/models/kanban_test.go
+++ b/pkg/models/kanban_test.go
@@ -89,6 +89,20 @@ func TestBucket_ReadAll(t *testing.T) {
assert.Len(t, buckets, 3)
assert.Equal(t, int64(2), buckets[0].Tasks[0].ID)
})
+ t.Run("link share", func(t *testing.T) {
+ db.LoadAndAssertFixtures(t)
+ s := db.NewSession()
+ defer s.Close()
+
+ testuser := &user.User{ID: 1}
+ b := &Bucket{ListID: 23}
+ result, _, _, err := b.ReadAll(s, testuser, "", 0, 0)
+ assert.NoError(t, err)
+ buckets, _ := result.([]*Bucket)
+ assert.Len(t, buckets, 1)
+ assert.NotNil(t, buckets[0].CreatedBy)
+ assert.Equal(t, int64(-2), buckets[0].CreatedByID)
+ })
}
func TestBucket_Delete(t *testing.T) {
diff --git a/pkg/models/link_sharing.go b/pkg/models/link_sharing.go
index 487d78ff..e8835e2d 100644
--- a/pkg/models/link_sharing.go
+++ b/pkg/models/link_sharing.go
@@ -42,6 +42,8 @@ type LinkSharing struct {
ID int64 `xorm:"bigint autoincr not null unique pk" json:"id" param:"share"`
// The public id to get this shared list
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
+ // The name of this link share. All actions someone takes while being authenticated with that link will appear with that name.
+ Name string `xorm:"text null" json:"name"`
// The ID of the shared list
ListID int64 `xorm:"bigint not null" json:"-" param:"list"`
// The right this list is shared with. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
@@ -84,6 +86,25 @@ func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error
return
}
+func (share *LinkSharing) getUserID() int64 {
+ return share.ID * -1
+}
+
+func (share *LinkSharing) toUser() *user.User {
+ suffix := "Link Share"
+ if share.Name != "" {
+ suffix = " (" + suffix + ")"
+ }
+
+ return &user.User{
+ ID: share.getUserID(),
+ Name: share.Name + suffix,
+ Username: share.Name,
+ Created: share.Created,
+ Updated: share.Updated,
+ }
+}
+
// Create creates a new link share for a given list
// @Summary Share a list via link
// @Description Share a list via link. The user needs to have write-access to the list to be able do this.
@@ -246,3 +267,23 @@ func GetListByShareHash(s *xorm.Session, hash string) (list *List, err error) {
list, err = GetListSimpleByID(s, share.ListID)
return
}
+
+// GetLinkShareByID returns a link share by its id.
+func GetLinkShareByID(s *xorm.Session, id int64) (share *LinkSharing, err error) {
+ share = &LinkSharing{}
+ has, err := s.Where("id = ?", id).Get(share)
+ if err != nil {
+ return
+ }
+ if !has {
+ return share, ErrListShareDoesNotExist{ID: id}
+ }
+ return
+}
+
+// GetLinkSharesByIDs returns all link shares from a slice of ids
+func GetLinkSharesByIDs(s *xorm.Session, ids []int64) (shares map[int64]*LinkSharing, err error) {
+ shares = make(map[int64]*LinkSharing)
+ err = s.In("id", ids).Find(&shares)
+ return
+}
diff --git a/pkg/models/task_attachment.go b/pkg/models/task_attachment.go
index 457d16d2..8ab8f56e 100644
--- a/pkg/models/task_attachment.go
+++ b/pkg/models/task_attachment.go
@@ -64,7 +64,17 @@ func (ta *TaskAttachment) NewAttachment(s *xorm.Session, f io.ReadCloser, realna
// Add an entry to the db
ta.FileID = file.ID
- ta.CreatedByID = a.GetID()
+
+ ta.CreatedBy, err = getUserOrLinkShareUser(s, a)
+ if err != nil {
+ // remove the uploaded file if adding it to the db fails
+ if err2 := file.Delete(); err2 != nil {
+ return err2
+ }
+ return err
+ }
+ ta.CreatedByID = ta.CreatedBy.ID
+
_, err = s.Insert(ta)
if err != nil {
// remove the uploaded file if adding it to the db fails
@@ -74,8 +84,6 @@ func (ta *TaskAttachment) NewAttachment(s *xorm.Session, f io.ReadCloser, realna
return err
}
- ta.CreatedBy, _ = user.GetFromAuth(a) // Ignoring cases where the auth is not a user
-
return nil
}
@@ -145,19 +153,19 @@ func (ta *TaskAttachment) ReadAll(s *xorm.Session, a web.Auth, search string, pa
return nil, 0, 0, err
}
- us := make(map[int64]*user.User)
- err = s.In("id", userIDs).Find(&us)
+ users, err := getUsersOrLinkSharesFromIDs(s, userIDs)
if err != nil {
return nil, 0, 0, err
}
for _, r := range attachments {
+ r.CreatedBy = users[r.CreatedByID]
+
// If the actual file does not exist, don't try to load it as that would fail with nil panic
if _, exists := fs[r.FileID]; !exists {
continue
}
r.File = fs[r.FileID]
- r.CreatedBy = us[r.CreatedByID]
}
numberOfTotalItems, err = s.
@@ -231,12 +239,9 @@ func getTaskAttachmentsByTaskIDs(s *xorm.Session, taskIDs []int64) (attachments
return
}
- users := make(map[int64]*user.User)
- if len(userIDs) > 0 {
- err = s.In("id", userIDs).Find(&users)
- if err != nil {
- return
- }
+ users, err := getUsersOrLinkSharesFromIDs(s, userIDs)
+ if err != nil {
+ return nil, err
}
// Obfuscate all user emails
diff --git a/pkg/models/task_attachment_test.go b/pkg/models/task_attachment_test.go
index 7d6dbad5..47650dee 100644
--- a/pkg/models/task_attachment_test.go
+++ b/pkg/models/task_attachment_test.go
@@ -146,8 +146,13 @@ func TestTaskAttachment_ReadAll(t *testing.T) {
as, _, _, err := ta.ReadAll(s, &user.User{ID: 1}, "", 0, 50)
attachments, _ := as.([]*TaskAttachment)
assert.NoError(t, err)
- assert.Len(t, attachments, 2)
+ assert.Len(t, attachments, 3)
assert.Equal(t, "test", attachments[0].File.Name)
+ for _, a := range attachments {
+ assert.NotNil(t, a.CreatedBy)
+ }
+ assert.Equal(t, int64(-2), attachments[2].CreatedByID)
+ assert.Equal(t, int64(-2), attachments[2].CreatedBy.ID)
}
func TestTaskAttachment_Delete(t *testing.T) {
diff --git a/pkg/models/task_collection_test.go b/pkg/models/task_collection_test.go
index 2f50fecf..f195cb12 100644
--- a/pkg/models/task_collection_test.go
+++ b/pkg/models/task_collection_test.go
@@ -59,6 +59,12 @@ func TestTaskCollection_ReadAll(t *testing.T) {
Created: testCreatedTime,
Updated: testUpdatedTime,
}
+ linkShareUser2 := &user.User{
+ ID: -2,
+ Name: "Link Share",
+ Created: testCreatedTime,
+ Updated: testUpdatedTime,
+ }
loc := config.GetTimeZone()
@@ -124,6 +130,21 @@ func TestTaskCollection_ReadAll(t *testing.T) {
CreatedBy: user1,
Created: testCreatedTime,
},
+ {
+ ID: 3,
+ TaskID: 1,
+ FileID: 1,
+ CreatedByID: -2,
+ CreatedBy: linkShareUser2,
+ Created: testCreatedTime,
+ File: &files.File{
+ ID: 1,
+ Name: "test",
+ Size: 100,
+ Created: time.Unix(1570998791, 0).In(loc),
+ CreatedByID: 1,
+ },
+ },
},
Created: time.Unix(1543626724, 0).In(loc),
Updated: time.Unix(1543626724, 0).In(loc),
diff --git a/pkg/models/task_comments.go b/pkg/models/task_comments.go
index 1924712c..d213ef88 100644
--- a/pkg/models/task_comments.go
+++ b/pkg/models/task_comments.go
@@ -67,24 +67,22 @@ func (tc *TaskComment) Create(s *xorm.Session, a web.Auth) (err error) {
return err
}
- tc.AuthorID = a.GetID()
+ tc.Author, err = getUserOrLinkShareUser(s, a)
+ if err != nil {
+ return err
+ }
+ tc.AuthorID = tc.Author.ID
+
_, err = s.Insert(tc)
if err != nil {
return
}
- doer, _ := user.GetFromAuth(a)
- err = events.Dispatch(&TaskCommentCreatedEvent{
+ return events.Dispatch(&TaskCommentCreatedEvent{
Task: &task,
Comment: tc,
- Doer: doer,
+ Doer: tc.Author,
})
- if err != nil {
- return err
- }
-
- tc.Author, err = user.GetUserByID(s, a.GetID())
- return
}
// Delete removes a task comment
@@ -215,14 +213,12 @@ func (tc *TaskComment) ReadAll(s *xorm.Session, auth web.Auth, search string, pa
return
}
- // Get all authors
- authors := make(map[int64]*user.User)
- err = s.
- Select("users.*").
- Table("task_comments").
- Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
- Join("INNER", "users", "users.id = task_comments.author_id").
- Find(&authors)
+ var authorIDs []int64
+ for _, comment := range comments {
+ authorIDs = append(authorIDs, comment.AuthorID)
+ }
+
+ authors, err := getUsersOrLinkSharesFromIDs(s, authorIDs)
if err != nil {
return
}
diff --git a/pkg/models/task_comments_test.go b/pkg/models/task_comments_test.go
index 3ad3abf8..ecc16ae0 100644
--- a/pkg/models/task_comments_test.go
+++ b/pkg/models/task_comments_test.go
@@ -184,4 +184,18 @@ func TestTaskComment_ReadAll(t *testing.T) {
assert.Error(t, err)
assert.True(t, IsErrGenericForbidden(err))
})
+ t.Run("comment from link share", func(t *testing.T) {
+ db.LoadAndAssertFixtures(t)
+ s := db.NewSession()
+ defer s.Close()
+
+ tc := &TaskComment{TaskID: 35}
+ u := &user.User{ID: 1}
+ result, _, _, err := tc.ReadAll(s, u, "", 0, -1)
+ comments := result.([]*TaskComment)
+ assert.NoError(t, err)
+ assert.Len(t, comments, 2)
+ assert.Equal(t, int64(-2), comments[1].AuthorID)
+ assert.NotNil(t, comments[1].Author)
+ })
}
diff --git a/pkg/models/task_relation.go b/pkg/models/task_relation.go
index 9f787405..b0bdd98b 100644
--- a/pkg/models/task_relation.go
+++ b/pkg/models/task_relation.go
@@ -144,13 +144,17 @@ func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error {
}
}
- rel.CreatedByID = a.GetID()
+ rel.CreatedBy, err = getUserOrLinkShareUser(s, a)
+ if err != nil {
+ return err
+ }
+ rel.CreatedByID = rel.CreatedBy.ID
// Build up the other relation (see the comment above for explanation)
otherRelation := &TaskRelation{
TaskID: rel.OtherTaskID,
OtherTaskID: rel.TaskID,
- CreatedByID: a.GetID(),
+ CreatedByID: rel.CreatedByID,
}
switch rel.RelationKind {
diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go
index e329e273..14c6431e 100644
--- a/pkg/models/tasks.go
+++ b/pkg/models/tasks.go
@@ -670,7 +670,7 @@ func addMoreInfoToTasks(s *xorm.Session, taskMap map[int64]*Task) (err error) {
return
}
- users, err := user.GetUsersByIDs(s, userIDs)
+ users, err := getUsersOrLinkSharesFromIDs(s, userIDs)
if err != nil {
return
}
@@ -817,17 +817,11 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
return err
}
- if _, is := a.(*LinkSharing); is {
- // A negative user id indicates user share links
- t.CreatedByID = a.GetID() * -1
- } else {
- u, err := user.GetUserByID(s, a.GetID())
- if err != nil {
- return err
- }
- t.CreatedByID = u.ID
- t.CreatedBy = u
+ createdBy, err := getUserOrLinkShareUser(s, a)
+ if err != nil {
+ return err
}
+ t.CreatedByID = createdBy.ID
// Generate a uuid if we don't already have one
if t.UID == "" {
@@ -856,6 +850,8 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
return err
}
+ t.CreatedBy = createdBy
+
// Update the assignees
if updateAssignees {
if err := t.updateTaskAssignees(s, t.Assignees, a); err != nil {
@@ -870,10 +866,9 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
t.setIdentifier(l)
- doer, _ := user.GetFromAuth(a)
err = events.Dispatch(&TaskCreatedEvent{
Task: t,
- Doer: doer,
+ Doer: createdBy,
})
if err != nil {
return err
diff --git a/pkg/models/tasks_test.go b/pkg/models/tasks_test.go
index 725c2c40..ff1e692d 100644
--- a/pkg/models/tasks_test.go
+++ b/pkg/models/tasks_test.go
@@ -579,4 +579,17 @@ func TestTask_ReadOne(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, task.Subscription)
})
+ t.Run("created by link share", func(t *testing.T) {
+ db.LoadAndAssertFixtures(t)
+ s := db.NewSession()
+ defer s.Close()
+
+ task := &Task{ID: 37}
+ err := task.ReadOne(s, u)
+ assert.NoError(t, err)
+ assert.Equal(t, "task #37", task.Title)
+ assert.Equal(t, int64(-2), task.CreatedByID)
+ assert.NotNil(t, task.CreatedBy)
+ assert.Equal(t, int64(-2), task.CreatedBy.ID)
+ })
}
diff --git a/pkg/models/users.go b/pkg/models/users.go
new file mode 100644
index 00000000..84fd3811
--- /dev/null
+++ b/pkg/models/users.go
@@ -0,0 +1,78 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public Licensee as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public Licensee for more details.
+//
+// You should have received a copy of the GNU Affero General Public Licensee
+// along with this program. If not, see .
+
+package models
+
+import (
+ "code.vikunja.io/api/pkg/user"
+ "code.vikunja.io/web"
+ "xorm.io/xorm"
+)
+
+// Returns either a user or a link share disguised as a user.
+func getUserOrLinkShareUser(s *xorm.Session, a web.Auth) (uu *user.User, err error) {
+ if u, is := a.(*user.User); is {
+ uu, err = user.GetUserByID(s, u.ID)
+ return
+ }
+
+ if ls, is := a.(*LinkSharing); is {
+ l, err := GetLinkShareByID(s, ls.ID)
+ if err != nil {
+ return nil, err
+ }
+ return l.toUser(), nil
+ }
+
+ return
+}
+
+// Returns all users or pseudo link shares from a slice of ids. ids < 0 are considered to be a link share in that case.
+func getUsersOrLinkSharesFromIDs(s *xorm.Session, ids []int64) (users map[int64]*user.User, err error) {
+ users = make(map[int64]*user.User)
+ var userIDs []int64
+ var linkShareIDs []int64
+ for _, id := range ids {
+ if id < 0 {
+ linkShareIDs = append(linkShareIDs, id*-1)
+ continue
+ }
+
+ userIDs = append(userIDs, id)
+ }
+
+ if len(userIDs) > 0 {
+ users, err = user.GetUsersByIDs(s, userIDs)
+ if err != nil {
+ return
+ }
+ }
+
+ if len(linkShareIDs) == 0 {
+ return
+ }
+
+ shares, err := GetLinkSharesByIDs(s, linkShareIDs)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, share := range shares {
+ users[share.ID*-1] = share.toUser()
+ }
+
+ return
+}
diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go
index 4d3d7b1f..91f3669a 100644
--- a/pkg/swagger/docs.go
+++ b/pkg/swagger/docs.go
@@ -7489,6 +7489,10 @@ var doc = `{
"description": "The ID of the shared thing",
"type": "integer"
},
+ "name": {
+ "description": "The name of this link share. All actions someone takes while being authenticated with that link will appear with that name.",
+ "type": "string"
+ },
"right": {
"description": "The right this list is shared with. 0 = Read only, 1 = Read \u0026 Write, 2 = Admin. See the docs for more details.",
"type": "integer",
diff --git a/pkg/swagger/swagger.json b/pkg/swagger/swagger.json
index d75e776c..d6ab5fe8 100644
--- a/pkg/swagger/swagger.json
+++ b/pkg/swagger/swagger.json
@@ -7472,6 +7472,10 @@
"description": "The ID of the shared thing",
"type": "integer"
},
+ "name": {
+ "description": "The name of this link share. All actions someone takes while being authenticated with that link will appear with that name.",
+ "type": "string"
+ },
"right": {
"description": "The right this list is shared with. 0 = Read only, 1 = Read \u0026 Write, 2 = Admin. See the docs for more details.",
"type": "integer",
diff --git a/pkg/swagger/swagger.yaml b/pkg/swagger/swagger.yaml
index b0240baa..cdd91476 100644
--- a/pkg/swagger/swagger.yaml
+++ b/pkg/swagger/swagger.yaml
@@ -310,6 +310,9 @@ definitions:
id:
description: The ID of the shared thing
type: integer
+ name:
+ description: The name of this link share. All actions someone takes while being authenticated with that link will appear with that name.
+ type: string
right:
default: 0
description: The right this list is shared with. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.