Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable29] enh: Fix display default expire date, add tests & tiny refactors #50693

Merged
merged 5 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions apps/files_sharing/lib/Controller/ShareAPIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
$roomShare = $this->getRoomShareHelper()->formatShare($share);
$result = array_merge($result, $roomShare);
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
}
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
$result['share_with'] = $share->getSharedWith();
Expand All @@ -317,7 +317,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
$deckShare = $this->getDeckShareHelper()->formatShare($share);
$result = array_merge($result, $deckShare);
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
}
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
$result['share_with'] = $share->getSharedWith();
Expand All @@ -327,7 +327,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
$result = array_merge($result, $scienceMeshShare);
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
}
}

Expand Down Expand Up @@ -663,7 +663,9 @@ public function createShare(
$share = $this->setShareAttributes($share, $attributes);
}

//Expire date
// Expire date checks
// Normally, null means no expiration date but we still set the default for backwards compatibility
// If the client sends an empty string, we set noExpirationDate to true
if ($expireDate !== null) {
if ($expireDate !== '') {
try {
Expand Down Expand Up @@ -756,7 +758,7 @@ public function createShare(
$share->setSharedWith($shareWith);
$share->setPermissions($permissions);
} elseif ($shareType === IShare::TYPE_CIRCLE) {
if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
if (!\OCP\Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
}

Expand All @@ -771,19 +773,19 @@ public function createShare(
} elseif ($shareType === IShare::TYPE_ROOM) {
try {
$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
}
} elseif ($shareType === IShare::TYPE_DECK) {
try {
$this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
}
} elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
try {
$this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
}
} else {
Expand Down Expand Up @@ -1770,10 +1772,10 @@ public function cleanup() {
* Returns the helper of ShareAPIController for room shares.
*
* If the Talk application is not enabled or the helper is not available
* a QueryException is thrown instead.
* a ContainerExceptionInterface is thrown instead.
*
* @return \OCA\Talk\Share\Helper\ShareAPIController
* @throws QueryException
* @throws ContainerExceptionInterface
*/
private function getRoomShareHelper() {
if (!$this->appManager->isEnabledForUser('spreed')) {
Expand All @@ -1787,10 +1789,10 @@ private function getRoomShareHelper() {
* Returns the helper of ShareAPIHelper for deck shares.
*
* If the Deck application is not enabled or the helper is not available
* a QueryException is thrown instead.
* a ContainerExceptionInterface is thrown instead.
*
* @return \OCA\Deck\Sharing\ShareAPIHelper
* @throws QueryException
* @throws ContainerExceptionInterface
*/
private function getDeckShareHelper() {
if (!$this->appManager->isEnabledForUser('deck')) {
Expand All @@ -1804,10 +1806,10 @@ private function getDeckShareHelper() {
* Returns the helper of ShareAPIHelper for sciencemesh shares.
*
* If the sciencemesh application is not enabled or the helper is not available
* a QueryException is thrown instead.
* a ContainerExceptionInterface is thrown instead.
*
* @return \OCA\Deck\Sharing\ShareAPIHelper
* @throws QueryException
* @throws ContainerExceptionInterface
*/
private function getSciencemeshShareHelper() {
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
Expand Down Expand Up @@ -1940,7 +1942,7 @@ private function shareProviderResharingRights(string $userId, IShare $share, $no
return true;
}

if ($share->getShareType() === IShare::TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
if ($share->getShareType() === IShare::TYPE_CIRCLE && \OCP\Server::get(IAppManager::class)->isEnabledForUser('circles')
&& class_exists('\OCA\Circles\Api\v1\Circles')) {
$hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
Expand All @@ -1956,7 +1958,7 @@ private function shareProviderResharingRights(string $userId, IShare $share, $no
return true;
}
return false;
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
return false;
}
}
Expand Down
17 changes: 13 additions & 4 deletions apps/files_sharing/src/components/SharingEntryLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@
:checked.sync="defaultExpirationDateEnabled"
:disabled="pendingEnforcedExpirationDate || saving"
class="share-link-expiration-date-checkbox"
@change="onDefaultExpirationDateEnabledChange">
@change="onExpirationDateToggleChange">
{{ config.isDefaultExpireDateEnforced ? t('files_sharing', 'Enable link expiration (enforced)') : t('files_sharing', 'Enable link expiration') }}
</NcActionCheckbox>

<!-- expiration date -->
<NcActionInput v-if="(pendingDefaultExpirationDate || pendingEnforcedExpirationDate) && defaultExpirationDateEnabled"
data-cy-files-sharing-expiration-date-input
class="share-link-expire-date"
:label="pendingEnforcedExpirationDate ? t('files_sharing', 'Enter expiration date (enforced)') : t('files_sharing', 'Enter expiration date')"
:disabled="saving"
Expand All @@ -117,7 +118,7 @@
type="date"
:min="dateTomorrow"
:max="maxExpirationDateEnforced"
@input="onExpirationChange /* let's not submit when picked, the user might want to still edit or copy the password */">
@change="expirationDateChanged($event)">
<template #icon>
<IconCalendarBlank :size="20" />
</template>
Expand Down Expand Up @@ -592,6 +593,9 @@ export default {
},
mounted() {
this.defaultExpirationDateEnabled = this.config.defaultExpirationDate instanceof Date
if (this.share && this.isNewShare) {
this.share.expireDate = this.defaultExpirationDateEnabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
}
},

methods: {
Expand Down Expand Up @@ -710,7 +714,7 @@ export default {
path,
shareType: ShareTypes.SHARE_TYPE_LINK,
password: share.password,
expireDate: share.expireDate,
expireDate: share.expireDate ?? '',
attributes: JSON.stringify(this.fileInfo.shareAttributes),
// we do not allow setting the publicUpload
// before the share creation.
Expand Down Expand Up @@ -866,9 +870,14 @@ export default {
this.onPasswordSubmit()
this.onNoteSubmit()
},
onDefaultExpirationDateEnabledChange(enabled) {
onExpirationDateToggleChange(enabled) {
this.share.expireDate = enabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
},
expirationDateChanged(event) {
const date = event.target.value
this.onExpirationChange(date)
this.defaultExpirationDateEnabled = !!date
},

/**
* Cancel the share creation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
</template>

<script>
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import DropdownIcon from 'vue-material-design-icons/TriangleSmallDown.vue'
import SharesMixin from '../mixins/SharesMixin.js'
import ShareDetails from '../mixins/ShareDetails.js'
Expand Down Expand Up @@ -141,7 +142,17 @@ export default {
created() {
this.selectedOption = this.preSelectedOption
},

mounted() {
subscribe('update:share', (share) => {
if (share.id === this.share.id) {
this.share.permissions = share.permissions
this.selectedOption = this.preSelectedOption
}
})
},
unmounted() {
unsubscribe('update:share')
},
methods: {
selectOption(optionLabel) {
this.selectedOption = optionLabel
Expand Down
16 changes: 5 additions & 11 deletions apps/files_sharing/src/mixins/SharesMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ export default {
monthFormat: 'MMM',
}
},
isNewShare() {
return !this.share.id
},
isFolder() {
return this.fileInfo.type === 'dir'
},
Expand Down Expand Up @@ -231,17 +234,8 @@ export default {
* @param {Date} date
*/
onExpirationChange(date) {
this.share.expireDate = this.formatDateToString(new Date(date))
},

/**
* Uncheck expire date
* We need this method because @update:checked
* is ran simultaneously as @uncheck, so
* so we cannot ensure data is up-to-date
*/
onExpirationDisable() {
this.share.expireDate = ''
const formattedDate = date ? this.formatDateToString(new Date(date)) : ''
this.share.expireDate = formattedDate
},

/**
Expand Down
4 changes: 1 addition & 3 deletions apps/files_sharing/src/views/SharingDetailsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -521,9 +521,6 @@ export default {
isGroupShare() {
return this.share.type === this.SHARE_TYPES.SHARE_TYPE_GROUP
},
isNewShare() {
return !this.share.id
},
allowsFileDrop() {
if (this.isFolder && this.config.isPublicUploadEnabled) {
if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_LINK || this.share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL) {
Expand Down Expand Up @@ -909,6 +906,7 @@ export default {
this.$emit('add:share', this.share)
} else {
this.$emit('update:share', this.share)
emit('update:share', this.share)
this.queueUpdate(...permissionsAndAttributes)
}

Expand Down
19 changes: 14 additions & 5 deletions cypress/e2e/files_sharing/public-share/setup-public-share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ export function setupData(user: User, shareName: string): void {
*/
function checkPasswordState(enforced: boolean, alwaysAskForPassword: boolean) {
if (enforced) {
cy.contains('Password protection (enforced)').should('exist')
cy.contains('Password protection (enforced)').should('exist')
} else if (alwaysAskForPassword) {
cy.contains('Password protection').should('exist')
cy.contains('Password protection').should('exist')
}
cy.contains('Enter a password')
.should('exist')
Expand All @@ -68,13 +68,22 @@ function checkPasswordState(enforced: boolean, alwaysAskForPassword: boolean) {
*/
function checkExpirationDateState(enforced: boolean, hasDefault: boolean) {
if (enforced) {
cy.contains('Enable link expiration (enforced)').should('exist')
cy.contains('Enable link expiration (enforced)').should('exist')
} else if (hasDefault) {
cy.contains('Enable link expiration').should('exist')
cy.contains('Enable link expiration').should('exist')
}
cy.contains('Enter expiration date')
.should('exist')
.and('not.be.disabled')
cy.get('input[data-cy-files-sharing-expiration-date-input]').should('exist')
cy.get('input[data-cy-files-sharing-expiration-date-input]')
.invoke('val')
.then((val) => {
const expectedDate = new Date()
expectedDate.setDate(expectedDate.getDate() + 2)
expect(new Date(val).toDateString()).to.eq(expectedDate.toDateString())
})

}

/**
Expand All @@ -90,7 +99,7 @@ export function createShare(context: ShareContext, shareName: string, options: S

cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createShare')
cy.findByRole('button', { name: 'Create a new share link' }).click()
// Conduct optional checks based on the provided options
// Conduct optional checks based on the provided options
if (options) {
cy.get('.sharing-entry__actions').should('be.visible') // Wait for the dialog to open
checkPasswordState(options.enforcePassword ?? false, options.alwaysAskForPassword ?? false)
Expand Down
3 changes: 0 additions & 3 deletions dist/113-113.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/113-113.js.map

This file was deleted.

3 changes: 3 additions & 0 deletions dist/221-221.js

Large diffs are not rendered by default.

File renamed without changes.
1 change: 1 addition & 0 deletions dist/221-221.js.map

Large diffs are not rendered by default.

Loading
Loading