fix(labels): allow link shares to add existing labels to a task
Resolves https://github.com/go-vikunja/vikunja/issues/252
This commit is contained in:
parent
1074a8d916
commit
574c7f218e
@ -34,6 +34,7 @@ import {useBaseStore} from '@/stores/base'
|
|||||||
import Logo from '@/components/home/Logo.vue'
|
import Logo from '@/components/home/Logo.vue'
|
||||||
import PoweredByLink from './PoweredByLink.vue'
|
import PoweredByLink from './PoweredByLink.vue'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
|
import {useLabelStore} from '@/stores/labels'
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const currentProject = computed(() => baseStore.currentProject)
|
const currentProject = computed(() => baseStore.currentProject)
|
||||||
@ -42,6 +43,9 @@ const logoVisible = computed(() => baseStore.logoVisible)
|
|||||||
|
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
projectStore.loadAllProjects()
|
projectStore.loadAllProjects()
|
||||||
|
|
||||||
|
const labelStore = useLabelStore()
|
||||||
|
labelStore.loadAllLabels()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -160,7 +160,6 @@ export const useLabelStore = defineStore('label', () => {
|
|||||||
deleteLabel,
|
deleteLabel,
|
||||||
updateLabel,
|
updateLabel,
|
||||||
createLabel,
|
createLabel,
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -309,6 +309,7 @@
|
|||||||
v-model="task.labels"
|
v-model="task.labels"
|
||||||
:disabled="!canWrite"
|
:disabled="!canWrite"
|
||||||
:task-id="taskId"
|
:task-id="taskId"
|
||||||
|
:creatable="!authStore.isLinkShareAuth"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -653,6 +654,7 @@ const projectStore = useProjectStore()
|
|||||||
const attachmentStore = useAttachmentStore()
|
const attachmentStore = useAttachmentStore()
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
const kanbanStore = useKanbanStore()
|
const kanbanStore = useKanbanStore()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
const task = ref<ITask>(new TaskModel())
|
const task = ref<ITask>(new TaskModel())
|
||||||
const taskTitle = computed(() => task.value.title)
|
const taskTitle = computed(() => task.value.title)
|
||||||
@ -877,7 +879,7 @@ function toggleTaskDone() {
|
|||||||
done: !task.value.done,
|
done: !task.value.done,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newTask.done && useAuthStore().settings.frontendSettings.playSoundWhenDone) {
|
if (newTask.done && authStore.settings.frontendSettings.playSoundWhenDone) {
|
||||||
playPopSound()
|
playPopSound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,23 +148,14 @@ func (l *Label) Delete(s *xorm.Session, _ web.Auth) (err error) {
|
|||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /labels [get]
|
// @Router /labels [get]
|
||||||
func (l *Label) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (ls interface{}, resultCount int, numberOfEntries int64, err error) {
|
func (l *Label) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (ls interface{}, resultCount int, numberOfEntries int64, err error) {
|
||||||
if _, is := a.(*LinkSharing); is {
|
|
||||||
return nil, 0, 0, ErrGenericForbidden{}
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := user.GetUserByID(s, a.GetID())
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||||
Search: []string{search},
|
Search: []string{search},
|
||||||
User: u,
|
User: a,
|
||||||
GetForUser: u.ID,
|
|
||||||
Page: page,
|
Page: page,
|
||||||
PerPage: perPage,
|
PerPage: perPage,
|
||||||
GetUnusedLabels: true,
|
GetUnusedLabels: true,
|
||||||
GroupByLabelIDsOnly: true,
|
GroupByLabelIDsOnly: true,
|
||||||
|
GetForUser: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/user"
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
@ -64,27 +63,29 @@ func (l *Label) isLabelOwner(s *xorm.Session, a web.Auth) (bool, error) {
|
|||||||
// Helper method to check if a user can see a specific label
|
// Helper method to check if a user can see a specific label
|
||||||
func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRight int, err error) {
|
func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRight int, err error) {
|
||||||
|
|
||||||
if _, is := a.(*LinkSharing); is {
|
linkShare, isLinkShare := a.(*LinkSharing)
|
||||||
return false, 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := user.GetUserByID(s, a.GetID())
|
var where builder.Cond
|
||||||
if err != nil {
|
var createdByID int64
|
||||||
return false, 0, err
|
if isLinkShare {
|
||||||
|
where = builder.Eq{"project_id": linkShare.ProjectID}
|
||||||
|
} else {
|
||||||
|
where = builder.In("project_id", getUserProjectsStatement(a.GetID(), "", false).Select("l.id"))
|
||||||
|
createdByID = a.GetID()
|
||||||
}
|
}
|
||||||
|
|
||||||
cond := builder.In("label_tasks.task_id",
|
cond := builder.In("label_tasks.task_id",
|
||||||
builder.
|
builder.
|
||||||
Select("id").
|
Select("id").
|
||||||
From("tasks").
|
From("tasks").
|
||||||
Where(builder.In("project_id", getUserProjectsStatement(u.ID, "", false).Select("l.id"))),
|
Where(where),
|
||||||
)
|
)
|
||||||
|
|
||||||
ll := &LabelTask{}
|
ll := &LabelTask{}
|
||||||
has, err = s.Table("labels").
|
has, err = s.Table("labels").
|
||||||
Select("label_tasks.*").
|
Select("label_tasks.*").
|
||||||
Join("LEFT", "label_tasks", "label_tasks.label_id = labels.id").
|
Join("LEFT", "label_tasks", "label_tasks.label_id = labels.id").
|
||||||
Where("label_tasks.label_id is not null OR labels.created_by_id = ?", u.ID).
|
Where("label_tasks.label_id is not null OR labels.created_by_id = ?", createdByID).
|
||||||
Or(cond).
|
Or(cond).
|
||||||
And("labels.id = ?", l.ID).
|
And("labels.id = ?", l.ID).
|
||||||
Exist(ll)
|
Exist(ll)
|
||||||
|
@ -144,7 +144,7 @@ func (lt *LabelTask) ReadAll(s *xorm.Session, a web.Auth, search string, page in
|
|||||||
}
|
}
|
||||||
|
|
||||||
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||||
User: &user.User{ID: a.GetID()},
|
User: a,
|
||||||
Search: []string{search},
|
Search: []string{search},
|
||||||
Page: page,
|
Page: page,
|
||||||
TaskIDs: []int64{lt.TaskID},
|
TaskIDs: []int64{lt.TaskID},
|
||||||
@ -159,23 +159,26 @@ type LabelWithTaskID struct {
|
|||||||
|
|
||||||
// LabelByTaskIDsOptions is a struct to not clutter the function with too many optional parameters.
|
// LabelByTaskIDsOptions is a struct to not clutter the function with too many optional parameters.
|
||||||
type LabelByTaskIDsOptions struct {
|
type LabelByTaskIDsOptions struct {
|
||||||
User *user.User
|
User web.Auth
|
||||||
Search []string
|
Search []string
|
||||||
Page int
|
Page int
|
||||||
PerPage int
|
PerPage int
|
||||||
TaskIDs []int64
|
TaskIDs []int64
|
||||||
GetUnusedLabels bool
|
GetUnusedLabels bool
|
||||||
GroupByLabelIDsOnly bool
|
GroupByLabelIDsOnly bool
|
||||||
GetForUser int64
|
GetForUser bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsByTaskIDs is a helper function to get all labels for a set of tasks
|
// GetLabelsByTaskIDs is a helper function to get all labels for a set of tasks
|
||||||
// Used when getting all labels for one task as well when getting all lables
|
// Used when getting all labels for one task as well when getting all lables
|
||||||
func GetLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*LabelWithTaskID, resultCount int, totalEntries int64, err error) {
|
func GetLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*LabelWithTaskID, resultCount int, totalEntries int64, err error) {
|
||||||
|
|
||||||
|
linkShare, isLinkShareAuth := opts.User.(*LinkSharing)
|
||||||
|
|
||||||
// We still need the task ID when we want to get all labels for a task, but because of this, we get the same label
|
// We still need the task ID when we want to get all labels for a task, but because of this, we get the same label
|
||||||
// multiple times when it is associated to more than one task.
|
// multiple times when it is associated to more than one task.
|
||||||
// Because of this whole thing, we need this extra switch here to only group by Task IDs if needed.
|
// Because of this whole thing, we need this extra switch here to only group by Task IDs if needed.
|
||||||
// Probably not the most ideal solution.
|
// Probably not the most ideataskdetaill solution.
|
||||||
var groupBy = "labels.id,label_tasks.task_id"
|
var groupBy = "labels.id,label_tasks.task_id"
|
||||||
var selectStmt = "labels.*, label_tasks.task_id"
|
var selectStmt = "labels.*, label_tasks.task_id"
|
||||||
if opts.GroupByLabelIDsOnly {
|
if opts.GroupByLabelIDsOnly {
|
||||||
@ -186,21 +189,26 @@ func GetLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*Lab
|
|||||||
// Get all labels associated with these tasks
|
// Get all labels associated with these tasks
|
||||||
var labels []*LabelWithTaskID
|
var labels []*LabelWithTaskID
|
||||||
cond := builder.And(builder.NotNull{"label_tasks.label_id"})
|
cond := builder.And(builder.NotNull{"label_tasks.label_id"})
|
||||||
if len(opts.TaskIDs) > 0 && opts.GetForUser == 0 {
|
if len(opts.TaskIDs) > 0 && !opts.GetForUser {
|
||||||
cond = builder.And(builder.In("label_tasks.task_id", opts.TaskIDs), cond)
|
cond = builder.And(builder.In("label_tasks.task_id", opts.TaskIDs), cond)
|
||||||
}
|
}
|
||||||
if opts.GetForUser != 0 {
|
if opts.GetForUser {
|
||||||
|
|
||||||
|
var projectIDs []int64
|
||||||
|
if isLinkShareAuth {
|
||||||
|
projectIDs = []int64{linkShare.ProjectID}
|
||||||
|
} else {
|
||||||
projects, _, _, err := getRawProjectsForUser(s, &projectOptions{
|
projects, _, _, err := getRawProjectsForUser(s, &projectOptions{
|
||||||
user: &user.User{ID: opts.GetForUser},
|
user: &user.User{ID: opts.User.GetID()},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
projectIDs := make([]int64, 0, len(projects))
|
projectIDs = make([]int64, 0, len(projects))
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
projectIDs = append(projectIDs, project.ID)
|
projectIDs = append(projectIDs, project.ID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cond = builder.And(builder.In("label_tasks.task_id",
|
cond = builder.And(builder.In("label_tasks.task_id",
|
||||||
builder.
|
builder.
|
||||||
@ -209,8 +217,8 @@ func GetLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*Lab
|
|||||||
Where(builder.In("project_id", projectIDs)),
|
Where(builder.In("project_id", projectIDs)),
|
||||||
), cond)
|
), cond)
|
||||||
}
|
}
|
||||||
if opts.GetUnusedLabels {
|
if opts.GetUnusedLabels && !isLinkShareAuth {
|
||||||
cond = builder.Or(cond, builder.Eq{"labels.created_by_id": opts.User.ID})
|
cond = builder.Or(cond, builder.Eq{"labels.created_by_id": opts.User.GetID()})
|
||||||
}
|
}
|
||||||
|
|
||||||
ids := []int64{}
|
ids := []int64{}
|
||||||
|
@ -419,7 +419,7 @@ func persistLabels(s *xorm.Session, a web.Auth, task *models.Task, labels []*mod
|
|||||||
existingLabels, _, _, err := models.GetLabelsByTaskIDs(s, &models.LabelByTaskIDsOptions{
|
existingLabels, _, _, err := models.GetLabelsByTaskIDs(s, &models.LabelByTaskIDsOptions{
|
||||||
Search: labelTitles,
|
Search: labelTitles,
|
||||||
User: u,
|
User: u,
|
||||||
GetForUser: u.ID,
|
GetForUser: true,
|
||||||
GetUnusedLabels: true,
|
GetUnusedLabels: true,
|
||||||
GroupByLabelIDsOnly: true,
|
GroupByLabelIDsOnly: true,
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user