Skip to content

Commit acff47e

Browse files
authored
Merge pull request #44 from cryptpad/fix-open-from-share-link
Release v0.4.0
2 parents ffbf898 + 11cf6d6 commit acff47e

13 files changed

Lines changed: 1570 additions & 2875 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-or-later
77

88
## [Unreleased]
99

10+
## [0.4.0] - 2025-08-04
11+
12+
- Fix: internal and external shares
13+
- Fix: "Open in Cryptpad" not showing
14+
- Fix: german translations.
15+
1016
## [0.3.7] - 2025-02-17
1117

1218
- Fix: Now working with Nextcloud versions 30 and 31

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ interface in the "General" tab.
5959
CryptPad". You can find it in the "Integration" category.
6060
3. Configure "Open in CryptPad" in the administration settings of Nextcloud.
6161
]]></description>
62-
<version>0.3.7</version>
62+
<version>0.4.0</version>
6363
<licence>agpl</licence>
6464
<author mail="contact@cryptpad.org" homepage="https://cryptpad.org">XWiki CryptPad Team and contributors</author>
6565
<namespace>OpenInCryptPad</namespace>

composer.lock

Lines changed: 1301 additions & 2815 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/AppInfo/Application.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// SPDX-License-Identifier: AGPL-3.0-or-later
66

77
namespace OCA\OpenInCryptPad\AppInfo;
8+
use OCA\OpenInCryptPad\Listener\PublicShareBeforeTemplateRenderedListener;
9+
use OCA\OpenInCryptPad\Listener\FilesLoadAdditionalScriptsListener;
810

911
use OCP\AppFramework\App;
1012
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -20,12 +22,19 @@ public function __construct() {
2022
}
2123

2224
public function register(IRegistrationContext $context): void {
25+
// "This event is triggered when the files app is rendered. It can be used to add additional scripts to the files app."
26+
// See: https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html#oca-files-event-loadadditionalscriptsevent
27+
$context->registerEventListener(\OCA\Files\Event\LoadAdditionalScriptsEvent::class, FilesLoadAdditionalScriptsListener::class);
28+
29+
// "Emitted before the rendering step of the public share page happens. The event holds a flag that specifies if it is the authentication page of a public share."
30+
// See: https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html#oca-files-sharing-event-beforetemplaterenderedevent
31+
$context->registerEventListener(\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent::class, PublicShareBeforeTemplateRenderedListener::class);
2332
}
2433

2534
public function boot(IBootContext $context): void {
2635
/**
2736
* Always add main script
2837
*/
29-
Util::addInitScript(self::APP_ID, 'openincryptpad-main', 'files');
38+
// Util::addInitScript(self::APP_ID, 'openincryptpad-main', 'files');
3039
}
3140
}

lib/Controller/EditorController.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ public function __construct(IRequest $request,
3131
* @NoAdminRequired
3232
* @NoCSRFRequired
3333
*/
34-
public function page($id, $path, $mimeType): TemplateResponse {
34+
public function page($id, $path, $mimeType, $isShared, $fileName): TemplateResponse {
3535
$app = SettingsService::APP_FOR_MIME_TYPE[$mimeType];
3636
$fileType = SettingsService::FILE_TYPE_FOR_MIME_TYPE[$mimeType];
3737
$cryptPadUrl = $this->settingsService->getCryptPadUrl($app);
3838
$apiUrl = $cryptPadUrl . '/cryptpad-api.js';
39-
$infoScript = $this->getInfoScript($id, $path, $mimeType, $fileType, $app, $cryptPadUrl);
39+
$infoScript = $this->getInfoScript($id, $path, $mimeType, $fileType, $app, $cryptPadUrl, $isShared, $fileName);
4040

4141
$response = new TemplateResponse(
4242
'openincryptpad',
@@ -54,14 +54,16 @@ public function page($id, $path, $mimeType): TemplateResponse {
5454
return $response;
5555
}
5656

57-
public function getInfoScript($id, $path, $mimeType, $fileType, $app, $cryptPadUrl): string {
57+
public function getInfoScript($id, $path, $mimeType, $fileType, $app, $cryptPadUrl, $isShared, $fileName): string {
5858
return 'window.OpenInCryptPadInfo = ' . json_encode([
5959
'fileId' => $id,
6060
'filePath' => $path,
6161
'mimeType' => $mimeType,
6262
'fileType' => $fileType,
6363
'app' => $app,
6464
'cryptPadUrl' => $cryptPadUrl,
65+
'isShared' => $isShared,
66+
'fileName' => $fileName
6567
]);
6668
}
6769

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OCA\OpenInCryptPad\Listener;
6+
7+
use OCA\OpenInCryptPad\AppInfo\Application;
8+
use OCP\EventDispatcher\Event;
9+
use OCP\EventDispatcher\IEventListener;
10+
use OCP\Util;
11+
12+
13+
/** @template-implements IEventListener<\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent> */
14+
class FilesLoadAdditionalScriptsListener implements IEventListener {
15+
16+
public function __construct() {
17+
}
18+
19+
public function handle(Event $event): void {
20+
Util::addInitScript(Application::APP_ID, 'openincryptpad-main');
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OCA\OpenInCryptPad\Listener;
6+
7+
use OCA\OpenInCryptPad\AppInfo\Application;
8+
use OCP\EventDispatcher\Event;
9+
use OCP\EventDispatcher\IEventListener;
10+
use OCP\Util;
11+
12+
13+
/** @template-implements IEventListener<\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent> */
14+
class PublicShareBeforeTemplateRenderedListener implements IEventListener {
15+
16+
public function __construct() {
17+
}
18+
19+
public function handle(Event $event): void {
20+
Util::addScript(Application::APP_ID, 'openincryptpad-public');
21+
}
22+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "openincryptpad",
33
"description": "Open files in CryptPad",
4-
"version": "0.3.6",
4+
"version": "0.4.0",
55
"author": "XWiki CryptPad Team <contact@cryptpad.org> and contributors",
66
"contributors": [],
77
"bugs": {

psalm.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,40 @@
1919
</projectFiles>
2020
<extraFiles>
2121
<directory name="vendor" />
22+
23+
<directory name="nextcloud-server/apps/admin_audit"/>
24+
<directory name="nextcloud-server/apps/cloud_federation_api"/>
25+
<directory name="nextcloud-server/apps/comments"/>
26+
<directory name="nextcloud-server/apps/contactsinteraction"/>
27+
<directory name="nextcloud-server/apps/dashboard"/>
28+
<directory name="nextcloud-server/apps/dav"/>
29+
<directory name="nextcloud-server/apps/encryption"/>
30+
<directory name="nextcloud-server/apps/federatedfilesharing"/>
31+
<directory name="nextcloud-server/apps/federation"/>
32+
<directory name="nextcloud-server/apps/files"/>
33+
<directory name="nextcloud-server/apps/files_external"/>
34+
<directory name="nextcloud-server/apps/files_sharing"/>
35+
<directory name="nextcloud-server/apps/files_trashbin"/>
36+
<directory name="nextcloud-server/apps/files_versions"/>
37+
<directory name="nextcloud-server/apps/lookup_server_connector"/>
38+
<directory name="nextcloud-server/apps/oauth2"/>
39+
<directory name="nextcloud-server/apps/provisioning_api"/>
40+
<directory name="nextcloud-server/apps/settings"/>
41+
<directory name="nextcloud-server/apps/sharebymail"/>
42+
<directory name="nextcloud-server/apps/systemtags"/>
43+
<directory name="nextcloud-server/apps/testing"/>
44+
<directory name="nextcloud-server/apps/theming"/>
45+
<directory name="nextcloud-server/apps/twofactor_backupcodes"/>
46+
<directory name="nextcloud-server/apps/updatenotification"/>
47+
<directory name="nextcloud-server/apps/user_ldap"/>
48+
<directory name="nextcloud-server/apps/user_status"/>
49+
<directory name="nextcloud-server/apps/weather_status"/>
50+
<directory name="nextcloud-server/apps/webhook_listeners"/>
51+
<directory name="nextcloud-server/apps/workflowengine"/>
52+
<directory name="nextcloud-server/core"/>
53+
<directory name="nextcloud-server/lib"/>
54+
<directory name="nextcloud-server/ocs"/>
55+
<directory name="nextcloud-server/ocs-provider"/>
2256
<ignoreFiles>
2357
<directory name="vendor/phpunit/php-code-coverage" />
2458
<directory name="vendor/psalm" />

src/editor.js

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,67 @@ window.addEventListener('DOMContentLoaded', async function() {
3131
fileType,
3232
app,
3333
cryptPadUrl,
34+
isShared,
35+
fileName,
3436
} = window.OpenInCryptPadInfo
35-
document.title = fileName(filePath) + ' - Nextcloud'
3637

37-
const sessionKey = await getSessionForFile(fileId)
38+
let blob
39+
let viewMode = ''
40+
document.title = fileName + ' - Nextcloud'
41+
// if opening file from a share link, we don't get access to the file path, but we can download it
42+
if (isShared.startsWith('true')) {
43+
viewMode = 'view'
44+
}
45+
46+
if (isShared.includes('External')) {
47+
blob = await loadFileContentShared(filePath, mimeType)
48+
} else {
49+
blob = await loadFileContent(filePath, mimeType)
50+
}
3851

39-
const blob = await loadFileContent(filePath, mimeType)
52+
let viewOnlyMode = false
53+
let sessionKey
54+
try {
55+
sessionKey = await getSessionForFile(fileId)
56+
} catch (e) {
57+
viewOnlyMode = true
58+
}
4059

4160
const docUrl = URL.createObjectURL(blob)
4261

43-
CryptPadAPI(cryptPadUrl, 'editor-content', {
44-
document: {
45-
url: docUrl,
46-
key: sessionKey,
47-
fileType,
48-
},
49-
documentType: app,
50-
events: {
51-
onSave: (data, cb) => onSave(filePath, data, cb),
62+
const events = viewOnlyMode
63+
? {
64+
onSave: (data, cb) => null,
65+
onNewKey: (data, cb) => cb(data.new), // Just accept and ignore any session key CryptPad wants to use
66+
onHasUnsavedChanges: (unsavedChanges) => null,
67+
onInsertImage,
68+
}
69+
: {
70+
onSave: (data, cb) => onSave(filePath, data, cb, isShared),
5271
onNewKey: (data, cb) => updateSessionForFile(fileId, data, cb),
5372
onHasUnsavedChanges: (unsavedChanges) => {
5473
const elem = document.querySelector('#unsaved-indicator')
5574
elem.className = unsavedChanges ? 'visible' : ''
5675
},
5776
onInsertImage,
77+
}
78+
79+
CryptPadAPI(cryptPadUrl, 'editor-content', {
80+
document: {
81+
url: docUrl,
82+
key: sessionKey,
83+
fileType,
5884
},
85+
documentType: app,
86+
mode: viewMode,
87+
events,
5988
width: '100%',
6089
height: '100%',
6190
})
6291

63-
checkForPermissionChange(filePath, () => resetCryptPadSession(fileId))
92+
if (isShared !== 'trueExternal') {
93+
checkForPermissionChange(filePath, () => resetCryptPadSession(fileId))
94+
}
6495
initBackButton()
6596

6697
} catch (e) {
@@ -159,18 +190,6 @@ async function getFilePermission(path) {
159190
.join()
160191
}
161192

162-
/**
163-
* @param {string} filePath the file path
164-
*/
165-
function fileName(filePath) {
166-
if (!filePath) {
167-
return
168-
}
169-
170-
const parts = filePath.split('/')
171-
return parts[parts.length - 1]
172-
}
173-
174193
/**
175194
*
176195
* @param {string} filePath the file path
@@ -190,16 +209,41 @@ async function loadFileContent(filePath, mimeType) {
190209
}
191210
}
192211

212+
/**
213+
*
214+
* @param {string} downloadPath the download path for the file
215+
* @param {string} mimeType the mime type
216+
*/
217+
async function loadFileContentShared(downloadPath, mimeType) {
218+
try {
219+
const response = await fetch(downloadPath)
220+
if (!response.ok) {
221+
throw new Error(`Failed to fetch file: ${response.statusText}`)
222+
}
223+
const blob = await response.blob()
224+
225+
return blob
226+
} catch (e) {
227+
console.log('MASSIVE ERROR')
228+
console.log(e)
229+
throw e[1]
230+
}
231+
}
232+
193233
/**
194234
*
195235
* @param {string} filePath the file path
196236
* @param {Blob} data the data to dave
197237
* @param {Function} cb callback
238+
* @param {string} isShared if file is shared
198239
*/
199-
function onSave(filePath, data, cb) {
200-
saveFileContent(filePath, data)
201-
.then(() => cb())
202-
.catch(cb)
240+
function onSave(filePath, data, cb, isShared) {
241+
if (isShared === 'false') {
242+
saveFileContent(filePath, data)
243+
.then(() => cb())
244+
.catch(cb)
245+
}
246+
// if it's through a share link, we shouldn't save (read only)
203247
}
204248

205249
/**
@@ -218,8 +262,10 @@ async function getSessionForFile(fileId) {
218262
if (response.ok) {
219263
const body = await response.json()
220264
return body.sessionKey
221-
} else {
265+
} else if (response.status === 404) {
222266
return null
267+
} else {
268+
throw new Error('no write permission')
223269
}
224270
}
225271

0 commit comments

Comments
 (0)