From 15aa8fb7b055a166b6f578ecdbf0d4faf5163f01 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Sun, 10 May 2026 18:40:56 +0500 Subject: [PATCH] =?UTF-8?q?add=20readonly=20support=20to=20MarkupEditor=20?= =?UTF-8?q?and=20improve=20hierarchy-based=20attr=E2=80=A6=20(#10835)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add readonly support to MarkupEditor and improve hierarchy-based attribute resolution and locking logic Signed-off-by: Denis Bykhov * perf: implement caching for card lookups in process-resources functions Signed-off-by: Denis Bykhov * refactor: update TagSelector to use tag IDs in selection set and optimize hierarchy access Signed-off-by: Denis Bykhov --------- Signed-off-by: Denis Bykhov --- .../src/components/MarkupProperties.svelte | 1 + .../src/components/MasterTagAttributes.svelte | 2 +- .../components/settings/TagSelector.svelte | 14 +++++----- .../src/components/MarkupEditor.svelte | 1 + plugins/view-resources/src/utils.ts | 26 ++++++++++++------- .../process-resources/src/functions.ts | 12 ++++++--- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/plugins/card-resources/src/components/MarkupProperties.svelte b/plugins/card-resources/src/components/MarkupProperties.svelte index d0a6b1d8f4..15fc8e9bdc 100644 --- a/plugins/card-resources/src/components/MarkupProperties.svelte +++ b/plugins/card-resources/src/components/MarkupProperties.svelte @@ -66,6 +66,7 @@
value.readonlySections?.includes(p)) ?? false $: canLock = canLockSection(value.space, $permissionsStore) $: canUnlock = canUnlockSection(value.space, $permissionsStore) diff --git a/plugins/process-resources/src/components/settings/TagSelector.svelte b/plugins/process-resources/src/components/settings/TagSelector.svelte index 0dd58d95d3..0c9e8fcc9b 100644 --- a/plugins/process-resources/src/components/settings/TagSelector.svelte +++ b/plugins/process-resources/src/components/settings/TagSelector.svelte @@ -25,26 +25,28 @@ export let includeBase: boolean = false const client = getClient() - const hierarchy = client.getHierarchy() + const h = client.getHierarchy() const dispatch = createEventDispatcher() function open (e: MouseEvent): void { - const res = new Set(includeBase ? [hierarchy.getClass(process.masterTag)] : []) - const ancestors = hierarchy.getAncestors(process.masterTag) + const res = new Set>() + if (includeBase) res.add(h.getBaseClass(process.masterTag)) + const ancestors = h.getAncestors(process.masterTag) const tags = client.getModel().findAllSync(cardPlugin.class.Tag, {}) for (const p of tags) { try { - const base = hierarchy.getBaseClass(p._id) + const base = h.getBaseClass(p._id) if (process.masterTag === base || ancestors.includes(base)) { - res.add(p) + res.add(p._id) } } catch (err) { console.log('error', err, p._id) } } const items: SelectPopupValueType[] = [] - res.forEach((cl) => { + res.forEach((_id) => { + const cl = h.getClass(_id) items.push({ id: cl._id, label: cl.label, diff --git a/plugins/view-resources/src/components/MarkupEditor.svelte b/plugins/view-resources/src/components/MarkupEditor.svelte index d7885bc114..4dfeccb678 100644 --- a/plugins/view-resources/src/components/MarkupEditor.svelte +++ b/plugins/view-resources/src/components/MarkupEditor.svelte @@ -73,6 +73,7 @@ alwaysEdit {kitOptions} mode={2} + {readonly} on:value={(e) => { onChange(e.detail) }} diff --git a/plugins/view-resources/src/utils.ts b/plugins/view-resources/src/utils.ts index 107d40ee0e..29e1f6c160 100644 --- a/plugins/view-resources/src/utils.ts +++ b/plugins/view-resources/src/utils.ts @@ -1245,7 +1245,7 @@ export async function sortCategories ( * @public */ export function canResolveAttribute ( - hierarchy: Hierarchy, + h: Hierarchy, _class: Ref>, key: string, lookup: Lookup | undefined @@ -1263,9 +1263,13 @@ export function canResolveAttribute ( if (key.length === 0) return true const parts = key.split('.') if (parts.length === 1) { - return hierarchy.findAttribute(_class, key) !== undefined - } else if (hierarchy.isDerived(parts[0] as Ref>, _class)) { - return hierarchy.findAttribute(parts[0] as Ref>, parts[1]) !== undefined + return h.findAttribute(_class, key) !== undefined + } else { + const target = parts[0] as Ref> + const attr = parts[1] + if (h.isDerived(target, _class) || (h.isMixin(target) && h.isDerived(_class, h.getBaseClass(target)))) { + return h.findAttribute(target, attr) !== undefined + } } return false } @@ -1276,6 +1280,7 @@ export function getKeyLabel ( key: string, lookup: Lookup | undefined ): IntlString { + const h = client.getHierarchy() if (key.startsWith('$relation')) { // Handle association: $relation.[associationId] const parts = key.split('.') @@ -1311,15 +1316,18 @@ export function getKeyLabel ( } return getEmbeddedLabel(assocName) } else if (key.length === 0) { - const clazz = client.getHierarchy().getClass(_class) + const clazz = h.getClass(_class) return clazz.label } else { const parts = key.split('.') - if (parts.length === 2 && client.getHierarchy().isDerived(parts[0] as Ref>, _class)) { - const attribute = client.getHierarchy().getAttribute(parts[0] as Ref>, parts[1]) - return attribute.label + if (parts.length === 2) { + const target = parts[0] as Ref> + if (h.isDerived(target, _class) || (h.isMixin(target) && h.isDerived(_class, h.getBaseClass(target)))) { + const attribute = h.getAttribute(target, parts[1]) + return attribute.label + } } - const attribute = client.getHierarchy().getAttribute(_class, key) + const attribute = h.getAttribute(_class, key) return attribute.label } } diff --git a/server-plugins/process-resources/src/functions.ts b/server-plugins/process-resources/src/functions.ts index faf0e7a8ac..ad9e4dc271 100644 --- a/server-plugins/process-resources/src/functions.ts +++ b/server-plugins/process-resources/src/functions.ts @@ -574,7 +574,8 @@ export async function LockSection ( if (params._id === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: '_id' }) const res: Tx[] = [] const rollback: Tx[] = [] - const card = await control.client.findOne(cardPlugin.class.Card, { _id: execution.card }) + const card: Card = + control.cache.get(execution.card) ?? (await control.client.findOne(cardPlugin.class.Card, { _id: execution.card })) if (card === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.card }) const readonlySections = new Set(card.readonlySections ?? []) const target = params._id as Ref @@ -618,7 +619,8 @@ export async function UnlockSection ( if (params._id === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: '_id' }) const res: Tx[] = [] const rollback: Tx[] = [] - const card = await control.client.findOne(cardPlugin.class.Card, { _id: execution.card }) + const card: Card = + control.cache.get(execution.card) ?? (await control.client.findOne(cardPlugin.class.Card, { _id: execution.card })) if (card === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.card }) const target = params._id as Ref const readonlySections = new Set(card.readonlySections ?? []) @@ -643,7 +645,8 @@ export async function LockField ( if (params.value === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: 'value' }) const res: Tx[] = [] const rollback: Tx[] = [] - const card = await control.client.findOne(cardPlugin.class.Card, { _id: execution.card }) + const card: Card = + control.cache.get(execution.card) ?? (await control.client.findOne(cardPlugin.class.Card, { _id: execution.card })) if (card === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.card }) const oldReadonlyFields = card.readonlyFields ?? [] const readonlyFields = [...oldReadonlyFields] @@ -678,7 +681,8 @@ export async function UnlockField ( if (params.value === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: 'value' }) const res: Tx[] = [] const rollback: Tx[] = [] - const card = await control.client.findOne(cardPlugin.class.Card, { _id: execution.card }) + const card: Card = + control.cache.get(execution.card) ?? (await control.client.findOne(cardPlugin.class.Card, { _id: execution.card })) if (card === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.card }) const oldReadonlyFields = card.readonlyFields ?? [] const targets = Array.isArray(params.value) ? params.value : [params.value]