@umbraco-cms/backoffice 17.4.0-rc → 17.4.0-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/custom-elements.json +1 -1
  2. package/dist-cms/apps/app/app.element.js +120 -7
  3. package/dist-cms/apps/backoffice/backoffice.context.js +1 -9
  4. package/dist-cms/apps/backoffice/backoffice.element.d.ts +0 -2
  5. package/dist-cms/apps/backoffice/backoffice.element.js +0 -58
  6. package/dist-cms/apps/backoffice/components/backoffice-main.element.d.ts +2 -2
  7. package/dist-cms/apps/backoffice/components/backoffice-main.element.js +16 -3
  8. package/dist-cms/libs/extension-api/controller/base-extension-initializer.controller.d.ts +1 -1
  9. package/dist-cms/libs/extension-api/controller/base-extension-initializer.controller.js +61 -19
  10. package/dist-cms/libs/extension-api/controller/extension-api-initializer.controller.d.ts +1 -1
  11. package/dist-cms/libs/extension-api/controller/extension-api-initializer.controller.js +10 -3
  12. package/dist-cms/libs/extension-api/controller/extension-element-and-api-initializer.controller.d.ts +1 -1
  13. package/dist-cms/libs/extension-api/controller/extension-element-and-api-initializer.controller.js +12 -3
  14. package/dist-cms/libs/extension-api/controller/extension-element-initializer.controller.d.ts +1 -1
  15. package/dist-cms/libs/extension-api/controller/extension-element-initializer.controller.js +11 -3
  16. package/dist-cms/libs/extension-api/controller/extension-manifest-initializer.controller.d.ts +1 -1
  17. package/dist-cms/libs/extension-api/controller/extension-manifest-initializer.controller.js +1 -1
  18. package/dist-cms/libs/extension-api/initializers/extension-initializer-base.d.ts +2 -2
  19. package/dist-cms/libs/extension-api/initializers/extension-initializer-base.js +7 -3
  20. package/dist-cms/libs/extension-api/registry/extension.registry.js +22 -2
  21. package/dist-cms/packages/block/block/conditions/block-workspace-is-readonly.condition.js +4 -2
  22. package/dist-cms/packages/block/block/context/block-entry.context.js +1 -4
  23. package/dist-cms/packages/block/block/workspace/block-element-manager.js +2 -2
  24. package/dist-cms/packages/block/block/workspace/block-workspace-editor.element.d.ts +1 -0
  25. package/dist-cms/packages/block/block/workspace/block-workspace-editor.element.js +31 -9
  26. package/dist-cms/packages/block/block/workspace/block-workspace-language-access.controller.d.ts +16 -0
  27. package/dist-cms/packages/block/block/workspace/block-workspace-language-access.controller.js +126 -0
  28. package/dist-cms/packages/block/block/workspace/block-workspace.context.js +23 -19
  29. package/dist-cms/packages/block/block/workspace/manifests.js +3 -0
  30. package/dist-cms/packages/block/block-custom-view/types.d.ts +1 -0
  31. package/dist-cms/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.js +4 -5
  32. package/dist-cms/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.js +2 -5
  33. package/dist-cms/packages/block/block-list/components/block-list-entry/block-list-entry.element.js +11 -6
  34. package/dist-cms/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.js +4 -5
  35. package/dist-cms/packages/block/block-single/components/block-single-entry/block-single-entry.element.js +4 -5
  36. package/dist-cms/packages/code-editor/umbraco-package.d.ts +1 -0
  37. package/dist-cms/packages/code-editor/umbraco-package.js +1 -0
  38. package/dist-cms/packages/content/content/global-components/content-workspace-property.element.js +1 -1
  39. package/dist-cms/packages/core/components/body-layout/body-layout.element.js +3 -1
  40. package/dist-cms/packages/core/extension-registry/conditions/condition-base.controller.js +3 -2
  41. package/dist-cms/packages/core/extension-registry/conditions/delay.condition.js +5 -1
  42. package/dist-cms/packages/core/extension-registry/conditions/switch.condition.d.ts +0 -2
  43. package/dist-cms/packages/core/extension-registry/conditions/switch.condition.js +13 -7
  44. package/dist-cms/packages/core/property/property-guard-manager/property-guard.manager.d.ts +1 -1
  45. package/dist-cms/packages/core/property/property-guard-manager/property-guard.manager.js +6 -3
  46. package/dist-cms/packages/core/property/property-guard-manager/variant-property-guard.manager.d.ts +1 -1
  47. package/dist-cms/packages/core/property/property-guard-manager/variant-property-guard.manager.js +7 -3
  48. package/dist-cms/packages/core/recycle-bin/conditions/is-trashed/entity-is-trashed.condition.js +2 -1
  49. package/dist-cms/packages/core/repository/data-mapper/mapping/data-mapping-resolver.js +1 -0
  50. package/dist-cms/packages/core/utils/guard-manager/guard.manager.base.d.ts +4 -2
  51. package/dist-cms/packages/core/utils/guard-manager/guard.manager.base.js +10 -5
  52. package/dist-cms/packages/core/utils/guard-manager/readonly-guard.manager.js +9 -5
  53. package/dist-cms/packages/core/utils/guard-manager/readonly-variant-guard.manager.d.ts +12 -2
  54. package/dist-cms/packages/core/utils/guard-manager/readonly-variant-guard.manager.js +26 -11
  55. package/dist-cms/packages/core/variant/variant-id.class.d.ts +4 -0
  56. package/dist-cms/packages/core/variant/variant-id.class.js +5 -1
  57. package/dist-cms/packages/core/workspace/components/workspace-action/common/submit/submit.action.d.ts +2 -2
  58. package/dist-cms/packages/core/workspace/components/workspace-action/common/submit/submit.action.js +8 -2
  59. package/dist-cms/packages/core/workspace/components/workspace-split-view/workspace-split-view-variant-selector.element.js +7 -10
  60. package/dist-cms/packages/core/workspace/namable/name-write-guard.manager.js +5 -2
  61. package/dist-cms/packages/core/workspace/workspace.element.d.ts +0 -2
  62. package/dist-cms/packages/core/workspace/workspace.element.js +1 -4
  63. package/dist-cms/packages/documents/document-types/property-type/manifests.js +2 -1
  64. package/dist-cms/packages/documents/documents/reference/repository/manifests.js +2 -1
  65. package/dist-cms/packages/documents/documents/user-permissions/document-property-value/conditions/document-property-value-user-permission.condition.js +2 -0
  66. package/dist-cms/packages/documents/documents/user-permissions/document-property-value/data/manifests.js +4 -2
  67. package/dist-cms/packages/documents/documents/user-permissions/document-property-value/workspace-context/document-block-property-value-user-permission.workspace-context.js +3 -5
  68. package/dist-cms/packages/documents/documents/workspace/actions/save.action.js +2 -0
  69. package/dist-cms/packages/documents/documents/workspace/document-workspace.context.js +6 -1
  70. package/dist-cms/packages/documents/umbraco-package.d.ts +1 -6
  71. package/dist-cms/packages/documents/umbraco-package.js +2 -8
  72. package/dist-cms/packages/media/media/reference/repository/manifests.js +2 -1
  73. package/dist-cms/packages/media/media-types/property-type/manifests.js +2 -1
  74. package/dist-cms/packages/members/member/reference/repository/manifests.js +2 -1
  75. package/dist-cms/packages/members/member-type/property-type/manifests.js +2 -1
  76. package/dist-cms/packages/rte/components/rte-base.element.d.ts +2 -1
  77. package/dist-cms/packages/rte/components/rte-base.element.js +19 -6
  78. package/dist-cms/packages/ufm/umbraco-package.d.ts +1 -0
  79. package/dist-cms/packages/ufm/umbraco-package.js +1 -0
  80. package/dist-cms/packages/user/current-user/current-user.context.d.ts +3 -1
  81. package/dist-cms/packages/user/current-user/current-user.context.js +11 -17
  82. package/dist-cms/tsconfig.build.tsbuildinfo +1 -1
  83. package/package.json +1 -1
  84. package/vscode-html-custom-data.json +3 -3
@@ -13,7 +13,7 @@ export class UmbExtensionManifestInitializer extends UmbBaseExtensionInitializer
13
13
  super(host, extensionRegistry, 'extManifest_', alias, onPermissionChanged);
14
14
  this._init();
15
15
  }
16
- async _conditionsAreGood() {
16
+ async _conditionsAreGood(_signal) {
17
17
  return true;
18
18
  }
19
19
  async _conditionsAreBad() {
@@ -9,8 +9,8 @@ import type { UmbElement } from '../../element-api/index.js';
9
9
  export declare abstract class UmbExtensionInitializerBase<Key extends string, T extends ManifestBase = SpecificManifestTypeOrManifestBase<UmbExtensionManifest, Key>> extends UmbControllerBase {
10
10
  #private;
11
11
  protected host: UmbElement;
12
- protected extensionRegistry: UmbExtensionRegistry<T, import("../index.js").UmbConditionConfigBase<string>, ManifestBase | T>;
13
- loaded: import("rxjs").Observable<void>;
12
+ protected extensionRegistry: UmbExtensionRegistry<T>;
13
+ loaded: import("rxjs").Observable<boolean | undefined>;
14
14
  constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry<T>, manifestType: Key);
15
15
  /**
16
16
  * Perform any logic required to instantiate the extension.
@@ -1,15 +1,17 @@
1
1
  import { UmbControllerBase } from '../../class-api/index.js';
2
- import { ReplaySubject } from '../../../external/rxjs/index.js';
2
+ import { UmbBooleanState } from '../../observable-api/index.js';
3
3
  /**
4
4
  * Base class for extension initializers, which are responsible for loading and unloading extensions.
5
5
  */
6
6
  export class UmbExtensionInitializerBase extends UmbControllerBase {
7
7
  #extensionMap;
8
+ // Use the value `undefined`, as that would not resolve a observation promise. [NL]
8
9
  #loaded;
9
10
  constructor(host, extensionRegistry, manifestType) {
10
11
  super(host);
11
12
  this.#extensionMap = new Map();
12
- this.#loaded = new ReplaySubject(1);
13
+ // Use the value `undefined`, as that would not resolve a observation promise. [NL]
14
+ this.#loaded = new UmbBooleanState(undefined);
13
15
  this.loaded = this.#loaded.asObservable();
14
16
  this.host = host;
15
17
  this.extensionRegistry = extensionRegistry;
@@ -26,7 +28,9 @@ export class UmbExtensionInitializerBase extends UmbControllerBase {
26
28
  this.#extensionMap.set(extension.alias, extension);
27
29
  return this.instantiateExtension(extension);
28
30
  }));
29
- this.#loaded.next();
31
+ if (extensions.length > 0) {
32
+ this.#loaded.setValue(true);
33
+ }
30
34
  });
31
35
  }
32
36
  }
@@ -176,8 +176,28 @@ export class UmbExtensionRegistry {
176
176
  * @memberof UmbExtensionRegistry
177
177
  */
178
178
  registerMany(manifests) {
179
- // we have to register extensions individually, so we ensure a manifest is valid before continuing to the next one
180
- manifests.forEach((manifest) => this.register(manifest));
179
+ const toAdd = [];
180
+ for (const manifest of manifests) {
181
+ // TODO: refactor this so this code is not duplicated between this and single extension register
182
+ if (!this.#validateExtension(manifest))
183
+ continue;
184
+ if (manifest.type === 'kind') {
185
+ this.defineKind(manifest);
186
+ continue;
187
+ }
188
+ // TODO: Revisit this, it could use the isExtensionApproved, but this code also checks alias duplication against the toAdd array. [NL]
189
+ if (!this.#acceptExtension(manifest))
190
+ continue;
191
+ const alias = manifest.alias;
192
+ if (this._extensions.getValue().find((e) => e.alias === alias) || toAdd.find((e) => e.alias === alias)) {
193
+ console.error(`Extension with alias ${alias} is already registered`);
194
+ continue;
195
+ }
196
+ toAdd.push(this.#appendAdditionalConditions(manifest));
197
+ }
198
+ if (toAdd.length) {
199
+ this._extensions.setValue([...this._extensions.getValue(), ...toAdd]);
200
+ }
181
201
  }
182
202
  /**
183
203
  * Unregister many extensions with the given aliases.
@@ -4,9 +4,11 @@ export class UmbBlockWorkspaceIsReadOnlyCondition extends UmbConditionBase {
4
4
  constructor(host, args) {
5
5
  super(host, args);
6
6
  this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (context) => {
7
- this.observe(context?.readOnlyGuard.permitted, (isReadOnly) => {
7
+ this.observe(context?.readOnlyGuard.isPermittedForObservableVariant(context.variantId), (isReadOnly) => {
8
+ // Only react to positives or negatives:
8
9
  if (isReadOnly !== undefined) {
9
- this.permitted = isReadOnly === (this.config.match !== undefined ? this.config.match : true);
10
+ const match = args.config.match ?? true;
11
+ this.permitted = isReadOnly === match;
10
12
  }
11
13
  }, 'observeIsReadOnly');
12
14
  });
@@ -427,14 +427,11 @@ export class UmbBlockEntryContext extends UmbContextBase {
427
427
  return;
428
428
  // TODO: Here is a potential future issue. This is parsing on the read only state of the variant that this is opened from, that is problematic when we enable switching variant within a Block. [NL]
429
429
  // TODO: This could benefit from a more dynamic approach, where we inherit all non-variant and variant scoped states. [NL]
430
- this.observe(
431
- // TODO: Instead transfer all variant states.
432
- this._manager.readOnlyState.isPermittedForObservableVariant(this._variantId), (isReadOnly) => {
430
+ this.observe(this._manager.readOnlyState.permitted, (isReadOnly) => {
433
431
  const unique = 'UMB_BLOCK_MANAGER_CONTEXT';
434
432
  if (isReadOnly) {
435
433
  const rule = {
436
434
  unique,
437
- variantId: this.#variantId.getValue(),
438
435
  };
439
436
  this.readOnlyGuard?.addRule(rule);
440
437
  }
@@ -59,12 +59,12 @@ export class UmbBlockElementManager extends UmbControllerBase {
59
59
  if (id) {
60
60
  this.structure.loadType(id);
61
61
  }
62
- });
62
+ }, null);
63
63
  this.observe(this.unique, (key) => {
64
64
  if (key) {
65
65
  this.validation.setDataPath('$.' + dataPathPropertyName + `[?(@.key == '${key}')]`);
66
66
  }
67
- });
67
+ }, null);
68
68
  this.observe(this.structure.contentTypeDataTypeUniques, (dataTypeUniques) => {
69
69
  this.#dataTypeItemManager.setUniques(dataTypeUniques);
70
70
  }, null);
@@ -2,6 +2,7 @@ import { UmbLitElement } from '../../../core/lit-element/index.js';
2
2
  export declare class UmbBlockWorkspaceEditorElement extends UmbLitElement {
3
3
  constructor();
4
4
  private _headline;
5
+ private _readOnly?;
5
6
  render(): import("lit-html").TemplateResult<1>;
6
7
  static readonly styles: import("lit").CSSResult[];
7
8
  }
@@ -5,25 +5,32 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { UMB_BLOCK_WORKSPACE_CONTEXT } from './index.js';
8
- import { css, customElement, html, state } from '../../../../external/lit/index.js';
8
+ import { css, customElement, html, nothing, state } from '../../../../external/lit/index.js';
9
9
  import { UmbLitElement } from '../../../core/lit-element/index.js';
10
10
  let UmbBlockWorkspaceEditorElement = class UmbBlockWorkspaceEditorElement extends UmbLitElement {
11
11
  constructor() {
12
12
  super();
13
13
  this._headline = '';
14
14
  this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (context) => {
15
- if (context) {
16
- this.observe(context.name, (name) => {
15
+ this.observe(context?.name, (name) => {
16
+ if (name) {
17
17
  this._headline = this.localize.string(name);
18
- }, 'observeOwnerContentElementTypeName');
19
- }
20
- else {
21
- this.removeUmbControllerByAlias('observeOwnerContentElementTypeName');
22
- }
18
+ }
19
+ }, 'observeOwnerContentElementTypeName');
20
+ this.observe(context?.readOnlyGuard.isPermittedForObservableVariant(context.variantId), (isReadOnly) => {
21
+ this._readOnly = isReadOnly;
22
+ }, 'observeIsReadOnly');
23
23
  });
24
24
  }
25
25
  render() {
26
- return html `<umb-workspace-editor headline=${this._headline}></umb-workspace-editor>`;
26
+ return html `<umb-workspace-editor
27
+ ><div slot="header">
28
+ <h3 id="headline" title="${this._headline}" data-mark="layout-headline">${this._headline}</h3>
29
+ ${this._readOnly
30
+ ? html `<uui-tag look="secondary">${this.localize.term('general_readOnly')}</uui-tag>`
31
+ : nothing}
32
+ </div></umb-workspace-editor
33
+ >`;
27
34
  }
28
35
  static { this.styles = [
29
36
  css `
@@ -32,12 +39,27 @@ let UmbBlockWorkspaceEditorElement = class UmbBlockWorkspaceEditorElement extend
32
39
  width: 100%;
33
40
  height: 100%;
34
41
  }
42
+ div {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: var(--uui-size-3);
46
+ }
47
+ #headline {
48
+ display: block;
49
+ white-space: nowrap;
50
+ overflow: hidden;
51
+ text-overflow: ellipsis;
52
+ min-width: 0;
53
+ }
35
54
  `,
36
55
  ]; }
37
56
  };
38
57
  __decorate([
39
58
  state()
40
59
  ], UmbBlockWorkspaceEditorElement.prototype, "_headline", void 0);
60
+ __decorate([
61
+ state()
62
+ ], UmbBlockWorkspaceEditorElement.prototype, "_readOnly", void 0);
41
63
  UmbBlockWorkspaceEditorElement = __decorate([
42
64
  customElement('umb-block-workspace-editor')
43
65
  ], UmbBlockWorkspaceEditorElement);
@@ -0,0 +1,16 @@
1
+ import type { UmbControllerHost } from '../../../../libs/controller-api/index.js';
2
+ import { UmbControllerBase } from '../../../../libs/class-api/index.js';
3
+ /**
4
+ * Configures the read-only state of a Block Workspace based on the parent Block Manager
5
+ * and the current user's language access.
6
+ *
7
+ * - For invariant blocks, the workspace inherits the read-only state of the parent Block Manager(Host Property).
8
+ * - For variant blocks (with a culture), the workspace is editable only when the current user
9
+ * has access to that culture (either via `hasAccessToAllLanguages` or an entry in their
10
+ * allowed languages).
11
+ */
12
+ export declare class UmbBlockLanguageAccessWorkspaceController extends UmbControllerBase {
13
+ #private;
14
+ constructor(host: UmbControllerHost);
15
+ }
16
+ export { UmbBlockLanguageAccessWorkspaceController as api };
@@ -0,0 +1,126 @@
1
+ import { UMB_BLOCK_MANAGER_CONTEXT } from '../context/block-manager.context-token.js';
2
+ import { UMB_BLOCK_WORKSPACE_CONTEXT } from './block-workspace.context-token.js';
3
+ import { UmbControllerBase } from '../../../../libs/class-api/index.js';
4
+ import { UMB_CURRENT_USER_CONTEXT } from '../../../user/current-user/index.js';
5
+ const IDENTIFIER_PREFIX = 'UMB_LANGUAGE_PERMISSION_';
6
+ /**
7
+ * Configures the read-only state of a Block Workspace based on the parent Block Manager
8
+ * and the current user's language access.
9
+ *
10
+ * - For invariant blocks, the workspace inherits the read-only state of the parent Block Manager(Host Property).
11
+ * - For variant blocks (with a culture), the workspace is editable only when the current user
12
+ * has access to that culture (either via `hasAccessToAllLanguages` or an entry in their
13
+ * allowed languages).
14
+ */
15
+ export class UmbBlockLanguageAccessWorkspaceController extends UmbControllerBase {
16
+ #workspaceContext;
17
+ #variantId;
18
+ #currentUserAllowedLanguages;
19
+ #currentUserHasAccessToAllLanguages;
20
+ #consumeBlockManager;
21
+ #appliedLanguageUnique;
22
+ constructor(host) {
23
+ super(host);
24
+ this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (instance) => {
25
+ this.#workspaceContext = instance;
26
+ this.#workspaceContext?.readOnlyGuard.fallbackToNotPermitted();
27
+ this.#workspaceContext?.content.readOnlyGuard.fallbackToNotPermitted();
28
+ this.#workspaceContext?.settings.readOnlyGuard.fallbackToNotPermitted();
29
+ this.observe(instance?.variantId, (variantId) => {
30
+ this.#variantId = variantId;
31
+ this.#observeBlockManager(variantId);
32
+ this.#checkForLanguageAccess();
33
+ }, 'observeBlockVariantId');
34
+ });
35
+ this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => {
36
+ this.observe(context?.languages, (languages) => {
37
+ this.#currentUserAllowedLanguages = languages;
38
+ this.#checkForLanguageAccess();
39
+ }, 'observeCurrentUserLanguages');
40
+ this.observe(context?.hasAccessToAllLanguages, (hasAccessToAllLanguages) => {
41
+ this.#currentUserHasAccessToAllLanguages = hasAccessToAllLanguages;
42
+ this.#checkForLanguageAccess();
43
+ }, 'observeCurrentUserHasAccessToAllLanguages');
44
+ });
45
+ }
46
+ #observeBlockManager(variantId) {
47
+ const unique = 'UMB_BLOCK_MANAGER_CONTEXT';
48
+ if (variantId?.isCultureInvariant()) {
49
+ /**
50
+ * If the Block Workspace is invariant, the readOnly state from the Block Manager should apply to the invariant fields(all) of this Workspace: [NL]
51
+ */
52
+ // Destroy any prior consumer before reassigning, so a re-emit of an invariant
53
+ // variantId does not leak the previous context consumer. [NL]
54
+ this.#consumeBlockManager?.destroy();
55
+ this.#consumeBlockManager = this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (manager) => {
56
+ this.observe(manager?.readOnlyState.permitted, (isReadOnly) => {
57
+ if (isReadOnly === undefined)
58
+ return;
59
+ if (isReadOnly) {
60
+ const rule = {
61
+ unique,
62
+ permitted: true,
63
+ };
64
+ this.#workspaceContext?.readOnlyGuard.addRule(rule);
65
+ this.#workspaceContext?.content.readOnlyGuard.addRule(rule);
66
+ this.#workspaceContext?.settings.readOnlyGuard.addRule(rule);
67
+ }
68
+ else {
69
+ this.#workspaceContext?.readOnlyGuard.removeRule(unique);
70
+ this.#workspaceContext?.content.readOnlyGuard.removeRule(unique);
71
+ this.#workspaceContext?.settings.readOnlyGuard.removeRule(unique);
72
+ }
73
+ }, 'observeManagerReadOnly');
74
+ });
75
+ }
76
+ else {
77
+ this.#workspaceContext?.readOnlyGuard.removeRule(unique);
78
+ this.#workspaceContext?.content.readOnlyGuard.removeRule(unique);
79
+ this.#workspaceContext?.settings.readOnlyGuard.removeRule(unique);
80
+ this.#consumeBlockManager?.destroy();
81
+ this.#consumeBlockManager = undefined;
82
+ this.removeUmbControllerByAlias('observeManagerReadOnly');
83
+ }
84
+ }
85
+ #checkForLanguageAccess() {
86
+ if (!this.#workspaceContext ||
87
+ this.#currentUserHasAccessToAllLanguages == undefined ||
88
+ this.#currentUserAllowedLanguages == undefined) {
89
+ return;
90
+ }
91
+ const culture = this.#variantId?.culture ?? undefined;
92
+ // If the block is invariant/segment-only, or the user has access to all languages,
93
+ // there is no language-based restriction to apply.
94
+ const allowed = !culture || this.#currentUserHasAccessToAllLanguages === true
95
+ ? true
96
+ : (this.#currentUserAllowedLanguages?.includes(culture) ?? false);
97
+ // Always remove the previously applied rule (tracked by the actual unique key,
98
+ // not just the current culture). Without this, switching the workspace's culture
99
+ // from A → B leaves a stale UMB_LANGUAGE_PERMISSION_<A> rule lingering in the
100
+ // guard manager — `findRule()` is variant-scoped so it stays harmless, but
101
+ // `getRules()` accumulates one entry per visited culture over the workspace's
102
+ // lifetime. [NL]
103
+ if (this.#appliedLanguageUnique) {
104
+ this.#workspaceContext.readOnlyGuard.removeRule(this.#appliedLanguageUnique);
105
+ this.#workspaceContext.content.readOnlyGuard.removeRule(this.#appliedLanguageUnique);
106
+ this.#workspaceContext.settings.readOnlyGuard.removeRule(this.#appliedLanguageUnique);
107
+ this.#appliedLanguageUnique = undefined;
108
+ }
109
+ if (allowed || !culture || !this.#variantId)
110
+ return;
111
+ const variantId = this.#variantId;
112
+ const unique = IDENTIFIER_PREFIX + culture;
113
+ const rule = {
114
+ unique,
115
+ variantId,
116
+ // `permitted: true` on a read-only guard means "to be read-only"
117
+ // — i.e. not editable. Combined with `fallbackToPermitted()` (default = read-only).
118
+ permitted: true,
119
+ };
120
+ this.#workspaceContext.readOnlyGuard.addRule(rule);
121
+ this.#workspaceContext.content.readOnlyGuard.addRule(rule);
122
+ this.#workspaceContext.settings.readOnlyGuard.addRule(rule);
123
+ this.#appliedLanguageUnique = unique;
124
+ }
125
+ }
126
+ export { UmbBlockLanguageAccessWorkspaceController as api };
@@ -2,12 +2,14 @@ import { UMB_BLOCK_ENTRIES_CONTEXT, UMB_BLOCK_MANAGER_CONTEXT } from '../context
2
2
  import { UmbBlockWorkspaceEditorElement } from './block-workspace-editor.element.js';
3
3
  import { UmbBlockElementManager } from './block-element-manager.js';
4
4
  import { UMB_BLOCK_WORKSPACE_VIEW_CONTENT, UMB_BLOCK_WORKSPACE_VIEW_SETTINGS } from './constants.js';
5
+ import { UmbBlockLanguageAccessWorkspaceController } from './block-workspace-language-access.controller.js';
5
6
  import { UmbSubmittableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceIsNewRedirectControllerAlias, } from '../../../core/workspace/index.js';
6
7
  import { UmbBooleanState, UmbClassState, UmbObjectState, UmbStringState, observeMultiple, } from '../../../../libs/observable-api/index.js';
7
8
  import { UMB_DISCARD_CHANGES_MODAL, UMB_MODAL_CONTEXT, UMB_MODAL_MANAGER_CONTEXT } from '../../../core/modal/index.js';
8
9
  import { decodeFilePath, UmbReadOnlyVariantGuardManager } from '../../../core/utils/index.js';
9
10
  import { UmbVariantId } from '../../../core/variant/index.js';
10
11
  import { UmbUfmVirtualRenderController } from '../../../ufm/index.js';
12
+ import { UMB_IS_TRASHED_ENTITY_CONTEXT } from '../../../core/recycle-bin/index.js';
11
13
  export class UmbBlockWorkspaceContext extends UmbSubmittableWorkspaceContextBase {
12
14
  //
13
15
  #blockManager;
@@ -109,6 +111,7 @@ export class UmbBlockWorkspaceContext extends UmbSubmittableWorkspaceContextBase
109
111
  const manifest = workspaceArgs.manifest;
110
112
  this.#entityType = manifest.meta?.entityType;
111
113
  window.addEventListener('willchangestate', this.#onWillNavigate);
114
+ new UmbBlockLanguageAccessWorkspaceController(this);
112
115
  this.content.view.inheritFrom(this.view);
113
116
  this.settings.view.inheritFrom(this.view);
114
117
  this.addValidationContext(this.content.validation);
@@ -125,6 +128,26 @@ export class UmbBlockWorkspaceContext extends UmbSubmittableWorkspaceContextBase
125
128
  this.#retrieveBlockEntries = this.consumeContext(UMB_BLOCK_ENTRIES_CONTEXT, (context) => {
126
129
  this.#blockEntries = context;
127
130
  }).asPromise({ preventTimeout: true });
131
+ this.consumeContext(UMB_IS_TRASHED_ENTITY_CONTEXT, (context) => {
132
+ this.observe(context?.isTrashed, (isTrashed) => {
133
+ const trashed = isTrashed === true;
134
+ const unique = 'UMB_PREVENT_EDIT_TRASHED_ITEM';
135
+ if (trashed) {
136
+ const rule = {
137
+ unique,
138
+ permitted: true,
139
+ };
140
+ this.readOnlyGuard.addRule(rule);
141
+ this.content.readOnlyGuard.addRule(rule);
142
+ this.settings.readOnlyGuard.addRule(rule);
143
+ }
144
+ else {
145
+ this.readOnlyGuard.removeRule(unique);
146
+ this.content.readOnlyGuard.removeRule(unique);
147
+ this.settings.readOnlyGuard.removeRule(unique);
148
+ }
149
+ }, 'observeIsTrashed');
150
+ }).skipHost();
128
151
  this.observe(this.variantId, (variantId) => {
129
152
  this.content.setVariantId(variantId);
130
153
  this.settings.setVariantId(variantId);
@@ -185,25 +208,6 @@ export class UmbBlockWorkspaceContext extends UmbSubmittableWorkspaceContextBase
185
208
  this.#exposed.setValue(exposed ?? false);
186
209
  }, 'observeHasExpose');
187
210
  }, 'observeContentKeyAndVariantId');
188
- this.observe(
189
- // TODO: Again we need to parse on all variants....
190
- manager.readOnlyState.isPermittedForObservableVariant(this.variantId), (isReadOnly) => {
191
- const unique = 'UMB_BLOCK_MANAGER_CONTEXT';
192
- if (isReadOnly) {
193
- const rule = {
194
- unique,
195
- variantId: this.#variantId.getValue(),
196
- };
197
- this.readOnlyGuard?.addRule(rule);
198
- this.content.propertyWriteGuard.addRule({ unique, permitted: false });
199
- this.settings.propertyWriteGuard.addRule({ unique, permitted: false });
200
- }
201
- else {
202
- this.readOnlyGuard?.removeRule(unique);
203
- this.content.propertyWriteGuard.removeRule(unique);
204
- this.settings.propertyWriteGuard.removeRule(unique);
205
- }
206
- }, 'observeIsReadOnly');
207
211
  this.observe(this.content.contentTypeId, (contentTypeId) => {
208
212
  this.observe(contentTypeId ? manager.blockTypeOf(contentTypeId) : undefined, async (blockType) => {
209
213
  if (blockType?.editorSize) {
@@ -1,3 +1,4 @@
1
+ import { UMB_BLOCK_WORKSPACE_CONTEXT } from './block-workspace.context-token.js';
1
2
  import { UMB_BLOCK_WORKSPACE_ALIAS, UMB_BLOCK_WORKSPACE_VIEW_CONTENT, UMB_BLOCK_WORKSPACE_VIEW_SETTINGS, } from './constants.js';
2
3
  import { UMB_WORKSPACE_CONDITION_ALIAS, UmbSubmitWorkspaceAction } from '../../../core/workspace/index.js';
3
4
  export const manifests = [
@@ -11,6 +12,7 @@ export const manifests = [
11
12
  label: '#general_create',
12
13
  look: 'primary',
13
14
  color: 'positive',
15
+ workspaceContextToken: UMB_BLOCK_WORKSPACE_CONTEXT,
14
16
  },
15
17
  conditions: [
16
18
  {
@@ -37,6 +39,7 @@ export const manifests = [
37
39
  label: '#general_update',
38
40
  look: 'primary',
39
41
  color: 'positive',
42
+ workspaceContextToken: UMB_BLOCK_WORKSPACE_CONTEXT,
40
43
  },
41
44
  conditions: [
42
45
  {
@@ -23,6 +23,7 @@ export interface UmbBlockEditorCustomViewProperties<LayoutType extends UmbBlockL
23
23
  settingsInvalid?: boolean;
24
24
  unsupported?: boolean;
25
25
  unpublished?: boolean;
26
+ readonly?: boolean;
26
27
  }
27
28
  export interface UmbBlockEditorCustomViewElement<LayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel, BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel> extends UmbBlockEditorCustomViewProperties<LayoutType, BlockType>, HTMLElement {
28
29
  }
@@ -198,7 +198,10 @@ let UmbBlockGridEntryElement = class UmbBlockGridEntryElement extends UmbLitElem
198
198
  this._workspaceEditSettingsPath = path;
199
199
  this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } });
200
200
  }, null);
201
- this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => (this._isReadOnly = isReadOnly), 'umbReadOnlyObserver');
201
+ this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => {
202
+ this._isReadOnly = isReadOnly;
203
+ this.#updateBlockViewProps({ readonly: isReadOnly });
204
+ }, 'umbReadOnlyObserver');
202
205
  }
203
206
  async #observeData() {
204
207
  this.observe(await this.#context.contentValues(), (content) => {
@@ -364,8 +367,6 @@ let UmbBlockGridEntryElement = class UmbBlockGridEntryElement extends UmbLitElem
364
367
  `;
365
368
  }
366
369
  #renderEditAction() {
367
- if (this._isReadOnly)
368
- return nothing;
369
370
  return html `
370
371
  ${when(this._showContentEdit && this._workspaceEditContentPath, () => html `
371
372
  <uui-button
@@ -389,8 +390,6 @@ let UmbBlockGridEntryElement = class UmbBlockGridEntryElement extends UmbLitElem
389
390
  `;
390
391
  }
391
392
  #renderEditSettingsAction() {
392
- if (this._isReadOnly)
393
- return nothing;
394
393
  return html `
395
394
  ${this._hasSettings && this._workspaceEditSettingsPath
396
395
  ? html `
@@ -128,17 +128,14 @@ let UmbPropertyEditorUIBlockGridElement = class UmbPropertyEditorUIBlockGridElem
128
128
  this.removeUmbControllerByAlias('observeBlockTypes');
129
129
  }
130
130
  }).passContextAliasMatches();
131
- this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
132
- this.observe(context?.dataPath, (dataPath) => {
131
+ this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => {
132
+ this.observe(propertyContext?.dataPath, (dataPath) => {
133
133
  if (dataPath) {
134
134
  // Set the data path for the local validation context:
135
135
  this.#validationContext.setDataPath(dataPath);
136
136
  this.#validationContext.autoReport();
137
137
  }
138
138
  }, 'observeDataPath');
139
- });
140
- // TODO: Prevent initial notification from these observes
141
- this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => {
142
139
  this.observe(observeMultiple([
143
140
  this.#managerContext.layouts,
144
141
  this.#managerContext.contents,
@@ -158,7 +158,10 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
158
158
  this._workspaceEditSettingsPath = path;
159
159
  this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } });
160
160
  }, null);
161
- this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => (this._isReadOnly = isReadOnly), 'umbReadOnlyObserver');
161
+ this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => {
162
+ this._isReadOnly = isReadOnly;
163
+ this.#updateBlockViewProps({ readonly: isReadOnly });
164
+ }, 'umbReadOnlyObserver');
162
165
  }
163
166
  async #observeData() {
164
167
  this.observe(await this.#context.contentValues(), (content) => {
@@ -229,6 +232,7 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
229
232
  .icon=${this._icon}
230
233
  .index=${this._blockViewProps.index}
231
234
  .unpublished=${!this._exposed}
235
+ .readOnly=${this._isReadOnly}
232
236
  .config=${this._blockViewProps.config}
233
237
  .content=${this._blockViewProps.content}
234
238
  .settings=${this._blockViewProps.settings}
@@ -242,6 +246,7 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
242
246
  .icon=${this._icon}
243
247
  .index=${this._blockViewProps.index}
244
248
  .unpublished=${!this._exposed}
249
+ .readOnly=${this._isReadOnly}
245
250
  .config=${this._blockViewProps.config}
246
251
  .content=${this._blockViewProps.content}
247
252
  .settings=${this._blockViewProps.settings}
@@ -288,8 +293,6 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
288
293
  `;
289
294
  }
290
295
  #renderEditContentAction() {
291
- if (this._isReadOnly)
292
- return nothing;
293
296
  return this._showContentEdit && this._workspaceEditContentPath
294
297
  ? html `<uui-button
295
298
  label="edit"
@@ -312,8 +315,6 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
312
315
  : nothing;
313
316
  }
314
317
  #renderEditSettingsAction() {
315
- if (this._isReadOnly)
316
- return nothing;
317
318
  return html `
318
319
  ${this._hasSettings && this._workspaceEditSettingsPath
319
320
  ? html `<uui-button
@@ -333,7 +334,11 @@ let UmbBlockListEntryElement = class UmbBlockListEntryElement extends UmbLitElem
333
334
  #renderDeleteAction() {
334
335
  if (this._isReadOnly)
335
336
  return nothing;
336
- return html ` <uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()} title=${this.localize.term('general_delete')}>
337
+ return html ` <uui-button
338
+ label="delete"
339
+ look="secondary"
340
+ @click=${() => this.#context.requestDelete()}
341
+ title=${this.localize.term('general_delete')}>
337
342
  <uui-icon name="icon-remove"></uui-icon>
338
343
  </uui-button>`;
339
344
  }
@@ -140,7 +140,10 @@ let UmbBlockRteEntryElement = class UmbBlockRteEntryElement extends UmbLitElemen
140
140
  this._workspaceEditSettingsPath = path;
141
141
  this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } });
142
142
  }, null);
143
- this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => (this._isReadOnly = isReadOnly), 'umbReadOnlyObserver');
143
+ this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => {
144
+ this._isReadOnly = isReadOnly;
145
+ this.#updateBlockViewProps({ readonly: isReadOnly });
146
+ }, 'umbReadOnlyObserver');
144
147
  }
145
148
  async #observeData() {
146
149
  this.observe(await this.#context.contentValues(), (content) => {
@@ -231,8 +234,6 @@ let UmbBlockRteEntryElement = class UmbBlockRteEntryElement extends UmbLitElemen
231
234
  .config=${this._blockViewProps.config}></umb-ref-rte-block>`;
232
235
  }
233
236
  #renderEditAction() {
234
- if (this._isReadOnly)
235
- return nothing;
236
237
  return this._showContentEdit && this._workspaceEditContentPath
237
238
  ? html `<uui-button
238
239
  label="edit"
@@ -254,8 +255,6 @@ let UmbBlockRteEntryElement = class UmbBlockRteEntryElement extends UmbLitElemen
254
255
  : nothing;
255
256
  }
256
257
  #renderEditSettingsAction() {
257
- if (this._isReadOnly)
258
- return nothing;
259
258
  return html `
260
259
  ${this._hasSettings && this._workspaceEditSettingsPath
261
260
  ? html `<uui-button
@@ -160,7 +160,10 @@ let UmbBlockSingleEntryElement = class UmbBlockSingleEntryElement extends UmbLit
160
160
  this._workspaceEditSettingsPath = path;
161
161
  this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } });
162
162
  }, null);
163
- this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => (this._isReadOnly = isReadOnly), 'umbReadOnlyObserver');
163
+ this.observe(this.#context.readOnlyGuard.permitted, (isReadOnly) => {
164
+ this._isReadOnly = isReadOnly;
165
+ this.#updateBlockViewProps({ readonly: isReadOnly });
166
+ }, 'umbReadOnlyObserver');
164
167
  }
165
168
  async #observeData() {
166
169
  this.observe(await this.#context.contentValues(), (content) => {
@@ -280,8 +283,6 @@ let UmbBlockSingleEntryElement = class UmbBlockSingleEntryElement extends UmbLit
280
283
  : nothing;
281
284
  }
282
285
  #renderEditContentAction() {
283
- if (this._isReadOnly)
284
- return nothing;
285
286
  return this._showContentEdit && this._workspaceEditContentPath
286
287
  ? html `<uui-button
287
288
  label="edit"
@@ -303,8 +304,6 @@ let UmbBlockSingleEntryElement = class UmbBlockSingleEntryElement extends UmbLit
303
304
  : nothing;
304
305
  }
305
306
  #renderEditSettingsAction() {
306
- if (this._isReadOnly)
307
- return nothing;
308
307
  return html `
309
308
  ${this._hasSettings && this._workspaceEditSettingsPath
310
309
  ? html `<uui-button
@@ -1,3 +1,4 @@
1
+ export declare const name = "Umbraco.CodeEditor";
1
2
  export declare const extensions: {
2
3
  name: string;
3
4
  alias: string;
@@ -1,3 +1,4 @@
1
+ export const name = 'Umbraco.CodeEditor';
1
2
  export const extensions = [
2
3
  {
3
4
  name: 'Umbraco Code Editor Bundle',