From 8dc6c706d719b8dff135b7501eee12424e97fc67 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Sat, 6 Apr 2024 22:37:20 +0200 Subject: [PATCH 01/22] update constants --- .../documents/user-permissions/constants.ts | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/constants.ts index b33399bf8f..6b789bd308 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/constants.ts @@ -1,16 +1,15 @@ -export const UMB_USER_PERMISSION_DOCUMENT_CREATE = 'Umb.UserPermission.Document.Create'; -export const UMB_USER_PERMISSION_DOCUMENT_READ = 'Umb.UserPermission.Document.Read'; -export const UMB_USER_PERMISSION_DOCUMENT_UPDATE = 'Umb.UserPermission.Document.Update'; -export const UMB_USER_PERMISSION_DOCUMENT_DELETE = 'Umb.UserPermission.Document.Delete'; -export const UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT = 'Umb.UserPermission.Document.CreateBlueprint'; -export const UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS = 'Umb.UserPermission.Document.Notifications'; -export const UMB_USER_PERMISSION_DOCUMENT_PUBLISH = 'Umb.UserPermission.Document.Publish'; -export const UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS = 'Umb.UserPermission.Document.Permissions'; -export const UMB_USER_PERMISSION_DOCUMENT_SEND_FOR_APPROVAL = 'Umb.UserPermission.Document.SendForApproval'; -export const UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH = 'Umb.UserPermission.Document.Unpublish'; -export const UMB_USER_PERMISSION_DOCUMENT_DUPLICATE = 'Umb.UserPermission.Document.Duplicate'; -export const UMB_USER_PERMISSION_DOCUMENT_MOVE = 'Umb.UserPermission.Document.Move'; -export const UMB_USER_PERMISSION_DOCUMENT_SORT = 'Umb.UserPermission.Document.Sort'; -export const UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES = 'Umb.UserPermission.Document.CultureAndHostnames'; -export const UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS = 'Umb.UserPermission.Document.PublicAccess'; -export const UMB_USER_PERMISSION_DOCUMENT_ROLLBACK = 'Umb.UserPermission.Document.Rollback'; +export const UMB_USER_PERMISSION_DOCUMENT_CREATE = 'Umb.Document.Create'; +export const UMB_USER_PERMISSION_DOCUMENT_READ = 'Umb.Document.Read'; +export const UMB_USER_PERMISSION_DOCUMENT_UPDATE = 'Umb.Document.Update'; +export const UMB_USER_PERMISSION_DOCUMENT_DELETE = 'Umb.Document.Delete'; +export const UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT = 'Umb.Document.CreateBlueprint'; +export const UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS = 'Umb.Document.Notifications'; +export const UMB_USER_PERMISSION_DOCUMENT_PUBLISH = 'Umb.Document.Publish'; +export const UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS = 'Umb.Document.Permissions'; +export const UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH = 'Umb.Document.Unpublish'; +export const UMB_USER_PERMISSION_DOCUMENT_DUPLICATE = 'Umb.Document.Duplicate'; +export const UMB_USER_PERMISSION_DOCUMENT_MOVE = 'Umb.Document.Move'; +export const UMB_USER_PERMISSION_DOCUMENT_SORT = 'Umb.Document.Sort'; +export const UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES = 'Umb.Document.CultureAndHostnames'; +export const UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS = 'Umb.Document.PublicAccess'; +export const UMB_USER_PERMISSION_DOCUMENT_ROLLBACK = 'Umb.Document.Rollback'; From 9fd36fa334cb3d3d8832845e42ca52ba07214bf7 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Sat, 6 Apr 2024 22:38:41 +0200 Subject: [PATCH 02/22] implement document user permission condition --- .../document-user-permission.condition.ts | 91 +++++++++++++++++++ .../documents/user-permissions/manifests.ts | 3 +- .../documents/user-permissions/utils.ts | 10 ++ .../user/user-permission/conditions/index.ts | 1 - .../conditions/user-permission.condition.ts | 45 --------- .../packages/user/user-permission/index.ts | 1 - .../user/user-permission/manifests.ts | 3 +- 7 files changed, 104 insertions(+), 50 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/utils.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/index.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/user-permission.condition.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts new file mode 100644 index 0000000000..b9b0d62a56 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts @@ -0,0 +1,91 @@ +import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; +import { UMB_CURRENT_USER_CONTEXT } from '../../../user/current-user/current-user.context.js'; +import { isDocumentUserPermission } from './utils.js'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; +import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; +import type { + ManifestCondition, + UmbConditionConfigBase, + UmbConditionControllerArguments, + UmbExtensionCondition, +} from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { DocumentPermissionPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; + +export class UmbDocumentUserPermissionCondition + extends UmbConditionBase + implements UmbExtensionCondition +{ + #entityType: string | undefined; + #unique: string | null | undefined; + #documentPermissions: Array = []; + #fallbackPermissions: string[] = []; + + constructor( + host: UmbControllerHost, + args: UmbConditionControllerArguments, + ) { + super(host, args); + + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { + this.observe( + context.currentUser, + (currentUser) => { + this.#documentPermissions = currentUser?.permissions?.filter(isDocumentUserPermission) || []; + this.#fallbackPermissions = currentUser?.fallbackPermissions || []; + this.#isAllowed(); + }, + 'umbUserPermissionConditionObserver', + ); + }); + + this.consumeContext(UMB_ENTITY_CONTEXT, (context) => { + if (!context) return; + + this.observe( + observeMultiple([context.entityType, context.unique]), + ([entityType, unique]) => { + this.#entityType = entityType; + this.#unique = unique; + this.#isAllowed(); + }, + 'umbUserPermissionEntityContextObserver', + ); + }); + } + + #isAllowed() { + if (!this.#entityType) return; + if (this.#unique === undefined) return; + + let verbs: Array = []; + + if (this.#documentPermissions) { + const permissionsForCurrentDocument = this.#documentPermissions.find( + (permission) => permission.document.id === this.#unique, + ); + const currentDocumentVerbs = permissionsForCurrentDocument ? permissionsForCurrentDocument.verbs : []; + verbs = this.#fallbackPermissions.concat(currentDocumentVerbs); + } + + this.permitted = verbs.includes(this.config.match); + } +} + +export type UmbDocumentUserPermissionConditionConfig = + UmbConditionConfigBase<'Umb.Condition.UserPermission.Document'> & { + /** + * + * + * @example + * "Umb.Document.Create" + */ + match: string; + }; + +export const manifest: ManifestCondition = { + type: 'condition', + name: 'Document User Permission Condition', + alias: 'Umb.Condition.UserPermission.Document', + api: UmbDocumentUserPermissionCondition, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts index 1fc1880cb9..be5dfcfca7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts @@ -17,6 +17,7 @@ import { UMB_USER_PERMISSION_DOCUMENT_ROLLBACK, } from './constants.js'; import { manifests as repositoryManifests } from './repository/manifests.js'; +import { manifest as conditionManifest } from './document-user-permission.condition.js'; import type { ManifestGranularUserPermission, ManifestEntityUserPermission, @@ -211,4 +212,4 @@ export const granularPermissions: Array = [ }, ]; -export const manifests = [...repositoryManifests, ...permissions, ...granularPermissions]; +export const manifests = [...repositoryManifests, ...permissions, ...granularPermissions, conditionManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/utils.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/utils.ts new file mode 100644 index 0000000000..f49a1d73eb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/utils.ts @@ -0,0 +1,10 @@ +import type { + DocumentPermissionPresentationModel, + UnknownTypePermissionPresentationModel, +} from '@umbraco-cms/backoffice/external/backend-api'; + +export function isDocumentUserPermission( + permission: DocumentPermissionPresentationModel | UnknownTypePermissionPresentationModel, +): permission is DocumentPermissionPresentationModel { + return (permission as DocumentPermissionPresentationModel).$type === 'DocumentPermissionPresentationModel'; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/index.ts deleted file mode 100644 index 64b786466c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user-permission.condition.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/user-permission.condition.ts deleted file mode 100644 index b8074b9fdc..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/conditions/user-permission.condition.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { UMB_CURRENT_USER_CONTEXT } from '../../current-user/current-user.context.js'; -import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; -import type { - ManifestCondition, - UmbConditionConfigBase, - UmbConditionControllerArguments, - UmbExtensionCondition, -} from '@umbraco-cms/backoffice/extension-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; - -export class UmbUserPermissionCondition - extends UmbConditionBase - implements UmbExtensionCondition -{ - constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { - super(host, args); - - this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { - this.observe( - context.currentUser, - (currentUser) => { - //this.permitted = currentUser?.permissions?.includes(this.config.match) || false; - }, - 'umbUserPermissionConditionObserver', - ); - }); - } -} - -export type UserPermissionConditionConfig = UmbConditionConfigBase<'Umb.Condition.UserPermission'> & { - /** - * - * - * @example - * "Umb.UserPermission.Document.Create" - */ - match: string; -}; - -export const manifest: ManifestCondition = { - type: 'condition', - name: 'User Permission Condition', - alias: 'Umb.Condition.UserPermission', - api: UmbUserPermissionCondition, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/index.ts index 33baabfff8..85f5141999 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/index.ts @@ -1,4 +1,3 @@ export * from './components/index.js'; -export * from './conditions/index.js'; export type { UmbUserPermissionModel } from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/manifests.ts index 3cdcdd9998..93d3d11fab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/manifests.ts @@ -1,4 +1,3 @@ -import { manifest as userPermissionConditionManifest } from './conditions/user-permission.condition.js'; import { manifests as userPermissionModalManifests } from './modals/manifests.js'; -export const manifests = [userPermissionConditionManifest, ...userPermissionModalManifests]; +export const manifests = [...userPermissionModalManifests]; From 4bc218a315fdd81ace63065637f4e47ed43f9410 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 12:24:14 +0200 Subject: [PATCH 03/22] add conditions --- .../entity-actions/create/manifests.ts | 6 +++ .../culture-and-hostnames/manifests.ts | 6 +++ .../documents/entity-actions/manifests.ts | 53 +++++++++++++++++++ .../entity-actions/public-access/manifests.ts | 6 +++ .../sort-children-of/manifests.ts | 6 +++ 5 files changed, 77 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts index 844d5dd2ff..bac22f39b7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts @@ -14,6 +14,12 @@ const entityActions: Array = [ icon: 'icon-add', label: 'Create...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_CREATE, + }, + ], }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts index 76f0b2f269..e7be38a554 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts @@ -15,6 +15,12 @@ const entityActions: Array = [ icon: 'icon-home', label: 'Culture and Hostnames...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES, + }, + ], }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts index 3d5c84ca4c..4b27b62e9f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts @@ -1,6 +1,11 @@ import { UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS } from '../repository/index.js'; import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import { UMB_DOCUMENT_PICKER_MODAL } from '../modals/index.js'; +import { + UMB_USER_PERMISSION_DOCUMENT_DELETE, + UMB_USER_PERMISSION_DOCUMENT_PUBLISH, + UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH, +} from '../user-permissions/constants.js'; import { manifests as createManifests } from './create/manifests.js'; import { manifests as publicAccessManifests } from './public-access/manifests.js'; import { manifests as cultureAndHostnamesManifests } from './culture-and-hostnames/manifests.js'; @@ -20,6 +25,12 @@ const entityActions: Array = [ itemRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS, pickerModalAlias: UMB_DOCUMENT_PICKER_MODAL, }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_DELETE, + }, + ], }, { type: 'entityAction', @@ -33,6 +44,12 @@ const entityActions: Array = [ icon: 'icon-blueprint', label: 'Create Document Blueprint (TBD)', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT, + }, + ], }, { type: 'entityAction', @@ -46,6 +63,12 @@ const entityActions: Array = [ itemRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS, pickerModelAlias: UMB_DOCUMENT_PICKER_MODAL, }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_MOVE, + }, + ], }, { type: 'entityAction', @@ -59,6 +82,12 @@ const entityActions: Array = [ itemRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS, pickerModal: UMB_DOCUMENT_PICKER_MODAL, }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_DUPLICATE, + }, + ], }, { type: 'entityAction', @@ -72,6 +101,12 @@ const entityActions: Array = [ icon: 'icon-globe', label: 'Publish', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_PUBLISH, + }, + ], }, { type: 'entityAction', @@ -85,6 +120,12 @@ const entityActions: Array = [ icon: 'icon-globe', label: 'Unpublish...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH, + }, + ], }, { type: 'entityAction', @@ -98,6 +139,12 @@ const entityActions: Array = [ icon: 'icon-name-badge', label: 'Permissions...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS, + }, + ], }, { type: 'entityAction', @@ -111,6 +158,12 @@ const entityActions: Array = [ icon: 'icon-megaphone', label: 'Notifications...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS, + }, + ], }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts index 6fafa57fb6..271f0274d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts @@ -15,6 +15,12 @@ const entityActions: Array = [ icon: 'icon-lock', label: 'Restrict Public Access...', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS, + }, + ], }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts index e38ec7363f..22c12d959b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts @@ -18,5 +18,11 @@ export const manifests: Array = [ sortChildrenOfRepositoryAlias: UMB_SORT_CHILDREN_OF_DOCUMENT_REPOSITORY_ALIAS, treeRepositoryAlias: UMB_DOCUMENT_TREE_REPOSITORY_ALIAS, }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + match: UMB_USER_PERMISSION_DOCUMENT_SORT, + }, + ], }, ]; From 7c910266d4a74a30e8d04dcb41e86f5934bbb2b4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 12:24:48 +0200 Subject: [PATCH 04/22] Update manifests.ts --- .../documents/documents/entity-actions/create/manifests.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts index bac22f39b7..2706244689 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts @@ -1,4 +1,5 @@ import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_ROOT_ENTITY_TYPE } from '../../entity.js'; +import { UMB_USER_PERMISSION_DOCUMENT_CREATE } from '../../user-permissions/index.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; const entityActions: Array = [ From 004427dfe22aabc02360ebeaee28a4e163efe3ab Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 13:17:10 +0200 Subject: [PATCH 05/22] implement allOf to allow multiple verbs for one permission condition --- .../extension-registry/conditions/types.ts | 10 ++++----- .../entity-actions/create/manifests.ts | 2 +- .../culture-and-hostnames/manifests.ts | 3 ++- .../documents/entity-actions/manifests.ts | 21 ++++++++++++------- .../entity-actions/public-access/manifests.ts | 3 ++- .../sort-children-of/manifests.ts | 3 ++- .../src/packages/documents/documents/types.ts | 1 + .../document-user-permission.condition.ts | 11 +++++----- 8 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts index f6747e7ed9..3c893630a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts @@ -3,26 +3,26 @@ import type { CollectionBulkActionPermissionConditionConfig } from '../../collec import type { UmbSectionUserPermissionConditionConfig } from '../../section/conditions/index.js'; import type { SectionAliasConditionConfig } from './section-alias.condition.js'; import type { SwitchConditionConfig } from './switch.condition.js'; -import type { UserPermissionConditionConfig } from '@umbraco-cms/backoffice/user-permission'; import type { BlockWorkspaceHasSettingsConditionConfig } from '@umbraco-cms/backoffice/block'; import type { WorkspaceAliasConditionConfig, WorkspaceEntityTypeConditionConfig, } from '@umbraco-cms/backoffice/workspace'; import type { UmbConditionConfigBase } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbDocumentUserPermissionConditionConfig } from '@umbraco-cms/backoffice/document'; /* TODO: in theory should't the core package import from other packages. Are there any other way we can do this? Niels: Sadly I don't see any other solutions currently. But are very open for ideas :-) now that I think about it maybe there is some ability to extend a global type, similar to the 'declare global' trick we use on Elements. */ export type ConditionTypes = + | BlockWorkspaceHasSettingsConditionConfig | CollectionAliasConditionConfig | CollectionBulkActionPermissionConditionConfig | SectionAliasConditionConfig - | WorkspaceAliasConditionConfig - | BlockWorkspaceHasSettingsConditionConfig - | WorkspaceEntityTypeConditionConfig | SwitchConditionConfig - | UserPermissionConditionConfig + | UmbDocumentUserPermissionConditionConfig | UmbSectionUserPermissionConditionConfig + | WorkspaceAliasConditionConfig + | WorkspaceEntityTypeConditionConfig | UmbConditionConfigBase; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts index 2706244689..55eeb98d8a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts @@ -18,7 +18,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_CREATE, + allOf: [UMB_USER_PERMISSION_DOCUMENT_CREATE], }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts index e7be38a554..d1f5066a78 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/manifests.ts @@ -1,4 +1,5 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; +import { UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES } from '../../user-permissions/index.js'; import { UmbDocumentCultureAndHostnamesEntityAction } from './culture-and-hostnames.action.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; @@ -18,7 +19,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES, + allOf: [UMB_USER_PERMISSION_DOCUMENT_CULTURE_AND_HOSTNAMES], }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts index 4b27b62e9f..c1958c0ffb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/manifests.ts @@ -2,7 +2,12 @@ import { UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS } from '../repository/index.js'; import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import { UMB_DOCUMENT_PICKER_MODAL } from '../modals/index.js'; import { + UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT, UMB_USER_PERMISSION_DOCUMENT_DELETE, + UMB_USER_PERMISSION_DOCUMENT_DUPLICATE, + UMB_USER_PERMISSION_DOCUMENT_MOVE, + UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS, + UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS, UMB_USER_PERMISSION_DOCUMENT_PUBLISH, UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH, } from '../user-permissions/constants.js'; @@ -28,7 +33,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_DELETE, + allOf: [UMB_USER_PERMISSION_DOCUMENT_DELETE], }, ], }, @@ -47,7 +52,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT, + allOf: [UMB_USER_PERMISSION_DOCUMENT_CREATE_BLUEPRINT], }, ], }, @@ -66,7 +71,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_MOVE, + allOf: [UMB_USER_PERMISSION_DOCUMENT_MOVE], }, ], }, @@ -85,7 +90,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_DUPLICATE, + allOf: [UMB_USER_PERMISSION_DOCUMENT_DUPLICATE], }, ], }, @@ -104,7 +109,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_PUBLISH, + allOf: [UMB_USER_PERMISSION_DOCUMENT_PUBLISH], }, ], }, @@ -123,7 +128,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH, + allOf: [UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH], }, ], }, @@ -142,7 +147,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS, + allOf: [UMB_USER_PERMISSION_DOCUMENT_PERMISSIONS], }, ], }, @@ -161,7 +166,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS, + allOf: [UMB_USER_PERMISSION_DOCUMENT_NOTIFICATIONS], }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts index 271f0274d4..0e0e684fe2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/manifests.ts @@ -1,4 +1,5 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; +import { UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS } from '../../user-permissions/index.js'; import { UmbDocumentPublicAccessEntityAction } from './public-access.action.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; @@ -18,7 +19,7 @@ const entityActions: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS, + allOf: [UMB_USER_PERMISSION_DOCUMENT_PUBLIC_ACCESS], }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts index 22c12d959b..0476d0180a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/manifests.ts @@ -1,6 +1,7 @@ import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_ROOT_ENTITY_TYPE } from '../../entity.js'; import { UMB_DOCUMENT_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js'; import { UMB_DOCUMENT_TREE_REPOSITORY_ALIAS } from '../../tree/index.js'; +import { UMB_USER_PERMISSION_DOCUMENT_SORT } from '../../user-permissions/index.js'; import { UMB_SORT_CHILDREN_OF_DOCUMENT_REPOSITORY_ALIAS } from './repository/constants.js'; import { manifests as repositoryManifests } from './repository/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; @@ -21,7 +22,7 @@ export const manifests: Array = [ conditions: [ { alias: 'Umb.Condition.UserPermission.Document', - match: UMB_USER_PERMISSION_DOCUMENT_SORT, + allOf: [UMB_USER_PERMISSION_DOCUMENT_SORT], }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts index 8c5903cfa5..fca17e55dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts @@ -3,6 +3,7 @@ import type { UmbVariantModel, UmbVariantOptionModel, UmbVariantPublishModel } f import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; import { DocumentVariantStateModel as UmbDocumentVariantState } from '@umbraco-cms/backoffice/external/backend-api'; export { UmbDocumentVariantState }; +export type { UmbDocumentUserPermissionConditionConfig } from './user-permissions/document-user-permission.condition.js'; export interface UmbDocumentDetailModel { documentType: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts index b9b0d62a56..91a0e19528 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts @@ -58,17 +58,18 @@ export class UmbDocumentUserPermissionCondition if (!this.#entityType) return; if (this.#unique === undefined) return; - let verbs: Array = []; + let verbs: Array = this.#fallbackPermissions; if (this.#documentPermissions) { const permissionsForCurrentDocument = this.#documentPermissions.find( (permission) => permission.document.id === this.#unique, ); const currentDocumentVerbs = permissionsForCurrentDocument ? permissionsForCurrentDocument.verbs : []; - verbs = this.#fallbackPermissions.concat(currentDocumentVerbs); + verbs = verbs.concat(currentDocumentVerbs); } - this.permitted = verbs.includes(this.config.match); + const uniqueVerbs = [...new Set(verbs)]; + this.permitted = this.config.allOf.every((verb) => uniqueVerbs.includes(verb)); } } @@ -78,9 +79,9 @@ export type UmbDocumentUserPermissionConditionConfig = * * * @example - * "Umb.Document.Create" + * ["Umb.Document.Save", "Umb.Document.Publish"] */ - match: string; + allOf: Array; }; export const manifest: ManifestCondition = { From ec820b0ca912051e9acf6f2175f393ccc4baf19e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 14:33:24 +0200 Subject: [PATCH 06/22] add user permissions to workspace actions --- .../documents/workspace/manifests.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index a46f146d5c..0591d7ba88 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -1,4 +1,9 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; +import { + UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH, + UMB_USER_PERMISSION_DOCUMENT_UPDATE, + UMB_USER_PERMISSION_DOCUMENT_PUBLISH, +} from '../user-permissions/index.js'; import { UmbDocumentSaveAndScheduleWorkspaceAction } from './actions/save-and-schedule.action.js'; import { UmbDocumentUnpublishWorkspaceAction } from './actions/unpublish.action.js'; import { UmbDocumentSaveAndPublishWorkspaceAction } from './actions/save-and-publish.action.js'; @@ -101,6 +106,10 @@ const workspaceActions: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH], + }, ], }, { @@ -120,6 +129,10 @@ const workspaceActions: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE], + }, ], }, /* @@ -155,6 +168,12 @@ const workspaceActionMenuItems: Array = [ label: 'Unpublish...', icon: 'icon-globe', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UNPUBLISH], + }, + ], }, { type: 'workspaceActionMenuItem', @@ -168,6 +187,12 @@ const workspaceActionMenuItems: Array = [ label: 'Publish with descendants...', icon: 'icon-globe', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH], + }, + ], }, { type: 'workspaceActionMenuItem', @@ -181,6 +206,12 @@ const workspaceActionMenuItems: Array = [ label: 'Schedule...', icon: 'icon-globe', }, + conditions: [ + { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH], + }, + ], }, ]; From 98f5cd2f56a25f9c52e0cbd6b7303861574ad07e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 15:31:13 +0200 Subject: [PATCH 07/22] add public methods to enable and disable the action --- .../workspace-action-base.controller.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/workspace-action-base.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/workspace-action-base.controller.ts index 605ca48cc8..791bf2c720 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/workspace-action-base.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/workspace-action-base.controller.ts @@ -37,4 +37,20 @@ export abstract class UmbWorkspaceActionBase public execute(): Promise { return Promise.resolve(); } + + /** + * Disables the action. + * @memberof UmbWorkspaceActionBase + */ + public disable(): void { + this._isDisabled.setValue(true); + } + + /** + * Enables the action. + * @memberof UmbWorkspaceActionBase + */ + public enable(): void { + this._isDisabled.setValue(false); + } } From 383fe6d05b62878236567354615b583e88ca166e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 15:31:58 +0200 Subject: [PATCH 08/22] programatically set the condition for save and publish so we can disable the button instead of removing it --- .../actions/save-and-publish.action.ts | 25 +++++++++++++++++++ .../documents/workspace/manifests.ts | 4 --- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts index 223602ad3c..b211cbfbaf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts @@ -1,7 +1,32 @@ import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../document-workspace.context-token.js'; +import { + UMB_USER_PERMISSION_DOCUMENT_PUBLISH, + UMB_USER_PERMISSION_DOCUMENT_UPDATE, +} from '../../user-permissions/constants.js'; +import { UmbDocumentUserPermissionCondition } from '../../user-permissions/document-user-permission.condition.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; export class UmbDocumentSaveAndPublishWorkspaceAction extends UmbWorkspaceActionBase { + constructor(host: UmbControllerHost, args: any) { + super(host, args); + + /* The action is disabled by default because the onChange callback + will first be triggered when the condition is changed to permitted */ + this.disable(); + + const condition = new UmbDocumentUserPermissionCondition(host, { + host, + config: { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH], + }, + onChange: () => { + condition.permitted ? this.enable() : this.disable(); + }, + }); + } + async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); return workspaceContext.saveAndPublish(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 0591d7ba88..65eb92d8b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -106,10 +106,6 @@ const workspaceActions: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, - { - alias: 'Umb.Condition.UserPermission.Document', - allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH], - }, ], }, { From a0a76306edbad1798ac0864e36074409b6ec61e8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 19:49:26 +0200 Subject: [PATCH 09/22] add save and preview button --- .../src/packages/documents/documents/workspace/manifests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 65eb92d8b6..0301e6e345 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -15,6 +15,7 @@ import type { ManifestWorkspaceActionMenuItem, ManifestWorkspaceView, } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbDocumentSaveAndPreviewWorkspaceAction } from './actions/save-and-preview.action.js'; export const UMB_DOCUMENT_WORKSPACE_ALIAS = 'Umb.Workspace.Document'; @@ -131,9 +132,9 @@ const workspaceActions: Array = [ }, ], }, - /* { type: 'workspaceAction', + kind: 'default', alias: 'Umb.WorkspaceAction.Document.SaveAndPreview', name: 'Save And Preview Document Workspace Action', weight: 90, @@ -148,7 +149,6 @@ const workspaceActions: Array = [ }, ], }, - */ ]; const workspaceActionMenuItems: Array = [ From b6fb7363eb9e08d2ec800a1dbcef32548850a9f6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 19:49:35 +0200 Subject: [PATCH 10/22] export as api --- .../reload-tree-item-children.action.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/reload-tree-item-children/reload-tree-item-children.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/reload-tree-item-children/reload-tree-item-children.action.ts index 16b21e69e3..d28f261947 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/reload-tree-item-children/reload-tree-item-children.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/reload-tree-item-children/reload-tree-item-children.action.ts @@ -21,4 +21,5 @@ export class UmbReloadTreeItemChildrenEntityAction extends UmbEntityActionBase Date: Tue, 9 Apr 2024 19:50:00 +0200 Subject: [PATCH 11/22] disable save and publish button when not allowed --- .../actions/save-and-preview.action.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts index 67fea0b564..b6c2596618 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts @@ -1,13 +1,29 @@ -import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../document-workspace.context-token.js'; +import { UmbDocumentUserPermissionCondition } from '../../user-permissions/document-user-permission.condition.js'; +import { UMB_USER_PERMISSION_DOCUMENT_UPDATE } from '../../user-permissions/index.js'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbDocumentSaveAndPreviewWorkspaceAction extends UmbWorkspaceActionBase { + constructor(host: UmbControllerHost, args: any) { + super(host, args); + + /* The action is disabled by default because the onChange callback + will first be triggered when the condition is changed to permitted */ + this.disable(); + + const condition = new UmbDocumentUserPermissionCondition(host, { + host, + config: { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE], + }, + onChange: () => { + condition.permitted ? this.enable() : this.disable(); + }, + }); + } + async execute() { - const workspaceContext = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); - //const document = workspaceContext.getData(); - // TODO: handle errors - //if (!document) return; - //this.workspaceContext.repository?.saveAndPreview(); - //Remember to return a promise. + alert('Save and preview'); } } From f36b09bccb6aebd9f3ef5d593d5bd8335902595d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 19:50:37 +0200 Subject: [PATCH 12/22] use extension initializer --- .../entity-actions-bundle.element.ts | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts index c696d25743..a9f1b10729 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts @@ -4,9 +4,10 @@ import { html, nothing, customElement, property, state, ifDefined } from '@umbra import type { UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section'; import { UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestEntityAction, ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionsManifestInitializer, createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; +import { UmbEntityContext } from '../../entity/entity.context'; @customElement('umb-entity-actions-bundle') export class UmbEntityActionsBundleElement extends UmbLitElement { @@ -30,6 +31,8 @@ export class UmbEntityActionsBundleElement extends UmbLitElement { #sectionSidebarContext?: UmbSectionSidebarContext; + #entityContext = new UmbEntityContext(this); + constructor() { super(); @@ -40,26 +43,35 @@ export class UmbEntityActionsBundleElement extends UmbLitElement { protected updated(_changedProperties: PropertyValueMap | Map): void { if (_changedProperties.has('entityType') && _changedProperties.has('unique')) { + this.#entityContext.setEntityType(this.entityType); + this.#entityContext.setUnique(this.unique); this.#observeEntityActions(); } } #observeEntityActions() { - this.observe( - umbExtensionsRegistry.byTypeAndFilter('entityAction', (ext) => ext.forEntityTypes.includes(this.entityType!)), + new UmbExtensionsManifestInitializer( + this, + umbExtensionsRegistry, + 'entityAction', + (ext) => ext.forEntityTypes.includes(this.entityType!), async (actions) => { this._numberOfActions = actions.length; this._firstActionManifest = - this._numberOfActions > 0 ? (actions[0] as ManifestEntityActionDefaultKind) : undefined; - if (!this._firstActionManifest) return; - this._firstActionApi = await createExtensionApi(this, this._firstActionManifest, [ - { unique: this.unique, entityType: this.entityType, meta: this._firstActionManifest.meta }, - ]); + this._numberOfActions > 0 ? (actions[0].manifest as ManifestEntityActionDefaultKind) : undefined; + this.#createFirstActionApi(); }, 'umbEntityActionsObserver', ); } + async #createFirstActionApi() { + if (!this._firstActionManifest) return; + this._firstActionApi = await createExtensionApi(this, this._firstActionManifest, [ + { unique: this.unique, entityType: this.entityType, meta: this._firstActionManifest.meta }, + ]); + } + #openContextMenu() { if (!this.entityType) throw new Error('Entity type is not defined'); if (this.unique === undefined) throw new Error('Unique is not defined'); @@ -73,7 +85,7 @@ export class UmbEntityActionsBundleElement extends UmbLitElement { render() { if (this._numberOfActions === 0) return nothing; - return html` ${this.#renderFirstAction()} ${this.#renderMore()} `; + return html`${this.#renderMore()} ${this.#renderFirstAction()} `; } #renderMore() { From d4330a122d5e77c7f1095e9c06ebcd8013ce5e44 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 20:15:28 +0200 Subject: [PATCH 13/22] add save action --- .../common/submit/submit.action.ts | 4 +-- .../workspace/actions/save.action.ts | 27 +++++++++++++++++++ .../documents/workspace/manifests.ts | 7 ++--- 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/submit/submit.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/submit/submit.action.ts index 3801e4c0f0..d44d4f9e0e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/submit/submit.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/submit/submit.action.ts @@ -22,9 +22,7 @@ export class UmbSubmitWorkspaceAction extends UmbWorkspaceActionBase { // We can't save if we don't have a unique if (unique === undefined) { - this._isDisabled.setValue(true); - } else { - this._isDisabled.setValue(false); + this.disable(); } }, 'saveWorkspaceActionUniqueObserver', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts new file mode 100644 index 0000000000..d72a6668a2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts @@ -0,0 +1,27 @@ +import { UMB_USER_PERMISSION_DOCUMENT_UPDATE } from '../../user-permissions/constants.js'; +import { UmbDocumentUserPermissionCondition } from '../../user-permissions/document-user-permission.condition.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; + +export class UmbDocumentSaveWorkspaceAction extends UmbSubmitWorkspaceAction { + constructor(host: UmbControllerHost, args: any) { + super(host, args); + + /* The action is disabled by default because the onChange callback + will first be triggered when the condition is changed to permitted */ + this.disable(); + + const condition = new UmbDocumentUserPermissionCondition(host, { + host, + config: { + alias: 'Umb.Condition.UserPermission.Document', + allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE], + }, + onChange: () => { + condition.permitted ? this.enable() : this.disable(); + }, + }); + } +} + +export { UmbDocumentSaveWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 0301e6e345..3158945b92 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -16,6 +16,7 @@ import type { ManifestWorkspaceView, } from '@umbraco-cms/backoffice/extension-registry'; import { UmbDocumentSaveAndPreviewWorkspaceAction } from './actions/save-and-preview.action.js'; +import { UmbDocumentSaveWorkspaceAction } from './actions/save.action.js'; export const UMB_DOCUMENT_WORKSPACE_ALIAS = 'Umb.Workspace.Document'; @@ -115,7 +116,7 @@ const workspaceActions: Array = [ alias: 'Umb.WorkspaceAction.Document.Save', name: 'Save Document Workspace Action', weight: 80, - api: UmbSubmitWorkspaceAction, + api: () => import('./actions/save.action.js'), meta: { label: 'Save', look: 'secondary', @@ -126,10 +127,6 @@ const workspaceActions: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, - { - alias: 'Umb.Condition.UserPermission.Document', - allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE], - }, ], }, { From 4b088db92517749e217b71496169acc981e15f3d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 20:26:53 +0200 Subject: [PATCH 14/22] lazy load all document workspace actions --- .../actions/publish-with-descendants.action.ts | 2 ++ .../actions/save-and-preview.action.ts | 2 ++ .../actions/save-and-publish.action.ts | 2 ++ .../actions/save-and-schedule.action.ts | 2 ++ .../workspace/actions/unpublish.action.ts | 2 ++ .../documents/documents/workspace/manifests.ts | 17 +++++------------ 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/publish-with-descendants.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/publish-with-descendants.action.ts index f1c1fe9fe3..1476c21406 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/publish-with-descendants.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/publish-with-descendants.action.ts @@ -7,3 +7,5 @@ export class UmbDocumentPublishWithDescendantsWorkspaceAction extends UmbWorkspa return workspaceContext.publishWithDescendants(); } } + +export { UmbDocumentPublishWithDescendantsWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts index b6c2596618..0b7bd4f7e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts @@ -27,3 +27,5 @@ export class UmbDocumentSaveAndPreviewWorkspaceAction extends UmbWorkspaceAction alert('Save and preview'); } } + +export { UmbDocumentSaveAndPreviewWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts index b211cbfbaf..8b63be05bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts @@ -32,3 +32,5 @@ export class UmbDocumentSaveAndPublishWorkspaceAction extends UmbWorkspaceAction return workspaceContext.saveAndPublish(); } } + +export { UmbDocumentSaveAndPublishWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts index 6419b22d54..2220f006ff 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts @@ -7,3 +7,5 @@ export class UmbDocumentSaveAndScheduleWorkspaceAction extends UmbWorkspaceActio return workspaceContext.schedule(); } } + +export { UmbDocumentSaveAndScheduleWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/unpublish.action.ts index dd07517d21..b74b5713b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/unpublish.action.ts @@ -7,3 +7,5 @@ export class UmbDocumentUnpublishWorkspaceAction extends UmbWorkspaceActionBase return workspaceContext.unpublish(); } } + +export { UmbDocumentUnpublishWorkspaceAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 3158945b92..db840a25bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -4,19 +4,12 @@ import { UMB_USER_PERMISSION_DOCUMENT_UPDATE, UMB_USER_PERMISSION_DOCUMENT_PUBLISH, } from '../user-permissions/index.js'; -import { UmbDocumentSaveAndScheduleWorkspaceAction } from './actions/save-and-schedule.action.js'; -import { UmbDocumentUnpublishWorkspaceAction } from './actions/unpublish.action.js'; -import { UmbDocumentSaveAndPublishWorkspaceAction } from './actions/save-and-publish.action.js'; -import { UmbDocumentPublishWithDescendantsWorkspaceAction } from './actions/publish-with-descendants.action.js'; -import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; import type { ManifestWorkspaces, ManifestWorkspaceActions, ManifestWorkspaceActionMenuItem, ManifestWorkspaceView, } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbDocumentSaveAndPreviewWorkspaceAction } from './actions/save-and-preview.action.js'; -import { UmbDocumentSaveWorkspaceAction } from './actions/save.action.js'; export const UMB_DOCUMENT_WORKSPACE_ALIAS = 'Umb.Workspace.Document'; @@ -97,7 +90,7 @@ const workspaceActions: Array = [ alias: 'Umb.WorkspaceAction.Document.SaveAndPublish', name: 'Save And Publish Document Workspace Action', weight: 70, - api: UmbDocumentSaveAndPublishWorkspaceAction, + api: () => import('./actions/save-and-publish.action.js'), meta: { label: 'Save And Publish', look: 'primary', @@ -135,7 +128,7 @@ const workspaceActions: Array = [ alias: 'Umb.WorkspaceAction.Document.SaveAndPreview', name: 'Save And Preview Document Workspace Action', weight: 90, - api: UmbDocumentSaveAndPreviewWorkspaceAction, + api: () => import('./actions/save-and-preview.action.js'), meta: { label: 'Save And Preview', }, @@ -155,7 +148,7 @@ const workspaceActionMenuItems: Array = [ alias: 'Umb.Document.WorkspaceActionMenuItem.Unpublish', name: 'Unpublish', weight: 0, - api: UmbDocumentUnpublishWorkspaceAction, + api: () => import('./actions/unpublish.action.js'), forWorkspaceActions: 'Umb.WorkspaceAction.Document.SaveAndPublish', meta: { label: 'Unpublish...', @@ -174,7 +167,7 @@ const workspaceActionMenuItems: Array = [ alias: 'Umb.Document.WorkspaceActionMenuItem.PublishWithDescendants', name: 'Publish with descendants', weight: 10, - api: UmbDocumentPublishWithDescendantsWorkspaceAction, + api: () => import('./actions/publish-with-descendants.action.js'), forWorkspaceActions: 'Umb.WorkspaceAction.Document.SaveAndPublish', meta: { label: 'Publish with descendants...', @@ -193,7 +186,7 @@ const workspaceActionMenuItems: Array = [ alias: 'Umb.Document.WorkspaceActionMenuItem.SchedulePublishing', name: 'Schedule publishing', weight: 20, - api: UmbDocumentSaveAndScheduleWorkspaceAction, + api: () => import('./actions/save-and-schedule.action.js'), forWorkspaceActions: 'Umb.WorkspaceAction.Document.SaveAndPublish', meta: { label: 'Schedule...', From fda38a8f3f98413bbc1194f506bb0b47eafa5ba1 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 22:01:39 +0200 Subject: [PATCH 15/22] use fallback permissions correctly --- .../document-user-permission.condition.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts index 91a0e19528..9114748e2f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts @@ -58,18 +58,31 @@ export class UmbDocumentUserPermissionCondition if (!this.#entityType) return; if (this.#unique === undefined) return; - let verbs: Array = this.#fallbackPermissions; + const hasDocumentPermissions = this.#documentPermissions.length > 0; - if (this.#documentPermissions) { + // if there is no permissions for any documents we use the fallback permissions + if (!hasDocumentPermissions) { + this.#check(this.#fallbackPermissions); + return; + } + + if (hasDocumentPermissions) { const permissionsForCurrentDocument = this.#documentPermissions.find( (permission) => permission.document.id === this.#unique, ); - const currentDocumentVerbs = permissionsForCurrentDocument ? permissionsForCurrentDocument.verbs : []; - verbs = verbs.concat(currentDocumentVerbs); - } - const uniqueVerbs = [...new Set(verbs)]; - this.permitted = this.config.allOf.every((verb) => uniqueVerbs.includes(verb)); + // no permissions for the current document - use the fallback permissions + if (!permissionsForCurrentDocument) { + this.#check(this.#fallbackPermissions); + return; + } + + this.#check(permissionsForCurrentDocument.verbs); + } + } + + #check(verbs: Array) { + this.permitted = this.config.allOf.every((verb) => verbs.includes(verb)); } } From c34790d4099a6d47bdb403dd86784fff43a937a6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 9 Apr 2024 22:01:53 +0200 Subject: [PATCH 16/22] set entity context after load and create --- .../documents/workspace/document-workspace.context.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index c017ba460c..1ab4c387a1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -166,8 +166,6 @@ export class UmbDocumentWorkspaceContext component: () => import('./document-workspace-editor.element.js'), setup: (_component, info) => { const unique = info.match.params.unique; - this.#entityContext.setEntityType(UMB_DOCUMENT_ENTITY_TYPE); - this.#entityContext.setUnique(unique); this.load(unique); }, }, @@ -193,6 +191,8 @@ export class UmbDocumentWorkspaceContext const { data, asObservable } = (await this.#getDataPromise) as GetDataType; if (data) { + this.#entityContext.setEntityType(UMB_DOCUMENT_ENTITY_TYPE); + this.#entityContext.setUnique(unique); this.setIsNew(false); this.#persistedData.setValue(data); this.#currentData.setValue(data); @@ -220,6 +220,8 @@ export class UmbDocumentWorkspaceContext const { data } = await this.#getDataPromise; if (!data) return undefined; + this.#entityContext.setEntityType(UMB_DOCUMENT_ENTITY_TYPE); + this.#entityContext.setUnique(data.unique); this.setIsNew(true); this.#persistedData.setValue(undefined); this.#currentData.setValue(data); From ee2f393030f70c7df8e2aa217bc8b5ede310dd5d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 08:45:42 +0200 Subject: [PATCH 17/22] don't add to the permission list if the last modal is rejected --- ...cument-granular-user-permission.element.ts | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts index 7eb0367957..f407672201 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts @@ -10,6 +10,7 @@ import { UmbChangeEvent, UmbSelectedEvent } from '@umbraco-cms/backoffice/event' import type { ManifestEntityUserPermission } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { c } from 'node_modules/msw/lib/glossary-de6278a9.js'; @customElement('umb-input-document-granular-user-permission') export class UmbInputDocumentGranularUserPermissionElement extends FormControlMixin(UmbLitElement) { @@ -66,7 +67,7 @@ export class UmbInputDocumentGranularUserPermissionElement extends FormControlMi this.dispatchEvent(new UmbChangeEvent()); } - #addGranularPermission() { + async #addGranularPermission() { this.#documentPickerModalContext = this.#modalManagerContext?.open(this, UMB_DOCUMENT_PICKER_MODAL, { data: { hideTreeRoot: true, @@ -83,17 +84,24 @@ export class UmbInputDocumentGranularUserPermissionElement extends FormControlMi if (!unique) return; const documentItem = await this.#requestDocumentItem(unique); - const result = await this.#selectEntityUserPermissionsForDocument(documentItem); - this.#documentPickerModalContext?.reject(); - const permissionItem: UmbDocumentUserPermissionModel = { - $type: 'DocumentPermissionPresentationModel', - document: { id: unique }, - verbs: result, - }; + this.#selectEntityUserPermissionsForDocument(documentItem).then( + (result) => { + this.#documentPickerModalContext?.reject(); - this.permissions = [...this._permissions, permissionItem]; - this.dispatchEvent(new UmbChangeEvent()); + const permissionItem: UmbDocumentUserPermissionModel = { + $type: 'DocumentPermissionPresentationModel', + document: { id: unique }, + verbs: result, + }; + + this.permissions = [...this._permissions, permissionItem]; + this.dispatchEvent(new UmbChangeEvent()); + }, + () => { + this.#documentPickerModalContext?.reject(); + }, + ); }); } @@ -127,7 +135,7 @@ export class UmbInputDocumentGranularUserPermissionElement extends FormControlMi const value = await this.#entityUserPermissionModalContext?.onSubmit(); return value?.allowedVerbs; } catch (error) { - return allowedVerbs; + throw new Error(); } } From efd514bc09b5c2e10ba2505ea459c88f7eed5842 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 10:51:44 +0200 Subject: [PATCH 18/22] allow for both allOf and oneOf in the condition --- .../document-user-permission.condition.ts | 27 ++++++++++++++++--- .../workspace/actions/save.action.ts | 7 +++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts index 9114748e2f..5c83f3e25d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts @@ -82,19 +82,40 @@ export class UmbDocumentUserPermissionCondition } #check(verbs: Array) { - this.permitted = this.config.allOf.every((verb) => verbs.includes(verb)); + /* we default to true se we don't require both allOf and oneOf to be defined + but they can be combined for more complex scenarios */ + let allOfPermitted = true; + let oneOfPermitted = true; + + if (this.config.allOf?.length) { + allOfPermitted = this.config.allOf.every((verb) => verbs.includes(verb)); + } + + if (this.config.oneOf?.length) { + oneOfPermitted = this.config.oneOf.some((verb) => verbs.includes(verb)); + } + + this.permitted = allOfPermitted && oneOfPermitted; } } export type UmbDocumentUserPermissionConditionConfig = UmbConditionConfigBase<'Umb.Condition.UserPermission.Document'> & { /** - * + * The user must have all of the permissions in this array for the condition to be met. * * @example * ["Umb.Document.Save", "Umb.Document.Publish"] */ - allOf: Array; + allOf?: Array; + + /** + * The user must have at least one of the permissions in this array for the condition to be met. + * + * @example + * ["Umb.Document.Save", "Umb.Document.Publish"] + */ + oneOf?: Array; }; export const manifest: ManifestCondition = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts index d72a6668a2..f49852254f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts @@ -1,4 +1,7 @@ -import { UMB_USER_PERMISSION_DOCUMENT_UPDATE } from '../../user-permissions/constants.js'; +import { + UMB_USER_PERMISSION_DOCUMENT_CREATE, + UMB_USER_PERMISSION_DOCUMENT_UPDATE, +} from '../../user-permissions/constants.js'; import { UmbDocumentUserPermissionCondition } from '../../user-permissions/document-user-permission.condition.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; @@ -15,7 +18,7 @@ export class UmbDocumentSaveWorkspaceAction extends UmbSubmitWorkspaceAction { host, config: { alias: 'Umb.Condition.UserPermission.Document', - allOf: [UMB_USER_PERMISSION_DOCUMENT_UPDATE], + oneOf: [UMB_USER_PERMISSION_DOCUMENT_CREATE, UMB_USER_PERMISSION_DOCUMENT_UPDATE], }, onChange: () => { condition.permitted ? this.enable() : this.disable(); From 9faa317e9df373b0875321f556cd31788b8e0191 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 11:14:54 +0200 Subject: [PATCH 19/22] clean up --- .../document-user-permission.condition.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts index 5c83f3e25d..607ac7f761 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/document-user-permission.condition.ts @@ -33,7 +33,7 @@ export class UmbDocumentUserPermissionCondition (currentUser) => { this.#documentPermissions = currentUser?.permissions?.filter(isDocumentUserPermission) || []; this.#fallbackPermissions = currentUser?.fallbackPermissions || []; - this.#isAllowed(); + this.#checkPermissions(); }, 'umbUserPermissionConditionObserver', ); @@ -47,14 +47,14 @@ export class UmbDocumentUserPermissionCondition ([entityType, unique]) => { this.#entityType = entityType; this.#unique = unique; - this.#isAllowed(); + this.#checkPermissions(); }, 'umbUserPermissionEntityContextObserver', ); }); } - #isAllowed() { + #checkPermissions() { if (!this.#entityType) return; if (this.#unique === undefined) return; @@ -66,6 +66,8 @@ export class UmbDocumentUserPermissionCondition return; } + /* If there are document permission we check if there are permissions for the current document + If there aren't we use the fallback permissions */ if (hasDocumentPermissions) { const permissionsForCurrentDocument = this.#documentPermissions.find( (permission) => permission.document.id === this.#unique, @@ -77,6 +79,7 @@ export class UmbDocumentUserPermissionCondition return; } + // we found permissions for the current document - check them this.#check(permissionsForCurrentDocument.verbs); } } @@ -87,14 +90,22 @@ export class UmbDocumentUserPermissionCondition let allOfPermitted = true; let oneOfPermitted = true; + // check if all of the verbs are present if (this.config.allOf?.length) { allOfPermitted = this.config.allOf.every((verb) => verbs.includes(verb)); } + // check if at least one of the verbs is present if (this.config.oneOf?.length) { oneOfPermitted = this.config.oneOf.some((verb) => verbs.includes(verb)); } + // if neither allOf or oneOf is defined we default to false + if (!allOfPermitted && !oneOfPermitted) { + allOfPermitted = false; + oneOfPermitted = false; + } + this.permitted = allOfPermitted && oneOfPermitted; } } From a499bfe6971e7a52bc0e9f42b4c2d53a7edb75cb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 11:19:10 +0200 Subject: [PATCH 20/22] add TODO --- .../documents/documents/workspace/actions/save.action.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts index f49852254f..385b7e565c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save.action.ts @@ -14,6 +14,10 @@ export class UmbDocumentSaveWorkspaceAction extends UmbSubmitWorkspaceAction { will first be triggered when the condition is changed to permitted */ this.disable(); + // TODO: this check is not sufficient. It will show the save button if a use + // has only create options. The best solution would be to split the two buttons into two separate actions + // with a condition on isNew to show/hide them + // The server will throw a permission error if this scenario happens const condition = new UmbDocumentUserPermissionCondition(host, { host, config: { From b7f8c609ac4bbc0bbad2d8b26f7075a015957cfb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 11:37:58 +0200 Subject: [PATCH 21/22] remove import --- .../input-document-granular-user-permission.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts index f407672201..8640f65ef9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/input-document-granular-user-permission/input-document-granular-user-permission.element.ts @@ -10,7 +10,6 @@ import { UmbChangeEvent, UmbSelectedEvent } from '@umbraco-cms/backoffice/event' import type { ManifestEntityUserPermission } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { c } from 'node_modules/msw/lib/glossary-de6278a9.js'; @customElement('umb-input-document-granular-user-permission') export class UmbInputDocumentGranularUserPermissionElement extends FormControlMixin(UmbLitElement) { From dc7b36bd92394ffe28366c6f49d5c5db40475720 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 10 Apr 2024 12:04:58 +0200 Subject: [PATCH 22/22] fix import --- .../entity-actions-bundle/entity-actions-bundle.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts index a9f1b10729..98b3c13bea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts @@ -1,13 +1,13 @@ +import { UmbEntityContext } from '../../entity/entity.context.js'; import type { UmbEntityAction } from '@umbraco-cms/backoffice/entity-action'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { html, nothing, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section'; import { UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { ManifestEntityAction, ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbExtensionsManifestInitializer, createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; -import { UmbEntityContext } from '../../entity/entity.context'; @customElement('umb-entity-actions-bundle') export class UmbEntityActionsBundleElement extends UmbLitElement {