Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 46 additions & 20 deletions cypress/e2e/propfind.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const user = randUser()

// Retries fail because folders / files already exist.
describe('Text PROPFIND extension ', { retries: 0 }, function () {
const richWorkspace = 'nc:rich-workspace'
const PROPERTY_WORKSPACE = 'nc:rich-workspace'
const PROPERTY_WORKSPACE_FLAT = 'nc:rich-workspace-flat'

before(function () {
cy.createUser(user)
Expand All @@ -24,36 +25,55 @@ describe('Text PROPFIND extension ', { retries: 0 }, function () {
cy.configureText('workspace_enabled', 1)
})

// Android app relies on this to detect rich workspace availability
it('always adds rich workspace property', function () {
cy.uploadFile('empty.md', 'text/markdown', '/Readme.md')
// FIXME: Ideally we do not need a page context for those tests at all
// For now the dashboard avoids that we have failing requests due to conflicts when updating the file
cy.visit('/apps/dashboard')
cy.propfindFolder('/').should('have.property', richWorkspace, '')
cy.propfindFolder('/', 0).should(
'have.property',
PROPERTY_WORKSPACE_FLAT,
'',
)
cy.uploadFile('test.md', 'text/markdown', '/Readme.md')
cy.propfindFolder('/').should(
cy.propfindFolder('/', 0).should(
'have.property',
richWorkspace,
PROPERTY_WORKSPACE_FLAT,
'## Hello world\n',
)
cy.deleteFile('/Readme.md')
cy.propfindFolder('/').should('have.property', richWorkspace, '')
cy.propfindFolder('/', 0).should(
'have.property',
PROPERTY_WORKSPACE_FLAT,
'',
)
})

it('never adds rich workspace property to nested folders for flat properties', function () {
cy.visit('/apps/dashboard')
cy.createFolder('/workspace-flat')
cy.propfindFolder('/', 1)
.then((results) => results.pop().propStat[0].properties)
.should('have.property', PROPERTY_WORKSPACE_FLAT, '')
cy.uploadFile('test.md', 'text/markdown', '/workspace-flat/Readme.md')
cy.propfindFolder('/', 1)
.then((results) => results.pop().propStat[0].properties)
.should('not.have.property', PROPERTY_WORKSPACE_FLAT)
cy.deleteFile('/workspace-flat/Readme.md')
cy.propfindFolder('/', 1)
.then((results) => results.pop().propStat[0].properties)
.should('have.property', PROPERTY_WORKSPACE_FLAT, '')
})

// Android app relies on this when navigating nested folders
it('adds rich workspace property to nested folders', function () {
// Android app relies on this to detect rich workspace availability in subfolders properly
it('adds rich workspace property to nested folders for the default properties', function () {
cy.createFolder('/workspace')
// FIXME: Ideally we do not need a page context for those tests at all
// For now the dashboard avoids that we have failing requests due to conflicts when updating the file
cy.visit('/apps/dashboard')
cy.propfindFolder('/', 1)
.then((results) => results.pop())
.should('have.property', richWorkspace, '')
.then((results) => results.pop().propStat[0].properties)
.should('have.property', PROPERTY_WORKSPACE, '')
cy.uploadFile('test.md', 'text/markdown', '/workspace/Readme.md')
cy.propfindFolder('/', 1)
.then((results) => results.pop())
.should('have.property', richWorkspace, '## Hello world\n')
.then((results) => results.pop().propStat[0].properties)
.should('have.property', PROPERTY_WORKSPACE, '## Hello world\n')
})
})

Expand All @@ -66,13 +86,19 @@ describe('Text PROPFIND extension ', { retries: 0 }, function () {
// FIXME: Ideally we do not need a page context for those tests at all
// For now the dashboard avoids that we have failing requests due to conflicts when updating the file
cy.visit('/apps/dashboard')
cy.propfindFolder('/').should('not.have.property', richWorkspace)
cy.propfindFolder('/', 1).should(
'not.have.property',
PROPERTY_WORKSPACE_FLAT,
)
cy.uploadFile('test.md', 'text/markdown', '/Readme.md')
cy.propfindFolder('/').should('not.have.property', richWorkspace)
cy.propfindFolder('/', 1).should(
'not.have.property',
PROPERTY_WORKSPACE_FLAT,
)
cy.createFolder('/without-workspace')
cy.propfindFolder('/', 1)
.then((results) => results.pop())
.should('not.have.property', richWorkspace)
.then((results) => results.pop().propStat[0].properties)
.should('not.have.property', PROPERTY_WORKSPACE_FLAT)
})
})
})
2 changes: 2 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ Cypress.Commands.add('propfindFolder', (path, depth = 0) => {
<d:prop>
<nc:rich-workspace />
<nc:rich-workspace-file />
<nc:rich-workspace-flat />
<nc:rich-workspace-file-flat />
</d:prop>
</d:propfind>`,
})
Expand Down
42 changes: 32 additions & 10 deletions lib/DAV/WorkspacePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
class WorkspacePlugin extends ServerPlugin {
public const WORKSPACE_PROPERTY = '{http://nextcloud.org/ns}rich-workspace';
public const WORKSPACE_FILE_PROPERTY = '{http://nextcloud.org/ns}rich-workspace-file';
public const WORKSPACE_PROPERTY_FLAT = '{http://nextcloud.org/ns}rich-workspace-flat';
public const WORKSPACE_FILE_PROPERTY_FLAT = '{http://nextcloud.org/ns}rich-workspace-file-flat';

/** @var Server */
private $server;
private Server $server;

public function __construct(
private WorkspaceService $workspaceService,
Expand Down Expand Up @@ -59,8 +60,12 @@ public function initialize(Server $server) {


public function propFind(PropFind $propFind, INode $node) {
if (!in_array(self::WORKSPACE_PROPERTY, $propFind->getRequestedProperties())
&& !in_array(self::WORKSPACE_FILE_PROPERTY, $propFind->getRequestedProperties())) {
if (!array_intersect([
self::WORKSPACE_PROPERTY,
self::WORKSPACE_FILE_PROPERTY,
self::WORKSPACE_PROPERTY_FLAT,
self::WORKSPACE_FILE_PROPERTY_FLAT
], $propFind->getRequestedProperties())) {
return;
}

Expand All @@ -75,15 +80,26 @@ public function propFind(PropFind $propFind, INode $node) {
return;
}

$shouldFetchChildren = array_intersect([
self::WORKSPACE_PROPERTY,
self::WORKSPACE_FILE_PROPERTY,
], $propFind->getRequestedProperties());

// In most cases we only need the workspace property for the root node
// So we can skip the propFind for further nodes for performance reasons
// Fetching the workspace property for all children is still required for mobile apps

if ($propFind->getDepth() !== $this->server->getHTTPDepth() && !$shouldFetchChildren) {
return;
}

$node = $node->getNode();
try {
$file = $this->workspaceService->getFile($node);
} catch (\Exception $e) {
$file = null;
}

// Only return the property for the parent node and ignore it for further in depth nodes
$propFind->handle(self::WORKSPACE_PROPERTY, function () use ($file) {
$workspaceContentCallback = function () use ($file) {
$cachedContent = '';
if ($file instanceof File) {
$cache = $this->cacheFactory->createDistributed('text_workspace');
Expand All @@ -107,12 +123,18 @@ public function propFind(PropFind $propFind, INode $node) {
}
}
return $cachedContent;
});
$propFind->handle(self::WORKSPACE_FILE_PROPERTY, function () use ($file) {
};

$workspaceFileCallback = function () use ($file) {
if ($file instanceof File) {
return $file->getFileInfo()->getId();
}
return '';
});
};

$propFind->handle(self::WORKSPACE_PROPERTY, $workspaceContentCallback);
$propFind->handle(self::WORKSPACE_PROPERTY_FLAT, $workspaceContentCallback);
$propFind->handle(self::WORKSPACE_FILE_PROPERTY, $workspaceFileCallback);
$propFind->handle(self::WORKSPACE_FILE_PROPERTY_FLAT, $workspaceFileCallback);
}
}
15 changes: 8 additions & 7 deletions src/helpers/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const addMenuRichWorkspace = () => {
if (!window?.OCA?.Text?.RichWorkspaceEnabled) {
return false
}
if (Number(context.attributes['rich-workspace-file'])) {
if (Number(context.attributes['rich-workspace-file-flat'])) {
return false
}
// Check read permission to not show option in file drop shares
Expand Down Expand Up @@ -75,8 +75,8 @@ export const addMenuRichWorkspace = () => {

showSuccess(t('text', 'Created "{name}"', { name: descriptionFile }))

context.attributes['rich-workspace-file'] = fileid
context.attributes['rich-workspace'] = ''
context.attributes['rich-workspace-file-flat'] = fileid
context.attributes['rich-workspace-flat'] = ''

emit('files:node:created', file)
emit('files:node:updated', context)
Expand Down Expand Up @@ -115,8 +115,9 @@ export const FilesWorkspaceHeader = {
console.debug('Destroying existing FilesHeaderRichWorkspaceInstance')
}

const hasRichWorkspace = !!latestFolder.attributes['rich-workspace-file']
const content = latestFolder.attributes['rich-workspace'] || ''
const hasRichWorkspace =
!!latestFolder.attributes['rich-workspace-file-flat']
const content = latestFolder.attributes['rich-workspace-flat'] || ''
const path = latestFolder.path || ''

// Create a new instance of the RichWorkspace component
Expand All @@ -140,10 +141,10 @@ export const FilesWorkspaceHeader = {
}

const hasRichWorkspace =
!!folder.attributes['rich-workspace-file'] && enabled(folder, view)
!!folder.attributes['rich-workspace-file-flat'] && enabled(folder, view)
FilesHeaderRichWorkspaceInstance.hasRichWorkspace = hasRichWorkspace
FilesHeaderRichWorkspaceInstance.content =
folder.attributes['rich-workspace'] || ''
folder.attributes['rich-workspace-flat'] || ''
FilesHeaderRichWorkspaceInstance.path = folder.path || ''
},
}
Expand Down
4 changes: 2 additions & 2 deletions src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import 'vite/modulepreload-polyfill'

const workspaceAvailable = loadState('text', 'workspace_available')

registerDavProperty('nc:rich-workspace', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:rich-workspace-file', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:rich-workspace-flat', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:rich-workspace-file-flat', { nc: 'http://nextcloud.org/ns' })

if (workspaceAvailable) {
addMenuRichWorkspace()
Expand Down
Loading