Make sure to respect board acls in the frontend all over the place
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
@@ -38,7 +38,7 @@ return [
|
|||||||
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
|
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
|
||||||
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
|
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
|
||||||
['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'],
|
['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'],
|
||||||
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'PUT'],
|
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'],
|
||||||
['name' => 'board#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
|
['name' => 'board#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
|
||||||
['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'],
|
['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'],
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<h2><a href="#">{{ board.title }}</a></h2>
|
<h2><a href="#">{{ board.title }}</a></h2>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="board" class="board-actions">
|
<div v-if="board" class="board-actions">
|
||||||
<div id="stack-add" v-click-outside="hideAddStack">
|
<div v-if="canManage" id="stack-add" v-click-outside="hideAddStack">
|
||||||
<Actions v-if="!isAddStackVisible">
|
<Actions v-if="!isAddStackVisible">
|
||||||
<ActionButton icon="icon-add" :title="t('deck', 'Add new stack')" @click.stop="showAddStack" />
|
<ActionButton icon="icon-add" :title="t('deck', 'Add new stack')" @click.stop="showAddStack" />
|
||||||
</Actions>
|
</Actions>
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import { Actions, ActionButton } from '@nextcloud/vue'
|
import { Actions, ActionButton } from '@nextcloud/vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -101,6 +101,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'canEdit',
|
||||||
|
'canManage',
|
||||||
|
]),
|
||||||
...mapState({
|
...mapState({
|
||||||
compactMode: state => state.compactMode,
|
compactMode: state => state.compactMode,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -29,7 +29,10 @@
|
|||||||
<p />
|
<p />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="board" class="board">
|
<div v-else-if="board" class="board">
|
||||||
<Container lock-axix="y" orientation="horizontal" @drop="onDropStack">
|
<Container lock-axix="y"
|
||||||
|
orientation="horizontal"
|
||||||
|
:drag-handle-selector="dragHandleSelector"
|
||||||
|
@drop="onDropStack">
|
||||||
<Draggable v-for="stack in stacksByBoard" :key="stack.id">
|
<Draggable v-for="stack in stacksByBoard" :key="stack.id">
|
||||||
<Stack :stack="stack" />
|
<Stack :stack="stack" />
|
||||||
</Draggable>
|
</Draggable>
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import Controls from '../Controls'
|
import Controls from '../Controls'
|
||||||
import Stack from './Stack'
|
import Stack from './Stack'
|
||||||
|
|
||||||
@@ -77,9 +80,15 @@ export default {
|
|||||||
board: state => state.currentBoard,
|
board: state => state.currentBoard,
|
||||||
showArchived: state => state.showArchived,
|
showArchived: state => state.showArchived,
|
||||||
}),
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
'canEdit',
|
||||||
|
]),
|
||||||
stacksByBoard() {
|
stacksByBoard() {
|
||||||
return this.$store.getters.stacksByBoard(this.board.id)
|
return this.$store.getters.stacksByBoard(this.board.id)
|
||||||
},
|
},
|
||||||
|
dragHandleSelector() {
|
||||||
|
return this.canEdit ? null : '.no-drag'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
id: 'fetchData',
|
id: 'fetchData',
|
||||||
|
|||||||
@@ -33,7 +33,10 @@
|
|||||||
<TagsTabSidebar :board="board" />
|
<TagsTabSidebar :board="board" />
|
||||||
</AppSidebarTab>
|
</AppSidebarTab>
|
||||||
|
|
||||||
<AppSidebarTab :order="2" name="Deleted items" icon="icon-delete">
|
<AppSidebarTab v-if="canEdit"
|
||||||
|
:order="2"
|
||||||
|
name="Deleted items"
|
||||||
|
icon="icon-delete">
|
||||||
<DeletedTabSidebar :board="board" />
|
<DeletedTabSidebar :board="board" />
|
||||||
</AppSidebarTab>
|
</AppSidebarTab>
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import SharingTabSidebar from './SharingTabSidebar'
|
import SharingTabSidebar from './SharingTabSidebar'
|
||||||
import TagsTabSidebar from './TagsTabSidebar'
|
import TagsTabSidebar from './TagsTabSidebar'
|
||||||
import DeletedTabSidebar from './DeletedTabSidebar'
|
import DeletedTabSidebar from './DeletedTabSidebar'
|
||||||
@@ -73,6 +76,7 @@ export default {
|
|||||||
board: state => state.currentBoard,
|
board: state => state.currentBoard,
|
||||||
labels: state => state.labels,
|
labels: state => state.labels,
|
||||||
}),
|
}),
|
||||||
|
...mapGetters(['canEdit']),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeSidebar() {
|
closeSidebar() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Multiselect
|
<Multiselect
|
||||||
|
v-if="canShare"
|
||||||
v-model="addAcl"
|
v-model="addAcl"
|
||||||
:placeholder="t('deck', 'Share board with a user, group or circle …')"
|
:placeholder="t('deck', 'Share board with a user, group or circle …')"
|
||||||
:options="formatedSharees"
|
:options="formatedSharees"
|
||||||
@@ -17,6 +18,9 @@
|
|||||||
<Avatar :user="board.owner.uid" />
|
<Avatar :user="board.owner.uid" />
|
||||||
<span class="has-tooltip username">
|
<span class="has-tooltip username">
|
||||||
{{ board.owner.displayname }}
|
{{ board.owner.displayname }}
|
||||||
|
<span v-if="!isCurrentUser(board.owner.uid)" class="board-owner-label">
|
||||||
|
{{ t('deck', 'Board owner') }}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li v-for="acl in board.acl" :key="acl.participant.uid">
|
<li v-for="acl in board.acl" :key="acl.participant.uid">
|
||||||
@@ -29,17 +33,17 @@
|
|||||||
<span v-if="acl.type===7">{{ t('deck', '(Circle)') }}</span>
|
<span v-if="acl.type===7">{{ t('deck', '(Circle)') }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ActionCheckbox :checked="acl.permissionEdit" @change="clickEditAcl(acl)">
|
<ActionCheckbox v-if="!isCurrentUser(acl.participant.uid) && (canManage || (canEdit && canShare))" :checked="acl.permissionEdit" @change="clickEditAcl(acl)">
|
||||||
{{ t('deck', 'Can edit') }}
|
{{ t('deck', 'Can edit') }}
|
||||||
</ActionCheckbox>
|
</ActionCheckbox>
|
||||||
<Actions>
|
<Actions v-if="!isCurrentUser(acl.participant.uid)" :force-menu="true">
|
||||||
<ActionCheckbox :checked="acl.permissionShare" @change="clickShareAcl(acl)">
|
<ActionCheckbox v-if="canManage || canShare" :checked="acl.permissionShare" @change="clickShareAcl(acl)">
|
||||||
{{ t('deck', 'Can share') }}
|
{{ t('deck', 'Can share') }}
|
||||||
</ActionCheckbox>
|
</ActionCheckbox>
|
||||||
<ActionCheckbox :checked="acl.permissionManage" @change="clickManageAcl(acl)">
|
<ActionCheckbox v-if="canManage" :checked="acl.permissionManage" @change="clickManageAcl(acl)">
|
||||||
{{ t('deck', 'Can manage') }}
|
{{ t('deck', 'Can manage') }}
|
||||||
</ActionCheckbox>
|
</ActionCheckbox>
|
||||||
<ActionButton icon="icon-delete" @click="clickDeleteAcl(acl)">
|
<ActionButton v-if="canManage" icon="icon-delete" @click="clickDeleteAcl(acl)">
|
||||||
{{ t('deck', 'Delete') }}
|
{{ t('deck', 'Delete') }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Actions>
|
</Actions>
|
||||||
@@ -61,6 +65,7 @@ import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
|||||||
import { ActionCheckbox } from '@nextcloud/vue/dist/Components/ActionCheckbox'
|
import { ActionCheckbox } from '@nextcloud/vue/dist/Components/ActionCheckbox'
|
||||||
import { CollectionList } from 'nextcloud-vue-collections'
|
import { CollectionList } from 'nextcloud-vue-collections'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SharingTabSidebar',
|
name: 'SharingTabSidebar',
|
||||||
@@ -86,9 +91,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters([
|
||||||
sharees: 'sharees',
|
'sharees',
|
||||||
}),
|
'canEdit',
|
||||||
|
'canManage',
|
||||||
|
'canShare',
|
||||||
|
]),
|
||||||
|
isCurrentUser() {
|
||||||
|
return (uid) => uid === getCurrentUser().uid
|
||||||
|
},
|
||||||
formatedSharees() {
|
formatedSharees() {
|
||||||
return this.unallocatedSharees.map(item => {
|
return this.unallocatedSharees.map(item => {
|
||||||
|
|
||||||
@@ -173,6 +184,9 @@ export default {
|
|||||||
padding: 12px 9px;
|
padding: 12px 9px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
.board-owner-label {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
.avatarLabel {
|
.avatarLabel {
|
||||||
padding: 6px
|
padding: 6px
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,10 @@
|
|||||||
<div class="stack">
|
<div class="stack">
|
||||||
<div class="stack--header">
|
<div class="stack--header">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<h3 v-if="!editing" @click="startEditing(stack)">
|
<h3 v-if="!canManage">
|
||||||
|
{{ stack.title }}
|
||||||
|
</h3>
|
||||||
|
<h3 v-else-if="!editing" @click="startEditing(stack)">
|
||||||
{{ stack.title }}
|
{{ stack.title }}
|
||||||
</h3>
|
</h3>
|
||||||
<form v-else @submit.prevent="finishedEdit(stack)">
|
<form v-else @submit.prevent="finishedEdit(stack)">
|
||||||
@@ -36,12 +39,12 @@
|
|||||||
value="">
|
value="">
|
||||||
</form>
|
</form>
|
||||||
</transition>
|
</transition>
|
||||||
<Actions :force-menu="true">
|
<Actions v-if="canManage" :force-menu="true">
|
||||||
<ActionButton icon="icon-delete" @click="deleteStack(stack)">
|
<ActionButton icon="icon-delete" @click="deleteStack(stack)">
|
||||||
{{ t('deck', 'Delete stack') }}
|
{{ t('deck', 'Delete stack') }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Actions>
|
</Actions>
|
||||||
<Actions>
|
<Actions v-if="canEdit">
|
||||||
<ActionButton icon="icon-add" @click="showAddCard=true">
|
<ActionButton icon="icon-add" @click="showAddCard=true">
|
||||||
{{ t('deck', 'Add card') }}
|
{{ t('deck', 'Add card') }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
@@ -63,7 +66,11 @@
|
|||||||
value="">
|
value="">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<Container :get-child-payload="payloadForCard(stack.id)" group-name="stack" @drop="($event) => onDropCard(stack.id, $event)">
|
<Container :get-child-payload="payloadForCard(stack.id)"
|
||||||
|
group-name="stack"
|
||||||
|
:drag-handle-selector="dragHandleSelector"
|
||||||
|
@should-accept-drop="canEdit"
|
||||||
|
@drop="($event) => onDropCard(stack.id, $event)">
|
||||||
<Draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
|
<Draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
|
||||||
<CardItem v-if="card" :id="card.id" />
|
<CardItem v-if="card" :id="card.id" />
|
||||||
</Draggable>
|
</Draggable>
|
||||||
@@ -73,6 +80,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||||
import { Actions } from '@nextcloud/vue/dist/Components/Actions'
|
import { Actions } from '@nextcloud/vue/dist/Components/Actions'
|
||||||
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
||||||
@@ -103,13 +111,19 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'canManage',
|
||||||
|
'canEdit',
|
||||||
|
]),
|
||||||
cardsByStack() {
|
cardsByStack() {
|
||||||
return (id) => this.$store.getters.cardsByStack(id)
|
return (id) => this.$store.getters.cardsByStack(id)
|
||||||
},
|
},
|
||||||
|
dragHandleSelector() {
|
||||||
|
return this.canEdit ? null : '.no-drag'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
onDropCard(stackId, event) {
|
onDropCard(stackId, event) {
|
||||||
const { addedIndex, removedIndex, payload } = event
|
const { addedIndex, removedIndex, payload } = event
|
||||||
const card = Object.assign({}, payload)
|
const card = Object.assign({}, payload)
|
||||||
|
|||||||
@@ -21,8 +21,14 @@
|
|||||||
<div :style="{ backgroundColor: `#${label.color}`, color:textColor(label.color) }" class="label-title">
|
<div :style="{ backgroundColor: `#${label.color}`, color:textColor(label.color) }" class="label-title">
|
||||||
<span>{{ label.title }}</span>
|
<span>{{ label.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button v-tooltip="t('deck', 'Edit')" class="icon-rename" @click="clickEdit(label)" />
|
<button v-if="canManage"
|
||||||
<button v-tooltip="t('deck', 'Delete')" class="icon-delete" @click="deleteLabel(label.id)" />
|
v-tooltip="t('deck', 'Edit')"
|
||||||
|
class="icon-rename"
|
||||||
|
@click="clickEdit(label)" />
|
||||||
|
<button v-if="canManage"
|
||||||
|
v-tooltip="t('deck', 'Delete')"
|
||||||
|
class="icon-delete"
|
||||||
|
@click="deleteLabel(label.id)" />
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -43,7 +49,7 @@
|
|||||||
<ColorPicker :value="'#' + addLabelObj.color" @input="updateColor" />
|
<ColorPicker :value="'#' + addLabelObj.color" @input="updateColor" />
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
<button @click="clickShowAddLabel()">
|
<button v-if="canManage" @click="clickShowAddLabel()">
|
||||||
<span class="icon-add" />{{ t('deck', 'Add a new label') }}
|
<span class="icon-add" />{{ t('deck', 'Add a new label') }}
|
||||||
</button>
|
</button>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -75,6 +81,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
labels: 'currentBoardLabels',
|
labels: 'currentBoardLabels',
|
||||||
|
canManage: 'canManage',
|
||||||
}),
|
}),
|
||||||
addLabelObjValidated() {
|
addLabelObjValidated() {
|
||||||
if (this.addLabelObj.title === '') {
|
if (this.addLabelObj.title === '') {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<div class="section-details">
|
<div class="section-details">
|
||||||
<Multiselect v-model="allLabels"
|
<Multiselect v-model="allLabels"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
|
:disabled="!canEdit"
|
||||||
:options="currentBoard.labels"
|
:options="currentBoard.labels"
|
||||||
:placeholder="t('deck', 'Assign a tag to this card…')"
|
:placeholder="t('deck', 'Assign a tag to this card…')"
|
||||||
:taggable="true"
|
:taggable="true"
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="section-details">
|
<div class="section-details">
|
||||||
<Multiselect v-model="assignedUsers"
|
<Multiselect v-model="assignedUsers"
|
||||||
|
:disabled="!canEdit"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:options="assignableUsers"
|
:options="assignableUsers"
|
||||||
:placeholder="t('deck', 'Assign a user to this card…')"
|
:placeholder="t('deck', 'Assign a user to this card…')"
|
||||||
@@ -85,10 +87,11 @@
|
|||||||
:placeholder="t('deck', 'Set a due date')"
|
:placeholder="t('deck', 'Set a due date')"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
lang="en"
|
lang="en"
|
||||||
|
:disabled="!canEdit"
|
||||||
format="YYYY-MM-DD HH:mm"
|
format="YYYY-MM-DD HH:mm"
|
||||||
confirm
|
confirm
|
||||||
@change="setDue()" />
|
@change="setDue()" />
|
||||||
<Actions>
|
<Actions v-if="canEdit">
|
||||||
<ActionButton v-if="copiedCard.duedate" icon="icon-delete" @click="removeDue()">
|
<ActionButton v-if="copiedCard.duedate" icon="icon-delete" @click="removeDue()">
|
||||||
{{ t('deck', 'Remove due date') }}
|
{{ t('deck', 'Remove due date') }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
@@ -104,6 +107,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5>{{ t('deck', 'Description') }}</h5>
|
<h5>{{ t('deck', 'Description') }}</h5>
|
||||||
|
<!-- FIXME: make sure the editor is disabled when canEdit is false -->
|
||||||
<VueEasymde ref="markdownEditor" v-model="copiedCard.description" :configs="mdeConfig" />
|
<VueEasymde ref="markdownEditor" v-model="copiedCard.description" :configs="mdeConfig" />
|
||||||
</AppSidebarTab>
|
</AppSidebarTab>
|
||||||
|
|
||||||
@@ -127,7 +131,7 @@ import { Multiselect } from '@nextcloud/vue/dist/Components/Multiselect'
|
|||||||
import { AppSidebar } from '@nextcloud/vue/dist/Components/AppSidebar'
|
import { AppSidebar } from '@nextcloud/vue/dist/Components/AppSidebar'
|
||||||
import { AppSidebarTab } from '@nextcloud/vue/dist/Components/AppSidebarTab'
|
import { AppSidebarTab } from '@nextcloud/vue/dist/Components/AppSidebarTab'
|
||||||
import { DatetimePicker } from '@nextcloud/vue/dist/Components/DatetimePicker'
|
import { DatetimePicker } from '@nextcloud/vue/dist/Components/DatetimePicker'
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import VueEasymde from 'vue-easymde/dist/VueEasyMDE.common'
|
import VueEasymde from 'vue-easymde/dist/VueEasyMDE.common'
|
||||||
import { Actions } from '@nextcloud/vue/dist/Components/Actions'
|
import { Actions } from '@nextcloud/vue/dist/Components/Actions'
|
||||||
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
||||||
@@ -186,6 +190,7 @@ export default {
|
|||||||
currentBoard: state => state.currentBoard,
|
currentBoard: state => state.currentBoard,
|
||||||
assignableUsers: state => state.assignableUsers,
|
assignableUsers: state => state.assignableUsers,
|
||||||
}),
|
}),
|
||||||
|
...mapGetters(['canEdit']),
|
||||||
currentCard() {
|
currentCard() {
|
||||||
return this.$store.getters.cardById(this.id)
|
return this.$store.getters.cardById(this.id)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
class="card"
|
class="card"
|
||||||
@click.self="openCard">
|
@click.self="openCard">
|
||||||
<div class="card-upper">
|
<div class="card-upper">
|
||||||
<h3 v-if="showArchived">
|
<h3 v-if="showArchived || !canEdit">
|
||||||
{{ card.title }}
|
{{ card.title }}
|
||||||
</h3>
|
</h3>
|
||||||
<h3 v-else-if="!editing" @click.stop="startEditing(card)">
|
<h3 v-else-if="!editing" @click.stop="startEditing(card)">
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<input type="button" class="icon-confirm" @click="finishedEdit(card)">
|
<input type="button" class="icon-confirm" @click="finishedEdit(card)">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<Actions v-if="!editing" @click.stop.prevent>
|
<Actions v-if="canEdit && !editing" @click.stop.prevent>
|
||||||
<ActionButton v-if="showArchived === false" icon="icon-user" @click="assignCardToMe()">
|
<ActionButton v-if="showArchived === false" icon="icon-user" @click="assignCardToMe()">
|
||||||
{{ t('deck', 'Assign to me') }}
|
{{ t('deck', 'Assign to me') }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
@@ -103,7 +103,7 @@ import { Actions } from '@nextcloud/vue/dist/Components/Actions'
|
|||||||
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
||||||
import { Multiselect } from '@nextcloud/vue/dist/Components/Multiselect'
|
import { Multiselect } from '@nextcloud/vue/dist/Components/Multiselect'
|
||||||
import ClickOutside from 'vue-click-outside'
|
import ClickOutside from 'vue-click-outside'
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
|
|
||||||
import CardBadges from './CardBadges'
|
import CardBadges from './CardBadges'
|
||||||
@@ -139,6 +139,9 @@ export default {
|
|||||||
showArchived: state => state.showArchived,
|
showArchived: state => state.showArchived,
|
||||||
currentBoard: state => state.currentBoard,
|
currentBoard: state => state.currentBoard,
|
||||||
}),
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
'canEdit',
|
||||||
|
]),
|
||||||
card() {
|
card() {
|
||||||
return this.$store.getters.cardById(this.id)
|
return this.$store.getters.cardById(this.id)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -124,7 +124,9 @@ export default {
|
|||||||
|
|
||||||
// do not show actions while the item is loading
|
// do not show actions while the item is loading
|
||||||
if (this.loading === false) {
|
if (this.loading === false) {
|
||||||
|
const canManage = this.board.permissions.PERMISSION_MANAGE
|
||||||
|
|
||||||
|
if (canManage) {
|
||||||
actions.push({
|
actions.push({
|
||||||
action: () => {
|
action: () => {
|
||||||
this.hideMenu()
|
this.hideMenu()
|
||||||
@@ -135,6 +137,7 @@ export default {
|
|||||||
icon: 'icon-rename',
|
icon: 'icon-rename',
|
||||||
text: t('deck', 'Edit board'),
|
text: t('deck', 'Edit board'),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
action: async() => {
|
action: async() => {
|
||||||
@@ -154,7 +157,7 @@ export default {
|
|||||||
icon: 'icon-clone',
|
icon: 'icon-clone',
|
||||||
text: t('deck', 'Clone board'),
|
text: t('deck', 'Clone board'),
|
||||||
})
|
})
|
||||||
|
if (canManage) {
|
||||||
if (!this.board.archived) {
|
if (!this.board.archived) {
|
||||||
actions.push({
|
actions.push({
|
||||||
action: () => {
|
action: () => {
|
||||||
@@ -193,6 +196,7 @@ export default {
|
|||||||
icon: 'icon-delete',
|
icon: 'icon-delete',
|
||||||
text: t('deck', 'Delete board'),
|
text: t('deck', 'Delete board'),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
action: () => {
|
action: () => {
|
||||||
|
|||||||
@@ -96,6 +96,15 @@ export default new Vuex.Store({
|
|||||||
currentBoardLabels: state => {
|
currentBoardLabels: state => {
|
||||||
return state.currentBoard ? state.currentBoard.labels : []
|
return state.currentBoard ? state.currentBoard.labels : []
|
||||||
},
|
},
|
||||||
|
canEdit: state => {
|
||||||
|
return state.currentBoard ? state.currentBoard.permissions.PERMISSION_EDIT : false
|
||||||
|
},
|
||||||
|
canManage: state => {
|
||||||
|
return state.currentBoard ? state.currentBoard.permissions.PERMISSION_MANAGE : false
|
||||||
|
},
|
||||||
|
canShare: state => {
|
||||||
|
return state.currentBoard ? state.currentBoard.permissions.PERMISSION_SHARE : false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
toggleShowArchived(state) {
|
toggleShowArchived(state) {
|
||||||
@@ -218,7 +227,7 @@ export default new Vuex.Store({
|
|||||||
updateAclFromCurrentBoard(state, acl) {
|
updateAclFromCurrentBoard(state, acl) {
|
||||||
for (const acl_ in state.currentBoard.acl) {
|
for (const acl_ in state.currentBoard.acl) {
|
||||||
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
||||||
state.currentBoard.acl[acl_] = acl
|
Vue.set(state.currentBoard.acl, acl_, acl)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,6 +255,12 @@ export default new Vuex.Store({
|
|||||||
commit('setAssignableUsers', board.users)
|
commit('setAssignableUsers', board.users)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async refreshBoard({ commit }, boardId) {
|
||||||
|
const board = await apiClient.loadById(boardId)
|
||||||
|
commit('setCurrentBoard', board)
|
||||||
|
commit('setAssignableUsers', board.users)
|
||||||
|
},
|
||||||
|
|
||||||
toggleShowArchived({ commit }) {
|
toggleShowArchived({ commit }) {
|
||||||
commit('toggleShowArchived')
|
commit('toggleShowArchived')
|
||||||
},
|
},
|
||||||
@@ -392,7 +407,7 @@ export default new Vuex.Store({
|
|||||||
apiClient.addAcl(newAcl)
|
apiClient.addAcl(newAcl)
|
||||||
.then((returnAcl) => {
|
.then((returnAcl) => {
|
||||||
commit('addAclToCurrentBoard', returnAcl)
|
commit('addAclToCurrentBoard', returnAcl)
|
||||||
dispatch('loadBoardById', newAcl.boardId)
|
dispatch('refreshBoard', newAcl.boardId)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateAclFromCurrentBoard({ commit }, acl) {
|
updateAclFromCurrentBoard({ commit }, acl) {
|
||||||
|
|||||||
Reference in New Issue
Block a user