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 @@
-
-
-
-
-
- {{ t('files_sharing', '{ownerDisplayName} shared a folder with you.', { ownerDisplayName }) }}
-
-
-
-
-
-
-
-
-
-
-
-
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'),