Skip to content

Commit

Permalink
Merge pull request #3641 from nextcloud/backport/3606/stable28
Browse files Browse the repository at this point in the history
[stable28] Redesign guest name picker and fix opening it for shared folders
  • Loading branch information
elzody authored Apr 30, 2024
2 parents dad8bd5 + 6cf7b37 commit f39f5e4
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 106 deletions.
79 changes: 63 additions & 16 deletions cypress/e2e/share-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,17 @@ describe('Public sharing of office documents', function() {
cy.uploadFile(shareOwner, 'document.odt', 'application/vnd.oasis.opendocument.text', '/my-share/document.odt')
})

const matrix = [shareOwner, otherUser, null]
const userMatrix = [shareOwner, otherUser]
const guestName = randHash()

describe('Open a shared file', function() {
for (const index in matrix) {
const viewingUser = matrix[index]
for (const index in userMatrix) {
const viewingUser = userMatrix[index]

it('Loads file as user: ' + viewingUser?.userId, () => {
cy.shareLink(shareOwner, '/document.odt').then((token) => {
if (viewingUser !== null) {
cy.login(viewingUser)
} else {
cy.logout()
}
cy.login(viewingUser)

cy.visit(`/s/${token}`, {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
Expand All @@ -58,38 +57,86 @@ describe('Public sharing of office documents', function() {
cy.get('@loleafletframe').within(() => {
cy.get('#closebutton').click()
})

cy.get('#viewer', { timeout: 5000 }).should('not.exist')
})
})
}

it('Loads file as guest: ' + guestName, () => {
cy.shareLink(shareOwner, '/document.odt').then((token) => {
cy.logout()

cy.visit(`/s/${token}`, {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
},
})

cy.inputCollaboraGuestName(guestName)
cy.waitForCollabora()
cy.waitForPostMessage('App_LoadingStatus', { Status: 'Document_Loaded' })

cy.get('@loleafletframe').within(() => {
cy.get('#closebutton').click()
})

cy.get('#viewer', { timeout: 5000 }).should('not.exist')
})
})
})

describe('Open a file in a shared folder', function() {
for (const index in matrix) {
const viewingUser = matrix[index]
for (const index in userMatrix) {
const viewingUser = userMatrix[index]
it('Loads file in shared folder as user: ' + viewingUser?.userId, () => {
if (viewingUser !== null) {
cy.login(viewingUser)
} else {
cy.logout()
}
cy.login(viewingUser)

cy.shareLink(shareOwner, '/my-share').then((token) => {
cy.visit(`/s/${token}`, {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
},
})

cy.get('tr[data-file="document.odt"] a.name').click()

cy.waitForViewer()
cy.waitForCollabora()
cy.waitForPostMessage('App_LoadingStatus', { Status: 'Document_Loaded' })

cy.get('@loleafletframe').within(() => {
cy.get('#closebutton').click()
})

cy.get('#viewer', { timeout: 5000 }).should('not.exist')
})
})

}

it('Loads file in shared folder as guest: ' + guestName, () => {
cy.shareLink(shareOwner, '/my-share').then((token) => {
cy.logout()

cy.visit(`/s/${token}`, {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
},
})

cy.get('tr[data-file="document.odt"] a.name').click()

cy.inputCollaboraGuestName(guestName)
cy.waitForViewer()
cy.waitForCollabora()
cy.waitForPostMessage('App_LoadingStatus', { Status: 'Document_Loaded' })

cy.get('@loleafletframe').within(() => {
cy.get('#closebutton').click()
})

cy.get('#viewer', { timeout: 5000 }).should('not.exist')
})
})
})
})
11 changes: 10 additions & 1 deletion cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Cypress.Commands.add('waitForViewer', () => {
.and('have.class', 'modal-mask')
.and('not.have.class', 'icon-loading')
})

Cypress.Commands.add('waitForCollabora', (wrapped = false) => {
if (wrapped) {
cy.get('[data-cy="documentframe"]', { timeout: 30000 })
Expand All @@ -223,6 +224,7 @@ Cypress.Commands.add('waitForCollabora', (wrapped = false) => {
.as('loleafletframe')
cy.get('@loleafletframe').find('#main-document-content').should('be.visible')
})

Cypress.Commands.add('waitForPostMessage', (messageId, values = undefined) => {
cy.get('@postMessage', { timeout: 20000 }).should(spy => {
const calls = spy.getCalls()
Expand All @@ -238,6 +240,13 @@ Cypress.Commands.add('waitForPostMessage', (messageId, values = undefined) => {
}
})
})

Cypress.Commands.add('inputCollaboraGuestName', (guestName = 'cloud') => {
cy.get('[data-cy="guestNameModal"]').should('be.visible')
cy.get('[data-cy="guestNameInput"]').type(guestName)
cy.get('[data-cy="guestNameSubmit"]').click()
})

Cypress.Commands.add('uploadSystemTemplate', () => {
cy.login(new User('admin', 'admin'))
cy.visit('/settings/admin/richdocuments')
Expand All @@ -248,4 +257,4 @@ Cypress.Commands.add('uploadSystemTemplate', () => {
mimeType: 'application/vnd.oasis.opendocument.presentation-template',
}, { force: true })
cy.get('#richdocuments-templates li').contains('systemtemplate.otp')
})
})
8 changes: 6 additions & 2 deletions lib/Controller/DocumentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,15 @@ public function editOnlineTarget(int $fileId, ?string $target = null): RedirectR
}

#[PublicPage]
public function token(int $fileId, ?string $shareToken = null, ?string $path = null): DataResponse {
public function token(int $fileId, ?string $shareToken = null, ?string $path = null, ?string $guestName = null): DataResponse {
try {
$share = $shareToken ? $this->shareManager->getShareByToken($shareToken) : null;
$file = $shareToken ? $this->getFileForShare($share, $fileId, $path) : $this->getFileForUser($fileId, $path);

if ($this->userId === null) {
$this->userId = $guestName;
}

$wopi = $this->getToken($file, $share);

return new DataResponse(array_merge(
Expand Down Expand Up @@ -471,7 +475,7 @@ private function getToken(File $file, ?IShare $share = null, int $version = null
return $this->tokenManager->generateWopiTokenForTemplate($templateFile, $share?->getShareOwner() ?? $this->userId, $file->getId());
}

return $this->tokenManager->generateWopiToken($this->getWopiFileId($file->getId(), $version), $share?->getToken(), $this->userId);
return $this->tokenManager->generateWopiToken($this->getWopiFileId($file->getId(), $version), $share?->getToken(), $this->userId);
}

private function getWopiFileId(int $fileId, int $version = null): string {
Expand Down
5 changes: 4 additions & 1 deletion lib/Listener/ShareLinkListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ public function handle(Event $event): void {
/** @var IShare $share */
$share = $event->getShare();
$owner = $share->getShareOwner();
$loggedInUser = $this->permissionManager->loggedInUser();

if ($this->permissionManager->isEnabledForUser($owner)) {
$this->initialStateService->prepareParams(['userId' => $loggedInUser]);
$this->initialStateService->provideCapabilities();

Util::addScript('richdocuments', 'richdocuments-files');
Util::addScript('richdocuments', 'richdocuments-viewer', 'viewer');
Util::addScript('richdocuments', 'richdocuments-public');
Util::addScript('richdocuments', 'richdocuments-public', 'viewer');
}
}
}
20 changes: 19 additions & 1 deletion lib/PermissionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private function userMatchesGroupList(?string $userId = null, ?array $groupList
$incognito = true;
}
$user = $this->userSession->getUser();
$userId = $user ? $user->getUID() : null;
$userId = $user?->getUID();
if ($incognito) {
\OC_User::setIncognitoMode(true);
}
Expand Down Expand Up @@ -98,6 +98,24 @@ private function userMatchesGroupList(?string $userId = null, ?array $groupList
return false;
}

public function loggedInUser(): ?string {
$incognito = false;

if (\OC_User::isIncognitoMode()) {
\OC_User::setIncognitoMode(false);
$incognito = true;
}

$user = $this->userSession->getUser();
$userId = $user?->getUID();

if ($incognito) {
\OC_User::setIncognitoMode(true);
}

return $userId;
}

public function isEnabledForUser(string $userId = null): bool {
if ($this->userMatchesGroupList($userId, $this->appConfig->getUseGroups())) {
return true;
Expand Down
4 changes: 4 additions & 0 deletions lib/Service/InitialStateService.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,18 @@ public function prepareParams(array $params): array {
}

private function provideOptions(): void {
$this->initialState->provideInitialState('loggedInUser', $this->userId ?? false);

$this->initialState->provideInitialState('theme', $this->config->getAppValue(Application::APPNAME, 'theme', 'nextcloud'));
$this->initialState->provideInitialState('uiDefaults', [
'UIMode' => $this->config->getAppValue(Application::APPNAME, 'uiDefaults-UIMode', 'notebookbar')
]);

$logoSet = $this->config->getAppValue('theming', 'logoheaderMime', '') !== '';
if (!$logoSet) {
$logoSet = $this->config->getAppValue('theming', 'logoMime', '') !== '';
}

$this->initialState->provideInitialState('theming-customLogo', ($logoSet ?
$this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo())
: false));
Expand Down
1 change: 1 addition & 0 deletions lib/TokenManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public function generateWopiToken(string $fileId, ?string $shareToken = null, ?s
$owneruid = null;
$hideDownload = false;
$rootFolder = $this->rootFolder;

// if the user is not logged-in do use the sharers storage
if ($shareToken !== null) {
/** @var File $file */
Expand Down
131 changes: 131 additions & 0 deletions src/components/GuestNamePicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<template>
<NcModal :can-close="false"
:out-transition="true"
size="small"
:show.sync="show">
<div class="modal__content" data-cy="guestNameModal">
<h3>
<NcIconSvgWrapper v-if="file.icon !== null"
:inline="true"
:svg="file.icon"
:size="35" />
{{ file.name }}
</h3>

<p>
{{ t('richdocuments', `Please enter the guest name you wish to use before proceeding to the document.
If you don\'t provide one, the default will be used.`) }}
</p>

<div class="modal__form">
<NcTextField :value="guestName"
data-cy="guestNameInput"
:label="t('richdocuments', 'Guest name')"
:placeholder="t('richdocuments', 'Anonymous guest')"
type="text"
@update:value="setGuestName" />
</div>
</div>

<div class="modal__buttons">
<NcButton data-cy="guestNameSubmit"
:aria-label="t('richdocuments', 'Submit name')"
type="primary"
@click="submit">
{{ t('richdocuments', 'Submit name') }}
</NcButton>
</div>
</NcModal>
</template>

<script>
import { NcButton, NcIconSvgWrapper, NcModal, NcTextField } from '@nextcloud/vue'
import { translate as t } from '@nextcloud/l10n'
import axios from '@nextcloud/axios'
import { setGuestNameCookie } from '../helpers/guestName.js'
export default {
name: 'GuestNamePicker',
components: {
NcModal,
NcButton,
NcTextField,
NcIconSvgWrapper,
},
props: {
onSubmit: {
type: Function,
default: () => {},
},
fileName: {
type: String,
default: '',
},
},
data() {
return {
guestName: '',
show: true,
file: {
name: this.fileName,
icon: null,
},
}
},
async mounted() {
const name = document.getElementById('filename').value
const mimeTypeIcon = async () => {
const url = document.getElementById('mimetypeIcon').value
let res
try {
res = await axios.get(url)
} catch (e) {
console.error(e)
}
return (res.status === 200) ? res.data : null
}
this.file = {
name: this.file.name === '' ? name : this.file.name,
icon: await mimeTypeIcon(),
}
},
methods: {
t,
setGuestName(guestName) {
this.guestName = guestName
},
async submit() {
setGuestNameCookie(this.guestName)
this.show = false
await this.onSubmit(this.guestName)
},
},
}
</script>

<style lang="scss" scoped>
$modal-padding: calc(var(--default-grid-baseline) * 4);
.modal__content {
padding: $modal-padding;
.modal__form {
padding: 15px 0;
}
}
.modal__buttons {
display: flex;
justify-content: center;
padding: 0 $modal-padding $modal-padding $modal-padding;
}
</style>
Loading

0 comments on commit f39f5e4

Please sign in to comment.