Skip to content

Commit e65409e

Browse files
Merge pull request #50694 from nextcloud/backport/50655/stable30
[stable30] enh: Fix display default expire date, add tests & tiny refactors
2 parents 3e93249 + baafbc4 commit e65409e

15 files changed

Lines changed: 70 additions & 47 deletions

apps/files_sharing/lib/Controller/ShareAPIController.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
277277
/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
278278
$roomShare = $this->getRoomShareHelper()->formatShare($share);
279279
$result = array_merge($result, $roomShare);
280-
} catch (QueryException $e) {
280+
} catch (ContainerExceptionInterface $e) {
281281
}
282282
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
283283
$result['share_with'] = $share->getSharedWith();
@@ -287,7 +287,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
287287
/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
288288
$deckShare = $this->getDeckShareHelper()->formatShare($share);
289289
$result = array_merge($result, $deckShare);
290-
} catch (QueryException $e) {
290+
} catch (ContainerExceptionInterface $e) {
291291
}
292292
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
293293
$result['share_with'] = $share->getSharedWith();
@@ -297,7 +297,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra
297297
/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
298298
$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
299299
$result = array_merge($result, $scienceMeshShare);
300-
} catch (QueryException $e) {
300+
} catch (ContainerExceptionInterface $e) {
301301
}
302302
}
303303

@@ -632,7 +632,9 @@ public function createShare(
632632
$share = $this->setShareAttributes($share, $attributes);
633633
}
634634

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

@@ -761,19 +763,19 @@ public function createShare(
761763
} elseif ($shareType === IShare::TYPE_ROOM) {
762764
try {
763765
$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
764-
} catch (QueryException $e) {
766+
} catch (ContainerExceptionInterface $e) {
765767
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
766768
}
767769
} elseif ($shareType === IShare::TYPE_DECK) {
768770
try {
769771
$this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
770-
} catch (QueryException $e) {
772+
} catch (ContainerExceptionInterface $e) {
771773
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
772774
}
773775
} elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
774776
try {
775777
$this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
776-
} catch (QueryException $e) {
778+
} catch (ContainerExceptionInterface $e) {
777779
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
778780
}
779781
} else {
@@ -1765,10 +1767,10 @@ public function cleanup() {
17651767
* Returns the helper of ShareAPIController for room shares.
17661768
*
17671769
* If the Talk application is not enabled or the helper is not available
1768-
* a QueryException is thrown instead.
1770+
* a ContainerExceptionInterface is thrown instead.
17691771
*
17701772
* @return \OCA\Talk\Share\Helper\ShareAPIController
1771-
* @throws QueryException
1773+
* @throws ContainerExceptionInterface
17721774
*/
17731775
private function getRoomShareHelper() {
17741776
if (!$this->appManager->isEnabledForUser('spreed')) {
@@ -1782,10 +1784,10 @@ private function getRoomShareHelper() {
17821784
* Returns the helper of ShareAPIHelper for deck shares.
17831785
*
17841786
* If the Deck application is not enabled or the helper is not available
1785-
* a QueryException is thrown instead.
1787+
* a ContainerExceptionInterface is thrown instead.
17861788
*
17871789
* @return \OCA\Deck\Sharing\ShareAPIHelper
1788-
* @throws QueryException
1790+
* @throws ContainerExceptionInterface
17891791
*/
17901792
private function getDeckShareHelper() {
17911793
if (!$this->appManager->isEnabledForUser('deck')) {
@@ -1799,10 +1801,10 @@ private function getDeckShareHelper() {
17991801
* Returns the helper of ShareAPIHelper for sciencemesh shares.
18001802
*
18011803
* If the sciencemesh application is not enabled or the helper is not available
1802-
* a QueryException is thrown instead.
1804+
* a ContainerExceptionInterface is thrown instead.
18031805
*
18041806
* @return \OCA\Deck\Sharing\ShareAPIHelper
1805-
* @throws QueryException
1807+
* @throws ContainerExceptionInterface
18061808
*/
18071809
private function getSciencemeshShareHelper() {
18081810
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
@@ -1935,7 +1937,7 @@ private function shareProviderResharingRights(string $userId, IShare $share, $no
19351937
return true;
19361938
}
19371939

1938-
if ($share->getShareType() === IShare::TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
1940+
if ($share->getShareType() === IShare::TYPE_CIRCLE && \OCP\Server::get(IAppManager::class)->isEnabledForUser('circles')
19391941
&& class_exists('\OCA\Circles\Api\v1\Circles')) {
19401942
$hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
19411943
$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
@@ -1951,7 +1953,7 @@ private function shareProviderResharingRights(string $userId, IShare $share, $no
19511953
return true;
19521954
}
19531955
return false;
1954-
} catch (QueryException $e) {
1956+
} catch (ContainerExceptionInterface $e) {
19551957
return false;
19561958
}
19571959
}

apps/files_sharing/src/components/SharingEntryLink.vue

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,13 @@
8585
:checked.sync="defaultExpirationDateEnabled"
8686
:disabled="pendingEnforcedExpirationDate || saving"
8787
class="share-link-expiration-date-checkbox"
88-
@change="onDefaultExpirationDateEnabledChange">
88+
@change="onExpirationDateToggleChange">
8989
{{ config.isDefaultExpireDateEnforced ? t('files_sharing', 'Enable link expiration (enforced)') : t('files_sharing', 'Enable link expiration') }}
9090
</NcActionCheckbox>
9191

9292
<!-- expiration date -->
9393
<NcActionInput v-if="(pendingDefaultExpirationDate || pendingEnforcedExpirationDate) && defaultExpirationDateEnabled"
94+
data-cy-files-sharing-expiration-date-input
9495
class="share-link-expire-date"
9596
:label="pendingEnforcedExpirationDate ? t('files_sharing', 'Enter expiration date (enforced)') : t('files_sharing', 'Enter expiration date')"
9697
:disabled="saving"
@@ -100,7 +101,7 @@
100101
type="date"
101102
:min="dateTomorrow"
102103
:max="maxExpirationDateEnforced"
103-
@input="onExpirationChange /* let's not submit when picked, the user might want to still edit or copy the password */">
104+
@change="expirationDateChanged($event)">
104105
<template #icon>
105106
<IconCalendarBlank :size="20" />
106107
</template>
@@ -589,6 +590,9 @@ export default {
589590
},
590591
mounted() {
591592
this.defaultExpirationDateEnabled = this.config.defaultExpirationDate instanceof Date
593+
if (this.share && this.isNewShare) {
594+
this.share.expireDate = this.defaultExpirationDateEnabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
595+
}
592596
},
593597
594598
methods: {
@@ -707,7 +711,7 @@ export default {
707711
path,
708712
shareType: ShareType.Link,
709713
password: share.password,
710-
expireDate: share.expireDate,
714+
expireDate: share.expireDate ?? '',
711715
attributes: JSON.stringify(this.fileInfo.shareAttributes),
712716
// we do not allow setting the publicUpload
713717
// before the share creation.
@@ -863,9 +867,14 @@ export default {
863867
this.onPasswordSubmit()
864868
this.onNoteSubmit()
865869
},
866-
onDefaultExpirationDateEnabledChange(enabled) {
870+
onExpirationDateToggleChange(enabled) {
867871
this.share.expireDate = enabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
868872
},
873+
expirationDateChanged(event) {
874+
const date = event.target.value
875+
this.onExpirationChange(date)
876+
this.defaultExpirationDateEnabled = !!date
877+
},
869878
870879
/**
871880
* Cancel the share creation

apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
<script>
3131
import { ShareType } from '@nextcloud/sharing'
32+
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
3233
import DropdownIcon from 'vue-material-design-icons/TriangleSmallDown.vue'
3334
import SharesMixin from '../mixins/SharesMixin.js'
3435
import ShareDetails from '../mixins/ShareDetails.js'
@@ -145,7 +146,17 @@ export default {
145146
created() {
146147
this.selectedOption = this.preSelectedOption
147148
},
148-
149+
mounted() {
150+
subscribe('update:share', (share) => {
151+
if (share.id === this.share.id) {
152+
this.share.permissions = share.permissions
153+
this.selectedOption = this.preSelectedOption
154+
}
155+
})
156+
},
157+
unmounted() {
158+
unsubscribe('update:share')
159+
},
149160
methods: {
150161
selectOption(optionLabel) {
151162
this.selectedOption = optionLabel

apps/files_sharing/src/mixins/SharesMixin.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ export default {
110110
monthFormat: 'MMM',
111111
}
112112
},
113+
isNewShare() {
114+
return !this.share.id
115+
},
113116
isFolder() {
114117
return this.fileInfo.type === 'dir'
115118
},
@@ -210,17 +213,8 @@ export default {
210213
* @param {Date} date
211214
*/
212215
onExpirationChange(date) {
213-
this.share.expireDate = this.formatDateToString(new Date(date))
214-
},
215-
216-
/**
217-
* Uncheck expire date
218-
* We need this method because @update:checked
219-
* is ran simultaneously as @uncheck, so
220-
* so we cannot ensure data is up-to-date
221-
*/
222-
onExpirationDisable() {
223-
this.share.expireDate = ''
216+
const formattedDate = date ? this.formatDateToString(new Date(date)) : ''
217+
this.share.expireDate = formattedDate
224218
},
225219

226220
/**

apps/files_sharing/src/views/SharingDetailsTab.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,9 +526,6 @@ export default {
526526
isGroupShare() {
527527
return this.share.type === ShareType.Group
528528
},
529-
isNewShare() {
530-
return !this.share.id
531-
},
532529
allowsFileDrop() {
533530
if (this.isFolder && this.config.isPublicUploadEnabled) {
534531
if (this.share.type === ShareType.Link || this.share.type === ShareType.Email) {
@@ -921,6 +918,7 @@ export default {
921918
this.$emit('add:share', this.share)
922919
} else {
923920
this.$emit('update:share', this.share)
921+
emit('update:share', this.share)
924922
this.queueUpdate(...permissionsAndAttributes)
925923
}
926924

cypress/e2e/files_sharing/public-share/setup-public-share.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ export function setupData(user: User, shareName: string): void {
5151
*/
5252
function checkPasswordState(enforced: boolean, alwaysAskForPassword: boolean) {
5353
if (enforced) {
54-
cy.contains('Password protection (enforced)').should('exist')
54+
cy.contains('Password protection (enforced)').should('exist')
5555
} else if (alwaysAskForPassword) {
56-
cy.contains('Password protection').should('exist')
56+
cy.contains('Password protection').should('exist')
5757
}
5858
cy.contains('Enter a password')
5959
.should('exist')
@@ -68,13 +68,22 @@ function checkPasswordState(enforced: boolean, alwaysAskForPassword: boolean) {
6868
*/
6969
function checkExpirationDateState(enforced: boolean, hasDefault: boolean) {
7070
if (enforced) {
71-
cy.contains('Enable link expiration (enforced)').should('exist')
71+
cy.contains('Enable link expiration (enforced)').should('exist')
7272
} else if (hasDefault) {
73-
cy.contains('Enable link expiration').should('exist')
73+
cy.contains('Enable link expiration').should('exist')
7474
}
7575
cy.contains('Enter expiration date')
7676
.should('exist')
7777
.and('not.be.disabled')
78+
cy.get('input[data-cy-files-sharing-expiration-date-input]').should('exist')
79+
cy.get('input[data-cy-files-sharing-expiration-date-input]')
80+
.invoke('val')
81+
.then((val) => {
82+
const expectedDate = new Date()
83+
expectedDate.setDate(expectedDate.getDate() + 2)
84+
expect(new Date(val).toDateString()).to.eq(expectedDate.toDateString())
85+
})
86+
7887
}
7988

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

91100
cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createShare')
92101
cy.findByRole('button', { name: 'Create a new share link' }).click()
93-
// Conduct optional checks based on the provided options
102+
// Conduct optional checks based on the provided options
94103
if (options) {
95104
cy.get('.sharing-entry__actions').should('be.visible') // Wait for the dialog to open
96105
checkPasswordState(options.enforcePassword ?? false, options.alwaysAskForPassword ?? false)

dist/5661-5661.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/5661-5661.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/5661-5661.js.map.license

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
5661-5661.js.license

0 commit comments

Comments
 (0)