Skip to content

Commit

Permalink
Don't edit not public tasks in lists shared to me
Browse files Browse the repository at this point in the history
Signed-off-by: Raimund Schlüßler <raimund.schluessler@mailbox.org>
  • Loading branch information
raimund-schluessler committed Feb 10, 2020
1 parent 00618b4 commit a103fd7
Show file tree
Hide file tree
Showing 10 changed files with 376 additions and 55 deletions.
5 changes: 5 additions & 0 deletions css/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
display: none;
}
}

.app-navigation-entry__utils .icon-loading {
height: 32px;
width: 32px;
}
}

&.edit {
Expand Down
45 changes: 26 additions & 19 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,34 @@ export default {
calendars: state => state.calendars.calendars,
}),
},
beforeMount() {
async beforeMount() {
// get calendars then get tasks
client.connect({ enableCalDAV: true }).then(() => {
this.$store.dispatch('getCalendars')
.then((calendars) => {
// No calendars? Create a new one!
if (calendars.length === 0) {
let color = '#0082C9'
if (this.$OCA.Theming) {
color = this.$OCA.Theming.color
}
this.$store.dispatch('appendCalendar', { displayName: this.$t('tasks', 'Tasks'), color })
.then(() => {
this.fetchTasks()
})
// else, let's get those tasks!
} else {
this.fetchTasks()
}
})
await client.connect({ enableCalDAV: true })
await this.$store.dispatch('fetchCurrentUserPrincipal')
const calendars = await this.$store.dispatch('getCalendars')
const owners = []
calendars.forEach((calendar) => {
if (owners.indexOf(calendar.owner) === -1) {
owners.push(calendar.owner)
}
})
owners.forEach((owner) => {
this.$store.dispatch('fetchPrincipalByUrl', {
url: owner,
})
})
// No calendars? Create a new one!
if (calendars.length === 0) {
let color = '#0082C9'
if (this.$OCA.Theming) {
color = this.$OCA.Theming.color
}
await this.$store.dispatch('appendCalendar', { displayName: this.$t('tasks', 'Tasks'), color })
this.fetchTasks()
// else, let's get those tasks!
} else {
this.fetchTasks()
}
},
methods: {
/**
Expand Down
53 changes: 43 additions & 10 deletions src/components/AppNavigation/ListItemCalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<AppNavigationIconBullet slot="icon" :color="calendar.color" />

<template v-if="!deleteTimeout" slot="counter">
<AppNavigationCounter>
{{ calendarCount(calendar.id) | counterFormatter }}
</AppNavigationCounter>
<Actions v-if="!calendar.readOnly">
<Actions v-if="calendar.canBeShared">
<ActionButton
:icon="sharingIconClass"
@click="toggleShare">
{{ sharedWithTooltip }}
</ActionButton>
</Actions>
<Avatar v-if="calendar.isSharedWithMe && calendar.loadedOwnerPrincipal" :user="calendar.ownerUserId" :display-name="calendar.ownerDisplayname" />
<div v-if="calendar.isSharedWithMe && !calendar.loadedOwnerPrincipal" class="icon icon-loading" />
<Avatar v-if="calendar.isSharedWithMe && loadedOwnerPrincipal" :user="ownerUserId" :display-name="ownerDisplayname" />
<div v-if="calendar.isSharedWithMe && !loadedOwnerPrincipal" class="icon icon-loading" />
<AppNavigationCounter v-if="calendarCount">
{{ calendarCount | counterFormatter }}
</AppNavigationCounter>
</template>

<template v-if="!deleteTimeout" slot="actions">
Expand All @@ -75,13 +75,12 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
{{ $t('tasks', 'Download') }}
</ActionLink>
<ActionButton
v-if="!calendar.readOnly"
v-tooltip="{
placement: 'left',
boundariesElement: 'body',
content: deleteMessage
}"
icon="icon-delete"
:icon="calendar.isSharedWithMe ? 'icon-close' : 'icon-delete'"
@click="scheduleDelete">
{{ !calendar.isSharedWithMe ? $t('tasks', 'Delete') : $t('tasks', 'Unshare') }}
</ActionButton>
Expand All @@ -91,7 +90,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<ActionButton
icon="icon-history"
@click.prevent.stop="cancelDelete">
{{ $n('tasks', 'Deleting the calendar in {countdown} second', 'Deleting the calendar in {countdown} seconds', countdown, { countdown }) }}
{{ undoDeleteMessage }}
</ActionButton>
</template>

Expand Down Expand Up @@ -130,6 +129,7 @@ import Colorpicker from './Colorpicker'
import ShareCalendar from './CalendarShare'
import ClickOutside from 'vue-click-outside'
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
import AppNavigationCounter from '@nextcloud/vue/dist/Components/AppNavigationCounter'
import AppNavigationIconBullet from '@nextcloud/vue/dist/Components/AppNavigationIconBullet'
Expand All @@ -143,6 +143,7 @@ export default {
components: {
Colorpicker,
ShareCalendar,
Avatar,
AppNavigationItem,
AppNavigationCounter,
AppNavigationIconBullet,
Expand Down Expand Up @@ -190,16 +191,26 @@ export default {
},
computed: {
...mapGetters({
calendarCount: 'getCalendarCount',
getCalendarCount: 'getCalendarCount',
isCalendarNameUsed: 'isCalendarNameUsed',
getTask: 'getTaskByUri',
getPrincipalByUrl: 'getPrincipalByUrl',
}),
calendarCount() {
return this.getCalendarCount(this.calendar.id)
},
deleteMessage() {
return !this.calendar.isSharedWithMe
? this.$t('tasks', 'This will delete the calendar "{calendar}" and all corresponding events and tasks.', { calendar: this.calendar.displayName })
: this.$t('tasks', 'This will unshare the calendar "{calendar}".', { calendar: this.calendar.displayName })
},
undoDeleteMessage() {
return !this.calendar.isSharedWithMe
? this.$n('tasks', 'Deleting the calendar in {countdown} second', 'Deleting the calendar in {countdown} seconds', this.countdown, { countdown: this.countdown })
: this.$n('tasks', 'Unsharing the calendar in {countdown} second', 'Unsharing the calendar in {countdown} seconds', this.countdown, { countdown: this.countdown })
},
sharingIconClass() {
if (this.calendar.shares.length) {
return 'icon-shared'
Expand Down Expand Up @@ -233,6 +244,28 @@ export default {
})
: '' // disable the tooltip
},
/**
* Whether or not the information about the owner principal was loaded
*
* @returns {Boolean}
*/
loadedOwnerPrincipal() {
return this.getPrincipalByUrl(this.calendar.owner) !== undefined
},
ownerUserId() {
const principal = this.getPrincipalByUrl(this.calendar.owner)
if (principal) {
return principal.userId
}
return ''
},
ownerDisplayname() {
const principal = this.getPrincipalByUrl(this.calendar.owner)
if (principal) {
return principal.displayname
}
return ''
},
},
methods: {
...mapActions([
Expand Down
56 changes: 35 additions & 21 deletions src/components/TheDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<template>
<div class="content-wrapper">
<div v-if="task"
:class="{'disabled': task.calendar.readOnly}"
:class="{'disabled': readOnly}"
class="flex-container">
<div :class="{'editing': edit=='summary'}" class="title">
<span class="detail-checkbox">
<input :id="'detailsToggleCompleted_' + task.uid"
type="checkbox"
class="checkbox"
name="detailsToggleCompleted"
:class="{'disabled': task.calendar.readOnly}"
:class="{'disabled': readOnly}"
:checked="task.completed"
:aria-checked="task.completed"
:disabled="task.calendar.readOnly"
:disabled="readOnly"
:aria-label="$t('tasks', 'Task is completed')"
@click="toggleCompleted(task)">
<label :for="'detailsToggleCompleted_' + task.uid" />
Expand All @@ -56,16 +56,16 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</div>
<TaskStatusDisplay :task="task" />
<button class="reactive inline" @click="togglePinned(task)">
<span :class="[{'disabled': task.calendar.readOnly}, iconPinned]" class="icon" />
<span :class="[{'disabled': readOnly}, iconPinned]" class="icon" />
</button>
<button class="reactive inline" @click="toggleStarred(task)">
<span :class="[{'disabled': task.calendar.readOnly}, iconStar]"
<span :class="[{'disabled': readOnly}, iconStar]"
class="icon" />
</button>
</div>
<div class="body">
<ul class="sections">
<li v-show="!task.calendar.readOnly || task.start"
<li v-show="!readOnly || task.start"
:class="{'date': task.startMoment.isValid(), 'editing': edit=='start', 'high': overdue(task.startMoment)}"
class="section detail-start">
<div v-click-outside="() => finishEditing('start')"
Expand Down Expand Up @@ -109,7 +109,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</button>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.due"
<li v-show="!readOnly || task.due"
:class="{'date': task.dueMoment.isValid(), 'editing': edit=='due', 'high': overdue(task.dueMoment)}"
class="section detail-date">
<div v-click-outside="() => finishEditing('due')"
Expand Down Expand Up @@ -160,10 +160,10 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
type="checkbox"
class="checkbox"
name="isAllDayPossible"
:class="{'disabled': task.calendar.readOnly}"
:class="{'disabled': readOnly}"
:aria-checked="allDay"
:checked="allDay"
:disabled="task.calendar.readOnly"
:disabled="readOnly"
@click="toggleAllDay(task)">
<label for="isAllDayPossible">
<span>{{ $t('tasks', 'All day') }}</span>
Expand All @@ -182,7 +182,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:value="task.calendar"
:multiple="false"
:allow-empty="false"
:disabled="task.calendar.readOnly"
:disabled="readOnly"
track-by="id"
:placeholder="$t('tasks', 'Select a calendar')"
label="displayName"
Expand All @@ -206,7 +206,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:value="classSelect.find( _ => _.type === task.class )"
:multiple="false"
:allow-empty="false"
:disabled="task.calendar.readOnly"
:disabled="readOnly || task.calendar.isSharedWithMe"
track-by="type"
:placeholder="$t('tasks', 'Select a classification')"
label="displayName"
Expand All @@ -218,7 +218,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.status"
<li v-show="!readOnly || task.status"
class="section detail-class reactive">
<div v-click-outside="() => finishEditing('status')"
class="section-content"
Expand All @@ -231,7 +231,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:value="statusSelect.find( _ => _.type === task.status )"
:multiple="false"
:allow-empty="false"
:disabled="task.calendar.readOnly"
:disabled="readOnly"
track-by="type"
:placeholder="$t('tasks', 'Select a status')"
label="displayName"
Expand All @@ -243,7 +243,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.priority"
<li v-show="!readOnly || task.priority"
:class="[{'editing': edit=='priority', 'date': task.priority>0}, priorityClass]"
class="section detail-priority">
<div v-click-outside="() => finishEditing('priority')"
Expand Down Expand Up @@ -279,7 +279,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</button>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.complete"
<li v-show="!readOnly || task.complete"
:class="{'editing': edit=='complete', 'date': task.complete>0}"
class="section detail-complete">
<div v-click-outside="() => finishEditing('complete')"
Expand Down Expand Up @@ -315,7 +315,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</button>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.categories.length>0" :class="{'active': task.categories.length>0}" class="section detail-categories">
<li v-show="!readOnly || task.categories.length>0" :class="{'active': task.categories.length>0}" class="section detail-categories">
<div class="section-content">
<span class="section-icon">
<span :class="[iconCategories]" class="icon detail-categories" />
Expand All @@ -325,7 +325,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
v-model="task.categories"
:multiple="true"
:searchable="true"
:disabled="task.calendar.readOnly"
:disabled="readOnly"
:options="task.categories"
:placeholder="$t('tasks', 'Select categories')"
:taggable="true"
Expand All @@ -337,7 +337,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</li>
<li v-show="!task.calendar.readOnly || task.note" class="section detail-note">
<li v-show="!readOnly || task.note" class="section detail-note">
<div class="section-content note">
<div v-click-outside="() => finishEditing('note')"
class="note-body selectable"
Expand All @@ -359,7 +359,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</ul>
</div>
<div class="footer">
<button :style="{visibility: task.calendar.readOnly ? 'hidden' : 'visible'}"
<button :style="{visibility: readOnly ? 'hidden' : 'visible'}"
class="close-all reactive inline"
@click="removeTask">
<span class="icon icon-bw icon-trash" />
Expand Down Expand Up @@ -446,6 +446,16 @@ export default {
}
},
computed: {
/**
* Whether we treat the task as read-only.
* We also treat tasks in shared calendars with an access class other than 'PUBLIC'
* as read-only.
*
* @returns {Boolean} Is the task read-only
*/
readOnly() {
return this.task.calendar.readOnly || (this.task.calendar.isSharedWithMe && this.task.class !== 'PUBLIC')
},
/**
* Whether the dates of a task are all-day
* When no dates are set, we consider the last used value.
Expand Down Expand Up @@ -583,7 +593,7 @@ export default {
+ (this.task.completed ? ('<br />' + this.$t('tasks', 'Completed {date}', { date: this.task.completedDateMoment.calendar() })) : '')
},
isAllDayPossible: function() {
return !this.task.calendar.readOnly && (this.task.due || this.task.start || ['start', 'due'].includes(this.edit))
return !this.readOnly && (this.task.due || this.task.start || ['start', 'due'].includes(this.edit))
},
priorityClass: function() {
if (+this.task.priority > 5) {
Expand Down Expand Up @@ -763,11 +773,15 @@ export default {
if (event && (event.target.classList.contains('mx-datepicker-btn-confirm') || event.target.tagName === 'A')) {
return
}
// Don't allow to change the access class in calendars shared with me.
if (this.task.calendar.isSharedWithMe && type === 'class') {
return
}
// Save possible edits before starting to edit another property.
if (this.edit !== type) {
this.finishEditing(this.edit)
}
if (!this.task.calendar.readOnly && this.edit !== type) {
if (!this.readOnly && this.edit !== type) {
this.edit = type
this.tmpTask[type] = this.task[type]
// If we edit the due or the start date, inintialize it.
Expand Down

0 comments on commit a103fd7

Please sign in to comment.