diff --git a/pkg/db/fixtures/buckets.yml b/pkg/db/fixtures/buckets.yml index 7f29402602a..c1ddcd94f6b 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 9bbbcb0ed6d..c203d5b34a0 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 16ce99a73dc..83163ed744c 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 0fb2a9864e6..73d392b6411 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 ac90457c4bb..d2291aa419f 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 4e01f829dd5..97d3bd6f14a 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 59e3a92d1a2..5aa9bb5f36a 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 bc3a71dc6cc..1a09f92c90f 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 00000000000..1ce91a4a35a --- /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 94d28d5455b..1e827a51034 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 6dc6e6ba76a..99b099d4bc8 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 487d78fffa5..e8835e2d6c6 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 457d16d2f56..8ab8f56e618 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 7d6dbad56c5..47650deea79 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 2f50fecf789..f195cb12d51 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 1924712c49f..d213ef88421 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 3ad3abf8da6..ecc16ae07c5 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 9f787405bf1..b0bdd98bbbd 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 e329e273815..14c6431eb2f 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 725c2c4020c..ff1e692d1ed 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 00000000000..84fd381105d --- /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 4d3d7b1f0b8..91f3669a58e 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 d75e776cefb..d6ab5fe8579 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 b0240baac3d..cdd914760bb 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.