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

[stable28] fix(files_sharing): Stop overwriting the share expiration date with the default expiration date #50202

Merged
merged 4 commits into from
Jan 16, 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
11 changes: 1 addition & 10 deletions apps/files_sharing/src/components/SharingEntryLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -534,16 +534,7 @@ export default {
},
},
mounted() {
if (this.share) {
this.defaultExpirationDateEnabled = this.config.defaultExpirationDate instanceof Date
this.share.expireDate = this.defaultExpirationDateEnabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
}
},
mounted() {
if (this.share) {
this.defaultExpirationDateEnabled = this.config.defaultExpirationDate instanceof Date
this.share.expireDate = this.defaultExpirationDateEnabled ? this.formatDateToString(this.config.defaultExpirationDate) : ''
}
this.defaultExpirationDateEnabled = this.config.defaultExpirationDate instanceof Date
},

methods: {
Expand Down
7 changes: 4 additions & 3 deletions apps/files_sharing/src/mixins/SharesMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export default {
return this.config.isDefaultExpireDateEnforced
}
if (this.isRemoteShare) {
return this.config.isDefaultRemoteExpireDateEnforced
return this.config.isDefaultRemoteExpireDateEnforced
}
return this.config.isDefaultInternalExpireDateEnforced
},
Expand Down Expand Up @@ -230,9 +230,10 @@ export default {
*
* @param {Date} date
*/
onExpirationChange: debounce(function(date) {
onExpirationChange(date) {
this.share.expireDate = this.formatDateToString(new Date(date))
}, 500),
},

/**
* Uncheck expire date
* We need this method because @update:checked
Expand Down
3 changes: 2 additions & 1 deletion apps/files_sharing/src/views/SharingDetailsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@
:value="new Date(share.expireDate ?? dateTomorrow)"
:min="dateTomorrow"
:max="maxExpirationDateEnforced"
:hide-label="true"
hide-label
:label="t('files_sharing', 'Expiration date')"
:placeholder="t('files_sharing', 'Expiration date')"
type="date"
@input="onExpirationChange" />
Expand Down
5 changes: 5 additions & 0 deletions cypress/e2e/files/FilesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
*
*/

export const closeSidebar = () => {
// {force: true} as it might be hidden behind toasts
cy.get('[data-cy-sidebar] .app-sidebar__close').click({ force: true })
}

export const getRowForFile = (filename: string) => cy.get(`[data-cy-files-list-row-name="${CSS.escape(filename)}"]`)

export const getActionsForFile = (filename: string) => getRowForFile(filename).find('[data-cy-files-list-row-actions]')
Expand Down
24 changes: 20 additions & 4 deletions cypress/e2e/files_sharing/FilesSharingUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
/* eslint-disable jsdoc/require-jsdoc */

import { triggerActionForFile } from '../files/FilesUtils'

export interface ShareSetting {
Expand All @@ -12,6 +13,7 @@ export interface ShareSetting {
share: boolean
download: boolean
note: string
expiryDate: Date
}

export function createShare(fileName: string, username: string, shareSettings: Partial<ShareSetting> = {}) {
Expand All @@ -31,15 +33,20 @@ export function createShare(fileName: string, username: string, shareSettings: P
updateShare(fileName, 0, shareSettings)
}

export function openSharingDetails(index: number) {
cy.get('#app-sidebar-vue').within(() => {
cy.get('[data-cy-files-sharing-share-actions]').eq(index).click()
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]').click()
})
}

export function updateShare(fileName: string, index: number, shareSettings: Partial<ShareSetting> = {}) {
openSharingPanel(fileName)
openSharingDetails(index)

cy.intercept({ times: 1, method: 'PUT', url: '**/apps/files_sharing/api/v1/shares/*' }).as('updateShare')

cy.get('#app-sidebar-vue').within(() => {
cy.get('[data-cy-files-sharing-share-actions]').eq(index).click()
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]').click()

if (shareSettings.download !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="download"]').find('input').as('downloadCheckbox')
if (shareSettings.download) {
Expand Down Expand Up @@ -86,13 +93,22 @@ export function updateShare(fileName: string, index: number, shareSettings: Part

if (shareSettings.note !== undefined) {
cy.findByRole('checkbox', { name: /note to recipient/i }).check({ force: true, scrollBehavior: 'nearest' })
cy.findByRole('textbox', { name: /note to recipient/i }).type(shareSettings.note)
cy.findByRole('textbox', { name: /note for the share recipient/i }).type(shareSettings.note)
}

if (shareSettings.expiryDate !== undefined) {
cy.findByRole('checkbox', { name: /expiration date/i })
.check({ force: true, scrollBehavior: 'nearest' })
cy.get('#share-date-picker')
.type(`${shareSettings.expiryDate.getFullYear()}-${String(shareSettings.expiryDate.getMonth() + 1).padStart(2, '0')}-${String(shareSettings.expiryDate.getDate()).padStart(2, '0')}`)
}

cy.get('[data-cy-files-sharing-share-editor-action="save"]').click({ scrollBehavior: 'nearest' })

cy.wait('@updateShare')
})
// close all toasts
cy.get('.toast-success').findAllByRole('button').click({ force: true, multiple: true })
}

export function openSharingPanel(fileName: string) {
Expand Down
125 changes: 125 additions & 0 deletions cypress/e2e/files_sharing/expiry-date.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*!
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { User } from '@nextcloud/cypress'
import { randomBytes } from 'crypto'
import { closeSidebar } from '../files/FilesUtils.ts'
import { createShare, openSharingDetails, openSharingPanel, updateShare } from './FilesSharingUtils.ts'

describe('files_sharing: Expiry date', () => {
const expectedDefaultDate = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000)
const expectedDefaultDateString = `${expectedDefaultDate.getFullYear()}-${String(expectedDefaultDate.getMonth() + 1).padStart(2, '0')}-${String(expectedDefaultDate.getDate()).padStart(2, '0')}`
const fortnight = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000)
const fortnightString = `${fortnight.getFullYear()}-${String(fortnight.getMonth() + 1).padStart(2, '0')}-${String(fortnight.getDate()).padStart(2, '0')}`

let alice: User
let bob: User

before(() => {
// Ensure we have the admin setting setup for default dates with 2 days in the future
cy.runOccCommand('config:app:set --value yes core shareapi_default_internal_expire_date')
cy.runOccCommand('config:app:set --value 2 core shareapi_internal_expire_after_n_days')

cy.createRandomUser().then((user) => {
alice = user
cy.login(alice)
})
cy.createRandomUser().then((user) => {
bob = user
})
})

after(() => {
cy.runOccCommand('config:app:delete core shareapi_default_internal_expire_date')
cy.runOccCommand('config:app:delete core shareapi_enforce_internal_expire_date')
cy.runOccCommand('config:app:delete core shareapi_internal_expire_after_n_days')
})

beforeEach(() => {
cy.runOccCommand('config:app:delete core shareapi_enforce_internal_expire_date')
})

it('See default expiry date is set and enforced', () => {
// Enforce the date
cy.runOccCommand('config:app:set --value yes core shareapi_enforce_internal_expire_date')
const dir = prepareDirectory()

validateExpiryDate(dir, expectedDefaultDateString)
cy.findByRole('checkbox', { name: /expiration date/i })
.should('be.checked')
.and('be.disabled')
})

it('See default expiry date is set also if not enforced', () => {
const dir = prepareDirectory()

validateExpiryDate(dir, expectedDefaultDateString)
cy.findByRole('checkbox', { name: /expiration date/i })
.should('be.checked')
.and('not.be.disabled')
.check({ force: true, scrollBehavior: 'nearest' })
})

it('Can set custom expiry date', () => {
const dir = prepareDirectory()
updateShare(dir, 0, { expiryDate: fortnight })
validateExpiryDate(dir, fortnightString)
})

it('Custom expiry date survives reload', () => {
const dir = prepareDirectory()
updateShare(dir, 0, { expiryDate: fortnight })
validateExpiryDate(dir, fortnightString)

cy.visit('/apps/files')
validateExpiryDate(dir, fortnightString)
})

/**
* Regression test for https://github.com/nextcloud/server/pull/50192
* Ensure that admin default settings do not always override the user set value.
*/
it('Custom expiry date survives unrelated update', () => {
const dir = prepareDirectory()
updateShare(dir, 0, { expiryDate: fortnight })
validateExpiryDate(dir, fortnightString)

closeSidebar()
updateShare(dir, 0, { note: 'Only note changed' })
validateExpiryDate(dir, fortnightString)

cy.visit('/apps/files')
validateExpiryDate(dir, fortnightString)
})

/**
* Prepare directory, login and share to bob
*/
function prepareDirectory(): string {
const name = randomBytes(4)
.toString('hex')
cy.mkdir(alice, `/${name}`)
cy.login(alice)
cy.visit('/apps/files')
createShare(name, bob.userId)
return name
}

/**
* Validate expiry date on a share
*
* @param filename The filename to validate
* @param expectedDate The expected date in YYYY-MM-dd
*/
function validateExpiryDate(filename: string, expectedDate: string) {
openSharingPanel(filename)
openSharingDetails(0)

cy.get('#share-date-picker')
.should('exist')
.and('have.value', expectedDate)
}

})
2 changes: 1 addition & 1 deletion cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"extends": "../tsconfig.json",
"include": ["./**/*.ts"],
"compilerOptions": {
"types": ["cypress", "cypress-axe", "cypress-wait-until", "dockerode"],
"types": ["@testing-library/cypress", "cypress", "cypress-axe", "cypress-wait-until", "dockerode"],
}
}
3 changes: 0 additions & 3 deletions dist/4235-4235.js

This file was deleted.

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

This file was deleted.

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

Large diffs are not rendered by default.

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

Large diffs are not rendered by default.

Loading
Loading