Skip to content

Commit 0b38308

Browse files
authored
Merge pull request #4808 from nextcloud/backport/4772/stable31
2 parents 6983057 + b41719c commit 0b38308

19 files changed

Lines changed: 549 additions & 90 deletions

.github/workflows/cypress-e2e.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,25 @@ jobs:
5858
git submodule sync --recursive
5959
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
6060
61+
- name: Register main git reference
62+
run: |
63+
main_app_ref="$(if [ "${{ matrix.server-versions }}" = "master" ]; then echo -n "main"; else echo -n "${{ matrix.server-versions }}"; fi)"
64+
echo "main_app_ref=$main_app_ref" >> $GITHUB_ENV
65+
6166
- name: Checkout viewer
6267
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
6368
with:
6469
repository: nextcloud/viewer
6570
ref: ${{ matrix.server-versions }}
6671
path: apps/viewer
6772

73+
- name: Checkout spreed
74+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
75+
with:
76+
repository: nextcloud/spreed
77+
ref: ${{ env.main_app_ref }}
78+
path: apps/spreed
79+
6880
- name: Checkout files_pdfviewer
6981
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
7082
with:
@@ -106,6 +118,13 @@ jobs:
106118
run: |
107119
composer install
108120
121+
- name: Build talk
122+
working-directory: apps/spreed
123+
run: |
124+
composer install --no-dev
125+
npm ci
126+
npm run dev
127+
109128
- name: Set up Nextcloud
110129
env:
111130
DB_PORT: 4444
@@ -125,6 +144,7 @@ jobs:
125144
php occ app:enable --force viewer
126145
php occ app:enable --force files_pdfviewer
127146
php occ app:enable --force richdocuments
147+
php occ app:enable --force spreed
128148
php occ app:list
129149
php occ config:system:set trusted_domains 1 --value="172.17.0.1"
130150

appinfo/info.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ You can also edit your documents off-line with the Collabora Office app from the
1515
<licence>agpl</licence>
1616
<author>Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk</author>
1717
<types>
18+
<filesystem />
1819
<prevent_group_restriction/>
1920
</types>
2021
<documentation>

cypress/e2e/integration.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,4 @@ describe('Nextcloud integration', function() {
180180
})
181181
})
182182
})
183-
})
183+
})

cypress/e2e/talk.spec.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2023 Julius Härtl <jus@bitgrid.net>
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
describe('Talk integraiton integration', function() {
7+
let randUser
8+
9+
const resetConfig = () => {
10+
cy.nextcloudTestingAppConfigSet('files', 'watermark_enabled', 'no')
11+
cy.nextcloudTestingAppConfigSet('files', 'watermark_text', '{userId}')
12+
cy.nextcloudTestingAppConfigSet('files', 'watermark_shareTalkPublic', 'no')
13+
cy.nextcloudTestingAppConfigSet('richdocuments', 'uiDefaults-UIMode', 'notebookbar')
14+
}
15+
16+
before(function() {
17+
resetConfig()
18+
cy.createRandomUser().then(user => {
19+
randUser = user
20+
cy.login(user)
21+
cy.uploadFile(user, 'document.odt', 'application/vnd.oasis.opendocument.text', '/document.odt')
22+
})
23+
})
24+
25+
afterEach(() => {
26+
resetConfig()
27+
})
28+
29+
const filename = 'document.odt'
30+
31+
beforeEach(function() {
32+
cy.login(randUser)
33+
})
34+
35+
it('Can share a file to a talk room and open it', function() {
36+
cy.createTalkRoom(randUser, {
37+
roomName: 'Test room',
38+
}).then(room => {
39+
cy.log(`Created talk room "${room.name}"`, room)
40+
cy.shareFileToTalkRoom(randUser, filename, room.token)
41+
cy.visit(`/call/${room.token}`)
42+
cy.get('.file-preview')
43+
.should('be.visible')
44+
.should('contain.text', filename)
45+
.click()
46+
47+
cy.waitForViewer()
48+
cy.waitForCollabora()
49+
})
50+
})
51+
52+
it('See that the file is shared without download', function() {
53+
cy.nextcloudTestingAppConfigSet('files', 'watermark_enabled', 'yes')
54+
cy.nextcloudTestingAppConfigSet('files', 'watermark_shareTalkPublic', 'yes')
55+
cy.nextcloudTestingAppConfigSet('files', 'watermark_text', 'TestingWatermark')
56+
57+
cy.createTalkRoom(randUser, {
58+
roomName: 'Secure room',
59+
}).then(room => {
60+
cy.log(`Created talk room "${room.name}"`, room)
61+
cy.shareFileToTalkRoom(randUser, filename, room.token, { permission: 1 })
62+
cy.makeTalkRoomPublic(randUser, room.token)
63+
64+
cy.logout()
65+
cy.clearAllLocalStorage()
66+
cy.visit(`/call/${room.token}`)
67+
cy.get('.username-form__input input[type="text"]')
68+
.should('be.visible')
69+
.type('Test user{enter}')
70+
71+
// Assert that the download button is hidden in talk
72+
cy.get('.messages:contains("document.odt")')
73+
.trigger('mouseover')
74+
75+
cy.get('.file-preview')
76+
.closest('.message')
77+
.find('button[aria-label="Actions"]')
78+
.should('be.visible')
79+
.click()
80+
81+
cy.get('.action:contains("Download")')
82+
.should('not.exist')
83+
84+
// Assert the file is still opening
85+
cy.get('.file-preview')
86+
.should('be.visible')
87+
.should('contain.text', filename)
88+
// We need to get the href to work around how cypress works with windows
89+
.invoke('attr', 'href')
90+
.then((href) => {
91+
cy.visit(href)
92+
93+
// FIXME: For some reason this shows up on main but not on stable31
94+
// cy.get('[data-cy="guestNameModal"]').should('be.visible')
95+
// cy.inputCollaboraGuestName('A guest')
96+
97+
cy.waitForCollabora()
98+
99+
cy.url().then(url => {
100+
const baseUrl = url.split('?')[0]
101+
cy.request({
102+
url: baseUrl + '/download',
103+
failOnStatusCode: false,
104+
}).then((response) => {
105+
expect(response.status).to.eq(403)
106+
})
107+
})
108+
})
109+
})
110+
})
111+
})

cypress/support/commands.js

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Cypress.Commands.add('uploadFile', (user, fixture, mimeType, target = `/${fixtur
8282
})
8383

8484
Cypress.Commands.add('ocsRequest', (user, options) => {
85-
const auth = { user: user.userId, password: user.password }
85+
const auth = user ? { user: user.userId, password: user.password } : null
8686
return cy.request({
8787
form: true,
8888
auth,
@@ -109,6 +109,22 @@ Cypress.Commands.add('shareFileToUser', (user, path, targetUser, shareData = {})
109109
})
110110
})
111111

112+
Cypress.Commands.add('shareFileToTalkRoom', (user, path, roomId, shareData = {}) => {
113+
cy.login(user)
114+
cy.ocsRequest(user, {
115+
method: 'POST',
116+
url: `${url}/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json`,
117+
body: {
118+
path,
119+
shareType: 10,
120+
shareWith: roomId,
121+
...shareData,
122+
},
123+
}).then(response => {
124+
cy.log(`${user.userId} shared ${path} with talk room ${roomId}`, response.status)
125+
})
126+
})
127+
112128
Cypress.Commands.add('shareFileToRemoteUser', (user, path, targetUser, shareData = {}) => {
113129
cy.login(user)
114130
const federatedId = `${targetUser.userId}@${url}`
@@ -396,10 +412,45 @@ Cypress.Commands.add('verifyTemplateFields', (fields, fileId) => {
396412
})
397413

398414
Cypress.Commands.add('pickFile', (filename) => {
399-
cy.get('.office-target-picker')
400-
.find(`tr[data-filename="${filename}"]`)
401-
.click()
402-
cy.get('.office-target-picker')
403-
.find('button[aria-label="Select file"]')
404-
.click()
415+
cy.get('.office-target-picker')
416+
.find(`tr[data-filename="${filename}"]`)
417+
.click()
418+
cy.get('.office-target-picker')
419+
.find('button[aria-label="Select file"]')
420+
.click()
405421
})
422+
423+
Cypress.Commands.add('createTalkRoom', (user, options = {}) => {
424+
cy.login(user)
425+
return cy.ocsRequest(user, {
426+
method: 'POST',
427+
url: `${url}/ocs/v2.php/apps/spreed/api/v4/room?format=json`,
428+
body: {
429+
roomType: options.roomType || 3, // Default to group conversation
430+
roomName: options.roomName,
431+
invite: options.invite || '',
432+
source: options.source || '',
433+
objectType: options.objectType || '',
434+
objectId: options.objectId || '',
435+
password: options.password || '',
436+
}
437+
}).then(response => {
438+
cy.log(`Created talk room "${options.roomName}"`, response.status)
439+
return cy.wrap(response.body.ocs.data)
440+
})
441+
})
442+
443+
Cypress.Commands.add('makeTalkRoomPublic', (user, token, password = '') => {
444+
cy.login(user)
445+
return cy.ocsRequest(user, {
446+
method: 'POST',
447+
url: `${url}/ocs/v2.php/apps/spreed/api/v4/room/${token}/public?format=json`,
448+
body: {
449+
password: password,
450+
}
451+
}).then(response => {
452+
cy.log(`Made talk room public`, response.status)
453+
return cy.wrap(response.body.ocs.data)
454+
})
455+
})
456+

lib/AppConfig.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,13 @@ public function useSecureViewAdditionalMimes(): bool {
202202
return $this->config->getAppValue(Application::APPNAME, self::USE_SECURE_VIEW_ADDITIONAL_MIMES, 'no') === 'yes';
203203
}
204204

205+
public function getMimeTypes(): array {
206+
return array_merge(
207+
Capabilities::MIMETYPES,
208+
Capabilities::MIMETYPES_MSOFFICE,
209+
);
210+
}
211+
205212
public function getDomainList(): array {
206213
$urls = array_merge(
207214
[ $this->domainOnly($this->getCollaboraUrlPublic()) ],

lib/AppInfo/Application.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace OCA\Richdocuments\AppInfo;
1010

1111
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
12+
use OCA\Richdocuments\AppConfig;
1213
use OCA\Richdocuments\Capabilities;
1314
use OCA\Richdocuments\Conversion\ConversionProvider;
1415
use OCA\Richdocuments\Db\WopiMapper;
@@ -20,6 +21,7 @@
2021
use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener;
2122
use OCA\Richdocuments\Listener\LoadAdditionalListener;
2223
use OCA\Richdocuments\Listener\LoadViewerListener;
24+
use OCA\Richdocuments\Listener\OverwritePublicSharePropertiesListener;
2325
use OCA\Richdocuments\Listener\ReferenceListener;
2426
use OCA\Richdocuments\Listener\RegisterTemplateFileCreatorListener;
2527
use OCA\Richdocuments\Listener\ShareLinkListener;
@@ -32,7 +34,9 @@
3234
use OCA\Richdocuments\Preview\OpenDocument;
3335
use OCA\Richdocuments\Preview\Pdf;
3436
use OCA\Richdocuments\Reference\OfficeTargetReferenceProvider;
37+
use OCA\Richdocuments\Storage\SecureViewWrapper;
3538
use OCA\Richdocuments\Template\CollaboraTemplateProvider;
39+
use OCA\Talk\Events\OverwritePublicSharePropertiesEvent;
3640
use OCA\Viewer\Event\LoadViewer;
3741
use OCP\AppFramework\App;
3842
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -41,12 +45,15 @@
4145
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
4246
use OCP\Collaboration\Reference\RenderReferenceEvent;
4347
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
48+
use OCP\Files\Storage\IStorage;
4449
use OCP\Files\Template\BeforeGetTemplatesEvent;
4550
use OCP\Files\Template\FileCreatedFromTemplateEvent;
4651
use OCP\Files\Template\RegisterTemplateCreatorEvent;
52+
use OCP\IAppConfig;
4753
use OCP\Preview\BeforePreviewFetchedEvent;
4854
use OCP\Security\CSP\AddContentSecurityPolicyEvent;
4955
use OCP\Security\FeaturePolicy\AddFeaturePolicyEvent;
56+
use OCP\Server;
5057

5158
class Application extends App implements IBootstrap {
5259
public const APPNAME = 'richdocuments';
@@ -56,6 +63,8 @@ public function __construct(array $urlParams = []) {
5663
}
5764

5865
public function register(IRegistrationContext $context): void {
66+
\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $this, 'addStorageWrapper');
67+
5968
$context->registerTemplateProvider(CollaboraTemplateProvider::class);
6069
$context->registerCapability(Capabilities::class);
6170
$context->registerMiddleWare(WOPIMiddleware::class);
@@ -70,6 +79,7 @@ public function register(IRegistrationContext $context): void {
7079
$context->registerEventListener(RenderReferenceEvent::class, ReferenceListener::class);
7180
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
7281
$context->registerEventListener(BeforeGetTemplatesEvent::class, BeforeGetTemplatesListener::class);
82+
$context->registerEventListener(OverwritePublicSharePropertiesEvent::class, OverwritePublicSharePropertiesListener::class);
7383
$context->registerReferenceProvider(OfficeTargetReferenceProvider::class);
7484
$context->registerSensitiveMethods(WopiMapper::class, [
7585
'getPathForToken',
@@ -88,4 +98,32 @@ public function register(IRegistrationContext $context): void {
8898

8999
public function boot(IBootContext $context): void {
90100
}
101+
102+
/**
103+
* @internal
104+
*/
105+
public function addStorageWrapper(): void {
106+
if (Server::get(IAppConfig::class)->getValueString(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_enabled', 'no') === 'no') {
107+
return;
108+
}
109+
110+
\OC\Files\Filesystem::addStorageWrapper('richdocuments', [$this, 'addStorageWrapperCallback'], -10);
111+
}
112+
113+
/**
114+
* @param $mountPoint
115+
* @param IStorage $storage
116+
* @return SecureViewWrapper|IStorage
117+
*@internal
118+
*/
119+
public function addStorageWrapperCallback($mountPoint, IStorage $storage) {
120+
if (!\OC::$CLI && $mountPoint !== '/') {
121+
return new SecureViewWrapper([
122+
'storage' => $storage,
123+
'mountPoint' => $mountPoint,
124+
]);
125+
}
126+
127+
return $storage;
128+
}
91129
}

lib/Controller/SettingsController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ public function updateWatermarkSettings($settings = []): JSONResponse {
213213
'watermark_shareAll',
214214
'watermark_shareRead',
215215
'watermark_shareDisabledDownload',
216+
'watermark_shareTalkPublic',
216217
'watermark_linkSecure',
217218
'watermark_linkRead',
218219
'watermark_linkAll',

0 commit comments

Comments
 (0)