From bf3ce79abdb9fbffc41e59fbc2a41a5271900de2 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Tue, 29 Apr 2025 16:47:27 +0200 Subject: [PATCH] feat(files_sharing): show Account menu on public pages Signed-off-by: skjnldsv --- .../DefaultPublicShareTemplateProvider.php | 5 +- .../LoadPublicFileRequestAuthListener.php | 10 +- apps/files_sharing/src/public-file-request.ts | 57 ------ .../src/public-nickname-handler.ts | 86 ++++++++ .../src/views/PublicAuthPrompt.vue | 138 ------------- .../tests/Controller/ShareControllerTest.php | 2 + .../AccountMenu/AccountMenuEntry.vue | 9 +- .../PublicPageMenu/PublicPageMenuEntry.vue | 8 +- core/src/public-page-user-menu.ts | 15 ++ core/src/views/AccountMenu.vue | 2 +- core/src/views/PublicPageUserMenu.vue | 135 ++++++++++++ core/templates/layout.public.php | 1 + ...up-public-share.ts => PublicShareUtils.ts} | 10 +- .../public-share/copy-move-files.cy.ts | 2 +- .../public-share/default-view.cy.ts | 8 +- .../files_sharing/public-share/download.cy.ts | 6 +- .../public-share/header-avatar.cy.ts | 193 ++++++++++++++++++ .../public-share/header-menu.cy.ts | 2 +- .../public-share/rename-files.cy.ts | 2 +- .../public-share/required-before-create.cy.ts | 24 +-- .../Http/Template/PublicTemplateResponse.php | 1 + package-lock.json | 14 +- package.json | 4 +- webpack.modules.js | 3 +- 24 files changed, 492 insertions(+), 245 deletions(-) delete mode 100644 apps/files_sharing/src/public-file-request.ts create mode 100644 apps/files_sharing/src/public-nickname-handler.ts delete mode 100644 apps/files_sharing/src/views/PublicAuthPrompt.vue create mode 100644 core/src/public-page-user-menu.ts create mode 100644 core/src/views/PublicPageUserMenu.vue rename cypress/e2e/files_sharing/public-share/{setup-public-share.ts => PublicShareUtils.ts} (95%) create mode 100644 cypress/e2e/files_sharing/public-share/header-avatar.cy.ts diff --git a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php index 645250ab2b5..afba45cac4a 100644 --- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php +++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php @@ -107,13 +107,12 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider Util::addInitScript(Application::APP_ID, 'init'); Util::addInitScript(Application::APP_ID, 'init-public'); Util::addScript('files', 'main'); + Util::addScript(Application::APP_ID, 'public-nickname-handler'); // Add file-request script if needed $attributes = $share->getAttributes(); $isFileRequest = $attributes?->getAttribute('fileRequest', 'enabled') === true; - if ($isFileRequest) { - Util::addScript(Application::APP_ID, 'public-file-request'); - } + $this->initialState->provideInitialState('isFileRequest', $isFileRequest); // Load Viewer scripts if (class_exists(LoadViewer::class)) { diff --git a/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php index f1e054c7ee5..6da2476194b 100644 --- a/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php +++ b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php @@ -10,6 +10,7 @@ namespace OCA\Files_Sharing\Listener; use OCA\Files_Sharing\AppInfo\Application; use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Share\IManager; @@ -19,6 +20,7 @@ use OCP\Util; class LoadPublicFileRequestAuthListener implements IEventListener { public function __construct( private IManager $shareManager, + private IInitialState $initialState, ) { } @@ -51,9 +53,9 @@ class LoadPublicFileRequestAuthListener implements IEventListener { // Ignore, this is not a file request or the share does not exist } - if ($isFileRequest) { - // Add the script to the public page - Util::addScript(Application::APP_ID, 'public-file-request'); - } + Util::addScript(Application::APP_ID, 'public-nickname-handler'); + + // Add file-request script if needed + $this->initialState->provideInitialState('isFileRequest', $isFileRequest); } } diff --git a/apps/files_sharing/src/public-file-request.ts b/apps/files_sharing/src/public-file-request.ts deleted file mode 100644 index 1d640c5ea5e..00000000000 --- a/apps/files_sharing/src/public-file-request.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -import { defineAsyncComponent } from 'vue' -import { getBuilder } from '@nextcloud/browser-storage' -import { getGuestNickname, setGuestNickname } from '@nextcloud/auth' -import { getUploader } from '@nextcloud/upload' -import { spawnDialog } from '@nextcloud/dialogs' - -import logger from './services/logger' - -const storage = getBuilder('files_sharing').build() - -/** - * Setup file-request nickname header for the uploader - * @param nickname The nickname - */ -function registerFileRequestHeader(nickname: string) { - const uploader = getUploader() - uploader.setCustomHeader('X-NC-Nickname', encodeURIComponent(nickname)) - logger.debug('Nickname header registered for uploader', { headers: uploader.customHeaders }) -} - -/** - * Callback when a nickname was chosen - * @param nickname The chosen nickname - */ -function onSetNickname(nickname: string): void { - // Set the nickname - setGuestNickname(nickname) - // Set the dialog as shown - storage.setItem('public-auth-prompt-shown', 'true') - // Register header for uploader - registerFileRequestHeader(nickname) -} - -window.addEventListener('DOMContentLoaded', () => { - const nickname = getGuestNickname() ?? '' - const dialogShown = storage.getItem('public-auth-prompt-shown') !== null - - // If we don't have a nickname or the public auth prompt hasn't been shown yet, show it - // We still show the prompt if the user has a nickname to double check - if (!nickname || !dialogShown) { - spawnDialog( - defineAsyncComponent(() => import('./views/PublicAuthPrompt.vue')), - { - nickname, - }, - onSetNickname as (...rest: unknown[]) => void, - ) - } else { - logger.debug('Public auth prompt already shown.', { nickname }) - registerFileRequestHeader(nickname) - } -}) diff --git a/apps/files_sharing/src/public-nickname-handler.ts b/apps/files_sharing/src/public-nickname-handler.ts new file mode 100644 index 00000000000..02bdc641aaf --- /dev/null +++ b/apps/files_sharing/src/public-nickname-handler.ts @@ -0,0 +1,86 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getBuilder } from '@nextcloud/browser-storage' +import { getGuestNickname, type NextcloudUser } from '@nextcloud/auth' +import { getUploader } from '@nextcloud/upload' +import { loadState } from '@nextcloud/initial-state' +import { showGuestUserPrompt } from '@nextcloud/dialogs' +import { t } from '@nextcloud/l10n' + +import logger from './services/logger' +import { subscribe } from '@nextcloud/event-bus' + +const storage = getBuilder('files_sharing').build() + +// Setup file-request nickname header for the uploader +const registerFileRequestHeader = (nickname: string) => { + const uploader = getUploader() + uploader.setCustomHeader('X-NC-Nickname', encodeURIComponent(nickname)) + logger.debug('Nickname header registered for uploader', { headers: uploader.customHeaders }) +} + +// Callback when a nickname was chosen +const onUserInfoChanged = (guest: NextcloudUser) => { + logger.debug('User info changed', { guest }) + registerFileRequestHeader(guest.displayName ?? '') +} + +// Monitor nickname changes +subscribe('user:info:changed', onUserInfoChanged) + +window.addEventListener('DOMContentLoaded', () => { + const nickname = getGuestNickname() ?? '' + const dialogShown = storage.getItem('public-auth-prompt-shown') !== null + + // Check if a nickname is mandatory + const isFileRequest = loadState('files_sharing', 'isFileRequest', false) + + const owner = loadState('files_sharing', 'owner', '') + const ownerDisplayName = loadState('files_sharing', 'ownerDisplayName', '') + const label = loadState('files_sharing', 'label', '') + const filename = loadState('files_sharing', 'filename', '') + + // If the owner provided a custom label, use it instead of the filename + const folder = label || filename + + const options = { + nickname, + notice: t('files_sharing', 'To upload files to {folder}, you need to provide your name first.', { folder }), + subtitle: undefined as string | undefined, + title: t('files_sharing', 'Upload files to {folder}', { folder }), + } + + // If the guest already has a nickname, we just make them double check + if (nickname) { + options.notice = t('files_sharing', 'Please confirm your name to upload files to {folder}', { folder }) + } + + // If the account owner set their name as public, + // we show it in the subtitle + if (owner) { + options.subtitle = t('files_sharing', '{ownerDisplayName} shared a folder with you.', { ownerDisplayName }) + } + + // If this is a file request, then we need a nickname + if (isFileRequest) { + // If we don't have a nickname or the public auth prompt hasn't been shown yet, show it + // We still show the prompt if the user has a nickname to double check + if (!nickname || !dialogShown) { + logger.debug('Showing public auth prompt.', { nickname }) + showGuestUserPrompt(options) + } + return + } + + if (!dialogShown && !nickname) { + logger.debug('Public auth prompt not shown yet but nickname is not mandatory.', { nickname }) + return + } + + // Else, we just register the nickname header if any. + logger.debug('Public auth prompt already shown.', { nickname }) + registerFileRequestHeader(nickname) +}) diff --git a/apps/files_sharing/src/views/PublicAuthPrompt.vue b/apps/files_sharing/src/views/PublicAuthPrompt.vue deleted file mode 100644 index 28955a87154..00000000000 --- a/apps/files_sharing/src/views/PublicAuthPrompt.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php index a6bef1bed56..27fa7ecf480 100644 --- a/apps/files_sharing/tests/Controller/ShareControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php @@ -336,6 +336,7 @@ class ShareControllerTest extends \Test\TestCase { 'fileId' => 111, 'owner' => 'ownerUID', 'ownerDisplayName' => 'ownerDisplay', + 'isFileRequest' => false, ]; $response = $this->shareController->showShare(); @@ -480,6 +481,7 @@ class ShareControllerTest extends \Test\TestCase { 'disclaimer' => 'My disclaimer text', 'owner' => 'ownerUID', 'ownerDisplayName' => 'ownerDisplay', + 'isFileRequest' => false, 'note' => 'The note', 'label' => 'A label', ]; diff --git a/core/src/components/AccountMenu/AccountMenuEntry.vue b/core/src/components/AccountMenu/AccountMenuEntry.vue index cafc04ea7ec..d983226d273 100644 --- a/core/src/components/AccountMenu/AccountMenuEntry.vue +++ b/core/src/components/AccountMenu/AccountMenuEntry.vue @@ -78,9 +78,14 @@ export default defineComponent({ }, methods: { - onClick(e) { - this.loading = true + onClick(e: MouseEvent) { this.$emit('click', e) + + // Allow to not show the loading indicator + // in case the click event was already handled + if (!e.defaultPrevented) { + this.loading = true + } }, }, }) diff --git a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue index 4a8640f38a8..413806c7089 100644 --- a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue +++ b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue @@ -11,22 +11,24 @@ role="presentation" @click="$emit('click')"> + + diff --git a/core/templates/layout.public.php b/core/templates/layout.public.php index 42f4ab73583..60460d60c83 100644 --- a/core/templates/layout.public.php +++ b/core/templates/layout.public.php @@ -77,6 +77,7 @@ p($theme->getTitle());
+
diff --git a/cypress/e2e/files_sharing/public-share/setup-public-share.ts b/cypress/e2e/files_sharing/public-share/PublicShareUtils.ts similarity index 95% rename from cypress/e2e/files_sharing/public-share/setup-public-share.ts rename to cypress/e2e/files_sharing/public-share/PublicShareUtils.ts index 5ff6bf2c657..e0cbd06a4c7 100644 --- a/cypress/e2e/files_sharing/public-share/setup-public-share.ts +++ b/cypress/e2e/files_sharing/public-share/PublicShareUtils.ts @@ -96,12 +96,12 @@ function checkExpirationDateState(enforced: boolean, hasDefault: boolean) { * @param shareName The name of the shared folder * @param options The share options */ -export function createShare(context: ShareContext, shareName: string, options: ShareOptions | null = null) { +export function createLinkShare(context: ShareContext, shareName: string, options: ShareOptions | null = null): Cypress.Chainable { cy.login(context.user) cy.visit('/apps/files') openSharingPanel(shareName) - cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createShare') + cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createLinkShare') cy.findByRole('button', { name: 'Create a new share link' }).click() // Conduct optional checks based on the provided options if (options) { @@ -111,14 +111,14 @@ export function createShare(context: ShareContext, shareName: string, options: S cy.findByRole('button', { name: 'Create share' }).click() } - return cy.wait('@createShare') + return cy.wait('@createLinkShare') .should(({ response }) => { expect(response?.statusCode).to.eq(200) const url = response?.body?.ocs?.data?.url expect(url).to.match(/^https?:\/\//) context.url = url }) - .then(() => cy.wrap(context.url)) + .then(() => cy.wrap(context.url as string)) } /** @@ -173,7 +173,7 @@ export function setupPublicShare(shareName = 'shared'): Cypress.Chainable setupData(defaultShareContext.user, shareName)) - .then(() => createShare(defaultShareContext, shareName)) + .then(() => createLinkShare(defaultShareContext, shareName)) .then((url) => { shareData.shareUrl = url }) diff --git a/cypress/e2e/files_sharing/public-share/copy-move-files.cy.ts b/cypress/e2e/files_sharing/public-share/copy-move-files.cy.ts index 078ecf747bf..87f16b01387 100644 --- a/cypress/e2e/files_sharing/public-share/copy-move-files.cy.ts +++ b/cypress/e2e/files_sharing/public-share/copy-move-files.cy.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { copyFile, getRowForFile, moveFile, navigateToFolder } from '../../files/FilesUtils.ts' -import { getShareUrl, setupPublicShare } from './setup-public-share.ts' +import { getShareUrl, setupPublicShare } from './PublicShareUtils.ts' describe('files_sharing: Public share - copy and move files', { testIsolation: true }, () => { diff --git a/cypress/e2e/files_sharing/public-share/default-view.cy.ts b/cypress/e2e/files_sharing/public-share/default-view.cy.ts index 62796a6420a..33e0a57da11 100644 --- a/cypress/e2e/files_sharing/public-share/default-view.cy.ts +++ b/cypress/e2e/files_sharing/public-share/default-view.cy.ts @@ -4,7 +4,7 @@ */ import type { User } from '@nextcloud/cypress' import { getRowForFile } from '../../files/FilesUtils.ts' -import { createShare, setupData } from './setup-public-share.ts' +import { createLinkShare, setupData } from './PublicShareUtils.ts' describe('files_sharing: Public share - setting the default view mode', () => { @@ -18,7 +18,7 @@ describe('files_sharing: Public share - setting the default view mode', () => { it('is by default in list view', () => { const context = { user } - createShare(context, 'shared') + createLinkShare(context, 'shared') .then((url) => { cy.logout() cy.visit(url!) @@ -34,7 +34,7 @@ describe('files_sharing: Public share - setting the default view mode', () => { it('can be toggled by user', () => { const context = { user } - createShare(context, 'shared') + createLinkShare(context, 'shared') .then((url) => { cy.logout() cy.visit(url!) @@ -67,7 +67,7 @@ describe('files_sharing: Public share - setting the default view mode', () => { it('can be changed to default grid view', () => { const context = { user } - createShare(context, 'shared') + createLinkShare(context, 'shared') .then((url) => { // Can set the "grid" view checkbox cy.findByRole('list', { name: 'Link shares' }) diff --git a/cypress/e2e/files_sharing/public-share/download.cy.ts b/cypress/e2e/files_sharing/public-share/download.cy.ts index 3afae7139f1..786a81deb4c 100644 --- a/cypress/e2e/files_sharing/public-share/download.cy.ts +++ b/cypress/e2e/files_sharing/public-share/download.cy.ts @@ -4,7 +4,7 @@ */ // @ts-expect-error The package is currently broken - but works... import { deleteDownloadsFolderBeforeEach } from 'cypress-delete-downloads-folder' -import { createShare, getShareUrl, openLinkShareDetails, setupPublicShare, type ShareContext } from './setup-public-share.ts' +import { createLinkShare, getShareUrl, openLinkShareDetails, setupPublicShare, type ShareContext } from './PublicShareUtils.ts' import { getRowForFile, getRowForFileId, triggerActionForFile, triggerActionForFileId } from '../../files/FilesUtils.ts' import { zipFileContains } from '../../../support/utils/assertions.ts' import type { User } from '@nextcloud/cypress' @@ -22,7 +22,7 @@ describe('files_sharing: Public share - downloading files', { testIsolation: tru cy.uploadContent(user, new Blob(['foo']), 'text/plain', '/file.txt') .then(({ headers }) => { fileId = Number.parseInt(headers['oc-fileid']) }) cy.login(user) - createShare(context, 'file.txt') + createLinkShare(context, 'file.txt') .then(() => cy.logout()) .then(() => cy.visit(context.url!)) }) @@ -179,7 +179,7 @@ describe('files_sharing: Public share - downloading files', { testIsolation: tru cy.mkdir(user, '/test') context = { user } - createShare(context, 'test') + createLinkShare(context, 'test') cy.login(context.user) cy.visit('/apps/files') }) diff --git a/cypress/e2e/files_sharing/public-share/header-avatar.cy.ts b/cypress/e2e/files_sharing/public-share/header-avatar.cy.ts new file mode 100644 index 00000000000..c7227062293 --- /dev/null +++ b/cypress/e2e/files_sharing/public-share/header-avatar.cy.ts @@ -0,0 +1,193 @@ +/*! + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import type { ShareContext } from './PublicShareUtils.ts' +import { createLinkShare, setupData } from './PublicShareUtils.ts' + +/** + * This tests ensures that on public shares the header avatar menu correctly works + */ +describe('files_sharing: Public share - header avatar menu', { testIsolation: true }, () => { + let context: ShareContext + let firstPublicShareUrl = '' + let secondPublicShareUrl = '' + + before(() => { + cy.createRandomUser() + .then((user) => { + context = { + user, + url: undefined, + } + setupData(context.user, 'public1') + setupData(context.user, 'public2') + createLinkShare(context, 'public1').then((shareUrl) => { + firstPublicShareUrl = shareUrl + cy.log(`Created first share with URL: ${shareUrl}`) + }) + createLinkShare(context, 'public2').then((shareUrl) => { + secondPublicShareUrl = shareUrl + cy.log(`Created second share with URL: ${shareUrl}`) + }) + }) + }) + + beforeEach(() => { + cy.logout() + cy.visit(firstPublicShareUrl) + }) + + it('See the undefined avatar menu', () => { + cy.get('header') + .findByRole('navigation', { name: /User menu/i }) + .should('be.visible') + .findByRole('button', { name: /User menu/i }) + .should('be.visible') + .click() + cy.get('#header-menu-public-page-user-menu') + .as('headerMenu') + + // Note that current guest user is not identified + cy.get('@headerMenu') + .should('be.visible') + .findByRole('note') + .should('be.visible') + .should('contain', 'not identified') + + // Button to set guest name + cy.get('@headerMenu') + .findByRole('link', { name: /Set public name/i }) + .should('be.visible') + }) + + it('Can set public name', () => { + cy.get('header') + .findByRole('navigation', { name: /User menu/i }) + .should('be.visible') + .findByRole('button', { name: /User menu/i }) + .should('be.visible') + .as('userMenuButton') + + // Open the user menu + cy.get('@userMenuButton').click() + cy.get('#header-menu-public-page-user-menu') + .as('headerMenu') + + cy.get('@headerMenu') + .findByRole('link', { name: /Set public name/i }) + .should('be.visible') + .click() + + // Check the dialog is visible + cy.findByRole('dialog', { name: /Guest identification/i }) + .should('be.visible') + .as('guestIdentificationDialog') + + // Check the note is visible + cy.get('@guestIdentificationDialog') + .findByRole('note') + .should('contain', 'not identified') + + // Check the input is visible + cy.get('@guestIdentificationDialog') + .findByRole('textbox', { name: /Name/i }) + .should('be.visible') + .type('{selectAll}John Doe{enter}') + + // Check that the dialog is closed + cy.get('@guestIdentificationDialog') + .should('not.exist') + + // Check that the avatar changed + cy.get('@userMenuButton') + .find('img') + .invoke('attr', 'src') + .should('include', 'avatar/guest/John%20Doe') + }) + + it('Guest name us persistent and can be changed', () => { + cy.get('header') + .findByRole('navigation', { name: /User menu/i }) + .should('be.visible') + .findByRole('button', { name: /User menu/i }) + .should('be.visible') + .as('userMenuButton') + + // Open the user menu + cy.get('@userMenuButton').click() + cy.get('#header-menu-public-page-user-menu') + .as('headerMenu') + + cy.get('@headerMenu') + .findByRole('link', { name: /Set public name/i }) + .should('be.visible') + .click() + + // Check the dialog is visible + cy.findByRole('dialog', { name: /Guest identification/i }) + .should('be.visible') + .as('guestIdentificationDialog') + + // Set the name + cy.get('@guestIdentificationDialog') + .findByRole('textbox', { name: /Name/i }) + .should('be.visible') + .type('{selectAll}Jane Doe{enter}') + + // Check that the dialog is closed + cy.get('@guestIdentificationDialog') + .should('not.exist') + + // Create another share + cy.visit(secondPublicShareUrl) + + cy.get('header') + .findByRole('navigation', { name: /User menu/i }) + .should('be.visible') + .findByRole('button', { name: /User menu/i }) + .should('be.visible') + .as('userMenuButton') + + // Open the user menu + cy.get('@userMenuButton').click() + cy.get('#header-menu-public-page-user-menu') + .as('headerMenu') + + // See the note with the current name + cy.get('@headerMenu') + .findByRole('note') + .should('contain', 'You will be identified as Jane Doe') + + cy.get('@headerMenu') + .findByRole('link', { name: /Change public name/i }) + .should('be.visible') + .click() + + // Check the dialog is visible + cy.findByRole('dialog', { name: /Guest identification/i }) + .should('be.visible') + .as('guestIdentificationDialog') + + // Check that the note states the current name + // cy.get('@guestIdentificationDialog') + // .findByRole('note') + // .should('contain', 'are currently identified as Jane Doe') + + // Change the name + cy.get('@guestIdentificationDialog') + .findByRole('textbox', { name: /Name/i }) + .should('be.visible') + .type('{selectAll}Foo Bar{enter}') + + // Check that the dialog is closed + cy.get('@guestIdentificationDialog') + .should('not.exist') + + // Check that the avatar changed with the second name + cy.get('@userMenuButton') + .find('img') + .invoke('attr', 'src') + .should('include', 'avatar/guest/Foo%20Bar') + }) +}) diff --git a/cypress/e2e/files_sharing/public-share/header-menu.cy.ts b/cypress/e2e/files_sharing/public-share/header-menu.cy.ts index c127adc56c6..1dd0de13477 100644 --- a/cypress/e2e/files_sharing/public-share/header-menu.cy.ts +++ b/cypress/e2e/files_sharing/public-share/header-menu.cy.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { haveValidity, zipFileContains } from '../../../support/utils/assertions.ts' -import { getShareUrl, setupPublicShare } from './setup-public-share.ts' +import { getShareUrl, setupPublicShare } from './PublicShareUtils.ts' /** * This tests ensures that on public shares the header actions menu correctly works diff --git a/cypress/e2e/files_sharing/public-share/rename-files.cy.ts b/cypress/e2e/files_sharing/public-share/rename-files.cy.ts index 5f2fe00e650..adeb6e52504 100644 --- a/cypress/e2e/files_sharing/public-share/rename-files.cy.ts +++ b/cypress/e2e/files_sharing/public-share/rename-files.cy.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { getRowForFile, haveValidity, triggerActionForFile } from '../../files/FilesUtils.ts' -import { getShareUrl, setupPublicShare } from './setup-public-share.ts' +import { getShareUrl, setupPublicShare } from './PublicShareUtils.ts' describe('files_sharing: Public share - renaming files', { testIsolation: true }, () => { diff --git a/cypress/e2e/files_sharing/public-share/required-before-create.cy.ts b/cypress/e2e/files_sharing/public-share/required-before-create.cy.ts index 30c7e12616d..772b7fa8380 100644 --- a/cypress/e2e/files_sharing/public-share/required-before-create.cy.ts +++ b/cypress/e2e/files_sharing/public-share/required-before-create.cy.ts @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { ShareContext } from './setup-public-share.ts' +import type { ShareContext } from './PublicShareUtils.ts' import type { ShareOptions } from '../ShareOptionsType.ts' import { defaultShareOptions } from '../ShareOptionsType.ts' -import { setupData, createShare } from './setup-public-share.ts' +import { setupData, createLinkShare } from './PublicShareUtils.ts' describe('files_sharing: Before create checks', () => { @@ -49,7 +49,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'passwordAndExpireEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -64,7 +64,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'passwordEnforcedDefaultExpire' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -79,7 +79,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'defaultPasswordExpireEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -93,7 +93,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'defaultPasswordAndExpire' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -109,7 +109,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'passwordEnforcedExpireSetNotEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -125,7 +125,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'defaultPasswordAndExpirationNotEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -141,7 +141,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'noPasswordExpireEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -157,7 +157,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions(shareOptions) const shareName = 'defaultExpireNoPasswordEnforced' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -173,7 +173,7 @@ describe('files_sharing: Before create checks', () => { const shareName = 'noPasswordExpireDefault' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, shareOptions).then((shareUrl) => { + createLinkShare(shareContext, shareName, shareOptions).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) @@ -183,7 +183,7 @@ describe('files_sharing: Before create checks', () => { applyShareOptions() const shareName = 'noPasswordNoExpireNoDefaults' setupData(shareContext.user, shareName) - createShare(shareContext, shareName, null).then((shareUrl) => { + createLinkShare(shareContext, shareName, null).then((shareUrl) => { shareContext.url = shareUrl cy.log(`Created share with URL: ${shareUrl}`) }) diff --git a/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php index ef5d2f67f7e..a620f44e677 100644 --- a/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php +++ b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php @@ -44,6 +44,7 @@ class PublicTemplateResponse extends TemplateResponse { ) { parent::__construct($appName, $templateName, $params, 'public', $status, $headers); \OCP\Util::addScript('core', 'public-page-menu'); + \OCP\Util::addScript('core', 'public-page-user-menu'); $state = \OCP\Server::get(IInitialStateService::class); $state->provideLazyInitialState('core', 'public-page-menu', function () { diff --git a/package-lock.json b/package-lock.json index f7e7c40ce70..a939399bc99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,13 +12,13 @@ "@chenfengyuan/vue-qrcode": "^1.0.2", "@mdi/js": "^7.4.47", "@mdi/svg": "^7.4.47", - "@nextcloud/auth": "^2.4.0", + "@nextcloud/auth": "^2.5.0", "@nextcloud/axios": "^2.5.1", "@nextcloud/browser-storage": "^0.4.0", "@nextcloud/browserslist-config": "^3.0.1", "@nextcloud/calendar-availability-vue": "^2.2.6", "@nextcloud/capabilities": "^1.2.0", - "@nextcloud/dialogs": "^6.3.0", + "@nextcloud/dialogs": "^6.3.1", "@nextcloud/event-bus": "^3.3.2", "@nextcloud/files": "^3.10.2", "@nextcloud/initial-state": "^2.2.0", @@ -3902,19 +3902,19 @@ "license": "MIT" }, "node_modules/@nextcloud/dialogs": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@nextcloud/dialogs/-/dialogs-6.3.0.tgz", - "integrity": "sha512-6WbWiBnGKvcj5UCG0raQhhU7fso1bNX1KEH2iN8PKTAGfxtXAD6XQ48HLuPjUtSZgrpm1azc2cAkECA18SXJaA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@nextcloud/dialogs/-/dialogs-6.3.1.tgz", + "integrity": "sha512-lklTssGdphRZKoR07pYU88btqguEKcQjEpKYom342i1eiMPiejgmoPZEignWJvJhpaN9CT5FoGndCrqqS3BswA==", "license": "AGPL-3.0-or-later", "dependencies": { "@mdi/js": "^7.4.47", - "@nextcloud/auth": "^2.5.0", + "@nextcloud/auth": "^2.5.1", "@nextcloud/axios": "^2.5.1", "@nextcloud/browser-storage": "^0.4.0", "@nextcloud/event-bus": "^3.3.2", "@nextcloud/files": "^3.10.2", "@nextcloud/initial-state": "^2.2.0", - "@nextcloud/l10n": "^3.2.0", + "@nextcloud/l10n": "^3.3.0", "@nextcloud/router": "^3.0.1", "@nextcloud/sharing": "^0.2.4", "@nextcloud/typings": "^1.9.1", diff --git a/package.json b/package.json index 83267fddc65..0a255cf2f91 100644 --- a/package.json +++ b/package.json @@ -43,13 +43,13 @@ "@chenfengyuan/vue-qrcode": "^1.0.2", "@mdi/js": "^7.4.47", "@mdi/svg": "^7.4.47", - "@nextcloud/auth": "^2.4.0", + "@nextcloud/auth": "^2.5.0", "@nextcloud/axios": "^2.5.1", "@nextcloud/browser-storage": "^0.4.0", "@nextcloud/browserslist-config": "^3.0.1", "@nextcloud/calendar-availability-vue": "^2.2.6", "@nextcloud/capabilities": "^1.2.0", - "@nextcloud/dialogs": "^6.3.0", + "@nextcloud/dialogs": "^6.3.1", "@nextcloud/event-bus": "^3.3.2", "@nextcloud/files": "^3.10.2", "@nextcloud/initial-state": "^2.2.0", diff --git a/webpack.modules.js b/webpack.modules.js index 5fb730025a6..9b6f6c5e330 100644 --- a/webpack.modules.js +++ b/webpack.modules.js @@ -19,6 +19,7 @@ module.exports = { main: path.join(__dirname, 'core/src', 'main.js'), maintenance: path.join(__dirname, 'core/src', 'maintenance.js'), 'public-page-menu': path.resolve(__dirname, 'core/src', 'public-page-menu.ts'), + 'public-page-user-menu': path.resolve(__dirname, 'core/src', 'public-page-user-menu.ts'), recommendedapps: path.join(__dirname, 'core/src', 'recommendedapps.js'), systemtags: path.resolve(__dirname, 'core/src', 'systemtags/merged-systemtags.js'), 'unified-search': path.join(__dirname, 'core/src', 'unified-search.ts'), @@ -58,7 +59,7 @@ module.exports = { 'init-public': path.join(__dirname, 'apps/files_sharing/src', 'init-public.ts'), main: path.join(__dirname, 'apps/files_sharing/src', 'main.ts'), 'personal-settings': path.join(__dirname, 'apps/files_sharing/src', 'personal-settings.js'), - 'public-file-request': path.join(__dirname, 'apps/files_sharing/src', 'public-file-request.ts'), + 'public-nickname-handler': path.join(__dirname, 'apps/files_sharing/src', 'public-nickname-handler.ts'), }, files_trashbin: { init: path.join(__dirname, 'apps/files_trashbin/src', 'files-init.ts'),