mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
feat(sharing): Allow to set default view mode for public shares
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
53b79b7f1e
commit
8003b270c5
4 changed files with 205 additions and 19 deletions
15
apps/files_sharing/src/eventbus.d.ts
vendored
Normal file
15
apps/files_sharing/src/eventbus.d.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { Folder, Node } from '@nextcloud/files'
|
||||
|
||||
declare module '@nextcloud/event-bus' {
|
||||
export interface NextcloudEvents {
|
||||
// mapping of 'event name' => 'event type'
|
||||
'files:list:updated': { folder: Folder, contents: Node[] }
|
||||
'files:config:updated': { key: string, value: boolean }
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
|
|
@ -2,13 +2,16 @@
|
|||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { getNavigation } from '@nextcloud/files'
|
||||
import type { ShareAttribute } from './sharing.d.ts'
|
||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
|
||||
import { Folder, getNavigation } from '@nextcloud/files'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import registerFileDropView from './files_views/publicFileDrop.ts'
|
||||
import registerPublicShareView from './files_views/publicShare.ts'
|
||||
import registerPublicFileShareView from './files_views/publicFileShare.ts'
|
||||
import RouterService from '../../files/src/services/RouterService'
|
||||
import router from './router'
|
||||
import RouterService from '../../files/src/services/RouterService.ts'
|
||||
import router from './router/index.ts'
|
||||
import logger from './services/logger.ts'
|
||||
|
||||
registerFileDropView()
|
||||
registerPublicShareView()
|
||||
|
|
@ -33,3 +36,28 @@ if (fileId !== null) {
|
|||
{ ...window.OCP.Files.Router.query, openfile: 'true' },
|
||||
)
|
||||
}
|
||||
|
||||
// When the file list is loaded we need to apply the "userconfig" setup on the share
|
||||
subscribe('files:list:updated', loadShareConfig)
|
||||
|
||||
/**
|
||||
* Event handler to load the view config for the current share.
|
||||
* This is done on the `files:list:updated` event to ensure the list and especially the config store was correctly initialized.
|
||||
*
|
||||
* @param context The event context
|
||||
* @param context.folder The current folder
|
||||
*/
|
||||
function loadShareConfig({ folder }: { folder: Folder }) {
|
||||
// Only setup config once
|
||||
unsubscribe('files:list:updated', loadShareConfig)
|
||||
|
||||
// Share attributes (the same) are set on all folders of a share
|
||||
if (folder.attributes['share-attributes']) {
|
||||
const shareAttributes = JSON.parse(folder.attributes['share-attributes'] || '[]') as Array<ShareAttribute>
|
||||
const gridViewAttribute = shareAttributes.find(({ scope, key }: ShareAttribute) => scope === 'config' && key === 'grid_view')
|
||||
if (gridViewAttribute !== undefined) {
|
||||
logger.debug('Loading share attributes', { gridViewAttribute })
|
||||
emit('files:config:updated', { key: 'grid_view', value: gridViewAttribute.value === true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@
|
|||
@update:checked="queueUpdate('hideDownload')">
|
||||
{{ t('files_sharing', 'Hide download') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-if="!isPublicShare"
|
||||
<NcCheckboxRadioSwitch v-else
|
||||
:disabled="!canSetDownload"
|
||||
:checked.sync="canDownload"
|
||||
data-cy-files-sharing-share-permissions-checkbox="download">
|
||||
|
|
@ -183,6 +183,10 @@
|
|||
:placeholder="t('files_sharing', 'Enter a note for the share recipient')"
|
||||
:value.sync="share.note" />
|
||||
</template>
|
||||
<NcCheckboxRadioSwitch v-if="isPublicShare && isFolder"
|
||||
:checked.sync="showInGridView">
|
||||
{{ t('files_sharing', 'Show files in grid view') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<ExternalShareAction v-for="action in externalLinkActions"
|
||||
:id="action.id"
|
||||
ref="externalLinkActions"
|
||||
|
|
@ -439,28 +443,29 @@ export default {
|
|||
this.updateAtomicPermissions({ isReshareChecked: checked })
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the default view for public shares from "list" to "grid"
|
||||
*/
|
||||
showInGridView: {
|
||||
get() {
|
||||
return this.getShareAttribute('config', 'grid_view', false)
|
||||
},
|
||||
/** @param {boolean} value If the default view should be changed to "grid" */
|
||||
set(value) {
|
||||
this.setShareAttribute('config', 'grid_view', value)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Can the sharee download files or only view them ?
|
||||
*/
|
||||
canDownload: {
|
||||
get() {
|
||||
return this.share.attributes?.find(attr => attr.key === 'download')?.value ?? true
|
||||
return this.getShareAttribute('permissions', 'download', true)
|
||||
},
|
||||
set(checked) {
|
||||
// Find the 'download' attribute and update its value
|
||||
const downloadAttr = this.share.attributes?.find(attr => attr.key === 'download')
|
||||
if (downloadAttr) {
|
||||
downloadAttr.value = checked
|
||||
} else {
|
||||
if (this.share.attributes === null) {
|
||||
this.$set(this.share, 'attributes', [])
|
||||
}
|
||||
this.share.attributes.push({
|
||||
scope: 'permissions',
|
||||
key: 'download',
|
||||
value: checked,
|
||||
})
|
||||
}
|
||||
this.setShareAttribute('permissions', 'download', checked)
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
|
@ -783,6 +788,42 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Set a share attribute on the current share
|
||||
* @param {string} scope The attribute scope
|
||||
* @param {string} key The attribute key
|
||||
* @param {boolean} value The value
|
||||
*/
|
||||
setShareAttribute(scope, key, value) {
|
||||
if (!this.share.attributes) {
|
||||
this.$set(this.share, 'attributes', [])
|
||||
}
|
||||
|
||||
const attribute = this.share.attributes
|
||||
.find((attr) => attr.scope === scope || attr.key === key)
|
||||
|
||||
if (attribute) {
|
||||
attribute.value = value
|
||||
} else {
|
||||
this.share.attributes.push({
|
||||
scope,
|
||||
key,
|
||||
value,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the value of a share attribute
|
||||
* @param {string} scope The attribute scope
|
||||
* @param {string} key The attribute key
|
||||
* @param {undefined|boolean} fallback The fallback to return if not found
|
||||
*/
|
||||
getShareAttribute(scope, key, fallback = undefined) {
|
||||
const attribute = this.share.attributes?.find((attr) => attr.scope === scope && attr.key === key)
|
||||
return attribute?.value ?? fallback
|
||||
},
|
||||
|
||||
async generateNewToken() {
|
||||
if (this.loadingToken) {
|
||||
return
|
||||
|
|
|
|||
102
cypress/e2e/files_sharing/public-share/default-view.cy.ts
Normal file
102
cypress/e2e/files_sharing/public-share/default-view.cy.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { User } from '@nextcloud/cypress'
|
||||
import { getRowForFile } from '../../files/FilesUtils.ts'
|
||||
import { createShare, setupData } from './setup-public-share.ts'
|
||||
|
||||
describe('files_sharing: Public share - setting the default view mode', () => {
|
||||
|
||||
let user: User
|
||||
|
||||
beforeEach(() => {
|
||||
cy.createRandomUser()
|
||||
.then(($user) => (user = $user))
|
||||
.then(() => setupData(user, 'shared'))
|
||||
})
|
||||
|
||||
it('is by default in list view', () => {
|
||||
const context = { user }
|
||||
createShare(context, 'shared')
|
||||
.then((url) => {
|
||||
cy.logout()
|
||||
cy.visit(url!)
|
||||
|
||||
// See file is visible
|
||||
getRowForFile('foo.txt').should('be.visible')
|
||||
// See we are in list view
|
||||
cy.findByRole('button', { name: 'Switch to grid view' })
|
||||
.should('be.visible')
|
||||
.and('not.be.disabled')
|
||||
})
|
||||
})
|
||||
|
||||
it('can be toggled by user', () => {
|
||||
const context = { user }
|
||||
createShare(context, 'shared')
|
||||
.then((url) => {
|
||||
cy.logout()
|
||||
cy.visit(url!)
|
||||
|
||||
// See file is visible
|
||||
getRowForFile('foo.txt')
|
||||
.should('be.visible')
|
||||
// See we are in list view
|
||||
.find('.files-list__row-icon')
|
||||
.should(($el) => expect($el.outerWidth()).to.be.lessThan(99))
|
||||
|
||||
// See the grid view toggle
|
||||
cy.findByRole('button', { name: 'Switch to grid view' })
|
||||
.should('be.visible')
|
||||
.and('not.be.disabled')
|
||||
// And can change to grid view
|
||||
.click()
|
||||
|
||||
// See we are in grid view
|
||||
getRowForFile('foo.txt')
|
||||
.find('.files-list__row-icon')
|
||||
.should(($el) => expect($el.outerWidth()).to.be.greaterThan(99))
|
||||
|
||||
// See the grid view toggle is now the list view toggle
|
||||
cy.findByRole('button', { name: 'Switch to list view' })
|
||||
.should('be.visible')
|
||||
.and('not.be.disabled')
|
||||
})
|
||||
})
|
||||
|
||||
it('can be changed to default grid view', () => {
|
||||
const context = { user }
|
||||
createShare(context, 'shared')
|
||||
.then((url) => {
|
||||
// Can set the "grid" view checkbox
|
||||
cy.findByRole('list', { name: 'Link shares' })
|
||||
.findAllByRole('listitem')
|
||||
.first()
|
||||
.findByRole('button', { name: /Actions/i })
|
||||
.click()
|
||||
cy.findByRole('menuitem', { name: /Customize link/i }).click()
|
||||
cy.findByRole('checkbox', { name: /Show files in grid view/i })
|
||||
.scrollIntoView()
|
||||
cy.findByRole('checkbox', { name: /Show files in grid view/i })
|
||||
.should('not.be.checked')
|
||||
.check({ force: true })
|
||||
|
||||
// Wait for the share update
|
||||
cy.intercept('PUT', '**/ocs/v2.php/apps/files_sharing/api/v1/shares/*').as('updateShare')
|
||||
cy.findByRole('button', { name: 'Update share' }).click()
|
||||
cy.wait('@updateShare').its('response.statusCode').should('eq', 200)
|
||||
|
||||
// Logout and visit the share
|
||||
cy.logout()
|
||||
cy.visit(url!)
|
||||
|
||||
// See file is visible
|
||||
getRowForFile('foo.txt').should('be.visible')
|
||||
// See we are in list view
|
||||
cy.findByRole('button', { name: 'Switch to list view' })
|
||||
.should('be.visible')
|
||||
.and('not.be.disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue