From 840f6573ba60e96049545386d9f6247ad0ab3a57 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:59:35 +0200 Subject: [PATCH 01/39] add mock handlers for the user part of mfa providers --- .../src/mocks/handlers/manifests.handlers.ts | 10 +++++++-- .../src/mocks/handlers/user/index.ts | 2 ++ .../src/mocks/handlers/user/mfa.handlers.ts | 21 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/mocks/handlers/user/mfa.handlers.ts diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts index e84f191d7f..751575284d 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts @@ -81,8 +81,14 @@ export const manifestDevelopmentHandler = rest.get(umbracoPath('/package/manifes extensions: [ { type: 'mfaLoginProvider', - alias: 'My.MfaLoginProvider.Custom', - name: 'My Custom MFA Provider', + alias: 'My.MfaLoginProvider.Custom.Google', + name: 'My Custom Google MFA Provider', + forProviderName: 'Google Authenticator', + }, + { + type: 'mfaLoginProvider', + alias: 'My.MfaLoginProvider.Custom.SMS', + name: 'My Custom SMS MFA Provider', forProviderName: 'sms', meta: { label: 'Setup SMS Verification', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/index.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/index.ts index 43a356f3bb..bcd348f3f4 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/index.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/index.ts @@ -8,6 +8,7 @@ import { handlers as changePasswordHandlers } from './change-password.handlers.j import { handlers as unlockHandlers } from './unlock.handlers.js'; import { handlers as inviteHandlers } from './invite.handlers.js'; import { handlers as filterHandlers } from './filter.handlers.js'; +import { handlers as mfaHandlers } from './mfa.handlers.js'; export const handlers = [ ...itemHandlers, @@ -20,4 +21,5 @@ export const handlers = [ ...filterHandlers, ...inviteHandlers, ...detailHandlers, + ...mfaHandlers, ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/mfa.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/mfa.handlers.ts new file mode 100644 index 0000000000..e3316bf1b0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user/mfa.handlers.ts @@ -0,0 +1,21 @@ +const { rest } = window.MockServiceWorker; +import { umbUserMockDb } from '../../data/user/user.db.js'; +import { UMB_SLUG } from './slug.js'; +import { umbracoPath } from '@umbraco-cms/backoffice/utils'; + +export const handlers = [ + rest.get(umbracoPath(`${UMB_SLUG}/:id/2fa`), (_req, res, ctx) => { + const mfaLoginProviders = umbUserMockDb.getMfaLoginProviders(); + return res(ctx.status(200), ctx.json(mfaLoginProviders)); + }), + rest.delete(umbracoPath(`${UMB_SLUG}/:id/2fa/:providerName`), async (req, res, ctx) => { + const mfaLoginProviders = umbUserMockDb.getMfaLoginProviders(); + const provider = mfaLoginProviders.find((p) => p.providerName === req.params.providerName); + if (!provider) { + return res(ctx.status(404)); + } + + provider.isEnabledOnUser = false; + return res(ctx.status(200)); + }), +]; From e43405bf41d6ae9d2ac78ce89436ec250892c106 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:59:52 +0200 Subject: [PATCH 02/39] add a util function to check if the current user is an admin --- .../user/current-user/current-user.context.ts | 9 +++++++++ .../utils/is-current-user.function.ts | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts index 7c7e759f0f..58494181b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts @@ -55,6 +55,15 @@ export class UmbCurrentUserContext extends UmbContextBase return currentUser?.unique === userUnique; } + /** + * Checks if the current user is an admin. + * @returns True if the current user is an admin, otherwise false + */ + async isCurrentUserAdmin(): Promise { + const currentUser = await firstValueFrom(this.currentUser); + return true; // TODO: Implement this + } + #observeIsAuthorized() { if (!this.#authContext) return; this.observe(this.#authContext.isAuthorized, (isAuthorized) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts index 820146e9a3..7992138140 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts @@ -15,5 +15,21 @@ export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) controller.destroy(); - return await currentUserContext!.isUserCurrentUser(userUnique); + return currentUserContext!.isUserCurrentUser(userUnique); +}; + +export const isCurrentUserAnAdmin = async (host: UmbControllerHost) => { + const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); + let currentUserContext = await ctrl.asPromise(); + ctrl.destroy(); + + const controller = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (context) => { + currentUserContext = context; + }); + + await controller.asPromise(); + + controller.destroy(); + + return currentUserContext!.isCurrentUserAdmin(); }; From 2372bed4e2df20f65e83cc83c35197c98ab82a0d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:00:15 +0200 Subject: [PATCH 03/39] add protected function to condition system to check if user is an admin --- .../conditions/user-allow-action-base.condition.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts index 5ffcee3d46..fe26878e80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts @@ -1,7 +1,7 @@ import type { UmbUserStateEnum } from '../types.js'; import { UMB_USER_WORKSPACE_CONTEXT } from '../workspace/user-workspace.context-token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { isCurrentUser } from '@umbraco-cms/backoffice/current-user'; +import { isCurrentUser, isCurrentUserAnAdmin } from '@umbraco-cms/backoffice/current-user'; import type { UmbConditionConfigBase, UmbConditionControllerArguments, @@ -51,6 +51,15 @@ export abstract class UmbUserActionConditionBase protected async isCurrentUser() { return this.userUnique ? isCurrentUser(this._host, this.userUnique) : false; } + + /** + * Check if the current user is an admin + * @protected + */ + protected async isCurrentUserAdmin() { + return isCurrentUserAnAdmin(this._host); + } + /** * Called when the user data changes * @protected From c6271e42a3e8cac2dec00c47e9b6e4fa0c26f2ab Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:01:18 +0200 Subject: [PATCH 04/39] add a condition to check if configuration of mfa providers is allowed for the current user --- .../user/user/conditions/manifests.ts | 2 ++ .../user-allow-mfa-action.condition.ts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts index 15d506c0c8..e085874d5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts @@ -1,11 +1,13 @@ import { manifest as userAllowDisableActionManifest } from './user-allow-disable-action.condition.js'; import { manifest as userAllowEnableActionManifest } from './user-allow-enable-action.condition.js'; import { manifest as userAllowUnlockActionManifest } from './user-allow-unlock-action.condition.js'; +import { manifest as userAllowMfaActionManifest } from './user-allow-mfa-action.condition.js'; import { manifest as userAllowDeleteActionManifest } from './user-allow-delete-action.condition.js'; export const manifests = [ userAllowDisableActionManifest, userAllowEnableActionManifest, userAllowUnlockActionManifest, + userAllowMfaActionManifest, userAllowDeleteActionManifest, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts new file mode 100644 index 0000000000..68f2d47c5f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts @@ -0,0 +1,30 @@ +import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js'; +import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +export class UmbUserAllowMfaActionCondition extends UmbUserActionConditionBase { + async _onUserDataChange() { + const isCurrentUser = await this.isCurrentUser(); + let isAdmin = false; + + // If it's not the current user being clicked, we need to check if the current logged-in user is an admin + if (!isCurrentUser) { + isAdmin = await this.isCurrentUserAdmin(); + } + + const isCurrentUserOrAdmin = isCurrentUser || isAdmin; + + this.observe( + umbExtensionsRegistry.byType('mfaLoginProvider'), + (exts) => (this.permitted = isCurrentUserOrAdmin && exts.length > 0), + '_hasProviders', + ); + } +} + +export const manifest: ManifestCondition = { + type: 'condition', + name: 'User Allow Mfa Action Condition', + alias: 'Umb.Condition.User.AllowMfaAction', + api: UmbUserAllowMfaActionCondition, +}; From 7e5ea3937e5c54b55aa248e13a47d4bff11bc31d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:01:47 +0200 Subject: [PATCH 05/39] add a modal to see the list of a user's mfa providers and also allow to disable them if current user or admin --- .../user/user/entity-actions/manifests.ts | 19 ++ .../entity-actions/mfa/mfa-user.action.ts | 42 +++++ .../packages/user/user/modals/manifests.ts | 6 + .../modals/user-mfa/user-mfa-modal.element.ts | 163 ++++++++++++++++++ .../modals/user-mfa/user-mfa-modal.stories.ts | 46 +++++ .../modals/user-mfa/user-mfa-modal.token.ts | 12 ++ .../change-user-password.repository.ts | 3 +- .../sources/user-mfa.server.data-source.ts | 56 ++++++ .../user/user/repository/user.repository.ts | 45 ++++- .../src/packages/user/user/types.ts | 4 +- 10 files changed, 385 insertions(+), 11 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.stories.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts index 879e196811..a8200fddfc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts @@ -4,6 +4,7 @@ import { UmbDisableUserEntityAction } from './disable/disable-user.action.js'; import { UmbEnableUserEntityAction } from './enable/enable-user.action.js'; import { UmbChangeUserPasswordEntityAction } from './change-password/change-user-password.action.js'; import { UmbUnlockUserEntityAction } from './unlock/unlock-user.action.js'; +import { UmbMfaUserEntityAction } from './mfa/mfa-user.action.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; const entityActions: Array = [ @@ -90,6 +91,24 @@ const entityActions: Array = [ }, ], }, + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.User.ConfigureMfa', + name: 'Configure MFA Entity Action', + weight: 500, + api: UmbMfaUserEntityAction, + forEntityTypes: [UMB_USER_ENTITY_TYPE], + meta: { + icon: 'icon-settings', + label: 'Configure Two-Factor', + }, + conditions: [ + { + alias: 'Umb.Condition.User.AllowMfaAction', + }, + ], + }, ]; export const manifests = [...entityActions]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts new file mode 100644 index 0000000000..cd96ab140e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts @@ -0,0 +1,42 @@ +import { UMB_USER_MFA_MODAL } from '../../modals/user-mfa/user-mfa-modal.token.js'; +import { UMB_CURRENT_USER_MFA_MODAL } from '../../../current-user/modals/current-user-mfa/current-user-mfa-modal.token.js'; +import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; +import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbMfaUserEntityAction extends UmbEntityActionBase { + constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { + super(host, args); + } + + async execute() { + const { unique } = this.args; + if (!unique) throw new Error('Unique is not available'); + + const currentUserContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); + const currentUserModel = await firstValueFrom(currentUserContext.currentUser); + + if (!currentUserModel) throw new Error('Current user is not available'); + + // If you clicked on yourself, we can just use the current user modal + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (currentUserModel.unique === unique) { + await modalManagerContext + .open(this, UMB_CURRENT_USER_MFA_MODAL) + .onSubmit() + .catch(() => undefined); + return; + } + + // Otherwise we will show the generic mfa modal + await modalManagerContext + .open(this, UMB_USER_MFA_MODAL, { + data: { unique }, + }) + .onSubmit() + .catch(() => undefined); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts index e01a970060..1489398aeb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts @@ -19,6 +19,12 @@ const modals: Array = [ name: 'User Picker Modal', js: () => import('./user-picker/user-picker-modal.element.js'), }, + { + type: 'modal', + alias: 'Umb.Modal.User.Mfa', + name: 'User Mfa Modal', + js: () => import('./user-mfa/user-mfa-modal.element.js'), + }, ]; export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts new file mode 100644 index 0000000000..e76a365753 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -0,0 +1,163 @@ +import { UmbUserRepository } from '../../repository/index.js'; +import type { UmbUserMfaProviderModel } from '../../types.js'; +import type { UmbUserMfaModalConfiguration } from './user-mfa-modal.token.js'; +import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; + +type UmbMfaLoginProviderOption = UmbUserMfaProviderModel & { + displayName: string; +}; + +@customElement('umb-user-mfa-modal') +export class UmbUserMfaModalElement extends UmbLitElement { + @property({ attribute: false }) + modalContext?: UmbModalContext; + + @state() + _items: Array = []; + + #unique = ''; + #userRepository = new UmbUserRepository(this); + + firstUpdated() { + this.#unique = this.modalContext?.data.unique ?? ''; + this.#loadProviders(); + } + + async #loadProviders() { + const serverLoginProviders$ = (await this.#userRepository.requestMfaProviders(this.#unique)).asObservable(); + const manifestLoginProviders$ = umbExtensionsRegistry.byType('mfaLoginProvider'); + + // Merge the server and manifest providers to get the final list of providers + const mfaLoginProviders$ = mergeObservables( + [serverLoginProviders$, manifestLoginProviders$], + ([serverLoginProviders, manifestLoginProviders]) => { + return manifestLoginProviders.map((manifestLoginProvider) => { + const serverLoginProvider = serverLoginProviders.find( + (serverLoginProvider) => serverLoginProvider.providerName === manifestLoginProvider.forProviderName, + ); + return { + isEnabledOnUser: serverLoginProvider?.isEnabledOnUser ?? false, + providerName: serverLoginProvider?.providerName ?? manifestLoginProvider.forProviderName, + displayName: + manifestLoginProvider.meta?.label ?? serverLoginProvider?.providerName ?? manifestLoginProvider.name, + } satisfies UmbMfaLoginProviderOption; + }); + }, + ); + + this.observe( + mfaLoginProviders$, + (providers) => { + this._items = providers; + }, + '_mfaLoginProviders', + ); + } + + #close() { + this.modalContext?.submit(); + } + + render() { + return html` + +
+ ${when( + this._items.length > 0, + () => html` + ${repeat( + this._items, + (item) => item.providerName, + (item) => this.#renderProvider(item), + )} + `, + )} +
+
+ + ${this.localize.term('general_close')} + +
+
+ `; + } + + /** + * Render a provider with a toggle to enable/disable it + */ + #renderProvider(item: UmbMfaLoginProviderOption) { + return html` + + ${when( + item.isEnabledOnUser, + () => html` +

+ This two-factor provider is enabled + +

+ this.#onProviderDisable(item)}> + `, + () => html` + + `, + )} +
+ `; + } + + /** + * This method is called when the user clicks the disable button on a provider. + * It will show a confirmation dialog and then disable the provider if the user confirms. + * NB! The user must have administrative rights before doing so. + */ + async #onProviderDisable(item: UmbMfaLoginProviderOption) { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const confirm = await modalManager + .open(this, UMB_CONFIRM_MODAL, { + data: { + headline: this.localize.term('actions_disable'), + content: this.localize.term('user_2faDisableForUser'), + confirmLabel: this.localize.term('actions_disable'), + color: 'danger', + }, + }) + .onSubmit() + .catch(() => false); + + if (confirm !== false) { + await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); + this.#loadProviders(); + } + } + + static styles = [ + UmbTextStyles, + css` + uui-box { + margin-bottom: var(--uui-size-space-3); + } + `, + ]; +} + +export default UmbUserMfaModalElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-user-mfa-modal': UmbUserMfaModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.stories.ts new file mode 100644 index 0000000000..1f5bac4788 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/web-components'; +import type { UmbUserMfaModalElement } from './user-mfa-modal.element.js'; +import { html } from '@umbraco-cms/backoffice/external/lit'; +import { UmbServerExtensionRegistrator } from '@umbraco-cms/backoffice/extension-api'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +import './user-mfa-modal.element.js'; + +class UmbServerExtensionsHostElement extends UmbLitElement { + constructor() { + super(); + new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerAllExtensions(); + } + + render() { + return html``; + } +} + +if (window.customElements.get('umb-server-extensions-host') === undefined) { + customElements.define('umb-server-extensions-host', UmbServerExtensionsHostElement); +} + +const meta: Meta = { + title: 'User/MFA/Configure MFA Providers', + component: 'umb-user-mfa-modal', + decorators: [ + (Story) => + html` + ${Story()} + `, + ], + parameters: { + layout: 'centered', + actions: { + disabled: true, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Overview: Story = {}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.token.ts new file mode 100644 index 0000000000..aec11cb24e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.token.ts @@ -0,0 +1,12 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export type UmbUserMfaModalConfiguration = { + unique: string; +}; + +export const UMB_USER_MFA_MODAL = new UmbModalToken('Umb.Modal.User.Mfa', { + modal: { + type: 'sidebar', + size: 'small', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts index 6c6792d6e4..b5844b38dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts @@ -5,7 +5,6 @@ import type { UmbNotificationContext } from '@umbraco-cms/backoffice/notificatio export class UmbChangeUserPasswordRepository extends UmbUserRepositoryBase { #changePasswordSource: UmbChangeUserPasswordServerDataSource; - #notificationContext?: UmbNotificationContext; constructor(host: UmbControllerHost) { super(host); @@ -21,7 +20,7 @@ export class UmbChangeUserPasswordRepository extends UmbUserRepositoryBase { if (!error) { const notification = { data: { message: `Password changed` } }; - this.#notificationContext?.peek('positive', notification); + this.notificationContext?.peek('positive', notification); } return { data, error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts new file mode 100644 index 0000000000..45e37c0d3a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts @@ -0,0 +1,56 @@ +import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * A data source for User MFA items that fetches data from the server + * @export + * @class UmbMfaServerDataSource + */ +export class UmbUserMfaServerDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbMfaServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbMfaServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Request the MFA providers for a user + * @param unique The unique id of the user + * @memberof UmbMfaServerDataSource + */ + requestMfaProviders(unique: string) { + if (!unique) throw new Error('User id is missing'); + + return tryExecuteAndNotify( + this.#host, + UserResource.getUserById2Fa({ + id: unique, + }), + ); + } + + /** + * Disables a MFA provider for a user + * @param unique The unique id of the user + * @param providerName The name of the provider + * @memberof UmbMfaServerDataSource + */ + disableMfaProvider(unique: string, providerName: string) { + if (!unique) throw new Error('User id is missing'); + if (!providerName) throw new Error('Provider is missing'); + + return tryExecuteAndNotify( + this.#host, + UserResource.deleteUserById2FaByProviderName({ + id: unique, + providerName, + }), + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts index 70546d58cf..b6f5704580 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts @@ -1,15 +1,12 @@ +import { UmbUserMfaServerDataSource } from './sources/user-mfa.server.data-source.js'; import { UmbUserSetGroupsServerDataSource } from './sources/user-set-group.server.data-source.js'; import { UmbUserRepositoryBase } from './user-repository-base.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { of } from '@umbraco-cms/backoffice/external/rxjs'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; export class UmbUserRepository extends UmbUserRepositoryBase { - #setUserGroupsSource: UmbUserSetGroupsServerDataSource; - - constructor(host: UmbControllerHost) { - super(host); - - this.#setUserGroupsSource = new UmbUserSetGroupsServerDataSource(host); - } + #setUserGroupsSource = new UmbUserSetGroupsServerDataSource(this._host); + #userMfaSource = new UmbUserMfaServerDataSource(this._host); async setUserGroups(userIds: Array, userGroupIds: Array) { if (userGroupIds.length === 0) throw new Error('User group ids are missing'); @@ -23,4 +20,36 @@ export class UmbUserRepository extends UmbUserRepositoryBase { return { error }; } + + /** + * Request the MFA providers for a user + * @param unique The unique id of the user + * @memberof UmbUserRepository + */ + async requestMfaProviders(unique: string) { + const { data, error } = await this.#userMfaSource.requestMfaProviders(unique); + return { data, error, asObservable: () => of(data ?? []) }; + } + + /** + * Disables a MFA provider for a user + * @param unique The unique id of the user + * @param providerName The name of the provider + * @memberof UmbUserRepository + */ + async disableMfaProvider(unique: string, providerName: string) { + const { data, error } = await this.#userMfaSource.disableMfaProvider(unique, providerName); + + const localize = new UmbLocalizationController(this._host); + + if (!error) { + const notification = { data: { message: localize.term('user_2faProviderIsDisabledMsg') } }; + this.notificationContext?.peek('positive', notification); + } else { + const notification = { data: { message: localize.term('user_2faProviderIsNotDisabledMsg') } }; + this.notificationContext?.peek('warning', notification); + } + + return { data, error }; + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts index 3cb879b380..a47d4dd994 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts @@ -1,5 +1,5 @@ import type { UmbUserEntityType } from './entity.js'; -import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { UserStateModel, type UserTwoFactorProviderModel } from '@umbraco-cms/backoffice/external/backend-api'; export type UmbUserStateEnum = UserStateModel; export const UmbUserStateEnum = UserStateModel; @@ -23,3 +23,5 @@ export interface UmbUserDetailModel { lastLockoutDate: string | null; lastPasswordChangeDate: string | null; } + +export type UmbUserMfaProviderModel = UserTwoFactorProviderModel; From 5c7a454005d5da8c640d22489bbe593ba08641f5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:06:55 +0200 Subject: [PATCH 06/39] improvements --- .../user/user/conditions/user-allow-mfa-action.condition.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts index 68f2d47c5f..d9731e89bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts @@ -14,10 +14,11 @@ export class UmbUserAllowMfaActionCondition extends UmbUserActionConditionBase { const isCurrentUserOrAdmin = isCurrentUser || isAdmin; + // Check if there are any MFA providers available and if the user is allowed to use them this.observe( umbExtensionsRegistry.byType('mfaLoginProvider'), (exts) => (this.permitted = isCurrentUserOrAdmin && exts.length > 0), - '_hasProviders', + '_userAllowMfaActionConditionProviders', ); } } From 7fe68649d7495262ba90e063beb1b72a75c8d256 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:08:20 +0200 Subject: [PATCH 07/39] UmbChangeUserPasswordRepository does not need to import localization context itself as that is already inherited --- .../change-password/change-user-password.repository.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts index b5844b38dd..404958ae52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.repository.ts @@ -1,7 +1,6 @@ import { UmbUserRepositoryBase } from '../user-repository-base.js'; import { UmbChangeUserPasswordServerDataSource } from './change-user-password.server.data-source.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; export class UmbChangeUserPasswordRepository extends UmbUserRepositoryBase { #changePasswordSource: UmbChangeUserPasswordServerDataSource; From 5bc8be81e31aa6ea7f799d729b65350d66c403d9 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:47:52 +0200 Subject: [PATCH 08/39] add action to check if the current logged-in user is allowed to perform actions on the selected user --- .../utils/is-current-user.function.ts | 20 +-------- .../user/user/conditions/manifests.ts | 2 + .../user-allow-action-base.condition.ts | 11 ++++- .../user-allow-mfa-action.condition.ts | 14 +----- .../user-can-perform-actions.condition.ts | 44 +++++++++++++++++++ 5 files changed, 60 insertions(+), 31 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts index 7992138140..2f088841db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts @@ -4,32 +4,16 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); - let currentUserContext = await ctrl.asPromise(); + const currentUserContext = await ctrl.asPromise(); ctrl.destroy(); - const controller = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (context) => { - currentUserContext = context; - }); - - await controller.asPromise(); - - controller.destroy(); - return currentUserContext!.isUserCurrentUser(userUnique); }; export const isCurrentUserAnAdmin = async (host: UmbControllerHost) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); - let currentUserContext = await ctrl.asPromise(); + const currentUserContext = await ctrl.asPromise(); ctrl.destroy(); - const controller = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (context) => { - currentUserContext = context; - }); - - await controller.asPromise(); - - controller.destroy(); - return currentUserContext!.isCurrentUserAdmin(); }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts index e085874d5e..de7909943d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts @@ -3,6 +3,7 @@ import { manifest as userAllowEnableActionManifest } from './user-allow-enable-a import { manifest as userAllowUnlockActionManifest } from './user-allow-unlock-action.condition.js'; import { manifest as userAllowMfaActionManifest } from './user-allow-mfa-action.condition.js'; import { manifest as userAllowDeleteActionManifest } from './user-allow-delete-action.condition.js'; +import { manifest as userCanPerformActionsManifest } from './user-can-perform-actions.condition.js'; export const manifests = [ userAllowDisableActionManifest, @@ -10,4 +11,5 @@ export const manifests = [ userAllowUnlockActionManifest, userAllowMfaActionManifest, userAllowDeleteActionManifest, + userCanPerformActionsManifest, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts index fe26878e80..f8f2bcc7c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts @@ -1,3 +1,4 @@ +import { isUserAdmin } from '../utils/index.js'; import type { UmbUserStateEnum } from '../types.js'; import { UMB_USER_WORKSPACE_CONTEXT } from '../workspace/user-workspace.context-token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -56,10 +57,18 @@ export abstract class UmbUserActionConditionBase * Check if the current user is an admin * @protected */ - protected async isCurrentUserAdmin() { + protected isCurrentUserAdmin() { return isCurrentUserAnAdmin(this._host); } + /** + * Check if the selected user is an admin + * @protected + */ + protected async isUserAdmin() { + return this.userUnique ? isUserAdmin(this._host, this.userUnique) : false; + } + /** * Called when the user data changes * @protected diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts index d9731e89bc..f5a47432b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts @@ -4,20 +4,10 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr export class UmbUserAllowMfaActionCondition extends UmbUserActionConditionBase { async _onUserDataChange() { - const isCurrentUser = await this.isCurrentUser(); - let isAdmin = false; - - // If it's not the current user being clicked, we need to check if the current logged-in user is an admin - if (!isCurrentUser) { - isAdmin = await this.isCurrentUserAdmin(); - } - - const isCurrentUserOrAdmin = isCurrentUser || isAdmin; - - // Check if there are any MFA providers available and if the user is allowed to use them + // Check if there are any MFA providers available this.observe( umbExtensionsRegistry.byType('mfaLoginProvider'), - (exts) => (this.permitted = isCurrentUserOrAdmin && exts.length > 0), + (exts) => (this.permitted = exts.length > 0), '_userAllowMfaActionConditionProviders', ); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts new file mode 100644 index 0000000000..9a885aa7aa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts @@ -0,0 +1,44 @@ +import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js'; +import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; + +/** + * Condition that checks if the current user is allowed to perform actions on the selected user. + * The logic is generally laid out so that admins can perform actions on any user, while users can only perform actions on other users. + */ +export class UmbUserCanPerformActionsCondition extends UmbUserActionConditionBase { + async _onUserDataChange() { + // Must have selected a user + if (this.userUnique === undefined) { + this.permitted = false; + return; + } + + // If the user is the current user, they can perform actions + if (await this.isCurrentUser()) { + this.permitted = true; + return; + } + + // If the current user is an admin, they can perform actions on any user + if (await this.isCurrentUserAdmin()) { + this.permitted = true; + return; + } + + // Otherwise, the current user can only perform actions on other users + if (await this.isUserAdmin()) { + this.permitted = false; + return; + } + + // The current user seems to be able to perform actions on the selected user + this.permitted = true; + } +} + +export const manifest: ManifestCondition = { + type: 'condition', + name: 'User Can Perform Actions Condition', + alias: 'Umb.Condition.User.CanPerformActions', + api: UmbUserCanPerformActionsCondition, +}; From a094719793293cf2fe6021ea4739015a3c2dfd2b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:48:02 +0200 Subject: [PATCH 09/39] add util function to check if user is an admin --- .../src/packages/user/user/index.ts | 3 ++- .../src/packages/user/user/utils/index.ts | 1 + .../src/packages/user/user/utils/is-user.function.ts | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/utils/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/index.ts index 516c4d7ade..0eb2746d9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/index.ts @@ -2,4 +2,5 @@ export * from './collection/index.js'; export * from './components/index.js'; export * from './invite/index.js'; export * from './repository/index.js'; -export * from './types.js'; +export type * from './types.js'; +export * from './utils/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/index.ts new file mode 100644 index 0000000000..5d1478f0fa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/index.ts @@ -0,0 +1 @@ +export * from './is-user.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts new file mode 100644 index 0000000000..2c1e139d8a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts @@ -0,0 +1,10 @@ +import { UmbUserDetailRepository } from '../repository/index.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export const isUserAdmin = async (host: UmbControllerHost, userUnique: string) => { + const repository = new UmbUserDetailRepository(host); + const { data: user } = await repository.requestByUnique(userUnique); + + //return user?.isAdmin; + return false; +}; From ad9bc3ac7d24c3fa1a145602bad143299ac1d4f8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:48:21 +0200 Subject: [PATCH 10/39] add extra condition on all user entity actions to check if they are allowed to perform actions in general --- .../user/user/entity-actions/manifests.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts index a8200fddfc..087a863c7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts @@ -19,6 +19,9 @@ const entityActions: Array = [ itemRepositoryAlias: UMB_USER_ITEM_REPOSITORY_ALIAS, }, conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, { alias: 'Umb.Condition.User.AllowDeleteAction', }, @@ -37,6 +40,9 @@ const entityActions: Array = [ label: 'Enable', }, conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, { alias: 'Umb.Condition.User.AllowEnableAction', }, @@ -55,6 +61,9 @@ const entityActions: Array = [ label: 'Disable', }, conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, { alias: 'Umb.Condition.User.AllowDisableAction', }, @@ -72,6 +81,11 @@ const entityActions: Array = [ icon: 'icon-key', label: 'Change Password', }, + conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, + ], }, { type: 'entityAction', @@ -86,6 +100,9 @@ const entityActions: Array = [ label: 'Unlock', }, conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, { alias: 'Umb.Condition.User.AllowUnlockAction', }, @@ -104,6 +121,9 @@ const entityActions: Array = [ label: 'Configure Two-Factor', }, conditions: [ + { + alias: 'Umb.Condition.User.CanPerformActions', + }, { alias: 'Umb.Condition.User.AllowMfaAction', }, From bdf40f2a5ff576862b4b2ee68f9a5c6e5dc39826 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:52:22 +0200 Subject: [PATCH 11/39] add docs --- .../user/current-user/utils/is-current-user.function.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts index 2f088841db..5fb077125e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts @@ -2,6 +2,9 @@ import { UMB_CURRENT_USER_CONTEXT } from '../current-user.context.js'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +/** + * Check if the current user is the user with the given unique id + */ export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); const currentUserContext = await ctrl.asPromise(); @@ -10,6 +13,9 @@ export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) return currentUserContext!.isUserCurrentUser(userUnique); }; +/** + * Check if the current user is an admin + */ export const isCurrentUserAnAdmin = async (host: UmbControllerHost) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); const currentUserContext = await ctrl.asPromise(); From 8cfb4dec1088677a3bb146bca94fa601806d7c7d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:52:37 +0200 Subject: [PATCH 12/39] add check for the `isAdmin` property --- .../src/packages/user/current-user/current-user.context.ts | 2 +- .../src/packages/user/user/utils/is-user.function.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts index 58494181b9..5de00944d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts @@ -61,7 +61,7 @@ export class UmbCurrentUserContext extends UmbContextBase */ async isCurrentUserAdmin(): Promise { const currentUser = await firstValueFrom(this.currentUser); - return true; // TODO: Implement this + return currentUser?.isAdmin ?? false; } #observeIsAuthorized() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts index 2c1e139d8a..42ee46012f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/utils/is-user.function.ts @@ -1,10 +1,12 @@ import { UmbUserDetailRepository } from '../repository/index.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +/** + * Check if the user is an admin + */ export const isUserAdmin = async (host: UmbControllerHost, userUnique: string) => { const repository = new UmbUserDetailRepository(host); const { data: user } = await repository.requestByUnique(userUnique); - //return user?.isAdmin; - return false; + return user?.isAdmin ?? false; }; From 3c8f954847a2b616b16e80b28eb066280b30345f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:00:10 +0200 Subject: [PATCH 13/39] check if item exists before getting the first one --- .../disable/disable-user.action.ts | 26 ++++++++++--------- .../enable/enable-user.action.ts | 24 +++++++++-------- .../unlock/unlock-user.action.ts | 24 +++++++++-------- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts index ff604d6691..79bc7b187b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts @@ -16,18 +16,20 @@ export class UmbDisableUserEntityAction extends UmbEntityActionBase { const itemRepository = new UmbUserItemRepository(this); const { data } = await itemRepository.requestItems([this.args.unique]); - if (data) { - const item = data[0]; - - await umbConfirmModal(this._host, { - headline: `Disable ${item.name}`, - content: 'Are you sure you want to disable this user?', - color: 'danger', - confirmLabel: 'Disable', - }); - - const disableUserRepository = new UmbDisableUserRepository(this); - await disableUserRepository.disable([this.args.unique]); + if (!data?.length) { + throw new Error('Item not found.'); } + + const item = data[0]; + + await umbConfirmModal(this._host, { + headline: `Disable ${item.name}`, + content: 'Are you sure you want to disable this user?', + color: 'danger', + confirmLabel: 'Disable', + }); + + const disableUserRepository = new UmbDisableUserRepository(this); + await disableUserRepository.disable([this.args.unique]); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts index a8b5b428eb..6e19458210 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts @@ -16,17 +16,19 @@ export class UmbEnableUserEntityAction extends UmbEntityActionBase { const itemRepository = new UmbUserItemRepository(this); const { data } = await itemRepository.requestItems([this.args.unique]); - if (data) { - const item = data[0]; - - await umbConfirmModal(this._host, { - headline: `Enable ${item.name}`, - content: 'Are you sure you want to enable this user?', - confirmLabel: 'Enable', - }); - - const enableRepository = new UmbEnableUserRepository(this); - await enableRepository.enable([this.args.unique]); + if (!data?.length) { + throw new Error('Item not found.'); } + + const item = data[0]; + + await umbConfirmModal(this._host, { + headline: `Enable ${item.name}`, + content: 'Are you sure you want to enable this user?', + confirmLabel: 'Enable', + }); + + const enableRepository = new UmbEnableUserRepository(this); + await enableRepository.enable([this.args.unique]); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts index 5fabd55505..00df1dbb47 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts @@ -16,17 +16,19 @@ export class UmbUnlockUserEntityAction extends UmbEntityActionBase { const itemRepository = new UmbUserItemRepository(this); const { data } = await itemRepository.requestItems([this.args.unique]); - if (data) { - const item = data[0]; - - await umbConfirmModal(this._host, { - headline: `Unlock ${item.name}`, - content: 'Are you sure you want to unlock this user?', - confirmLabel: 'Unlock', - }); - - const unlockUserRepository = new UmbUnlockUserRepository(this); - await unlockUserRepository?.unlock([this.args.unique]); + if (!data?.length) { + throw new Error('Item not found.'); } + + const item = data[0]; + + await umbConfirmModal(this._host, { + headline: `Unlock ${item.name}`, + content: 'Are you sure you want to unlock this user?', + confirmLabel: 'Unlock', + }); + + const unlockUserRepository = new UmbUnlockUserRepository(this); + await unlockUserRepository?.unlock([this.args.unique]); } } From a0167b1b27cf683a85a342f33d60b3962b8027ce Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:10:26 +0200 Subject: [PATCH 14/39] make the confirmModal controller send a boolean back depending on the user's answer this avoids the error in console "uncaught exception in promise" --- .../confirm/confirm-modal.controller.ts | 9 ++++-- .../disable/disable-user.action.ts | 13 ++++++--- .../enable/enable-user.action.ts | 4 ++- .../unlock/unlock-user.action.ts | 4 ++- .../modals/user-mfa/user-mfa-modal.element.ts | 28 ++++++++----------- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts index 61fb44d6a4..6bede39f5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts @@ -27,6 +27,11 @@ export class UmbConfirmModalController extends UmbControllerBase { } } -export function umbConfirmModal(host: UmbControllerHost, args: UmbConfirmModalArgs) { - return new UmbConfirmModalController(host).open(args); +export async function umbConfirmModal(host: UmbControllerHost, args: UmbConfirmModalArgs): Promise { + try { + await new UmbConfirmModalController(host).open(args); + return true; + } catch { + return false; + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts index 79bc7b187b..a087eb675f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts @@ -4,6 +4,7 @@ import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action' import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; export class UmbDisableUserEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -22,13 +23,17 @@ export class UmbDisableUserEntityAction extends UmbEntityActionBase { const item = data[0]; - await umbConfirmModal(this._host, { - headline: `Disable ${item.name}`, - content: 'Are you sure you want to disable this user?', + const localize = new UmbLocalizationController(this._host); + + const confirm = await umbConfirmModal(this._host, { + headline: `${localize.term('user_disabled')} ${item.name}`, + content: localize.term('defaultdialogs_confirmdisable'), color: 'danger', - confirmLabel: 'Disable', + confirmLabel: localize.term('actions_disable'), }); + if (!confirm) return; + const disableUserRepository = new UmbDisableUserRepository(this); await disableUserRepository.disable([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts index 6e19458210..c0c2b2884b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts @@ -22,12 +22,14 @@ export class UmbEnableUserEntityAction extends UmbEntityActionBase { const item = data[0]; - await umbConfirmModal(this._host, { + const confirm = await umbConfirmModal(this._host, { headline: `Enable ${item.name}`, content: 'Are you sure you want to enable this user?', confirmLabel: 'Enable', }); + if (!confirm) return; + const enableRepository = new UmbEnableUserRepository(this); await enableRepository.enable([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts index 00df1dbb47..d788952211 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts @@ -22,12 +22,14 @@ export class UmbUnlockUserEntityAction extends UmbEntityActionBase { const item = data[0]; - await umbConfirmModal(this._host, { + const confirm = await umbConfirmModal(this._host, { headline: `Unlock ${item.name}`, content: 'Are you sure you want to unlock this user?', confirmLabel: 'Unlock', }); + if (!confirm) return; + const unlockUserRepository = new UmbUnlockUserRepository(this); await unlockUserRepository?.unlock([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts index e76a365753..24aea7b669 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -3,7 +3,7 @@ import type { UmbUserMfaProviderModel } from '../../types.js'; import type { UmbUserMfaModalConfiguration } from './user-mfa-modal.token.js'; import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; @@ -125,23 +125,17 @@ export class UmbUserMfaModalElement extends UmbLitElement { * NB! The user must have administrative rights before doing so. */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const confirm = await modalManager - .open(this, UMB_CONFIRM_MODAL, { - data: { - headline: this.localize.term('actions_disable'), - content: this.localize.term('user_2faDisableForUser'), - confirmLabel: this.localize.term('actions_disable'), - color: 'danger', - }, - }) - .onSubmit() - .catch(() => false); + const confirm = await umbConfirmModal(this, { + headline: this.localize.term('actions_disable'), + content: this.localize.term('user_2faDisableForUser'), + confirmLabel: this.localize.term('actions_disable'), + color: 'danger', + }); - if (confirm !== false) { - await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); - this.#loadProviders(); - } + if (!confirm) return; + + await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); + this.#loadProviders(); } static styles = [ From 73b6b146531d04107b7972b83aba78b1a822d00c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:11:50 +0200 Subject: [PATCH 15/39] Revert "make the confirmModal controller send a boolean back depending on the user's answer" This reverts commit a0167b1b27cf683a85a342f33d60b3962b8027ce. --- .../confirm/confirm-modal.controller.ts | 9 ++---- .../disable/disable-user.action.ts | 13 +++------ .../enable/enable-user.action.ts | 4 +-- .../unlock/unlock-user.action.ts | 4 +-- .../modals/user-mfa/user-mfa-modal.element.ts | 28 +++++++++++-------- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts index 6bede39f5f..61fb44d6a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts @@ -27,11 +27,6 @@ export class UmbConfirmModalController extends UmbControllerBase { } } -export async function umbConfirmModal(host: UmbControllerHost, args: UmbConfirmModalArgs): Promise { - try { - await new UmbConfirmModalController(host).open(args); - return true; - } catch { - return false; - } +export function umbConfirmModal(host: UmbControllerHost, args: UmbConfirmModalArgs) { + return new UmbConfirmModalController(host).open(args); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts index a087eb675f..79bc7b187b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/disable/disable-user.action.ts @@ -4,7 +4,6 @@ import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action' import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; -import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; export class UmbDisableUserEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -23,17 +22,13 @@ export class UmbDisableUserEntityAction extends UmbEntityActionBase { const item = data[0]; - const localize = new UmbLocalizationController(this._host); - - const confirm = await umbConfirmModal(this._host, { - headline: `${localize.term('user_disabled')} ${item.name}`, - content: localize.term('defaultdialogs_confirmdisable'), + await umbConfirmModal(this._host, { + headline: `Disable ${item.name}`, + content: 'Are you sure you want to disable this user?', color: 'danger', - confirmLabel: localize.term('actions_disable'), + confirmLabel: 'Disable', }); - if (!confirm) return; - const disableUserRepository = new UmbDisableUserRepository(this); await disableUserRepository.disable([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts index c0c2b2884b..6e19458210 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/enable/enable-user.action.ts @@ -22,14 +22,12 @@ export class UmbEnableUserEntityAction extends UmbEntityActionBase { const item = data[0]; - const confirm = await umbConfirmModal(this._host, { + await umbConfirmModal(this._host, { headline: `Enable ${item.name}`, content: 'Are you sure you want to enable this user?', confirmLabel: 'Enable', }); - if (!confirm) return; - const enableRepository = new UmbEnableUserRepository(this); await enableRepository.enable([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts index d788952211..00df1dbb47 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/unlock/unlock-user.action.ts @@ -22,14 +22,12 @@ export class UmbUnlockUserEntityAction extends UmbEntityActionBase { const item = data[0]; - const confirm = await umbConfirmModal(this._host, { + await umbConfirmModal(this._host, { headline: `Unlock ${item.name}`, content: 'Are you sure you want to unlock this user?', confirmLabel: 'Unlock', }); - if (!confirm) return; - const unlockUserRepository = new UmbUnlockUserRepository(this); await unlockUserRepository?.unlock([this.args.unique]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts index 24aea7b669..e76a365753 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -3,7 +3,7 @@ import type { UmbUserMfaProviderModel } from '../../types.js'; import type { UmbUserMfaModalConfiguration } from './user-mfa-modal.token.js'; import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { umbConfirmModal, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; +import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; @@ -125,17 +125,23 @@ export class UmbUserMfaModalElement extends UmbLitElement { * NB! The user must have administrative rights before doing so. */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { - const confirm = await umbConfirmModal(this, { - headline: this.localize.term('actions_disable'), - content: this.localize.term('user_2faDisableForUser'), - confirmLabel: this.localize.term('actions_disable'), - color: 'danger', - }); + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const confirm = await modalManager + .open(this, UMB_CONFIRM_MODAL, { + data: { + headline: this.localize.term('actions_disable'), + content: this.localize.term('user_2faDisableForUser'), + confirmLabel: this.localize.term('actions_disable'), + color: 'danger', + }, + }) + .onSubmit() + .catch(() => false); - if (!confirm) return; - - await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); - this.#loadProviders(); + if (confirm !== false) { + await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); + this.#loadProviders(); + } } static styles = [ From 239aae12509ea0072dedb2f107e16c5bd1279776 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:12:40 +0200 Subject: [PATCH 16/39] use the `umbConfirmModal` to ask the user to disable 2fa --- .../modals/user-mfa/user-mfa-modal.element.ts | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts index e76a365753..7d002c19ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -3,7 +3,7 @@ import type { UmbUserMfaProviderModel } from '../../types.js'; import type { UmbUserMfaModalConfiguration } from './user-mfa-modal.token.js'; import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; @@ -125,23 +125,15 @@ export class UmbUserMfaModalElement extends UmbLitElement { * NB! The user must have administrative rights before doing so. */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const confirm = await modalManager - .open(this, UMB_CONFIRM_MODAL, { - data: { - headline: this.localize.term('actions_disable'), - content: this.localize.term('user_2faDisableForUser'), - confirmLabel: this.localize.term('actions_disable'), - color: 'danger', - }, - }) - .onSubmit() - .catch(() => false); + await umbConfirmModal(this, { + headline: this.localize.term('actions_disable'), + content: this.localize.term('user_2faDisableForUser'), + confirmLabel: this.localize.term('actions_disable'), + color: 'danger', + }); - if (confirm !== false) { - await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); - this.#loadProviders(); - } + await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); + this.#loadProviders(); } static styles = [ From 474b4fb3c31500f76e618fe373753530f9dea3c1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:31:33 +0200 Subject: [PATCH 17/39] add mocked data for `isAdmin` --- .../src/mocks/data/user/user.data.ts | 15 ++++++++++++--- .../src/mocks/data/user/user.db.ts | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.data.ts index c57db6caeb..b2e34eae17 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.data.ts @@ -25,6 +25,7 @@ export const data: Array = [ userGroupIds: ['user-group-administrators-id', 'user-group-editors-id'], userName: '', avatarUrls: [], + isAdmin: true, }, { id: '82e11d3d-b91d-43c9-9071-34d28e62e81d', @@ -43,6 +44,7 @@ export const data: Array = [ userGroupIds: ['user-group-administrators-id'], userName: '', avatarUrls: [], + isAdmin: true, }, { id: 'aa1d83a9-bc7f-47d2-b288-58d8a31f5017', @@ -58,9 +60,10 @@ export const data: Array = [ updateDate: '2023-10-12T18:30:32.879Z', createDate: '2023-10-12T18:30:32.879Z', failedLoginAttempts: 0, - userGroupIds: ['user-group-administrators-id'], + userGroupIds: ['user-group-editors-id'], userName: '', avatarUrls: [], + isAdmin: false, }, { id: 'ff2f4a50-d3d4-4bc4-869d-c7948c160e54', @@ -76,9 +79,10 @@ export const data: Array = [ updateDate: '2023-10-12T18:30:32.879Z', createDate: '2023-10-12T18:30:32.879Z', failedLoginAttempts: 0, - userGroupIds: ['user-group-administrators-id'], + userGroupIds: ['user-group-editors-id'], userName: '', avatarUrls: [], + isAdmin: false, }, { id: 'c290c6d9-9f12-4838-8567-621b52a178de', @@ -94,12 +98,17 @@ export const data: Array = [ updateDate: '2023-10-12T18:30:32.879Z', createDate: '2023-10-12T18:30:32.879Z', failedLoginAttempts: 25, - userGroupIds: ['user-group-administrators-id'], + userGroupIds: ['user-group-editors-id', 'user-group-sensitive-data-id'], userName: '', avatarUrls: [], + isAdmin: false, }, ]; +/** + * Mock data for MFA login providers + * This is usually linked to a user, but for the sake of the mock, we're just going to have a list of providers + */ export const mfaLoginProviders: Array = [ { isEnabledOnUser: true, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts index 016cca4749..be76d85f50 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts @@ -203,6 +203,7 @@ const createMockMapper = (item: CreateUserRequestModel): UmbMockUserModel => { lastLoginDate: null, lastLockoutDate: null, lastPasswordChangeDate: null, + isAdmin: item.userGroupIds.includes(umbUserGroupMockDb.getAll()[0].id), }; }; @@ -224,6 +225,7 @@ const detailResponseMapper = (item: UmbMockUserModel): UserResponseModel => { lastLoginDate: item.lastLoginDate, lastLockoutDate: item.lastLockoutDate, lastPasswordChangeDate: item.lastPasswordChangeDate, + isAdmin: item.isAdmin, }; }; From 13db74fb901da9e78e60d0b61b902f17eab2fd06 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:36:39 +0200 Subject: [PATCH 18/39] add the displayName of the provider to the localizations when disabling it --- src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts | 6 +++--- src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts | 6 +++--- .../user/modals/user-mfa/user-mfa-modal.element.ts | 6 +++--- .../packages/user/user/repository/user.repository.ts | 11 ++++++++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index d802dcc5cd..37219bb6a3 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -1923,9 +1923,9 @@ export default { noUserGroupsAdded: 'Ingen brugere er blevet tilføjet', '2faDisableText': 'Hvis du ønsker at slå denne totrinsbekræftelse fra, så skal du nu indtaste koden fra din enhed:', '2faProviderIsEnabled': 'Denne totrinsbekræftelse er slået til', - '2faProviderIsDisabledMsg': 'Den valgte totrinsbekræftelse er nu slået fra', - '2faProviderIsNotDisabledMsg': 'Der skete en ukendt fejl da denne totrinsbekræftelse skulles slåes fra', - '2faDisableForUser': 'Er du sikker på, at du vil fjerne denne totrinsbekræftelse for denne bruger?', + '2faProviderIsDisabledMsg': '{0} er nu slået fra', + '2faProviderIsNotDisabledMsg': 'Der skete en ukendt fejl da {0} skulles slåes fra', + '2faDisableForUser': 'Er du sikker på, at du vil fjerne "{0}" for denne bruger?', emailRequired: 'Required - enter an email address for this user', duplicateLogin: 'A user with this login already exists', nameRequired: 'Required - enter a name for this user', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 3d2682e341..3e46ed564c 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1934,9 +1934,9 @@ export default { '2faProviderIsEnabled': 'This two-factor provider is enabled', '2faProviderIsEnabledMsg': 'This two-factor provider is now enabled', '2faProviderIsNotEnabledMsg': 'Something went wrong with trying to enable this two-factor provider', - '2faProviderIsDisabledMsg': 'This two-factor provider is now disabled', - '2faProviderIsNotDisabledMsg': 'Something went wrong with trying to disable this two-factor provider', - '2faDisableForUser': 'Do you want to disable this two-factor provider for this user?', + '2faProviderIsDisabledMsg': '{0} is now disabled', + '2faProviderIsNotDisabledMsg': 'Something went wrong with trying to disable {0}', + '2faDisableForUser': 'Do you want to disable "{0}" on this user?', '2faQrCodeAlt': 'QR code for two-factor authentication with {0}', '2faQrCodeTitle': 'QR code for two-factor authentication with {0}', '2faQrCodeDescription': 'Scan this QR code with your authenticator app to enable two-factor authentication', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts index 7d002c19ad..dc61ad9ac5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -126,13 +126,13 @@ export class UmbUserMfaModalElement extends UmbLitElement { */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { await umbConfirmModal(this, { - headline: this.localize.term('actions_disable'), - content: this.localize.term('user_2faDisableForUser'), + headline: `${this.localize.term('actions_disable')} "${item.displayName}"`, + content: this.localize.term('user_2faDisableForUser', item.displayName), confirmLabel: this.localize.term('actions_disable'), color: 'danger', }); - await this.#userRepository.disableMfaProvider(this.#unique, item.providerName); + await this.#userRepository.disableMfaProvider(this.#unique, item.providerName, item.displayName); this.#loadProviders(); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts index b6f5704580..428b6f3fef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts @@ -35,18 +35,23 @@ export class UmbUserRepository extends UmbUserRepositoryBase { * Disables a MFA provider for a user * @param unique The unique id of the user * @param providerName The name of the provider + * @param displayName The display name of the provider to show in the notification (optional) * @memberof UmbUserRepository */ - async disableMfaProvider(unique: string, providerName: string) { + async disableMfaProvider(unique: string, providerName: string, displayName?: string) { const { data, error } = await this.#userMfaSource.disableMfaProvider(unique, providerName); const localize = new UmbLocalizationController(this._host); if (!error) { - const notification = { data: { message: localize.term('user_2faProviderIsDisabledMsg') } }; + const notification = { + data: { message: localize.term('user_2faProviderIsDisabledMsg', displayName ?? providerName) }, + }; this.notificationContext?.peek('positive', notification); } else { - const notification = { data: { message: localize.term('user_2faProviderIsNotDisabledMsg') } }; + const notification = { + data: { message: localize.term('user_2faProviderIsNotDisabledMsg', displayName ?? providerName) }, + }; this.notificationContext?.peek('warning', notification); } From 8c5df5b08ed6a14db29a4db7994a3e6b0a9809fc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:38:14 +0200 Subject: [PATCH 19/39] add the displayName of the provider to the localizations when disabling it --- .../current-user-mfa-disable-provider-modal.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts index c80bd47e77..93ccc585b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts @@ -102,7 +102,7 @@ export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseEl const success = await this.#currentUserRepository.disableMfaProvider(this.data.providerName, code); if (success) { - this.#peek(this.localize.term('user_2faProviderIsDisabledMsg')); + this.#peek(this.localize.term('user_2faProviderIsDisabledMsg', this.data.displayName)); this.modalContext?.submit(); this._buttonState = 'success'; } else { @@ -119,7 +119,7 @@ export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseEl async #peek(message: string, color?: UmbNotificationColor) { this.#notificationContext?.peek(color ?? 'positive', { data: { - headline: this.localize.term('member_2fa'), + headline: `${this.localize.term('member_2fa')} "${this.data?.displayName ?? this.data?.providerName}"`, message, }, }); From 2583152d2fbc87c1d629e86569860309079a9a47 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:44:48 +0200 Subject: [PATCH 20/39] add missing Danish texts --- src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index 37219bb6a3..0d96709dcf 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -1923,9 +1923,17 @@ export default { noUserGroupsAdded: 'Ingen brugere er blevet tilføjet', '2faDisableText': 'Hvis du ønsker at slå denne totrinsbekræftelse fra, så skal du nu indtaste koden fra din enhed:', '2faProviderIsEnabled': 'Denne totrinsbekræftelse er slået til', + '2faProviderIsEnabledMsg': '{0} er nu slået til', + '2faProviderIsNotEnabledMsg': 'Der skete en fejl da {0} skulles slåes til', '2faProviderIsDisabledMsg': '{0} er nu slået fra', - '2faProviderIsNotDisabledMsg': 'Der skete en ukendt fejl da {0} skulles slåes fra', + '2faProviderIsNotDisabledMsg': 'Der skete en fejl da {0} skulles slåes fra', '2faDisableForUser': 'Er du sikker på, at du vil fjerne "{0}" for denne bruger?', + '2faQrCodeAlt': 'QR kode for totrinsbekræftelse med {0}', + '2faQrCodeTitle': 'QR kode for totrinsbekræftelse med {0}', + '2faQrCodeDescription': 'Scan QR koden med din autentificeringsapp', + '2faCodeInput': 'Indtast din verifikationskode', + '2faCodeInputHelp': 'Indtast din verifikationskode fra din autentificeringsapp', + '2faInvalidCode': 'Den indtastede kode er ugyldig', emailRequired: 'Required - enter an email address for this user', duplicateLogin: 'A user with this login already exists', nameRequired: 'Required - enter a name for this user', From c354b7a2cafbf52b0875811e0fbf779c1a54ec7d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:44:58 +0200 Subject: [PATCH 21/39] add provider displayName to the enabled notifications --- src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts | 4 ++-- .../components/mfa-provider-default.element.ts | 8 ++++++-- .../current-user-mfa-disable-provider-modal.element.ts | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 3e46ed564c..40987bee26 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1932,8 +1932,8 @@ export default { '2faDisableText': 'If you wish to disable this two-factor provider, then you must enter the code shown on your authentication device:', '2faProviderIsEnabled': 'This two-factor provider is enabled', - '2faProviderIsEnabledMsg': 'This two-factor provider is now enabled', - '2faProviderIsNotEnabledMsg': 'Something went wrong with trying to enable this two-factor provider', + '2faProviderIsEnabledMsg': '{0} is now enabled', + '2faProviderIsNotEnabledMsg': 'Something went wrong with trying to enable {0}', '2faProviderIsDisabledMsg': '{0} is now disabled', '2faProviderIsNotDisabledMsg': 'Something went wrong with trying to disable {0}', '2faDisableForUser': 'Do you want to disable "{0}" on this user?', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts index 3bbe517784..b2c6fabc7a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts @@ -150,7 +150,7 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf protected peek(message: string, color?: UmbNotificationColor) { this.notificationContext?.peek(color ?? 'positive', { data: { - headline: this.localize.term('member_2fa'), + headline: `${this.localize.term('member_2fa')} ${this.displayName ?? this.providerName}`, message, }, }); @@ -177,10 +177,14 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf const successful = await this.callback(this.providerName, code, this._secret); if (successful) { - this.peek(this.localize.term('user_2faProviderIsEnabled')); + this.peek(this.localize.term('user_2faProviderIsEnabledMsg', this.displayName ?? this.providerName)); this._buttonState = 'success'; this.close(); } else { + this.peek( + this.localize.term('user_2faProviderIsNotEnabledMsg', this.displayName ?? this.providerName), + 'warning', + ); this.codeField?.setCustomValidity(this.localize.term('user_2faInvalidCode')); this.codeField?.focus(); this._buttonState = 'failed'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts index 93ccc585b2..3e6ad6e0a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts @@ -102,10 +102,14 @@ export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseEl const success = await this.#currentUserRepository.disableMfaProvider(this.data.providerName, code); if (success) { - this.#peek(this.localize.term('user_2faProviderIsDisabledMsg', this.data.displayName)); + this.#peek(this.localize.term('user_2faProviderIsDisabledMsg', this.data.displayName ?? this.data.providerName)); this.modalContext?.submit(); this._buttonState = 'success'; } else { + this.#peek( + this.localize.term('user_2faProviderIsNotDisabledMsg', this.data.displayName ?? this.data.providerName), + 'warning', + ); this._codeInput.setCustomValidity(this.localize.term('user_2faInvalidCode')); this._codeInput.focus(); this._buttonState = 'failed'; From bd9f493968928eeaa4f27cf3401b14e84b6f3471 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:27:14 +0200 Subject: [PATCH 22/39] map up isAdmin to save a lookup on the user condition --- .../collection/repository/user-collection.server.data-source.ts | 1 + .../user/repository/detail/user-detail.server.data-source.ts | 2 ++ src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts | 1 + 3 files changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts index aeeda6459f..a6aeed6726 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts @@ -56,6 +56,7 @@ export class UmbUserCollectionServerDataSource implements UmbCollectionDataSourc lastLoginDate: item.lastLoginDate || null, lastLockoutDate: item.lastLockoutDate || null, lastPasswordChangeDate: item.lastPasswordChangeDate || null, + isAdmin: item.isAdmin, }; return userDetail; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts index c1d2a6f98f..419c410264 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts @@ -50,6 +50,7 @@ export class UmbUserServerDataSource implements UmbDetailDataSource Date: Wed, 3 Apr 2024 14:27:23 +0200 Subject: [PATCH 23/39] map up isAdmin to save a lookup on the user condition --- .../current-user.server.data-source.ts | 1 + .../repository/current-user.store.ts | 1 + .../src/packages/user/current-user/types.ts | 1 + .../user-allow-action-base.condition.ts | 40 ++++++++----------- .../user-can-perform-actions.condition.ts | 2 +- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 69a1bb8b58..79be8d757d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -43,6 +43,7 @@ export class UmbCurrentUserServerDataSource { fallbackPermissions: data.fallbackPermissions, permissions: data.permissions, allowedSections: data.allowedSections, + isAdmin: data.isAdmin, }; return { data: user }; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.store.ts index 9d30056316..1e6a9ffa1f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.store.ts @@ -72,6 +72,7 @@ export class UmbCurrentUserStore extends UmbContextBase { documentStartNodeUniques: updatedCurrentUser.documentStartNodeUniques, mediaStartNodeUniques: updatedCurrentUser.mediaStartNodeUniques, avatarUrls: updatedCurrentUser.avatarUrls, + isAdmin: updatedCurrentUser.isAdmin, }; this.update(mappedCurrentUser); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts index f6c67bf129..e5e6632324 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts @@ -18,6 +18,7 @@ export interface UmbCurrentUserModel { allowedSections: Array; fallbackPermissions: Array; permissions: Array; + isAdmin: boolean; } export type UmbCurrentUserMfaProviderModel = UserTwoFactorProviderModel; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts index f8f2bcc7c5..6bcdd483f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts @@ -1,4 +1,3 @@ -import { isUserAdmin } from '../utils/index.js'; import type { UmbUserStateEnum } from '../types.js'; import { UMB_USER_WORKSPACE_CONTEXT } from '../workspace/user-workspace.context-token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -14,7 +13,19 @@ export abstract class UmbUserActionConditionBase extends UmbConditionBase implements UmbExtensionCondition { + /** + * The unique identifier of the user being edited + */ protected userUnique?: string; + + /** + * Whether the user being edited is an admin + */ + protected userAdmin?: boolean; + + /** + * The state of the user being edited + */ protected userState?: UmbUserStateEnum | null; constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { @@ -22,24 +33,15 @@ export abstract class UmbUserActionConditionBase this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (context) => { this.observe( - context.unique, - (unique) => { - this.userUnique = unique; + context.data, + (user) => { + this.userUnique = user?.unique; + this.userAdmin = user?.isAdmin; + this.userState = user?.state; this._onUserDataChange(); }, 'umbUserUnique', ); - this.observe( - context.state, - (state) => { - this.userState = state; - // TODO: Investigate if we can remove this observation and just use the unique change to trigger the state change. [NL] - // Can user state change over time? if not then this observation is not needed and then we just need to retrieve the state when the unique has changed. [NL] - // These two could also be combined via the observeMultiple method, that could prevent triggering onUserDataChanged twice. [NL] - this._onUserDataChange(); - }, - 'umbUserState', - ); }); } @@ -61,14 +63,6 @@ export abstract class UmbUserActionConditionBase return isCurrentUserAnAdmin(this._host); } - /** - * Check if the selected user is an admin - * @protected - */ - protected async isUserAdmin() { - return this.userUnique ? isUserAdmin(this._host, this.userUnique) : false; - } - /** * Called when the user data changes * @protected diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts index 9a885aa7aa..988783c49b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts @@ -26,7 +26,7 @@ export class UmbUserCanPerformActionsCondition extends UmbUserActionConditionBas } // Otherwise, the current user can only perform actions on other users - if (await this.isUserAdmin()) { + if (this.userAdmin) { this.permitted = false; return; } From a1a43d46eabf402248d8163d4788000adbb39324 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:30:18 +0200 Subject: [PATCH 24/39] make sure the current user flows react on their own errors to be able to detect if the code was invalid --- .../src/packages/core/resources/index.ts | 1 + .../mfa-provider-default.element.ts | 32 ++++++++++++------- ...user-mfa-disable-provider-modal.element.ts | 26 +++++++++------ .../repository/current-user.repository.ts | 29 ++++++----------- .../current-user.server.data-source.ts | 32 +++++++++++++++++-- .../src/packages/user/current-user/types.ts | 8 +++-- 6 files changed, 83 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index 6b2ae359d4..7b0f05fef4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -2,3 +2,4 @@ export * from './resource.controller.js'; export * from './tryExecute.function.js'; export * from './tryExecuteAndNotify.function.js'; export * from './extractUmbColorVariable.function.js'; +export * from './apiTypeValidators.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts index b2c6fabc7a..9c735beda3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/components/mfa-provider-default.element.ts @@ -1,8 +1,8 @@ -import type { UmbMfaProviderConfigurationElementProps } from '../types.js'; +import type { UmbMfaProviderConfigurationCallback, UmbMfaProviderConfigurationElementProps } from '../types.js'; import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; import { css, customElement, html, property, state, query } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { isApiError, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import { UMB_NOTIFICATION_CONTEXT, type UmbNotificationColor } from '@umbraco-cms/backoffice/notification'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; @@ -20,7 +20,8 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf displayName = ''; @property({ attribute: false }) - callback: (providerName: string, code: string, secret: string) => Promise = async () => false; + callback: (providerName: string, code: string, secret: string) => UmbMfaProviderConfigurationCallback = + async () => ({}); @property({ attribute: false }) close = () => {}; @@ -150,7 +151,7 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf protected peek(message: string, color?: UmbNotificationColor) { this.notificationContext?.peek(color ?? 'positive', { data: { - headline: `${this.localize.term('member_2fa')} ${this.displayName ?? this.providerName}`, + headline: this.localize.term('member_2fa'), message, }, }); @@ -174,20 +175,27 @@ export class UmbMfaProviderDefaultElement extends UmbLitElement implements UmbMf if (!code) return; this._buttonState = 'waiting'; - const successful = await this.callback(this.providerName, code, this._secret); + const { error } = await this.callback(this.providerName, code, this._secret); - if (successful) { + if (!error) { this.peek(this.localize.term('user_2faProviderIsEnabledMsg', this.displayName ?? this.providerName)); this._buttonState = 'success'; this.close(); } else { - this.peek( - this.localize.term('user_2faProviderIsNotEnabledMsg', this.displayName ?? this.providerName), - 'warning', - ); - this.codeField?.setCustomValidity(this.localize.term('user_2faInvalidCode')); - this.codeField?.focus(); this._buttonState = 'failed'; + if (isApiError(error)) { + if (error.body.operationStatus === 'InvalidCode') { + this.codeField?.setCustomValidity(this.localize.term('user_2faInvalidCode')); + this.codeField?.focus(); + } else { + this.peek( + this.localize.term('user_2faProviderIsNotEnabledMsg', this.displayName ?? this.providerName), + 'warning', + ); + } + } else { + this.peek(error.message, 'warning'); + } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts index 3e6ad6e0a9..709a336d2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa-disable-provider/current-user-mfa-disable-provider-modal.element.ts @@ -5,6 +5,7 @@ import { css, customElement, html, query, state } from '@umbraco-cms/backoffice/ import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { isApiError } from '@umbraco-cms/backoffice/resources'; @customElement('umb-current-user-mfa-disable-provider-modal') export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseElement< @@ -99,20 +100,27 @@ export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseEl if (!code) return; this._buttonState = 'waiting'; - const success = await this.#currentUserRepository.disableMfaProvider(this.data.providerName, code); + const { error } = await this.#currentUserRepository.disableMfaProvider(this.data.providerName, code); - if (success) { + if (!error) { this.#peek(this.localize.term('user_2faProviderIsDisabledMsg', this.data.displayName ?? this.data.providerName)); this.modalContext?.submit(); this._buttonState = 'success'; } else { - this.#peek( - this.localize.term('user_2faProviderIsNotDisabledMsg', this.data.displayName ?? this.data.providerName), - 'warning', - ); - this._codeInput.setCustomValidity(this.localize.term('user_2faInvalidCode')); - this._codeInput.focus(); this._buttonState = 'failed'; + if (isApiError(error)) { + if (error.body.operationStatus === 'InvalidCode') { + this._codeInput.setCustomValidity(this.localize.term('user_2faInvalidCode')); + this._codeInput.focus(); + } else { + this.#peek( + this.localize.term('user_2faProviderIsNotDisabledMsg', this.data.displayName ?? this.data.providerName), + 'warning', + ); + } + } else { + this.#peek(error.message, 'warning'); + } } } @@ -123,7 +131,7 @@ export class UmbCurrentUserMfaDisableProviderModalElement extends UmbModalBaseEl async #peek(message: string, color?: UmbNotificationColor) { this.#notificationContext?.peek(color ?? 'positive', { data: { - headline: `${this.localize.term('member_2fa')} "${this.data?.displayName ?? this.data?.providerName}"`, + headline: this.localize.term('member_2fa'), message, }, }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts index ae069e9f2b..40026ffe1a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts @@ -1,10 +1,7 @@ -import type { UmbCurrentUserMfaProviderModel } from '../types.js'; import { UmbCurrentUserServerDataSource } from './current-user.server.data-source.js'; import { UMB_CURRENT_USER_STORE_CONTEXT } from './current-user.store.js'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; -import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; /** * A repository for the current user @@ -13,15 +10,13 @@ import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; * @extends {UmbRepositoryBase} */ export class UmbCurrentUserRepository extends UmbRepositoryBase { - #currentUserSource: UmbCurrentUserServerDataSource; + #currentUserSource = new UmbCurrentUserServerDataSource(this._host); #currentUserStore?: typeof UMB_CURRENT_USER_STORE_CONTEXT.TYPE; #init: Promise; constructor(host: UmbControllerHost) { super(host); - this.#currentUserSource = new UmbCurrentUserServerDataSource(host); - this.#init = Promise.all([ this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT, (instance) => { this.#currentUserStore = instance; @@ -67,19 +62,16 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { * @param code The activation code of the provider to enable * @memberof UmbCurrentUserRepository */ - async enableMfaProvider(providerName: string, code: string, secret: string): Promise { - const { error } = await tryExecuteAndNotify( - this._host, - UserResource.postUserCurrent2FaByProviderName({ providerName, requestBody: { code, secret } }), - ); + async enableMfaProvider(providerName: string, code: string, secret: string) { + const { error } = await this.#currentUserSource.enableMfaProvider(providerName, code, secret); if (error) { - return false; + return { error }; } this.#currentUserStore?.updateMfaProvider({ providerName, isEnabledOnUser: true }); - return true; + return {}; } /** @@ -88,19 +80,16 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { * @param code The activation code of the provider to disable * @memberof UmbCurrentUserRepository */ - async disableMfaProvider(providerName: string, code: string): Promise { - const { error } = await tryExecuteAndNotify( - this._host, - UserResource.deleteUserCurrent2FaByProviderName({ providerName, code }), - ); + async disableMfaProvider(providerName: string, code: string) { + const { error } = await this.#currentUserSource.disableMfaProvider(providerName, code); if (error) { - return false; + return { error }; } this.#currentUserStore?.updateMfaProvider({ providerName, isEnabledOnUser: false }); - return true; + return {}; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 79be8d757d..1d0fcb88aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbCurrentUserModel } from '../types.js'; -import { SecurityResource, UserResource } from '@umbraco-cms/backoffice/external/backend-api'; +import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; /** * A data source for the current user that fetches data from the server @@ -64,4 +64,32 @@ export class UmbCurrentUserServerDataSource { return { error }; } + + /** + * Enable an MFA provider + */ + async enableMfaProvider(providerName: string, code: string, secret: string) { + const { error } = await tryExecute( + UserResource.postUserCurrent2FaByProviderName({ providerName, requestBody: { code, secret } }), + ); + + if (error) { + return { error }; + } + + return {}; + } + + /** + * Disable an MFA provider + */ + async disableMfaProvider(providerName: string, code: string) { + const { error } = await tryExecute(UserResource.deleteUserCurrent2FaByProviderName({ providerName, code })); + + if (error) { + return { error }; + } + + return {}; + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts index e5e6632324..ba8898ade5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts @@ -1,4 +1,6 @@ import type { + ApiError, + CancelError, DocumentPermissionPresentationModel, UnknownTypePermissionPresentationModel, UserTwoFactorProviderModel, @@ -23,6 +25,8 @@ export interface UmbCurrentUserModel { export type UmbCurrentUserMfaProviderModel = UserTwoFactorProviderModel; +export type UmbMfaProviderConfigurationCallback = Promise<{ error?: ApiError | CancelError }>; + export interface UmbMfaProviderConfigurationElementProps { /** * The name of the provider reflecting the provider name in the backend. @@ -39,9 +43,9 @@ export interface UmbMfaProviderConfigurationElementProps { * @param providerName The name of the provider to enable. * @param code The authentication code from the authentication method. * @param secret The secret from the authentication backend. - * @returns True if the provider action was executed successfully. + * @returns A promise that resolves when the action is completed with an error if the action failed. */ - callback: (providerName: string, code: string, secret: string) => Promise; + callback: (providerName: string, code: string, secret: string) => UmbMfaProviderConfigurationCallback; /** * Call this function to close the modal. From 86f68fd325dcccd0802f7310bdab598e1ad98e4d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:34:01 +0200 Subject: [PATCH 25/39] handle errors self for disable user mfa provider --- .../user/user/modals/user-mfa/user-mfa-modal.element.ts | 2 +- .../user/repository/sources/user-mfa.server.data-source.ts | 5 ++--- .../src/packages/user/user/repository/user.repository.ts | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts index dc61ad9ac5..faa4bdfded 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/user-mfa/user-mfa-modal.element.ts @@ -126,7 +126,7 @@ export class UmbUserMfaModalElement extends UmbLitElement { */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { await umbConfirmModal(this, { - headline: `${this.localize.term('actions_disable')} "${item.displayName}"`, + headline: this.localize.term('actions_disable'), content: this.localize.term('user_2faDisableForUser', item.displayName), confirmLabel: this.localize.term('actions_disable'), color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts index 45e37c0d3a..2c7b3c42f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts @@ -1,6 +1,6 @@ import { UserResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; /** * A data source for User MFA items that fetches data from the server @@ -45,8 +45,7 @@ export class UmbUserMfaServerDataSource { if (!unique) throw new Error('User id is missing'); if (!providerName) throw new Error('Provider is missing'); - return tryExecuteAndNotify( - this.#host, + return tryExecute( UserResource.deleteUserById2FaByProviderName({ id: unique, providerName, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts index 428b6f3fef..841bca97e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/user.repository.ts @@ -49,6 +49,7 @@ export class UmbUserRepository extends UmbUserRepositoryBase { }; this.notificationContext?.peek('positive', notification); } else { + console.error('Failed to disable MFA provider', error); const notification = { data: { message: localize.term('user_2faProviderIsNotDisabledMsg', displayName ?? providerName) }, }; From 623019fe6a42ba458ea8172c0905cdc68d74f986 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:15:25 +0200 Subject: [PATCH 26/39] improve mfa action condition so it only spins up the observer in the constructor once (it doesn't need to know about the selected user) --- .../conditions/user-allow-mfa-action.condition.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts index f5a47432b8..4f931a894b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts @@ -1,10 +1,14 @@ -import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbConditionBase, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +export class UmbUserAllowMfaActionCondition extends UmbConditionBase { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(host: UmbControllerHost, args: any) { + super(host, args); -export class UmbUserAllowMfaActionCondition extends UmbUserActionConditionBase { - async _onUserDataChange() { // Check if there are any MFA providers available + this.permitted = false; this.observe( umbExtensionsRegistry.byType('mfaLoginProvider'), (exts) => (this.permitted = exts.length > 0), From 2bef0bef9910ae4f93cbac8e4339b2994291389b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:16:13 +0200 Subject: [PATCH 27/39] no need to specify default value for permitted --- .../user/user/conditions/user-allow-mfa-action.condition.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts index 4f931a894b..af54e401b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-mfa-action.condition.ts @@ -8,7 +8,6 @@ export class UmbUserAllowMfaActionCondition extends UmbConditionBase { super(host, args); // Check if there are any MFA providers available - this.permitted = false; this.observe( umbExtensionsRegistry.byType('mfaLoginProvider'), (exts) => (this.permitted = exts.length > 0), From bb20f463c29bf90a93e8a9eff8203e9f3a32a5e0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:16:44 +0200 Subject: [PATCH 28/39] give controlleralias a fitting name --- .../user/user/conditions/user-allow-action-base.condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts index 6bcdd483f1..9bd1517773 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts @@ -40,7 +40,7 @@ export abstract class UmbUserActionConditionBase this.userState = user?.state; this._onUserDataChange(); }, - 'umbUserUnique', + '_umbActiveUser', ); }); } From 7e9ce89115957c4937e92ad951ccb5d2dfac6960 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:17:18 +0200 Subject: [PATCH 29/39] remove the canPerform condition since it only creates overhead and there is no scenario where it would ever be permitted=false --- .../user/user/conditions/manifests.ts | 2 - .../user-can-perform-actions.condition.ts | 44 ------------------- .../user/user/entity-actions/manifests.ts | 20 --------- 3 files changed, 66 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts index de7909943d..e085874d5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/manifests.ts @@ -3,7 +3,6 @@ import { manifest as userAllowEnableActionManifest } from './user-allow-enable-a import { manifest as userAllowUnlockActionManifest } from './user-allow-unlock-action.condition.js'; import { manifest as userAllowMfaActionManifest } from './user-allow-mfa-action.condition.js'; import { manifest as userAllowDeleteActionManifest } from './user-allow-delete-action.condition.js'; -import { manifest as userCanPerformActionsManifest } from './user-can-perform-actions.condition.js'; export const manifests = [ userAllowDisableActionManifest, @@ -11,5 +10,4 @@ export const manifests = [ userAllowUnlockActionManifest, userAllowMfaActionManifest, userAllowDeleteActionManifest, - userCanPerformActionsManifest, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts deleted file mode 100644 index 988783c49b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-can-perform-actions.condition.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js'; -import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; - -/** - * Condition that checks if the current user is allowed to perform actions on the selected user. - * The logic is generally laid out so that admins can perform actions on any user, while users can only perform actions on other users. - */ -export class UmbUserCanPerformActionsCondition extends UmbUserActionConditionBase { - async _onUserDataChange() { - // Must have selected a user - if (this.userUnique === undefined) { - this.permitted = false; - return; - } - - // If the user is the current user, they can perform actions - if (await this.isCurrentUser()) { - this.permitted = true; - return; - } - - // If the current user is an admin, they can perform actions on any user - if (await this.isCurrentUserAdmin()) { - this.permitted = true; - return; - } - - // Otherwise, the current user can only perform actions on other users - if (this.userAdmin) { - this.permitted = false; - return; - } - - // The current user seems to be able to perform actions on the selected user - this.permitted = true; - } -} - -export const manifest: ManifestCondition = { - type: 'condition', - name: 'User Can Perform Actions Condition', - alias: 'Umb.Condition.User.CanPerformActions', - api: UmbUserCanPerformActionsCondition, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts index 087a863c7d..a8200fddfc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts @@ -19,9 +19,6 @@ const entityActions: Array = [ itemRepositoryAlias: UMB_USER_ITEM_REPOSITORY_ALIAS, }, conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, { alias: 'Umb.Condition.User.AllowDeleteAction', }, @@ -40,9 +37,6 @@ const entityActions: Array = [ label: 'Enable', }, conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, { alias: 'Umb.Condition.User.AllowEnableAction', }, @@ -61,9 +55,6 @@ const entityActions: Array = [ label: 'Disable', }, conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, { alias: 'Umb.Condition.User.AllowDisableAction', }, @@ -81,11 +72,6 @@ const entityActions: Array = [ icon: 'icon-key', label: 'Change Password', }, - conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, - ], }, { type: 'entityAction', @@ -100,9 +86,6 @@ const entityActions: Array = [ label: 'Unlock', }, conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, { alias: 'Umb.Condition.User.AllowUnlockAction', }, @@ -121,9 +104,6 @@ const entityActions: Array = [ label: 'Configure Two-Factor', }, conditions: [ - { - alias: 'Umb.Condition.User.CanPerformActions', - }, { alias: 'Umb.Condition.User.AllowMfaAction', }, From c33e0a5d70db062f6dfbe6340488556fc9af6efd Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:21:12 +0200 Subject: [PATCH 30/39] fix todo by using observeMultiple --- .../user-allow-action-base.condition.ts | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts index 9bd1517773..813640f348 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/conditions/user-allow-action-base.condition.ts @@ -1,13 +1,14 @@ import type { UmbUserStateEnum } from '../types.js'; import { UMB_USER_WORKSPACE_CONTEXT } from '../workspace/user-workspace.context-token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { isCurrentUser, isCurrentUserAnAdmin } from '@umbraco-cms/backoffice/current-user'; +import { isCurrentUser } from '@umbraco-cms/backoffice/current-user'; import type { UmbConditionConfigBase, UmbConditionControllerArguments, UmbExtensionCondition, } from '@umbraco-cms/backoffice/extension-api'; import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; export abstract class UmbUserActionConditionBase extends UmbConditionBase @@ -18,11 +19,6 @@ export abstract class UmbUserActionConditionBase */ protected userUnique?: string; - /** - * Whether the user being edited is an admin - */ - protected userAdmin?: boolean; - /** * The state of the user being edited */ @@ -33,11 +29,10 @@ export abstract class UmbUserActionConditionBase this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (context) => { this.observe( - context.data, - (user) => { - this.userUnique = user?.unique; - this.userAdmin = user?.isAdmin; - this.userState = user?.state; + observeMultiple([context.unique, context.state]), + ([unique, state]) => { + this.userUnique = unique; + this.userState = state; this._onUserDataChange(); }, '_umbActiveUser', @@ -55,14 +50,6 @@ export abstract class UmbUserActionConditionBase return this.userUnique ? isCurrentUser(this._host, this.userUnique) : false; } - /** - * Check if the current user is an admin - * @protected - */ - protected isCurrentUserAdmin() { - return isCurrentUserAnAdmin(this._host); - } - /** * Called when the user data changes * @protected From 4606db37e72aa32edd43d449a800161e2ab8e6d0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:37:32 +0200 Subject: [PATCH 31/39] add exitCode = 1 to fail the build --- .../devops/build/check-path-length.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js b/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js index d21097639b..6bedb82371 100644 --- a/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js +++ b/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js @@ -7,6 +7,9 @@ const IS_CI = process.env.CI === 'true'; const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true'; const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true'; const FILE_PATH_COLOR = '\x1b[36m%s\x1b[0m'; +const ERROR_COLOR = '\x1b[31m%s\x1b[0m'; +const SUCCESS_COLOR = '\x1b[32m%s\x1b[0m'; +const processExitCode = 1; // Default to 1 to fail the build, 0 to just log the issues console.log(`Checking path length in ${PROJECT_DIR} for paths exceeding ${MAX_PATH_LENGTH}...`); console.log('CI detected:', IS_CI); @@ -17,14 +20,12 @@ console.log('-----------------------------------\n'); function checkPathLength(dir) { const files = readdirSync(dir); + let hasError = false; files.forEach(file => { const filePath = join(dir, file); if (filePath.length > MAX_PATH_LENGTH) { - - if (IS_CI) { - //process.exitCode = 1; // TODO: Uncomment this line to fail the build - } + hasError = true; if (IS_AZURE_PIPELINES) { console.error(`##vso[task.logissue type=warning;sourcepath=${filePath};]Path exceeds maximum length of ${MAX_PATH_LENGTH} characters: ${filePath} with ${filePath.length} characters`); @@ -36,9 +37,27 @@ function checkPathLength(dir) { } if (statSync(filePath).isDirectory()) { - checkPathLength(filePath, MAX_PATH_LENGTH); + const subHasError = checkPathLength(filePath); + if (subHasError) { + hasError = true; + } } }); + + return hasError; } -checkPathLength(PROJECT_DIR, MAX_PATH_LENGTH); +const hasError = checkPathLength(PROJECT_DIR, MAX_PATH_LENGTH); + +if (hasError) { + console.error('\n-----------------------------------'); + console.error(ERROR_COLOR, 'Path length check failed'); + console.error('-----------------------------------\n'); + if (processExitCode) { + process.exit(processExitCode); + } +} else { + console.log('\n-----------------------------------'); + console.log(SUCCESS_COLOR, 'Path length check passed'); + console.log('-----------------------------------\n'); +} From e2d99208493434dc7af1da87cbf23f1cdeef05f6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:37:54 +0200 Subject: [PATCH 32/39] only fail the build on CI --- src/Umbraco.Web.UI.Client/devops/build/check-path-length.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js b/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js index 6bedb82371..e195c7e7c0 100644 --- a/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js +++ b/src/Umbraco.Web.UI.Client/devops/build/check-path-length.js @@ -53,7 +53,7 @@ if (hasError) { console.error('\n-----------------------------------'); console.error(ERROR_COLOR, 'Path length check failed'); console.error('-----------------------------------\n'); - if (processExitCode) { + if (IS_CI && processExitCode) { process.exit(processExitCode); } } else { From 056ff565ddc64ceee40704ef405364901b7695a2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:40:32 +0200 Subject: [PATCH 33/39] rename element to avoid long path names --- .../block-grid-area-config-entry/workspace/views/manifests.ts | 2 +- ...e-workspace-view-settings.element.ts => settings.element.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/{block-grid-area-type-workspace-view-settings.element.ts => settings.element.ts} (100%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/manifests.ts index 10a379a924..c92f2b2d77 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/manifests.ts @@ -6,7 +6,7 @@ export const workspaceViews: Array = [ type: 'workspaceView', alias: 'Umb.WorkspaceView.BlockGridAreaType.Settings', name: 'Block Grid Area Type Workspace Settings View', - js: () => import('./block-grid-area-type-workspace-view-settings.element.js'), + js: () => import('./settings.element.js'), weight: 1000, meta: { label: 'Settings', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/block-grid-area-type-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/block-grid-area-type-workspace-view-settings.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts From 5417a2b941665520a370b5de2f8233ee5a361438 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:43:00 +0200 Subject: [PATCH 34/39] rename element to avoid long path names --- .../config/bulk-action-permissions/manifests.ts | 2 +- ...ection-view-bulk-action-permissions.stories.ts | 15 --------------- ...tor-ui-collection-view-permissions.element.ts} | 12 ++++++------ ...itor-ui-collection-view-permissions.stories.ts | 15 +++++++++++++++ ...editor-ui-collection-view-permissions.test.ts} | 10 +++++----- 5 files changed, 27 insertions(+), 27 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.stories.ts rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{property-editor-ui-collection-view-bulk-action-permissions.element.ts => property-editor-ui-collection-view-permissions.element.ts} (86%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{property-editor-ui-collection-view-bulk-action-permissions.test.ts => property-editor-ui-collection-view-permissions.test.ts} (53%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts index f0fda71aaa..b18d13a316 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.BulkActionPermissions', name: 'Collection View Bulk Action Permissions Property Editor UI', - element: () => import('./property-editor-ui-collection-view-bulk-action-permissions.element.js'), + element: () => import('./property-editor-ui-collection-view-permissions.element.js'), meta: { label: 'Collection View Bulk Action Permissions', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.stories.ts deleted file mode 100644 index 1fdeb9e17a..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.stories.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewBulkActionPermissionsElement } from './property-editor-ui-collection-view-bulk-action-permissions.element.js'; -import { html } from '@umbraco-cms/backoffice/external/lit'; - -import './property-editor-ui-collection-view-bulk-action-permissions.element.js'; - -export default { - title: 'Property Editor UIs/Collection View Bulk Action Permissions', - component: 'umb-property-editor-ui-collection-view-bulk-action-permissions', - id: 'umb-property-editor-ui-collection-view-bulk-action-permissions', -} as Meta; - -export const AAAOverview: Story = () => - html``; -AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.element.ts similarity index 86% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.element.ts index 4915a0d8ca..9233e7b7d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.element.ts @@ -1,4 +1,4 @@ -import type { UmbCollectionBulkActionPermissions } from '../../../../../../core/collection/types.js'; +import type { UmbCollectionBulkActionPermissions } from '../../../../../collection/types.js'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property, css } from '@umbraco-cms/backoffice/external/lit'; import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui'; @@ -15,10 +15,10 @@ type BulkActionPermissionType = | 'allowBulkUnpublish'; /** - * @element umb-property-editor-ui-collection-view-bulk-action-permissions + * @element umb-property-editor-ui-collection-view-permissions */ -@customElement('umb-property-editor-ui-collection-view-bulk-action-permissions') -export class UmbPropertyEditorUICollectionViewBulkActionPermissionsElement +@customElement('umb-property-editor-ui-collection-view-permissions') +export class UmbPropertyEditorUICollectionViewPermissionsElement extends UmbLitElement implements UmbPropertyEditorUiElement { @@ -98,10 +98,10 @@ export class UmbPropertyEditorUICollectionViewBulkActionPermissionsElement ]; } -export default UmbPropertyEditorUICollectionViewBulkActionPermissionsElement; +export default UmbPropertyEditorUICollectionViewPermissionsElement; declare global { interface HTMLElementTagNameMap { - 'umb-property-editor-ui-collection-view-bulk-action-permissions': UmbPropertyEditorUICollectionViewBulkActionPermissionsElement; + 'umb-property-editor-ui-collection-view-permissions': UmbPropertyEditorUICollectionViewPermissionsElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts new file mode 100644 index 0000000000..d842bfbeac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts @@ -0,0 +1,15 @@ +import type { Meta, Story } from '@storybook/web-components'; +import type { UmbPropertyEditorUICollectionViewPermissionsElement } from './property-editor-ui-collection-view-permissions.element.js'; +import { html } from '@umbraco-cms/backoffice/external/lit'; + +import './property-editor-ui-collection-view-permissions.element.js'; + +export default { + title: 'Property Editor UIs/Collection View Bulk Action Permissions', + component: 'umb-property-editor-ui-collection-view-permissions', + id: 'umb-property-editor-ui-collection-view-permissions', +} as Meta; + +export const AAAOverview: Story = () => + html``; +AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts similarity index 53% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts index 019979333e..447e49951c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts @@ -1,18 +1,18 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewBulkActionPermissionsElement } from './property-editor-ui-collection-view-bulk-action-permissions.element.js'; +import { UmbPropertyEditorUICollectionViewPermissionsElement } from './property-editor-ui-collection-view-permissions.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; -describe('UmbPropertyEditorUICollectionViewBulkActionPermissionsElement', () => { - let element: UmbPropertyEditorUICollectionViewBulkActionPermissionsElement; +describe('UmbPropertyEditorUICollectionViewPermissionsElement', () => { + let element: UmbPropertyEditorUICollectionViewPermissionsElement; beforeEach(async () => { element = await fixture(html` - + `); }); it('is defined with its own instance', () => { - expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewBulkActionPermissionsElement); + expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewPermissionsElement); }); if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { From 16ccbdd765f5c3dc015b48682d96be2b2afdc03b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:55:23 +0200 Subject: [PATCH 35/39] collection view: rename long file paths to avoid errors --- ...ions.element.ts => collection-view-permissions.element.ts} | 0 ...ions.stories.ts => collection-view-permissions.stories.ts} | 4 ++-- ...ermissions.test.ts => collection-view-permissions.test.ts} | 2 +- .../config/bulk-action-permissions/manifests.ts | 2 +- ...ent.ts => collection-view-column-configuration.element.ts} | 0 ...ies.ts => collection-view-column-configuration.stories.ts} | 4 ++-- ...n.test.ts => collection-view-column-configuration.test.ts} | 2 +- .../collection-view/config/column-configuration/manifests.ts | 2 +- ...ent.ts => collection-view-layout-configuration.element.ts} | 0 ...ies.ts => collection-view-layout-configuration.stories.ts} | 4 ++-- ...n.test.ts => collection-view-layout-configuration.test.ts} | 2 +- .../collection-view/config/layout-configuration/manifests.ts | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{property-editor-ui-collection-view-permissions.element.ts => collection-view-permissions.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{property-editor-ui-collection-view-permissions.stories.ts => collection-view-permissions.stories.ts} (82%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{property-editor-ui-collection-view-permissions.test.ts => collection-view-permissions.test.ts} (92%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/{property-editor-ui-collection-view-column-configuration.element.ts => collection-view-column-configuration.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/{property-editor-ui-collection-view-column-configuration.stories.ts => collection-view-column-configuration.stories.ts} (80%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/{property-editor-ui-collection-view-column-configuration.test.ts => collection-view-column-configuration.test.ts} (91%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/{property-editor-ui-collection-view-layout-configuration.element.ts => collection-view-layout-configuration.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/{property-editor-ui-collection-view-layout-configuration.stories.ts => collection-view-layout-configuration.stories.ts} (80%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/{property-editor-ui-collection-view-layout-configuration.test.ts => collection-view-layout-configuration.test.ts} (91%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts similarity index 82% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts index d842bfbeac..c3d487d518 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewPermissionsElement } from './property-editor-ui-collection-view-permissions.element.js'; +import type { UmbPropertyEditorUICollectionViewPermissionsElement } from './collection-view-permissions.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './property-editor-ui-collection-view-permissions.element.js'; +import './collection-view-permissions.element.js'; export default { title: 'Property Editor UIs/Collection View Bulk Action Permissions', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts similarity index 92% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts index 447e49951c..2c5ad39706 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-permissions.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewPermissionsElement } from './property-editor-ui-collection-view-permissions.element.js'; +import { UmbPropertyEditorUICollectionViewPermissionsElement } from './collection-view-permissions.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewPermissionsElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts index b18d13a316..c92c2dd5a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.BulkActionPermissions', name: 'Collection View Bulk Action Permissions Property Editor UI', - element: () => import('./property-editor-ui-collection-view-permissions.element.js'), + element: () => import('./collection-view-permissions.element.js'), meta: { label: 'Collection View Bulk Action Permissions', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts index 961a2454cf..38716911dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './property-editor-ui-collection-view-column-configuration.element.js'; +import type { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './collection-view-column-configuration.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './property-editor-ui-collection-view-column-configuration.element.js'; +import './collection-view-column-configuration.element.js'; export default { title: 'Property Editor UIs/Collection View Column Configuration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts similarity index 91% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts index 9bc2a6e955..8046077b20 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './property-editor-ui-collection-view-column-configuration.element.js'; +import { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './collection-view-column-configuration.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewColumnConfigurationElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts index d06c7f1ade..02fe1f5ad4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.ColumnConfiguration', name: 'Collection View Column Configuration Property Editor UI', - element: () => import('./property-editor-ui-collection-view-column-configuration.element.js'), + element: () => import('./collection-view-column-configuration.element.js'), meta: { label: 'Collection View Column Configuration', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts index c1e3cd0f17..7820bd82e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './property-editor-ui-collection-view-layout-configuration.element.js'; +import type { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './collection-view-layout-configuration.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './property-editor-ui-collection-view-layout-configuration.element.js'; +import './collection-view-layout-configuration.element.js'; export default { title: 'Property Editor UIs/Collection View Layout Configuration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts similarity index 91% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts index 602083f78a..3540c4ec5a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './property-editor-ui-collection-view-layout-configuration.element.js'; +import { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './collection-view-layout-configuration.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewLayoutConfigurationElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts index 411d187f3d..f37a31e531 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.LayoutConfiguration', name: 'Collection View Column Configuration Property Editor UI', - element: () => import('./property-editor-ui-collection-view-layout-configuration.element.js'), + element: () => import('./collection-view-layout-configuration.element.js'), meta: { label: 'Collection View Layout Configuration', icon: 'icon-autofill', From 5e586ab58b03d4d7e5463d836f17b7a06e366954 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:00:48 +0200 Subject: [PATCH 36/39] remove prefix of "collection-view" and "property-editor-ui" from collection views to limit the path lengths --- .../config/bulk-action-permissions/manifests.ts | 2 +- ...ion-view-permissions.element.ts => permissions.element.ts} | 0 ...ion-view-permissions.stories.ts => permissions.stories.ts} | 4 ++-- ...ollection-view-permissions.test.ts => permissions.test.ts} | 2 +- .../column-configuration.element.ts} | 0 .../column-configuration.stories.ts} | 4 ++-- .../column-configuration.test.ts} | 2 +- .../config/{column-configuration => column}/manifests.ts | 2 +- .../layout-configuration.element.ts} | 0 .../layout-configuration.stories.ts} | 4 ++-- .../layout-configuration.test.ts} | 2 +- .../config/{layout-configuration => layout}/manifests.ts | 2 +- .../uis/collection-view/config/order-by/manifests.ts | 2 +- ...ollection-view-order-by.element.ts => order-by.element.ts} | 0 ...ollection-view-order-by.stories.ts => order-by.stories.ts} | 4 ++-- ...r-ui-collection-view-order-by.test.ts => order-by.test.ts} | 2 +- .../core/property-editor/uis/collection-view/manifests.ts | 4 ++-- 17 files changed, 18 insertions(+), 18 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{collection-view-permissions.element.ts => permissions.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{collection-view-permissions.stories.ts => permissions.stories.ts} (86%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/{collection-view-permissions.test.ts => permissions.test.ts} (94%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{column-configuration/collection-view-column-configuration.element.ts => column/column-configuration.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{column-configuration/collection-view-column-configuration.stories.ts => column/column-configuration.stories.ts} (84%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{column-configuration/collection-view-column-configuration.test.ts => column/column-configuration.test.ts} (93%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{column-configuration => column}/manifests.ts (84%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{layout-configuration/collection-view-layout-configuration.element.ts => layout/layout-configuration.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{layout-configuration/collection-view-layout-configuration.stories.ts => layout/layout-configuration.stories.ts} (84%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{layout-configuration/collection-view-layout-configuration.test.ts => layout/layout-configuration.test.ts} (93%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/{layout-configuration => layout}/manifests.ts (84%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/{property-editor-ui-collection-view-order-by.element.ts => order-by.element.ts} (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/{property-editor-ui-collection-view-order-by.stories.ts => order-by.stories.ts} (82%) rename src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/{property-editor-ui-collection-view-order-by.test.ts => order-by.test.ts} (93%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts index c92c2dd5a8..1a9b24ee5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.BulkActionPermissions', name: 'Collection View Bulk Action Permissions Property Editor UI', - element: () => import('./collection-view-permissions.element.js'), + element: () => import('./permissions.element.js'), meta: { label: 'Collection View Bulk Action Permissions', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.stories.ts similarity index 86% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.stories.ts index c3d487d518..f9d86ec016 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewPermissionsElement } from './collection-view-permissions.element.js'; +import type { UmbPropertyEditorUICollectionViewPermissionsElement } from './permissions.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './collection-view-permissions.element.js'; +import './permissions.element.js'; export default { title: 'Property Editor UIs/Collection View Bulk Action Permissions', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.test.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.test.ts index 2c5ad39706..94d53fe617 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/collection-view-permissions.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/permissions.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewPermissionsElement } from './collection-view-permissions.element.js'; +import { UmbPropertyEditorUICollectionViewPermissionsElement } from './permissions.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewPermissionsElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.stories.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.stories.ts index 38716911dd..21ef92946c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './collection-view-column-configuration.element.js'; +import type { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './column-configuration.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './collection-view-column-configuration.element.js'; +import './column-configuration.element.js'; export default { title: 'Property Editor UIs/Collection View Column Configuration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.test.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.test.ts index 8046077b20..bb0b17070f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/collection-view-column-configuration.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/column-configuration.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './collection-view-column-configuration.element.js'; +import { UmbPropertyEditorUICollectionViewColumnConfigurationElement } from './column-configuration.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewColumnConfigurationElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/manifests.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/manifests.ts index 02fe1f5ad4..7f1500ee96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.ColumnConfiguration', name: 'Collection View Column Configuration Property Editor UI', - element: () => import('./collection-view-column-configuration.element.js'), + element: () => import('./column-configuration.element.js'), meta: { label: 'Collection View Column Configuration', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.stories.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.stories.ts index 7820bd82e6..e9da7ae5df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './collection-view-layout-configuration.element.js'; +import type { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './layout-configuration.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './collection-view-layout-configuration.element.js'; +import './layout-configuration.element.js'; export default { title: 'Property Editor UIs/Collection View Layout Configuration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.test.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.test.ts index 3540c4ec5a..bb009fe9ea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/collection-view-layout-configuration.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/layout-configuration.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './collection-view-layout-configuration.element.js'; +import { UmbPropertyEditorUICollectionViewLayoutConfigurationElement } from './layout-configuration.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewLayoutConfigurationElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/manifests.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/manifests.ts index f37a31e531..310c0ce22b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.LayoutConfiguration', name: 'Collection View Column Configuration Property Editor UI', - element: () => import('./collection-view-layout-configuration.element.js'), + element: () => import('./layout-configuration.element.js'), meta: { label: 'Collection View Layout Configuration', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts index d8871bb956..4ce645e9fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.OrderBy', name: 'Collection View Column Configuration Property Editor UI', - element: () => import('./property-editor-ui-collection-view-order-by.element.js'), + element: () => import('./order-by.element.js'), meta: { label: 'Collection View Order By', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.stories.ts similarity index 82% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.stories.ts index 611de8458b..d16e448f1e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.stories.ts @@ -1,8 +1,8 @@ import type { Meta, Story } from '@storybook/web-components'; -import type { UmbPropertyEditorUICollectionViewOrderByElement } from './property-editor-ui-collection-view-order-by.element.js'; +import type { UmbPropertyEditorUICollectionViewOrderByElement } from './order-by.element.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; -import './property-editor-ui-collection-view-order-by.element.js'; +import './order-by.element.js'; export default { title: 'Property Editor UIs/Collection View Order By', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.test.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.test.ts index 15614d6781..a3c53b41db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/order-by.test.ts @@ -1,5 +1,5 @@ import { expect, fixture, html } from '@open-wc/testing'; -import { UmbPropertyEditorUICollectionViewOrderByElement } from './property-editor-ui-collection-view-order-by.element.js'; +import { UmbPropertyEditorUICollectionViewOrderByElement } from './order-by.element.js'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; describe('UmbPropertyEditorUICollectionViewOrderByElement', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts index 33e003f94a..a327ffc009 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts @@ -1,6 +1,6 @@ import { manifest as bulkActionPermissions } from './config/bulk-action-permissions/manifests.js'; -import { manifest as columnConfiguration } from './config/column-configuration/manifests.js'; -import { manifest as layoutConfiguration } from './config/layout-configuration/manifests.js'; +import { manifest as columnConfiguration } from './config/column/manifests.js'; +import { manifest as layoutConfiguration } from './config/layout/manifests.js'; import { manifest as orderBy } from './config/order-by/manifests.js'; import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; From e6222423a8dd97f23ab78d05d4248384fbb5d5c0 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 4 Apr 2024 14:10:18 +0100 Subject: [PATCH 37/39] `super` context does not exist within a callback --- .../section/components/input-section/input-section.element.ts | 2 +- .../components/data-type-input/data-type-input.element.ts | 2 +- .../input-document-type/input-document-type.element.ts | 2 +- .../components/input-document/input-document.element.ts | 2 +- .../components/input-language/input-language.element.ts | 2 +- .../components/input-media-type/input-media-type.element.ts | 2 +- .../media/media/components/input-media/input-media.element.ts | 2 +- .../components/input-member-group/input-member-group.element.ts | 2 +- .../components/input-member-type/input-member-type.element.ts | 2 +- .../member/components/input-member/input-member.element.ts | 2 +- .../components/input-static-file/input-static-file.element.ts | 2 +- .../components/stylesheet-input/stylesheet-input.element.ts | 2 +- .../components/input-user-group/user-group-input.element.ts | 2 +- .../user/user/components/user-input/user-input.element.ts | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts index f06139c632..40040eca48 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts @@ -91,7 +91,7 @@ export class UmbInputSectionElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts index f9cfb17624..0e39df71c7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-input/data-type-input.element.ts @@ -89,7 +89,7 @@ export class UmbDataTypeInputElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts index befb7d5a4e..c022d1000d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts @@ -121,7 +121,7 @@ export class UmbInputDocumentTypeElement extends FormControlMixin(UmbLitElement) () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts index 74711e310c..f643a477a6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts @@ -120,7 +120,7 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { this._editDocumentPath = routeBuilder({}); }); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts b/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts index fea8574cc8..1f99dde40e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/components/input-language/input-language.element.ts @@ -92,7 +92,7 @@ export class UmbInputLanguageElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts index a443132a7e..111e7e7fca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/components/input-media-type/input-media-type.element.ts @@ -89,7 +89,7 @@ export class UmbInputMediaTypeElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts index 55e79a1541..1367987c5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts @@ -120,7 +120,7 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) { this._editMediaPath = routeBuilder({}); }); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); this.addValidator( diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts index daca53081b..c0fc8a2ae8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts @@ -122,7 +122,7 @@ export class UmbInputMemberGroupElement extends FormControlMixin(UmbLitElement) this._editMemberGroupPath = routeBuilder({}); }); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => { this._items = selectedItems; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/components/input-member-type/input-member-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/components/input-member-type/input-member-type.element.ts index fbf120f6f9..c42c94ae53 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/components/input-member-type/input-member-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/components/input-member-type/input-member-type.element.ts @@ -89,7 +89,7 @@ export class UmbInputMemberTypeElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts index 3a0caf44b3..f4dee267b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts @@ -121,7 +121,7 @@ export class UmbInputMemberElement extends FormControlMixin(UmbLitElement) { this._editMemberPath = routeBuilder({}); }); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => { this._items = selectedItems; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/static-file/components/input-static-file/input-static-file.element.ts b/src/Umbraco.Web.UI.Client/src/packages/static-file/components/input-static-file/input-static-file.element.ts index c06d66dfba..941e03254b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/static-file/components/input-static-file/input-static-file.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/static-file/components/input-static-file/input-static-file.element.ts @@ -90,7 +90,7 @@ export class UmbInputStaticFileElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-input/stylesheet-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-input/stylesheet-input.element.ts index 3d3784132c..9cd434ba75 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-input/stylesheet-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/components/stylesheet-input/stylesheet-input.element.ts @@ -89,7 +89,7 @@ export class UmbStylesheetInputElement extends FormControlMixin(UmbLitElement) { () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/input-user-group/user-group-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/input-user-group/user-group-input.element.ts index 8457ae2134..e0342b556b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/input-user-group/user-group-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/components/input-user-group/user-group-input.element.ts @@ -91,7 +91,7 @@ export class UmbUserGroupInputElement extends FormControlMixin(UmbLitElement) { this.observe( this.#pickerContext.selection, - (selection) => (super.value = selection.join(',')), + (selection) => (this.value = selection.join(',')), 'umbUserGroupInputSelectionObserver', ); this.observe( diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts index 6b4f8537e7..dd6846ac76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts @@ -93,7 +93,7 @@ export class UmbUserInputElement extends FormControlMixin(UmbLitElement) { this.observe( this.#pickerContext.selection, - (selection) => (super.value = selection.join(',')), + (selection) => (this.value = selection.join(',')), 'umbUserInputSelectionObserver', ); this.observe( From b343f6846a199fc85b4f865784f12c625a3761b8 Mon Sep 17 00:00:00 2001 From: Enkel Media Date: Fri, 5 Apr 2024 08:36:57 +0200 Subject: [PATCH 38/39] Update outdated docs for Adjustments for the Storybook docs about umb-debug, I guess it would be valuable to move them into the official docs in the future but this ensures to reflect the change of the property `enabled` to `visible` --- .../src/packages/core/debug/stories/debug.mdx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/debug/stories/debug.mdx b/src/Umbraco.Web.UI.Client/src/packages/core/debug/stories/debug.mdx index bc1d5399d0..188a7a3c28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/debug/stories/debug.mdx +++ b/src/Umbraco.Web.UI.Client/src/packages/core/debug/stories/debug.mdx @@ -16,24 +16,24 @@ This can help with the developer experience to quickly see what is available to ### Usage -The `` component can be used in two different ways, either as a button or as a dialog. By default it is rendered as a button and the debug information about available contexts is dissplayed inline to where the element is placed. +The `` component can be used in two different ways, either as a button or as a dialog. By default it is rendered as a button and the debug information about available contexts is displayed inline to where the `umb-debug` element is placed. ```typescript // This will add a Debug button to the UI and once clicked the information about avilable contextes will slide down - + ``` #### Dialog -This example uses an additional property/attribute `dialog` which adds a smaller badge to the UI as opposed to a button and will open the information in a small dialog/modal from the right hand side, this may be more useful to use when space is limited in the UI to add a button and pane of information directly to where the element is placed. +This example uses an additional property/attribute `dialog` which adds a smaller badge to the UI as opposed to a button and will open the information in a dialog/modal on the right hand side, this may be more useful to use when space is limited in the UI to add a button and pane of information directly to where the element is placed. ```typescript // This will open the debug information in a small dialog/modal from the right hand side - + ``` #### Disable @@ -41,6 +41,8 @@ This example uses an additional property/attribute `dialog` which adds a smaller You may wish to temporarily hide or disable the debug information but return to it later on in the development process. ```typescript -// To hide or remove the button ensure you remove the enabled attribute or set the enabled property to false +// To hide or remove the button ensure you remove the `visible` attribute or set it to false + + ``` From e268200ed99dc51a044f7fa73df588cf37639ce9 Mon Sep 17 00:00:00 2001 From: Markus Johansson Date: Fri, 5 Apr 2024 08:56:52 +0200 Subject: [PATCH 39/39] #1544 fixed umb-debug dialog --- .../src/packages/core/debug/debug.element.ts | 8 ++++++-- .../core/debug/modals/debug/debug-modal.element.ts | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/debug/debug.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/debug/debug.element.ts index 4cf6debdd6..1059ecd908 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/debug/debug.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/debug/debug.element.ts @@ -65,6 +65,9 @@ export class UmbDebugElement extends UmbLitElement { } private _openDialog() { + + this._update(); + this._modalContext?.open(this, UMB_CONTEXT_DEBUGGER_MODAL, { data: { content: html`${this._renderContextAliases()}`, @@ -73,9 +76,10 @@ export class UmbDebugElement extends UmbLitElement { } private _renderDialog() { - return html`
+ return html` +
- Debug +  Debug
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/debug/modals/debug/debug-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/debug/modals/debug/debug-modal.element.ts index d782460c12..255ce97a88 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/debug/modals/debug/debug-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/debug/modals/debug/debug-modal.element.ts @@ -13,7 +13,9 @@ export default class UmbContextDebuggerModalElement extends UmbModalBaseElement< return html` Debug: Contexts - ${this.data?.content} + + ${this.data?.content} + Close `;