Skip to content
29 changes: 28 additions & 1 deletion apps/files_sharing/lib/Controller/ShareAPIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ public function deleteShare(string $id): DataResponse {
* @param string $sendPasswordByTalk
* @param string $expireDate
* @param string $label
* @param string $hideDownload
*
* @return DataResponse
* @throws NotFoundException
Expand All @@ -449,7 +450,8 @@ public function createShare(
string $password = '',
string $sendPasswordByTalk = null,
string $expireDate = '',
string $label = ''
string $label = '',
string $hideDownload = null
): DataResponse {
$share = $this->shareManager->newShare();

Expand Down Expand Up @@ -506,6 +508,15 @@ public function createShare(
}
$share->setSharedWith($shareWith);
$share->setPermissions($permissions);

if ($expireDate !== '') {
try {
$expireDate = $this->parseDate($expireDate);
$share->setExpirationDate($expireDate);
} catch (\Exception $e) {
throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
}
}
} elseif ($shareType === IShare::TYPE_GROUP) {
if (!$this->shareManager->allowGroupSharing()) {
throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
Expand All @@ -517,6 +528,15 @@ public function createShare(
}
$share->setSharedWith($shareWith);
$share->setPermissions($permissions);

if ($expireDate !== '') {
try {
$expireDate = $this->parseDate($expireDate);
$share->setExpirationDate($expireDate);
} catch (\Exception $e) {
throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
}
}
} elseif ($shareType === IShare::TYPE_LINK
|| $shareType === IShare::TYPE_EMAIL) {

Expand Down Expand Up @@ -544,6 +564,13 @@ public function createShare(
$permissions = Constants::PERMISSION_READ;
}

// Update hide download state
if ($hideDownload === 'true') {
$share->setHideDownload(true);
} elseif ($hideDownload === 'false') {
$share->setHideDownload(false);
}

// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
if (($permissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
$permissions |= Constants::PERMISSION_SHARE;
Expand Down
240 changes: 80 additions & 160 deletions apps/files_sharing/src/components/SharingEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,106 +38,49 @@
<span>{{ share.status.icon || '' }}</span>
<span>{{ share.status.message || '' }}</span>
</p>

<template v-if="share.canEdit">
<!-- folder -->
<template v-if="isFolder && fileHasCreatePermission && config.isPublicUploadEnabled">
<select
:name="randomId"
@change="togglePermissions">
<option :value="publicUploadRValue" :selected="sharePermissions === publicUploadRValue">{{ t('files_sharing', 'Read only') }}</option>
<option :value="publicUploadRWValue" :selected="sharePermissions === publicUploadRWValue">{{ t('files_sharing', 'Read, write and upload') }}</option>
</select>
</template>
<!-- files -->
<template v-else>
<select
:name="randomId"
@change="togglePermissions">
<option :value="publicUploadRValue" :selected="sharePermissions === publicUploadRValue">{{ t('files_sharing', 'Read only') }}</option>
<option :value="publicUploadEValue" :selected="sharePermissions === publicUploadEValue">{{ t('files_sharing', 'Read and write') }}</option>
</select>
</template>
</template>
</component>
<Actions
<Actions v-if="open === false"
menu-align="right"
class="sharing-entry__actions"
@close="onMenuClose">
<template v-if="share.canEdit">
<!-- edit permission -->
<ActionCheckbox
ref="canEdit"
:checked.sync="canEdit"
:value="permissionsEdit"
:disabled="saving || !canSetEdit">
{{ t('files_sharing', 'Allow editing') }}
</ActionCheckbox>

<!-- create permission -->
<ActionCheckbox
v-if="isFolder"
ref="canCreate"
:checked.sync="canCreate"
:value="permissionsCreate"
:disabled="saving || !canSetCreate">
{{ t('files_sharing', 'Allow creating') }}
</ActionCheckbox>

<!-- delete permission -->
<ActionCheckbox
v-if="isFolder"
ref="canDelete"
:checked.sync="canDelete"
:value="permissionsDelete"
:disabled="saving || !canSetDelete">
{{ t('files_sharing', 'Allow deleting') }}
</ActionCheckbox>

<!-- reshare permission -->
<ActionCheckbox
v-if="config.isResharingAllowed"
ref="canReshare"
:checked.sync="canReshare"
:value="permissionsShare"
:disabled="saving || !canSetReshare">
{{ t('files_sharing', 'Allow resharing') }}
</ActionCheckbox>

<!-- expiration date -->
<ActionCheckbox :checked.sync="hasExpirationDate"
:disabled="config.isDefaultInternalExpireDateEnforced || saving"
@uncheck="onExpirationDisable">
{{ config.isDefaultInternalExpireDateEnforced
? t('files_sharing', 'Expiration date enforced')
: t('files_sharing', 'Set expiration date') }}
</ActionCheckbox>
<ActionInput v-if="hasExpirationDate"
ref="expireDate"
v-tooltip.auto="{
content: errors.expireDate,
show: errors.expireDate,
trigger: 'manual'
}"
:class="{ error: errors.expireDate}"
:disabled="saving"
:first-day-of-week="firstDay"
:lang="lang"
:value="share.expireDate"
value-type="format"
icon="icon-calendar-dark"
type="date"
:disabled-date="disabledDate"
@update:value="onExpirationChange">
{{ t('files_sharing', 'Enter a date') }}
</ActionInput>

<!-- note -->
<template v-if="canHaveNote">
<ActionCheckbox
:checked.sync="hasNote"
:disabled="saving"
@uncheck="queueUpdate('note')">
{{ t('files_sharing', 'Note to recipient') }}
</ActionCheckbox>
<ActionTextEditable v-if="hasNote"
ref="note"
v-tooltip.auto="{
content: errors.note,
show: errors.note,
trigger: 'manual'
}"
:class="{ error: errors.note}"
:disabled="saving"
:value="share.newNote || share.note"
icon="icon-edit"
@update:value="onNoteChange"
@submit="onNoteSubmit" />
</template>
</template>

<ActionButton v-if="share.canEdit"
icon="icon-settings"
:disabled="saving || !canSetEdit"
:share="share"
@click.prevent="editPermissions">
{{ t('files_sharing', 'Advanced permission') }}
</ActionButton>
<ActionButton v-if="share.canEdit"
icon="icon-mail"
:disabled="saving || !canSetEdit"
:share="share"
@click.prevent="editNotes">
{{ t('files_sharing', 'Send new mail') }}
</ActionButton>
<ActionButton v-if="share.canDelete"
icon="icon-close"
:disabled="saving"
:disabled="saving || !canSetEdit"
@click.prevent="onDelete">
{{ t('files_sharing', 'Unshare') }}
</ActionButton>
Expand Down Expand Up @@ -181,10 +124,33 @@ export default {
permissionsDelete: OC.PERMISSION_DELETE,
permissionsRead: OC.PERMISSION_READ,
permissionsShare: OC.PERMISSION_SHARE,

publicUploadRWValue: OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE,
publicUploadRValue: OC.PERMISSION_READ,
publicUploadWValue: OC.PERMISSION_CREATE,
publicUploadEValue: OC.PERMISSION_UPDATE | OC.PERMISSION_READ,
}
},

computed: {
/**
* Return the current share permissions
* We always ignore the SHARE permission as this is used for the
* federated sharing.
* @returns {number}
*/
sharePermissions() {
return this.share.permissions & ~OC.PERMISSION_SHARE
},

/**
* Generate a unique random id for this SharingEntry only
* @returns {string}
*/
randomId() {
return Math.random().toString(27).substr(2)
},

title() {
let title = this.share.shareWithDisplayName
if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_GROUP) {
Expand Down Expand Up @@ -221,10 +187,6 @@ export default {
return null
},

canHaveNote() {
return !this.isRemote
},

isRemote() {
return this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE
|| this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE_GROUP
Expand All @@ -242,42 +204,6 @@ export default {
return (this.fileInfo.sharePermissions & OC.PERMISSION_UPDATE) || this.canEdit
},

/**
* Can the sharer set whether the sharee can create the file ?
*
* @returns {boolean}
*/
canSetCreate() {
// If the owner revoked the permission after the resharer granted it
// the share still has the permission, and the resharer is still
// allowed to revoke it too (but not to grant it again).
return (this.fileInfo.sharePermissions & OC.PERMISSION_CREATE) || this.canCreate
},

/**
* Can the sharer set whether the sharee can delete the file ?
*
* @returns {boolean}
*/
canSetDelete() {
// If the owner revoked the permission after the resharer granted it
// the share still has the permission, and the resharer is still
// allowed to revoke it too (but not to grant it again).
return (this.fileInfo.sharePermissions & OC.PERMISSION_DELETE) || this.canDelete
},

/**
* Can the sharer set whether the sharee can reshare the file ?
*
* @returns {boolean}
*/
canSetReshare() {
// If the owner revoked the permission after the resharer granted it
// the share still has the permission, and the resharer is still
// allowed to revoke it too (but not to grant it again).
return (this.fileInfo.sharePermissions & OC.PERMISSION_SHARE) || this.canReshare
},

/**
* Can the sharee edit the shared file ?
*/
Expand Down Expand Up @@ -337,38 +263,20 @@ export default {
},

/**
* Is the current share a folder ?
* Does the current file/folder have create permissions
* TODO: move to a proper FileInfo model?
* @returns {boolean}
*/
isFolder() {
return this.fileInfo.type === 'dir'
fileHasCreatePermission() {
return !!(this.fileInfo.permissions & OC.PERMISSION_CREATE)
},

/**
* Does the current share have an expiration date
* Is the current share a folder ?
* @returns {boolean}
*/
hasExpirationDate: {
get() {
return this.config.isDefaultInternalExpireDateEnforced || !!this.share.expireDate
},
set(enabled) {
this.share.expireDate = enabled
? this.config.defaultInternalExpirationDateString !== ''
? this.config.defaultInternalExpirationDateString
: moment().format('YYYY-MM-DD')
: ''
},
},

dateMaxEnforced() {
if (!this.isRemote) {
return this.config.isDefaultInternalExpireDateEnforced
&& moment().add(1 + this.config.defaultInternalExpireDate, 'days')
} else {
return this.config.isDefaultRemoteExpireDateEnforced
&& moment().add(1 + this.config.defaultRemoteExpireDate, 'days')
}
isFolder() {
return this.fileInfo.type === 'dir'
},

/**
Expand Down Expand Up @@ -404,6 +312,18 @@ export default {
onMenuClose() {
this.onNoteSubmit()
},

editPermissions() {
this.$store.commit('addFromInput', false)
this.$store.commit('addShare', this.share)
this.$store.commit('addCurrentTab', 'permissions')
},

editNotes() {
this.$store.commit('addFromInput', false)
this.$store.commit('addShare', this.share)
this.$store.commit('addCurrentTab', 'notes')
},
},
}
</script>
Expand All @@ -412,7 +332,7 @@ export default {
.sharing-entry {
display: flex;
align-items: center;
height: 44px;
min-height: 44px;
&__desc {
display: flex;
flex-direction: column;
Expand Down
Loading