Skip to content

Commit f14d115

Browse files
committed
feat(files_sharing): show Account menu on public pages
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
1 parent 45899eb commit f14d115

13 files changed

Lines changed: 237 additions & 168 deletions

File tree

apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,12 @@ public function renderPage(IShare $share, string $token, string $path): Template
104104
Util::addInitScript(Application::APP_ID, 'init');
105105
Util::addInitScript(Application::APP_ID, 'init-public');
106106
Util::addScript('files', 'main');
107+
Util::addScript(Application::APP_ID, 'public-file-request');
107108

108109
// Add file-request script if needed
109110
$attributes = $share->getAttributes();
110111
$isFileRequest = $attributes?->getAttribute('fileRequest', 'enabled') === true;
111-
if ($isFileRequest) {
112-
Util::addScript(Application::APP_ID, 'public-file-request');
113-
}
112+
$this->initialState->provideInitialState('isFileRequest', $isFileRequest);
114113

115114
// Load Viewer scripts
116115
if (class_exists(LoadViewer::class)) {

apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use OCA\Files_Sharing\AppInfo\Application;
1111
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
1212
use OCP\AppFramework\Http\TemplateResponse;
13+
use OCP\AppFramework\Services\IInitialState;
1314
use OCP\EventDispatcher\Event;
1415
use OCP\EventDispatcher\IEventListener;
1516
use OCP\Share\IManager;
@@ -19,6 +20,7 @@
1920
class LoadPublicFileRequestAuthListener implements IEventListener {
2021
public function __construct(
2122
private IManager $shareManager,
23+
private IInitialState $initialState,
2224
) {
2325
}
2426

@@ -51,9 +53,10 @@ public function handle(Event $event): void {
5153
// Ignore, this is not a file request or the share does not exist
5254
}
5355

54-
if ($isFileRequest) {
55-
// Add the script to the public page
56-
Util::addScript(Application::APP_ID, 'public-file-request');
57-
}
56+
Util::addScript(Application::APP_ID, 'public-file-request');
57+
58+
// Add file-request script if needed
59+
$attributes = $share->getAttributes();
60+
$this->initialState->provideInitialState('isFileRequest', $isFileRequest);
5861
}
5962
}

apps/files_sharing/src/public-file-request.ts

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,84 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import { defineAsyncComponent } from 'vue'
76
import { getBuilder } from '@nextcloud/browser-storage'
8-
import { getGuestNickname, setGuestNickname } from '@nextcloud/auth'
7+
import { getGuestNickname, type NextcloudUser } from '@nextcloud/auth'
98
import { getUploader } from '@nextcloud/upload'
10-
import { spawnDialog } from '@nextcloud/dialogs'
9+
import { loadState } from '@nextcloud/initial-state'
10+
import { showGuestUserPrompt } from '@nextcloud/dialogs'
11+
import { t } from '@nextcloud/l10n'
1112

1213
import logger from './services/logger'
14+
import { subscribe } from '@nextcloud/event-bus'
1315

1416
const storage = getBuilder('files_sharing').build()
1517

16-
/**
17-
* Setup file-request nickname header for the uploader
18-
* @param nickname The nickname
19-
*/
20-
function registerFileRequestHeader(nickname: string) {
18+
// Setup file-request nickname header for the uploader
19+
const registerFileRequestHeader = (nickname: string) => {
2120
const uploader = getUploader()
2221
uploader.setCustomHeader('X-NC-Nickname', encodeURIComponent(nickname))
2322
logger.debug('Nickname header registered for uploader', { headers: uploader.customHeaders })
2423
}
2524

26-
/**
27-
* Callback when a nickname was chosen
28-
* @param nickname The chosen nickname
29-
*/
30-
function onSetNickname(nickname: string): void {
31-
// Set the nickname
32-
setGuestNickname(nickname)
33-
// Set the dialog as shown
34-
storage.setItem('public-auth-prompt-shown', 'true')
35-
// Register header for uploader
36-
registerFileRequestHeader(nickname)
25+
// Callback when a nickname was chosen
26+
const onUserInfoChanged = (guest: NextcloudUser) => {
27+
logger.debug('User info changed', { guest })
28+
registerFileRequestHeader(guest.displayName ?? '')
3729
}
3830

31+
// Monitor nickname changes
32+
subscribe('user:info:changed', onUserInfoChanged)
33+
3934
window.addEventListener('DOMContentLoaded', () => {
4035
const nickname = getGuestNickname() ?? ''
4136
const dialogShown = storage.getItem('public-auth-prompt-shown') !== null
4237

43-
// If we don't have a nickname or the public auth prompt hasn't been shown yet, show it
44-
// We still show the prompt if the user has a nickname to double check
45-
if (!nickname || !dialogShown) {
46-
spawnDialog(
47-
defineAsyncComponent(() => import('./views/PublicAuthPrompt.vue')),
48-
{
49-
nickname,
50-
},
51-
onSetNickname as (...rest: unknown[]) => void,
52-
)
53-
} else {
54-
logger.debug('Public auth prompt already shown.', { nickname })
55-
registerFileRequestHeader(nickname)
38+
// Check if a nickname is mandatory
39+
const isFileRequest = loadState('files_sharing', 'isFileRequest', false)
40+
41+
const owner = loadState('files_sharing', 'owner', '')
42+
const ownerDisplayName = loadState('files_sharing', 'ownerDisplayName', '')
43+
const label = loadState('files_sharing', 'label', '')
44+
const filename = loadState('files_sharing', 'filename', '')
45+
46+
// If the owner provided a custom label, use it instead of the filename
47+
const folder = label || filename
48+
49+
const options = {
50+
nickname,
51+
notice: t('files_sharing', 'To upload files to {folder}, you need to provide your name first.', { folder }),
52+
subtitle: undefined as string | undefined,
53+
title: t('files_sharing', 'Upload files to {folder}', { folder }),
5654
}
55+
56+
// If the guest already has a nickname, we just make them double check
57+
if (nickname) {
58+
options.notice = t('files_sharing', 'Please confirm your name to upload files to {folder}', { folder })
59+
}
60+
61+
// If the account owner set their name as public,
62+
// we show it in the subtitle
63+
if (owner) {
64+
options.subtitle = t('files_sharing', '{ownerDisplayName} shared a folder with you.', { ownerDisplayName })
65+
}
66+
67+
// If this is a file request, then we need a nickname
68+
if (isFileRequest) {
69+
// If we don't have a nickname or the public auth prompt hasn't been shown yet, show it
70+
// We still show the prompt if the user has a nickname to double check
71+
if (!nickname || !dialogShown) {
72+
logger.debug('Showing public auth prompt.', { nickname })
73+
showGuestUserPrompt(options)
74+
}
75+
return
76+
}
77+
78+
if (!dialogShown && !nickname) {
79+
logger.debug('Public auth prompt not shown yet but nickname is not mandatory.', { nickname })
80+
return
81+
}
82+
83+
// Else, we just register the nickname header if any.
84+
logger.debug('Public auth prompt already shown.', { nickname })
85+
registerFileRequestHeader(nickname)
5786
})

apps/files_sharing/src/views/PublicAuthPrompt.vue

Lines changed: 0 additions & 123 deletions
This file was deleted.

apps/files_sharing/tests/Controller/ShareControllerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ public function testShowShare(): void {
332332
'fileId' => 111,
333333
'owner' => 'ownerUID',
334334
'ownerDisplayName' => 'ownerDisplay',
335+
'isFileRequest' => false,
335336
];
336337

337338
$response = $this->shareController->showShare();
@@ -474,6 +475,7 @@ public function testShowFileDropShare(): void {
474475
'disclaimer' => 'My disclaimer text',
475476
'owner' => 'ownerUID',
476477
'ownerDisplayName' => 'ownerDisplay',
478+
'isFileRequest' => false,
477479
];
478480

479481
$response = $this->shareController->showShare();

core/src/components/AccountMenu/AccountMenuEntry.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,14 @@ export default defineComponent({
7878
},
7979
8080
methods: {
81-
onClick(e) {
82-
this.loading = true
81+
onClick(e: MouseEvent) {
8382
this.$emit('click', e)
83+
84+
// Allow to not show the loading indicator
85+
// in case the click event was already handled
86+
if (!e.defaultPrevented) {
87+
this.loading = true
88+
}
8489
},
8590
},
8691
})

core/src/components/PublicPageMenu/PublicPageMenuEntry.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,24 @@
1111
role="presentation"
1212
@click="$emit('click')">
1313
<template #icon>
14-
<div role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" />
14+
<slot v-if="$scopedSlots.icon" name="icon" />
15+
<div v-else role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" />
1516
</template>
1617
</NcListItem>
1718
</template>
1819

1920
<script setup lang="ts">
20-
import NcListItem from '@nextcloud/vue/components/NcListItem'
2121
import { onMounted } from 'vue'
2222
23+
import NcListItem from '@nextcloud/vue/components/NcListItem'
24+
2325
const props = defineProps<{
2426
/** Only emit click event but do not open href */
2527
clickOnly?: boolean
2628
// menu entry props
2729
id: string
2830
label: string
29-
icon: string
31+
icon?: string
3032
href: string
3133
details?: string
3234
}>()

core/src/public-page-user-menu.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { getCSPNonce } from '@nextcloud/auth'
7+
import Vue from 'vue'
8+
9+
import PublicPageUserMenu from './views/PublicPageUserMenu.vue'
10+
11+
__webpack_nonce__ = getCSPNonce()
12+
13+
const View = Vue.extend(PublicPageUserMenu)
14+
const instance = new View()
15+
instance.$mount('#public-page-user-menu')

core/src/views/AccountMenu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export default defineComponent({
211211
}
212212
}
213213
214-
// Ensure we do not wast space, as the header menu sets a default width of 350px
214+
// Ensure we do not waste space, as the header menu sets a default width of 350px
215215
:deep(.header-menu__content) {
216216
width: fit-content !important;
217217
}

0 commit comments

Comments
 (0)