Skip to content

Commit

Permalink
Added expiration date to collaborators
Browse files Browse the repository at this point in the history
Added acceptance tests
  • Loading branch information
LukasHirt committed Feb 28, 2020
1 parent 8427f34 commit eb1a9ec
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 40 deletions.
8 changes: 5 additions & 3 deletions apps/files/src/components/Collaborators/Collaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
(me)
</translate>
</div>
<span class="oc-text"><span class="files-collaborators-collaborator-role">{{ originalRole.label }}</span><template v-if="collaborator.expires"> | <translate :translate-params="{expires: formDateFromNow(collaborator.expires)}">Expires: %{expires}</translate></template></span>
<span class="uk-text-meta files-collaborators-collaborator-share-type" v-text="$_ocCollaborators_collaboratorType(collaborator.shareType)" />
<span class="oc-text"><span class="files-collaborators-collaborator-role">{{ originalRole.label }}</span><template v-if="collaborator.expires"> | <translate class="files-collaborators-collaborator-expires" :translate-params="{expires: formDateFromNow(collaborator.expires)}">Expires %{expires}</translate></template></span>
</div>
</oc-table-cell>
<oc-table-cell shrink>
Expand Down Expand Up @@ -94,12 +94,14 @@
import { mapGetters } from 'vuex'
import { shareTypes } from '../../helpers/shareTypes'
import { basename, dirname } from 'path'
import Mixins from '../../mixins/collaborators'
import CollaboratorsMixins from '../../mixins/collaborators'
import Mixins from '../../mixins'
export default {
name: 'Collaborator',
mixins: [
Mixins
Mixins,
CollaboratorsMixins
],
props: {
collaborator: {
Expand Down
176 changes: 163 additions & 13 deletions apps/files/src/components/Collaborators/CollaboratorsEditOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,54 @@
:collaboratorsPermissions="collaboratorsPermissions"
@permissionChecked="checkAdditionalPermissions"
/>
<oc-grid v-if="$_ocCollaborators_expirationSupported" gutter="small">
<div class="uk-width-1-1">
<label class="oc-label" for="files-collaborators-new-collaborator-expiration">
<translate>Expiration date</translate>
<translate class="uk-text-meta uk-remove-margin">(optional)</translate>
</label>
<oc-text-input type="date" class="uk-width-1-1 oc-button-role" id="files-collaborators-new-collaborator-expiration" :value="expirationDate">04 - 07 - 2019</oc-text-input>
<div v-if="expirationSupported">
<label for="files-collaborators-collaborator-expiration-input">
<translate>Expiration date:</translate>
<translate v-if="expirationDateEnforced" tag="em">(required)</translate>
</label>
<div class="uk-position-relative">
<oc-datepicker
id="files-collaborators-collaborator-expiration-input"
:key="`collaborator-datepicker-${enteredExpirationDate}`"
:date="enteredExpirationDate"
:maxDatetime="maxExpirationDate"
:minDatetime="minExpirationDate"
:placeholder="expirationDatePlaceholder"
@input="setExpirationDate"
/>
<div
v-if="canResetExpirationDate"
id="files-collaborators-collaborator-expiration-delete"
class="uk-position-small uk-position-center-right oc-cursor-pointer"
:uk-tooltip="expirationDateRemoveTooltip"
@click="resetExpirationDate"
uk-close
/>
</div>
</oc-grid>
</div>
</oc-grid>
</template>

<script>
import { mapGetters } from 'vuex'
import moment from 'moment'
import collaboratorsMixins from '../../mixins/collaborators'
const RolesSelect = () => import('../Roles/RolesSelect.vue')
const AdditionalPermissions = () => import('./AdditionalPermissions.vue')
export default {
name: 'CollaboratorsEditOptions',
components: {
RolesSelect,
AdditionalPermissions
},
mixins: [
collaboratorsMixins
],
props: {
existingRole: {
type: Object,
Expand All @@ -51,19 +72,35 @@ export default {
required: false
},
expirationDate: {
type: String,
type: Date,
required: false
},
existingCollaboratorType: {
type: [Object, String],
required: false,
validator: function (value) {
return ['user', 'group'].indexOf(value) > -1 || value === null
}
}
},
data () {
return {
selectedRole: null,
additionalPermissions: null
additionalPermissions: null,
enteredExpirationDate: null
}
},
computed: {
$_ocCollaborators_expirationSupported () {
return false
...mapGetters(['capabilities']),
editingUser () {
return this.existingCollaboratorType === 'user'
},
editingGroup () {
return this.existingCollaboratorType === 'group'
},
$_ocCollaborators_hasAdditionalPermissions () {
Expand Down Expand Up @@ -98,8 +135,104 @@ export default {
}
return this.selectedRole
},
expirationSupported () {
return this.userExpirationDate && this.groupExpirationDate
},
defaultExpirationDateSet () {
if (this.editingUser) {
return this.userExpirationDate.enabled
}
if (this.editingGroup) {
return this.groupExpirationDate.enabled
}
return this.userExpirationDate.enabled || this.groupExpirationDate.enabled
},
userExpirationDate () {
return this.capabilities.files_sharing.user.expire_date
},
groupExpirationDate () {
return this.capabilities.files_sharing.group.expire_date
},
defaultExpirationDate () {
if (!this.defaultExpirationDateSet) {
return null
}
const userMaxExpirationDays = parseInt(this.userExpirationDate.days, 10)
const groupMaxExpirationDays = parseInt(this.groupExpirationDate.days, 10)
if (this.editingUser) {
return moment().add(userMaxExpirationDays, 'days').endOf('day').toISOString()
}
if (this.editingGroup) {
return moment().add(groupMaxExpirationDays, 'days').endOf('day').toISOString()
}
// Since we are not separating process for adding users and groups as collaborators
// we are using the one which is smaller as enforced date
let days = 0
if (userMaxExpirationDays && groupMaxExpirationDays) {
days = Math.min(userMaxExpirationDays, groupMaxExpirationDays)
} else {
days = userMaxExpirationDays || groupMaxExpirationDays
}
return moment().add(days, 'days').endOf('day').toISOString()
},
expirationDateEnforced () {
if (this.editingUser) {
return this.userExpirationDate.enforced
}
if (this.editingGroup) {
return this.groupExpirationDate.enforced
}
return this.userExpirationDate.enforced || this.groupExpirationDate.enforced
},
maxExpirationDate () {
if (!this.expirationDateEnforced) {
return null
}
return this.defaultExpirationDate
},
minExpirationDate () {
return moment().add(1, 'days').endOf('day').toISOString()
},
expirationDatePlaceholder () {
return this.$gettext('Expiration date')
},
expirationDateRemoveTooltip () {
return this.$gettext('Remove expiration date')
},
canResetExpirationDate () {
return !this.expirationDateEnforced && this.enteredExpirationDate
}
},
mounted () {
// FIXME: Datepicker is not displaying correct timezone so for now we add it manually
// this.enteredExpirationDate = this.expirationDate ? moment(this.expirationDate).toISOString(true) : this.defaultExpirationDate
this.enteredExpirationDate = this.expirationDate ? moment(this.expirationDate).add(moment().utcOffset(), 'm').toISOString() : this.defaultExpirationDate
},
methods: {
selectRole (role) {
this.selectedRole = role
Expand All @@ -111,8 +244,25 @@ export default {
this.publishChange()
},
setExpirationDate (date) {
this.enteredExpirationDate = date
this.publishChange()
},
resetExpirationDate () {
this.enteredExpirationDate = null
this.publishChange()
// Emit reset event to register a change
this.$emit('expirationDateResetted')
},
publishChange () {
this.$emit('optionChange', { role: this.selectedRole, permissions: this.additionalPermissions, expirationDate: this.expirationDate })
this.$emit('optionChange', {
role: this.selectedRole,
permissions: this.additionalPermissions,
expirationDate: this.enteredExpirationDate
})
}
}
}
Expand Down
55 changes: 53 additions & 2 deletions apps/files/src/components/Collaborators/EditCollaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
<collaborators-edit-options
:existingRole="$_originalRole"
:collaboratorsPermissions="$_originalPermissions"
:expirationDate="originalExpirationDate"
:existingCollaboratorType="collaboratorType"
@optionChange="collaboratorOptionChanged"
@expirationDateResetted="registerExpirationDateReset"
class="uk-margin-bottom"
/>
<hr class="divider" />
Expand All @@ -31,7 +34,9 @@
<script>
import filterObject from 'filter-obj'
import { mapGetters, mapActions } from 'vuex'
import moment from 'moment'
import { roleToBitmask, bitmaskToRole } from '../../helpers/collaborators'
import { shareTypes } from '../../helpers/shareTypes'
import Collaborator from './Collaborator.vue'
import CollaboratorsEditOptions from './CollaboratorsEditOptions.vue'
import Mixins from '../../mixins/collaborators'
Expand All @@ -55,13 +60,34 @@ export default {
return {
selectedRole: null,
additionalPermissions: null,
saving: false
saving: false,
expirationDate: null,
// Since expiration date can ba reset to null we can't depend on it when checking for changes
expirationDateResetted: false
}
},
computed: {
...mapGetters('Files', ['highlightedFile']),
...mapGetters(['user']),
collaboratorType () {
const collaboratorShareType = this.collaborator.shareType
if (
collaboratorShareType === shareTypes.user ||
collaboratorShareType === shareTypes.guest ||
collaboratorShareType === shareTypes.remote
) {
return 'user'
}
if (collaboratorShareType === shareTypes.group) {
return 'group'
}
return null
},
$_originalPermissions () {
const permissions = this.collaborator.customPermissions
return filterObject(permissions, (key, value) => value)
Expand All @@ -82,8 +108,26 @@ export default {
// if the role has changed, always return true. The user doesn't need to understand if two bitmasks of different roles are the same!
return true
}
// FIXME: Datepicker is not displaying correct timezone so for now we add it manually
const originalExpirationDate = moment(this.originalExpirationDate).add(moment().utcOffset(), 'm').toISOString()
if ((this.expirationDate || this.expirationDateResetted) && this.expirationDate !== originalExpirationDate) {
return true
}
const originalBitmask = roleToBitmask(this.$_originalRole, Object.keys(this.$_originalPermissions))
return originalBitmask !== this.$_permissionsBitmask
},
originalExpirationDate () {
const expirationDate = this.collaborator.expires
if (expirationDate) {
return expirationDate
}
return null
}
},
methods: {
Expand All @@ -100,7 +144,8 @@ export default {
share: this.collaborator,
// Map bitmask to role to get the correct role in case the advanced role was mapped to existing role
role: bitmaskToRole(bitmask, this.highlightedFile.type === 'folder'),
permissions: bitmask
permissions: bitmask,
expirationDate: this.expirationDate
})
.then(() => this.$_ocCollaborators_cancelChanges())
.catch(() => {
Expand All @@ -111,12 +156,18 @@ export default {
$_ocCollaborators_cancelChanges () {
this.selectedRole = null
this.additionalPermissions = null
this.expirationDate = this.originalExpirationDate
this.expirationDateResetted = false
this.saving = false
this.close()
},
close () {
this.$emit('close')
},
registerExpirationDateReset () {
this.expirationDateResetted = true
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions apps/files/src/components/Collaborators/NewCollaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ export default {
selectedCollaborators: [],
selectedRole: null,
additionalPermissions: null,
saving: false
saving: false,
expirationDate: null
}
},
computed: {
Expand Down Expand Up @@ -209,7 +210,8 @@ export default {
$gettext: this.$gettext,
shareWith: collaborator.value.shareWith,
shareType: collaborator.value.shareType,
permissions: roleToBitmask(this.selectedRole, this.additionalPermissions)
permissions: roleToBitmask(this.selectedRole, this.additionalPermissions),
expirationDate: this.expirationDate
})))
})
Expand Down
Loading

0 comments on commit eb1a9ec

Please sign in to comment.