user conditions + workspace refactor

This commit is contained in:
Niels Lyngsø
2024-02-28 09:22:39 +01:00
parent 6a306d21f1
commit 0883f60bef
10 changed files with 62 additions and 82 deletions

View File

@@ -2,12 +2,10 @@ 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';
export const isCurrentUser = async (host: UmbControllerHost, userId: string) => {
let currentUserContext: typeof UMB_CURRENT_USER_CONTEXT.TYPE | undefined;
export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) => {
const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT);
const currentUserContext = await ctrl.asPromise();
ctrl.destroy();
await new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (context) => {
currentUserContext = context;
}).asPromise();
return await currentUserContext!.isUserCurrentUser(userId);
return await currentUserContext!.isUserCurrentUser(userUnique);
};

View File

@@ -1,5 +1,5 @@
import type { UmbUserDetailModel } from '../types.js';
import type { UmbUserWorkspaceContext } from '../workspace/user-workspace.context.js';
import type { UmbUserStateEnum } from '../types.js';
import { UMB_USER_WORKSPACE_CONTEXT } from '../workspace/user-workspace.context.js';
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
import { isCurrentUser } from '@umbraco-cms/backoffice/current-user';
import type {
@@ -7,28 +7,38 @@ import type {
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
export class UmbUserActionConditionBase extends UmbBaseController implements UmbExtensionCondition {
config: UmbConditionConfigBase;
permitted = false;
#onChange: () => void;
protected userData?: UmbUserDetailModel;
protected userUnique?: string;
protected userState?: UmbUserStateEnum | null;
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args.host);
this.config = args.config;
this.#onChange = args.onChange;
this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => {
const userContext = context as UmbUserWorkspaceContext;
this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (context) => {
this.observe(
userContext.data,
(data) => {
this.userData = data;
context.unique,
(unique) => {
this.userUnique = unique;
this.onUserDataChange();
},
'umbUserDataActionConditionObserver',
'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',
);
});
}
@@ -40,7 +50,7 @@ export class UmbUserActionConditionBase extends UmbBaseController implements Umb
* @memberof UmbUserActionConditionBase
*/
protected async isCurrentUser() {
return this.userData?.unique ? isCurrentUser(this._host, this.userData.unique) : false;
return this.userUnique ? isCurrentUser(this._host, this.userUnique) : false;
}
/**

View File

@@ -1,18 +1,10 @@
import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js';
import type {
ManifestCondition,
UmbConditionConfigBase,
UmbConditionControllerArguments,
} from '@umbraco-cms/backoffice/extension-api';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';
export class UmbUserAllowDeleteActionCondition extends UmbUserActionConditionBase {
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args);
}
async onUserDataChange() {
// don't allow the current user to delete themselves
if (!this.userData || !this.userData.unique || (await this.isCurrentUser())) {
if (!this.userUnique || (await this.isCurrentUser())) {
this.permitted = false;
} else {
this.permitted = true;

View File

@@ -1,25 +1,17 @@
import { UmbUserStateEnum } from '../types.js';
import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js';
import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type {
ManifestCondition,
UmbConditionConfigBase,
UmbConditionControllerArguments,
} from '@umbraco-cms/backoffice/extension-api';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';
export class UmbUserAllowDisableActionCondition extends UmbUserActionConditionBase {
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args);
}
async onUserDataChange() {
// don't allow the current user to disable themselves
if (!this.userData || !this.userData.unique || (await this.isCurrentUser())) {
if (!this.userUnique || (await this.isCurrentUser())) {
this.permitted = false;
super.onUserDataChange();
return;
}
this.permitted = this.userData?.state !== UserStateModel.DISABLED;
this.permitted = this.userState !== UmbUserStateEnum.DISABLED;
super.onUserDataChange();
}
}

View File

@@ -1,25 +1,17 @@
import { UmbUserStateEnum } from '../types.js';
import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js';
import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type {
ManifestCondition,
UmbConditionConfigBase,
UmbConditionControllerArguments,
} from '@umbraco-cms/backoffice/extension-api';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';
export class UmbUserAllowEnableActionCondition extends UmbUserActionConditionBase {
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args);
}
async onUserDataChange() {
// don't allow the current user to enable themselves
if (!this.userData || !this.userData.unique || (await this.isCurrentUser())) {
if (!this.userUnique || (await this.isCurrentUser())) {
this.permitted = false;
super.onUserDataChange();
return;
}
this.permitted = this.userData?.state === UserStateModel.DISABLED;
this.permitted = this.userState === UmbUserStateEnum.DISABLED;
super.onUserDataChange();
}
}

View File

@@ -1,25 +1,17 @@
import { UmbUserStateEnum } from '../types.js';
import { UmbUserActionConditionBase } from './user-allow-action-base.condition.js';
import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type {
ManifestCondition,
UmbConditionConfigBase,
UmbConditionControllerArguments,
} from '@umbraco-cms/backoffice/extension-api';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';
export class UmbUserAllowUnlockActionCondition extends UmbUserActionConditionBase {
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args);
}
async onUserDataChange() {
// don't allow the current user to unlock themselves
if (!this.userData || !this.userData.unique || (await this.isCurrentUser())) {
if (!this.userUnique || (await this.isCurrentUser())) {
this.permitted = false;
super.onUserDataChange();
return;
}
this.permitted = this.userData?.state === UserStateModel.LOCKED_OUT;
this.permitted = this.userState === UmbUserStateEnum.LOCKED_OUT;
super.onUserDataChange();
}
}

View File

@@ -1,24 +1,16 @@
import { UmbUserActionConditionBase } from '../../../conditions/user-allow-action-base.condition.js';
import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type {
ManifestCondition,
UmbConditionConfigBase,
UmbConditionControllerArguments,
} from '@umbraco-cms/backoffice/extension-api';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';
export class UmbUserAllowResendInviteActionCondition extends UmbUserActionConditionBase {
constructor(args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(args);
}
async onUserDataChange() {
if (!this.userData || !this.userData.unique) {
if (!this.userUnique) {
this.permitted = false;
super.onUserDataChange();
return;
}
this.permitted = this.userData?.state === UserStateModel.INVITED;
this.permitted = this.userState === UserStateModel.INVITED;
super.onUserDataChange();
}
}

View File

@@ -1,5 +1,8 @@
import type { UmbUserEntityType } from './entity.js';
import type { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api';
export type UmbUserStateEnum = UserStateModel;
export const UmbUserStateEnum = UserStateModel;
export interface UmbUserDetailModel {
entityType: UmbUserEntityType;
@@ -12,7 +15,7 @@ export interface UmbUserDetailModel {
documentStartNodeUniques: Array<string>;
mediaStartNodeUniques: Array<string>;
avatarUrls: Array<string>;
state: UserStateModel | null;
state: UmbUserStateEnum | null;
failedLoginAttempts: number;
createDate: string | null;
updateDate: string | null;

View File

@@ -57,7 +57,7 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement {
description=${this.localize.term('user_languageHelp')}>
<umb-ui-culture-input
slot="editor"
value=${ifDefined(this._user?.languageIsoCode)}
value=${ifDefined(this._user?.languageIsoCode ?? undefined)}
@change="${this.#onLanguageChange}"
name="language"
label="${this.localize.term('user_language')}"></umb-ui-culture-input>

View File

@@ -1,4 +1,4 @@
import type { UmbUserDetailModel } from '../types.js';
import type { UmbUserDetailModel, UmbUserStateEnum } from '../types.js';
import { UMB_USER_ENTITY_TYPE } from '../entity.js';
import { UmbUserDetailRepository } from '../repository/index.js';
import { UmbUserAvatarRepository } from '../repository/avatar/index.js';
@@ -24,12 +24,15 @@ export class UmbUserWorkspaceContext
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
//data = this.#currentData.asObservable();
readonly data = this.#currentData.asObservable();
readonly state = this.#currentData.asObservablePart((x) => x?.state);
readonly unique = this.#currentData.asObservablePart((x) => x?.unique);
async load(unique: string) {
const { data, asObservable } = await this.detailRepository.requestByUnique(unique);
if (data) {
this.setIsNew(false);
this.#persistedData.update(data);
this.#currentData.update(data);
}
@@ -49,6 +52,9 @@ export class UmbUserWorkspaceContext
getUnique(): string | undefined {
return this.getData()?.unique;
}
getState(): UmbUserStateEnum | null | undefined {
return this.getData()?.state;
}
getEntityType(): string {
return UMB_USER_ENTITY_TYPE;
@@ -69,14 +75,15 @@ export class UmbUserWorkspaceContext
let newData = undefined;
if (this.getIsNew()) {
const { data } = await this.detailRepository.create(this.#data.value);
const { data } = await this.detailRepository.create(this.#currentData.value);
newData = data;
} else {
const { data } = await this.detailRepository.save(this.#data.value);
const { data } = await this.detailRepository.save(this.#currentData.value);
newData = data;
}
if (newData) {
this.#persistedData.setValue(newData);
this.#currentData.setValue(newData);
this.saveComplete(newData);
}
@@ -98,6 +105,8 @@ export class UmbUserWorkspaceContext
destroy(): void {
this.#persistedData.destroy();
this.#currentData.destroy();
this.detailRepository.destroy();
this.avatarRepository.destroy();
super.destroy();
}
}