Skip to content

Commit

Permalink
Merge pull request #1098 from nextcloud/backport/1077/stable30
Browse files Browse the repository at this point in the history
[stable30] Show error when trying to open a shared PDF without download permissions
  • Loading branch information
danxuliu authored Jan 29, 2025
2 parents 2f0e814 + 4eef855 commit 5a39d42
Show file tree
Hide file tree
Showing 20 changed files with 136 additions and 23 deletions.
2 changes: 2 additions & 0 deletions js/files_pdfviewer-init.js

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions js/files_pdfviewer-init.js.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
SPDX-License-Identifier: MIT
SPDX-License-Identifier: ISC
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
SPDX-FileCopyrightText: escape-html developers
SPDX-FileCopyrightText: Roman Shtylman <[email protected]>
SPDX-FileCopyrightText: Roeland Jago Douma
SPDX-FileCopyrightText: John Molakvoæ <[email protected]>
SPDX-FileCopyrightText: James Halliday
SPDX-FileCopyrightText: GitHub Inc.
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <[email protected]> (https://cure53.de/)
SPDX-FileCopyrightText: Christoph Wurst
SPDX-FileCopyrightText: Alkemics


This file is generated from multiple sources. Included packages:
- @nextcloud/browser-storage
- version: 0.4.0
- license: GPL-3.0-or-later
- @nextcloud/capabilities
- version: 1.2.0
- license: GPL-3.0-or-later
- semver
- version: 7.6.2
- license: ISC
- @nextcloud/initial-state
- version: 2.2.0
- license: GPL-3.0-or-later
- @nextcloud/l10n
- version: 3.1.0
- license: GPL-3.0-or-later
- cancelable-promise
- version: 4.3.1
- license: MIT
- dompurify
- version: 3.1.5
- license: (MPL-2.0 OR Apache-2.0)
- escape-html
- version: 1.0.3
- license: MIT
- path-browserify
- version: 1.0.1
- license: MIT
- process
- version: 0.11.10
- license: MIT
- files_pdfviewer
- version: 3.0.0
- license: AGPL-3.0-or-later
1 change: 1 addition & 0 deletions js/files_pdfviewer-init.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions js/files_pdfviewer-init.js.map.license
4 changes: 2 additions & 2 deletions js/files_pdfviewer-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/files_pdfviewer-main.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/files_pdfviewer-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/files_pdfviewer-public.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/files_pdfviewer-workersrc.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/files_pdfviewer-workersrc.js.map

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions lib/Listeners/LoadViewerListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ public function handle(Event $event): void {
if (!$event instanceof LoadViewer) {
return;
}

// The PDF viewer needs to register the "share-attributes" DAV property
// before the viewer is loaded in public share pages. Therefore an init
// script is needed rather than a regular script, and it needs to be
// done for "LoadViewer" rather than the "BeforeTemplateRenderedEvent",
// as when that event is handled the viewer is already initialized.
Util::addInitScript(Application::APP_ID, 'files_pdfviewer-init');

Util::addScript(Application::APP_ID, 'files_pdfviewer-main', 'viewer');
}
}
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@nextcloud/auth": "^2.2.1",
"@nextcloud/axios": "^2.5.1",
"@nextcloud/dialogs": "^6.0.0",
"@nextcloud/files": "^3.10.1",
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/logger": "^3.0.2",
"@nextcloud/router": "^3.0.1",
Expand Down
17 changes: 17 additions & 0 deletions src/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { registerDavProperty } from '@nextcloud/files'

import isPublicPage from './utils/isPublicPage.js'
import isPdf from './utils/isPdf.js'
import isSecureViewerAvailable from './utils/isSecureViewerAvailable.js'

// The "share-attributes" DAV property needs to be explicitly registered so the
// viewer returns it in the file info. This is implicitly done by the Files app,
// so it does not need to be explicitly done by the PDF viewer when it is opened
// in the Files app, only for public shares pages.
if (isPublicPage() && isPdf() && !isSecureViewerAvailable()) {
registerDavProperty('nc:share-attributes', { nc: 'http://nextcloud.org/ns' })
}
8 changes: 4 additions & 4 deletions src/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
import { generateUrl } from '@nextcloud/router'

import logger from './services/logger.js'
import canDownload from './utils/canDownload.js'
import hideDownload from './utils/hideDownload.js'
import isPublicPage from './utils/isPublicPage.js'
import isPdf from './utils/isPdf.js'
import isSecureViewerAvailable from './utils/isSecureViewerAvailable.js'

window.addEventListener('DOMContentLoaded', function() {
logger.debug('Initializing for public page', {
isPublicPage: isPublicPage(),
canDownload: canDownload(),
hideDownload: hideDownload(),
isSecureViewerAvailable: isSecureViewerAvailable(),
})

Expand All @@ -36,8 +36,8 @@ window.addEventListener('DOMContentLoaded', function() {

const sharingToken = sharingTokenElmt.value
const downloadUrl = generateUrl('/s/{token}/download', { token: sharingToken })
const viewerUrl = generateUrl('/apps/files_pdfviewer/?file={downloadUrl}&canDownload={canDownload}#page={page}', {
canDownload: canDownload() ? 1 : 0,
const viewerUrl = generateUrl('/apps/files_pdfviewer/?file={downloadUrl}&hideDownload={hideDownload}#page={page}', {
hideDownload: hideDownload() ? 1 : 0,
downloadUrl,
page,
})
Expand Down
2 changes: 1 addition & 1 deletion src/utils/canDownload.js → src/utils/hideDownload.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
const hideDownloadElmt = document.getElementById('hideDownload')
export default () => !hideDownloadElmt || (hideDownloadElmt && hideDownloadElmt.value !== 'true')
export default () => hideDownloadElmt && hideDownloadElmt.value === 'true'
4 changes: 2 additions & 2 deletions src/utils/isSecureViewerAvailable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import canDownload from './canDownload.js'
import hideDownload from './hideDownload.js'

export default () => !canDownload() && typeof OCA.RichDocuments !== 'undefined'
export default () => hideDownload() && typeof OCA.RichDocuments !== 'undefined'
39 changes: 35 additions & 4 deletions src/views/PDFView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<iframe ref="iframe"
<iframe v-if="isDownloadable"
ref="iframe"
:src="iframeSrc"
@load="onIFrameLoaded" />
<div v-else id="emptycontent">
<div class="icon-error" />
<h3>{{ t('files_pdfviewer', 'To view a shared PDF file, the download needs to be allowed for this file share') }}</h3>
</div>
</template>

<script>
Expand All @@ -14,7 +19,7 @@ import { getLanguage } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import logger from '../services/logger.js'
import uploadPdfFile from '../services/uploadPdfFile.js'
import canDownload from '../utils/canDownload.js'
import hideDownload from '../utils/hideDownload.js'
import isPdf from '../utils/isPdf.js'
import isPublicPage from '../utils/isPublicPage.js'

Expand All @@ -30,8 +35,8 @@ export default {

computed: {
iframeSrc() {
return generateUrl('/apps/files_pdfviewer/?file={file}&canDownload={canDownload}', {
canDownload: canDownload() ? 1 : 0,
return generateUrl('/apps/files_pdfviewer/?file={file}&hideDownload={hideDownload}', {
hideDownload: hideDownload() ? 1 : 0,
file: this.source ?? this.davPath,
})
},
Expand All @@ -41,12 +46,32 @@ export default {
return this.fileList.find((file) => file.fileid === this.fileid)
},

isDownloadable() {
if (!this.file.shareAttributes) {
return true
}

const shareAttributes = JSON.parse(this.file.shareAttributes)
const downloadPermissions = shareAttributes.find(({ scope, key }) => scope === 'permissions' && key === 'download')
if (downloadPermissions) {
return downloadPermissions.value
}

return true
},

isEditable() {
return this.file?.permissions?.indexOf('W') >= 0
},
},

async mounted() {
if (!this.isDownloadable) {
this.doneLoading()

return
}

document.addEventListener('webviewerloaded', this.handleWebviewerloaded)

if (isPublicPage() && isPdf()) {
Expand Down Expand Up @@ -186,6 +211,12 @@ export default {
</script>

<style lang="scss" scoped>
#emptycontent {
margin: 0;
padding: 10% 5%;
background-color: var(--color-main-background);
}

iframe {
width: 100%;
height: calc(100vh - var(--header-height));
Expand Down
6 changes: 3 additions & 3 deletions src/workersrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import redirectIfNotIframe from './utils/redirectIfNotIframe.js'
// Checks if the page is displayed in an iframe. If not redirect to /.
redirectIfNotIframe()

// Retrieve the canDownload from the url, this is
// Retrieve the hideDownload from the url, this is
// the most easy way to pass the prop to this iframe
const queryString = window.location.search
const urlParams = new URLSearchParams(queryString)
const canDownload = urlParams.get('canDownload')
const hideDownload = urlParams.get('hideDownload')

function initializeCustomPDFViewerApplication() {
const head = document.getElementsByTagName('head')[0]
Expand All @@ -32,7 +32,7 @@ function initializeCustomPDFViewerApplication() {
PDFViewerApplicationOptions.set('imageResourcesPath', head.getAttribute('data-imageresourcespath'))
PDFViewerApplicationOptions.set('enableScripting', head.getAttribute('data-enableScripting') === true)

if (canDownload === '0') {
if (hideDownload === '1') {
const pdfViewer = window.document.querySelector('.pdfViewer')

if (pdfViewer) {
Expand Down
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const l10nContent = readdirSync(path.resolve(__dirname, 'js', 'pdfjs', 'web', 'l

webpackConfig.entry.workersrc = path.resolve(path.join('src', 'workersrc.js'))
webpackConfig.entry.public = path.resolve(path.join('src', 'public.js'))
webpackConfig.entry.init = path.resolve(path.join('src', 'init.js'))

// keep pdfjs vendor in the js folder
webpackConfig.output.clean = false
Expand Down

0 comments on commit 5a39d42

Please sign in to comment.