Skip to content
Draft
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
57 changes: 55 additions & 2 deletions lib/Service/ContextService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Service;

use InvalidArgumentException;
Expand Down Expand Up @@ -237,6 +238,56 @@ public function update(int $contextId, string $userId, ?string $name, ?string $i
$currentPages[$updatedContent->getPageId()]['content'][$updatedContent->getId()] = $updatedContent->jsonSerialize();
}
unset($nodesBeingAdded);

// Update order of nodes
$startPageId = null;
foreach ($currentPages as $page) {
if ($page['page_type'] === Page::TYPE_STARTPAGE) {
$startPageId = $page['id'];
break;
}
}

if ($startPageId !== null && isset($currentPages[$startPageId]['content'])) {
$nodeTypeIdToRelId = [];
foreach ($currentNodes as $relId => $nodeData) {
$key = sprintf('t%di%d', $nodeData['node_type'], $nodeData['node_id']);
$nodeTypeIdToRelId[$key] = $relId;
}

$relIdToContentId = [];
foreach ($currentPages[$startPageId]['content'] as $contentId => $content) {
$relIdToContentId[$content['node_rel_id']] = $contentId;
}

$contentsToUpdate = [];
foreach ($nodes as $index => $node) {
$key = sprintf('t%di%d', $node['type'], $node['id']);
if (isset($nodeTypeIdToRelId[$key])) {
$relId = $nodeTypeIdToRelId[$key];
if (isset($relIdToContentId[$relId])) {
$contentId = $relIdToContentId[$relId];
$newOrder = ($index + 1) * 10;

// Optimistic check: if order is already correct in memory, we might skip?
// However, simpler to just add to update list, updateContentOrder should handle exceptions.
// But to avoid DB writes if nothing changed:
if ($currentPages[$startPageId]['content'][$contentId]['order'] !== $newOrder) {
$contentsToUpdate[] = [
'id' => $contentId,
'order' => $newOrder
];
$currentPages[$startPageId]['content'][$contentId]['order'] = $newOrder;
$hasUpdatedNodeInformation = true;
}
}
}
}

if (!empty($contentsToUpdate)) {
$this->updateContentOrder($startPageId, $contentsToUpdate);
}
}
}

$context = $this->contextMapper->update($context);
Expand Down Expand Up @@ -305,8 +356,10 @@ public function transfer(int $contextId, string $newOwnerId, int $newOwnerType):
$context = $this->contextMapper->update($context);

$auditEvent = new CriticalActionPerformedEvent(
sprintf('Tables application with ID %d was transferred to user %s',
$contextId, $newOwnerId,
sprintf(
'Tables application with ID %d was transferred to user %s',
$contextId,
$newOwnerId,
)
);

Expand Down
56 changes: 33 additions & 23 deletions src/modules/modals/EditContext.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcDialog v-if="showModal"
:name="t('tables', 'Edit application')"
size="normal"
data-cy="editContextModal"
<NcDialog v-if="showModal" :name="t('tables', 'Edit application')" size="normal" data-cy="editContextModal"
@closing="actionCancel">
<div class="modal__content" data-cy="editContextModal">
<div class="row">
Expand All @@ -30,7 +27,8 @@
<div class="col-4">
{{ t('tables', 'Description') }}
</div>
<input v-model="description" type="text" data-cy="editContextDes" :placeholder="t('tables', 'Description of the application')">
<input v-model="description" type="text" data-cy="editContextDes"
:placeholder="t('tables', 'Description of the application')">
</div>
<div class="col-4 row space-T">
<div class="col-4">
Expand All @@ -55,7 +53,8 @@
{{ t('tables', 'I really want to delete this application!') }}
</NcButton>
<div class="right-additional-button">
<NcButton v-if="ownsContext(localContext)" data-cy="transferContextSubmitBtn" @click="actionTransfer">
<NcButton v-if="ownsContext(localContext)" data-cy="transferContextSubmitBtn"
@click="actionTransfer">
{{ t('tables', 'Transfer application') }}
</NcButton>
<NcButton type="primary" data-cy="editContextSubmitBtn" @click="submit">
Expand Down Expand Up @@ -232,24 +231,35 @@ export default {
},
getContextResources(context) {
const resources = []
const nodes = Object.values(context.nodes)
for (const node of nodes) {
if (parseInt(node.node_type) === NODE_TYPE_TABLE || parseInt(node.node_type) === NODE_TYPE_VIEW) {
const element = parseInt(node.node_type) === NODE_TYPE_TABLE ? this.tables.find(t => t.id === node.node_id) : this.views.find(v => v.id === node.node_id)
if (element) {
const elementKey = parseInt(node.node_type) === NODE_TYPE_TABLE ? 'table-' : 'view-'
const resource = {
title: element.title,
emoji: element.emoji,
key: `${elementKey}` + element.id,
nodeType: parseInt(node.node_type) === NODE_TYPE_TABLE ? NODE_TYPE_TABLE : NODE_TYPE_VIEW,
id: (element.id).toString(),
permissionRead: this.getPermissionFromBitmask(node.permissions, PERMISSION_READ),
permissionCreate: this.getPermissionFromBitmask(node.permissions, PERMISSION_CREATE),
permissionUpdate: this.getPermissionFromBitmask(node.permissions, PERMISSION_UPDATE),
permissionDelete: this.getPermissionFromBitmask(node.permissions, PERMISSION_DELETE),
if (context && context.pages) {
const pages = Object.values(context.pages)
const startPage = pages.find(p => p.page_type === 'startpage')

if (startPage && startPage.content) {
const sortedContent = Object.values(startPage.content).sort((a, b) => a.order - b.order)

for (const content of sortedContent) {
const node = context.nodes[content.node_rel_id]
if (!node) continue

if (parseInt(node.node_type) === NODE_TYPE_TABLE || parseInt(node.node_type) === NODE_TYPE_VIEW) {
const element = parseInt(node.node_type) === NODE_TYPE_TABLE ? this.tables.find(t => t.id === node.node_id) : this.views.find(v => v.id === node.node_id)
if (element) {
const elementKey = parseInt(node.node_type) === NODE_TYPE_TABLE ? 'table-' : 'view-'
const resource = {
title: element.title,
emoji: element.emoji,
key: `${elementKey}` + element.id,
nodeType: parseInt(node.node_type) === NODE_TYPE_TABLE ? NODE_TYPE_TABLE : NODE_TYPE_VIEW,
id: (element.id).toString(),
permissionRead: this.getPermissionFromBitmask(node.permissions, PERMISSION_READ),
permissionCreate: this.getPermissionFromBitmask(node.permissions, PERMISSION_CREATE),
permissionUpdate: this.getPermissionFromBitmask(node.permissions, PERMISSION_UPDATE),
permissionDelete: this.getPermissionFromBitmask(node.permissions, PERMISSION_DELETE),
}
resources.push(resource)
}
}
resources.push(resource)
}
}
}
Expand Down
86 changes: 51 additions & 35 deletions src/pages/Context.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,46 +165,62 @@ export default {

this.icon = await this.getContextIcon(this.activeContext.iconName)

if (this.context && this.context.nodes) {
for (const [, node] of Object.entries(this.context.nodes)) {
try {
const nodeType = parseInt(node.node_type)
if (nodeType === NODE_TYPE_TABLE) {
const table = this.tables.find(table => table.id === node.node_id)
if (table) {
await this.loadColumnsFromBE({
view: null,
tableId: table.id,
})
await this.loadRowsFromBE({
viewId: null,
tableId: table.id,
})
table.key = (table.id).toString()
table.isView = false
this.contextResources.push(table)
}
this.icon = await this.getContextIcon(this.activeContext.iconName)

if (this.context && this.context.pages) {
const pages = Object.values(this.context.pages)
const startPage = pages.find(p => p.page_type === 'startpage')

if (startPage && startPage.content) {
const sortedContent = Object.values(startPage.content).sort((a, b) => a.order - b.order)

for (const content of sortedContent) {
const node = this.context.nodes[content.node_rel_id]
if (!node) continue

try {
const nodeType = parseInt(node.node_type)
if (nodeType === NODE_TYPE_TABLE) {
const table = this.tables.find(table => table.id === node.node_id)
if (table) {
await this.loadColumnsFromBE({
view: null,
tableId: table.id,
})
await this.loadRowsFromBE({
viewId: null,
tableId: table.id,
})
table.key = (table.id).toString()
table.isView = false
this.contextResources.push(table)
}

} else if (nodeType === NODE_TYPE_VIEW) {
const view = this.views.find(view => view.id === node.node_id)
if (view) {
await this.loadColumnsFromBE({
view,
})
await this.loadRowsFromBE({
viewId: view.id,
tableId: view.tableId,
})
view.key = 'view-' + (view.id).toString()
view.isView = true
this.contextResources.push(view)
} else if (nodeType === NODE_TYPE_VIEW) {
const view = this.views.find(view => view.id === node.node_id)
if (view) {
await this.loadColumnsFromBE({
view,
})
await this.loadRowsFromBE({
viewId: view.id,
tableId: view.tableId,
})
view.key = 'view-' + (view.id).toString()
view.isView = true
this.contextResources.push(view)
}
}
} catch (err) {
console.error(`Failed to load resource ${node.node_id}:`, err)
this.errorMessage = t('tables', 'Some resources in this application could not be loaded')
}
} catch (err) {
console.error(`Failed to load resource ${node.node_id}:`, err)
this.errorMessage = t('tables', 'Some resources in this application could not be loaded')
}
}
// TODO: check this
// Fallback if no startpage or content (though unexpected for valid contexts with nodes)
// If nodes exist but not in startpage content, they won't be shown.
// This matches backend logic where nodes are added to startpage.
}
} catch (e) {
if (e.message === 'NOT_FOUND') {
Expand Down
13 changes: 10 additions & 3 deletions src/shared/components/ncContextResource/NcContextResource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
<div>
<div>
<ResourceForm :resources="localResources" data-cy="contextResourceForm" @add="addResource" />
<ResourceList :resources="localResources" data-cy="contextResourceList" @remove="removeResource" />
<ResourceSharees :select-users="true" :select-groups="true" :receivers="localReceivers" data-cy="contextResourceShare" @update="updateReceivers" />
<ResourceSharePermissions :resources="localResources" data-cy="contextResourcePerms" @update="updateResourcePermissions" />
<ResourceList :resources="localResources" data-cy="contextResourceList" @remove="removeResource"
@update:resources="updateOrder" />
<ResourceSharees :select-users="true" :select-groups="true" :receivers="localReceivers"
data-cy="contextResourceShare" @update="updateReceivers" />
<ResourceSharePermissions :resources="localResources" data-cy="contextResourcePerms"
@update="updateResourcePermissions" />
</div>
</div>
</template>
Expand Down Expand Up @@ -83,6 +86,10 @@ export default {
this.contextResources.push(resource)
this.localResources = this.contextResources
},
updateOrder(resources) {
this.contextResources = resources
this.localResources = this.contextResources
},
updateReceivers(receivers) {
this.localReceivers = receivers
},
Expand Down
Loading
Loading