Feature: Content Type Workspace Context Base (#17542)

* Create content-type-workspace-context-base.ts

* make detail model with entityType

* allow repository alias

* export base class

* fix type check

* add method to get unpersisted changes

* remove duplicate code

* remove duplicate code

* remove duplicate code

* wip porting code to the base class

* improve extendability

* clean up

* clean up

* move logic to base

* allow to preset the scaffold

* pass preset

* add public tag

* clean up

* simplify the number of places we store the entity type

* add js docs

* rename private method to clear

* remove debugger

* use flag instead of a data state

* set persisted data after create + update

* Update entity-detail-workspace-base.ts

* add js docs

* add protected tag

* call super

* make linter happy

* add comment

* type casting

* no need create observables for unique and entityType it is already handled

* add null check

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
This commit is contained in:
Mads Rasmussen
2024-11-18 15:03:15 +01:00
committed by GitHub
parent 66569f7fa8
commit 2d69eb66ef
10 changed files with 477 additions and 584 deletions

View File

@@ -17,6 +17,8 @@ import {
} from '@umbraco-cms/backoffice/observable-api';
import { incrementString } from '@umbraco-cms/backoffice/utils';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
import { umbExtensionsRegistry, type ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
type UmbPropertyTypeId = UmbPropertyTypeModel['id'];
@@ -35,7 +37,16 @@ export class UmbContentTypeStructureManager<
> extends UmbControllerBase {
#init!: Promise<unknown>;
#repository: UmbDetailRepository<T>;
#repository?: UmbDetailRepository<T>;
#initRepositoryResolver?: () => void;
#initRepository = new Promise<void>((resolve) => {
if (this.#repository) {
resolve();
} else {
this.#initRepositoryResolver = resolve;
}
});
#ownerContentTypeUnique?: string;
#contentTypeObservers = new Array<UmbController>();
@@ -84,9 +95,15 @@ export class UmbContentTypeStructureManager<
return this.#containers.asObservablePart((x) => x.find((y) => y.id === id));
}
constructor(host: UmbControllerHost, typeRepository: UmbDetailRepository<T>) {
constructor(host: UmbControllerHost, typeRepository: UmbDetailRepository<T> | string) {
super(host);
this.#repository = typeRepository;
if (typeof typeRepository === 'string') {
this.#observeRepository(typeRepository);
} else {
this.#repository = typeRepository;
this.#initRepositoryResolver?.();
}
// Observe owner content type compositions, as we only allow one level of compositions at this moment. [NL]
// But, we could support more, we would just need to flatMap all compositions and make sure the entries are unique and then base the observation on that. [NL]
@@ -107,7 +124,7 @@ export class UmbContentTypeStructureManager<
public async loadType(unique?: string) {
//if (!unique) return;
//if (this.#ownerContentTypeUnique === unique) return;
this._reset();
this.#clear();
this.#ownerContentTypeUnique = unique;
@@ -117,10 +134,11 @@ export class UmbContentTypeStructureManager<
return promise;
}
public async createScaffold() {
this._reset();
public async createScaffold(preset?: Partial<T>) {
await this.#initRepository;
this.#clear();
const { data } = await this.#repository.createScaffold();
const { data } = await this.#repository!.createScaffold(preset);
if (!data) return {};
this.#ownerContentTypeUnique = data.unique;
@@ -135,10 +153,11 @@ export class UmbContentTypeStructureManager<
* @returns {Promise} - A promise that will be resolved when the content type is saved.
*/
public async save() {
await this.#initRepository;
const contentType = this.getOwnerContentType();
if (!contentType || !contentType.unique) throw new Error('Could not find the Content Type to save');
const { error, data } = await this.#repository.save(contentType);
const { error, data } = await this.#repository!.save(contentType);
if (error || !data) {
throw error?.message ?? 'Repository did not return data after save.';
}
@@ -155,12 +174,13 @@ export class UmbContentTypeStructureManager<
* @returns {Promise} - a promise that is resolved when the content type has been created.
*/
public async create(parentUnique: string | null) {
await this.#initRepository;
const contentType = this.getOwnerContentType();
if (!contentType || !contentType.unique) {
throw new Error('Could not find the Content Type to create');
}
const { data } = await this.#repository.create(contentType, parentUnique);
const { data } = await this.#repository!.create(contentType, parentUnique);
if (!data) return Promise.reject();
// Update state with latest version:
@@ -200,9 +220,10 @@ export class UmbContentTypeStructureManager<
async #loadType(unique?: string) {
if (!unique) return {};
await this.#initRepository;
// Lets initiate the content type:
const { data, asObservable } = await this.#repository.requestByUnique(unique);
const { data, asObservable } = await this.#repository!.requestByUnique(unique);
if (!data) return {};
await this.#observeContentType(data);
@@ -211,12 +232,13 @@ export class UmbContentTypeStructureManager<
async #observeContentType(data: T) {
if (!data.unique) return;
await this.#initRepository;
// Notice we do not store the content type in the store here, cause it will happen shortly after when the observations gets its first initial callback. [NL]
const ctrl = this.observe(
// Then lets start observation of the content type:
await this.#repository.byUnique(data.unique),
await this.#repository!.byUnique(data.unique),
(docType) => {
if (docType) {
this.#contentTypes.appendOne(docType);
@@ -725,13 +747,29 @@ export class UmbContentTypeStructureManager<
);
}
private _reset() {
#observeRepository(repositoryAlias: string) {
if (!repositoryAlias) throw new Error('Content Type structure manager must have a repository alias.');
new UmbExtensionApiInitializer<ManifestRepository<UmbDetailRepository<T>>>(
this,
umbExtensionsRegistry,
repositoryAlias,
[this._host],
(permitted, ctrl) => {
this.#repository = permitted ? ctrl.api : undefined;
this.#initRepositoryResolver?.();
},
);
}
#clear() {
this.#contentTypes.setValue([]);
this.#contentTypeObservers.forEach((observer) => observer.destroy());
this.#contentTypeObservers = [];
this.#contentTypes.setValue([]);
this.#containers.setValue([]);
}
public override destroy() {
this.#contentTypes.destroy();
this.#containers.destroy();

View File

@@ -12,7 +12,13 @@ export interface UmbPropertyTypeContainerModel {
type: UmbPropertyContainerTypes;
sortOrder: number;
}
/**
*
* @deprecated
* This model is deprecated and will be removed in version 17. Please use the UmbContentTypeDetailModel instead.
* @export
* @interface UmbContentTypeModel
*/
export interface UmbContentTypeModel {
unique: string;
name: string;
@@ -30,6 +36,10 @@ export interface UmbContentTypeModel {
collection: UmbReferenceByUnique | null;
}
export interface UmbContentTypeDetailModel extends UmbContentTypeModel {
entityType: string;
}
export interface UmbPropertyTypeScaffoldModel extends Omit<UmbPropertyTypeModel, 'dataType'> {
dataType?: UmbPropertyTypeModel['dataType'];
}

View File

@@ -0,0 +1,249 @@
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import {
UmbEntityDetailWorkspaceContextBase,
type UmbEntityDetailWorkspaceContextArgs,
type UmbEntityDetailWorkspaceContextCreateArgs,
type UmbRoutableWorkspaceContext,
} from '@umbraco-cms/backoffice/workspace';
import type { UmbContentTypeWorkspaceContext } from './content-type-workspace-context.interface.js';
import type { UmbContentTypeCompositionModel, UmbContentTypeDetailModel, UmbContentTypeSortModel } from '../types.js';
import { UmbValidationContext } from '@umbraco-cms/backoffice/validation';
import { UmbContentTypeStructureManager } from '../structure/index.js';
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
import { jsonStringComparison, type Observable } from '@umbraco-cms/backoffice/observable-api';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbContentTypeWorkspaceContextArgs extends UmbEntityDetailWorkspaceContextArgs {}
export abstract class UmbContentTypeWorkspaceContextBase<
DetailModelType extends UmbContentTypeDetailModel = UmbContentTypeDetailModel,
DetailRepositoryType extends UmbDetailRepository<DetailModelType> = UmbDetailRepository<DetailModelType>,
>
extends UmbEntityDetailWorkspaceContextBase<DetailModelType, DetailRepositoryType>
implements UmbContentTypeWorkspaceContext<DetailModelType>, UmbRoutableWorkspaceContext
{
public readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true;
public readonly name: Observable<string | undefined>;
public readonly alias: Observable<string | undefined>;
public readonly description: Observable<string | undefined>;
public readonly icon: Observable<string | undefined>;
public readonly allowedAtRoot: Observable<boolean | undefined>;
public readonly variesByCulture: Observable<boolean | undefined>;
public readonly variesBySegment: Observable<boolean | undefined>;
public readonly isElement: Observable<boolean | undefined>;
public readonly allowedContentTypes: Observable<Array<UmbContentTypeSortModel> | undefined>;
public readonly compositions: Observable<Array<UmbContentTypeCompositionModel> | undefined>;
public readonly collection: Observable<UmbReferenceByUnique | null | undefined>;
public readonly structure: UmbContentTypeStructureManager<DetailModelType>;
constructor(host: UmbControllerHost, args: UmbContentTypeWorkspaceContextArgs) {
super(host, args);
this.structure = new UmbContentTypeStructureManager<DetailModelType>(this, args.detailRepositoryAlias);
this.addValidationContext(new UmbValidationContext(this));
this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name);
this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias);
this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description);
this.icon = this.structure.ownerContentTypeObservablePart((data) => data?.icon);
this.allowedAtRoot = this.structure.ownerContentTypeObservablePart((data) => data?.allowedAtRoot);
this.variesByCulture = this.structure.ownerContentTypeObservablePart((data) => data?.variesByCulture);
this.variesBySegment = this.structure.ownerContentTypeObservablePart((data) => data?.variesBySegment);
this.isElement = this.structure.ownerContentTypeObservablePart((data) => data?.isElement);
this.allowedContentTypes = this.structure.ownerContentTypeObservablePart((data) => data?.allowedContentTypes);
this.compositions = this.structure.ownerContentTypeObservablePart((data) => data?.compositions);
this.collection = this.structure.ownerContentTypeObservablePart((data) => data?.collection);
}
/**
* Creates a new scaffold
* @param { UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType> } args The arguments for creating a new scaffold
* @returns { Promise<DetailModelType | undefined> } The new scaffold
*/
public override async createScaffold(
args: UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType>,
): Promise<DetailModelType | undefined> {
this.resetState();
this.setParent(args.parent);
const request = this.structure.createScaffold(args.preset);
this._getDataPromise = request;
let { data } = await request;
if (!data) return undefined;
this.setUnique(data.unique);
if (this.modalContext) {
data = { ...data, ...this.modalContext.data.preset };
}
this.setIsNew(true);
this._data.setPersisted(data);
return data;
}
/**
* Loads the data for the workspace
* @param { string } unique The unique identifier of the data to load
* @returns { Promise<DetailModelType> } The loaded data
*/
override async load(unique: string) {
this.resetState();
this.setUnique(unique);
this._getDataPromise = this.structure.loadType(unique);
const response = await this._getDataPromise;
const data = response.data;
if (data) {
this._data.setPersisted(data);
this.setIsNew(false);
}
return response;
}
/**
* Creates the Content Type
* @param { DetailModelType } currentData The current data
* @param { UmbEntityModel } parent The parent entity
* @memberof UmbContentTypeWorkspaceContextBase
*/
override async _create(currentData: DetailModelType, parent: UmbEntityModel) {
try {
await this.structure.create(parent?.unique);
this._data.setPersisted(this.structure.getOwnerContentType());
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadChildrenOfEntityEvent({
entityType: parent.entityType,
unique: parent.unique,
});
eventContext.dispatchEvent(event);
this.setIsNew(false);
} catch (error) {
console.error(error);
}
}
/**
* Updates the content type for the workspace
* @memberof UmbContentTypeWorkspaceContextBase
*/
override async _update() {
try {
await this.structure.save();
this._data.setPersisted(this.structure.getOwnerContentType());
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.getUnique()!,
entityType: this.getEntityType(),
});
actionEventContext.dispatchEvent(event);
} catch (error) {
console.error(error);
}
}
/**
* Gets the name of the content type
* @returns { string | undefined } The name of the content type
*/
public getName(): string | undefined {
return this.structure.getOwnerContentType()?.name;
}
/**
* Sets the name of the content type
* @param { string } name The name of the content type
*/
public setName(name: string) {
this.structure.updateOwnerContentType({ name } as Partial<DetailModelType>);
}
/**
* Gets the alias of the content type
* @returns { string | undefined } The alias of the content type
*/
public getAlias(): string | undefined {
return this.structure.getOwnerContentType()?.alias;
}
/**
* Sets the alias of the content type
* @param { string } alias The alias of the content type
*/
public setAlias(alias: string) {
this.structure.updateOwnerContentType({ alias } as Partial<DetailModelType>);
}
/**
* Gets the description of the content type
* @returns { string | undefined } The description of the content type
*/
public getDescription(): string | undefined {
return this.structure.getOwnerContentType()?.description;
}
/**
* Sets the description of the content type
* @param { string } description The description of the content type
*/
public setDescription(description: string) {
this.structure.updateOwnerContentType({ description } as Partial<DetailModelType>);
}
/**
* Gets the compositions of the content type
* @returns { string | undefined } The icon of the content type
*/
public getCompositions(): Array<UmbContentTypeCompositionModel> | undefined {
return this.structure.getOwnerContentType()?.compositions;
}
/**
* Sets the compositions of the content type
* @param { string } compositions The compositions of the content type
* @returns { void }
*
*/
public setCompositions(compositions: Array<UmbContentTypeCompositionModel>) {
this.structure.updateOwnerContentType({ compositions } as Partial<DetailModelType>);
}
// TODO: manage setting icon color alias?
public setIcon(icon: string) {
this.structure.updateOwnerContentType({ icon } as Partial<DetailModelType>);
}
public override getData() {
return this.structure.getOwnerContentType();
}
protected override _getHasUnpersistedChanges(): boolean {
const currentData = this.structure.getOwnerContentType();
const persistedData = this._data.getPersisted();
return jsonStringComparison(persistedData, currentData) === false;
}
public override destroy(): void {
this.structure.destroy();
super.destroy();
}
}

View File

@@ -1,3 +1,4 @@
export type * from './content-type-workspace-context.interface.js';
export * from './content-type-workspace.context-token.js';
export * from './views/design/content-type-design-editor-property.context-token.js';
export * from './content-type-workspace-context-base.js';

View File

@@ -78,8 +78,6 @@ export abstract class UmbContentDetailWorkspaceContextBase<
/* Content Data */
protected override readonly _data = new UmbContentWorkspaceDataManager<DetailModelType, VariantModelType>(this);
public override readonly entityType = this._data.createObservablePartOfCurrent((data) => data?.entityType);
public override readonly unique = this._data.createObservablePartOfCurrent((data) => data?.unique);
public readonly values = this._data.createObservablePartOfCurrent((data) => data?.values);
public readonly variants = this._data.createObservablePartOfCurrent((data) => data?.variants ?? []);

View File

@@ -43,18 +43,19 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
protected readonly _data = new UmbEntityWorkspaceDataManager<DetailModelType>(this);
public readonly data = this._data.current;
public readonly entityType = this._data.createObservablePartOfCurrent((data) => data?.entityType);
public readonly unique = this._data.createObservablePartOfCurrent((data) => data?.unique);
protected _getDataPromise?: Promise<any>;
protected _detailRepository?: DetailRepositoryType;
#entityContext = new UmbEntityContext(this);
#entityType: string;
public readonly entityType = this.#entityContext.entityType;
public readonly unique = this.#entityContext.unique;
#parent = new UmbObjectState<{ entityType: string; unique: UmbEntityUnique } | undefined>(undefined);
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
public readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
public readonly parentEntityType = this.#parent.asObservablePart((parent) =>
parent ? parent.entityType : undefined,
);
#initResolver?: () => void;
#initialized = false;
@@ -67,10 +68,9 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
}
});
constructor(host: UmbControllerHost, args: UmbEntityWorkspaceContextArgs) {
constructor(host: UmbControllerHost, args: UmbEntityDetailWorkspaceContextArgs) {
super(host, args.workspaceAlias);
this.#entityType = args.entityType;
this.#entityContext.setEntityType(this.#entityType);
this.#entityContext.setEntityType(args.entityType);
window.addEventListener('willchangestate', this.#onWillNavigate);
this.#observeRepository(args.detailRepositoryAlias);
}
@@ -80,7 +80,9 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
* @returns { string } The entity type
*/
getEntityType(): string {
return this.#entityType;
const entityType = this.#entityContext.getEntityType();
if (!entityType) throw new Error('Entity type is not set');
return entityType;
}
/**
@@ -96,7 +98,11 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
* @returns { string | undefined } The unique identifier
*/
getUnique(): UmbEntityUnique | undefined {
return this._data.getCurrent()?.unique;
return this.getData()?.unique;
}
setUnique(unique: string) {
this.#entityContext.setUnique(unique);
}
/**
@@ -107,6 +113,10 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
return this.#parent.getValue();
}
setParent(parent: UmbEntityModel) {
this.#parent.setValue(parent);
}
/**
* Get the parent unique
* @returns { string | undefined } The parent unique identifier
@@ -120,7 +130,6 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
}
async load(unique: string) {
this.#entityContext.setEntityType(this.#entityType);
this.#entityContext.setUnique(unique);
await this.#init;
this.resetState();
@@ -156,21 +165,22 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
* @param {Partial<DetailModelType>} args.preset The preset data.
* @returns { Promise<any> | undefined } The data of the scaffold.
*/
async createScaffold(args: CreateArgsType) {
public async createScaffold(args: CreateArgsType) {
await this.#init;
this.resetState();
this.#parent.setValue(args.parent);
this.setParent(args.parent);
const request = this._detailRepository!.createScaffold(args.preset);
this._getDataPromise = request;
let { data } = await request;
if (!data) return undefined;
this.#entityContext.setEntityType(this.#entityType);
this.#entityContext.setUnique(data.unique);
if (this.modalContext) {
data = { ...data, ...this.modalContext.data.preset };
}
this.setIsNew(true);
this._data.setPersisted(data);
this._data.setCurrent(data);
@@ -180,7 +190,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
async submit() {
await this.#init;
const currentData = this._data.getCurrent();
const currentData = this.getData();
if (!currentData) {
throw new Error('Data is not set');
@@ -191,9 +201,12 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
}
if (this.getIsNew()) {
await this.#create(currentData);
const parent = this.#parent.getValue();
if (parent?.unique === undefined) throw new Error('Parent unique is missing');
if (!parent.entityType) throw new Error('Parent entity type is missing');
await this._create(currentData, parent);
} else {
await this.#update(currentData);
await this._update(currentData);
}
}
@@ -217,12 +230,9 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
return !newUrl.includes(this.routes.getActiveLocalPath());
}
async #create(currentData: DetailModelType) {
protected async _create(currentData: DetailModelType, parent: UmbEntityModel) {
if (!this._detailRepository) throw new Error('Detail repository is not set');
const parent = this.#parent.getValue();
if (!parent) throw new Error('Parent is not set');
const { error, data } = await this._detailRepository.create(currentData, parent.unique);
if (error || !data) {
throw error?.message ?? 'Repository did not return data after create.';
@@ -240,7 +250,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
this.setIsNew(false);
}
async #update(currentData: DetailModelType) {
protected async _update(currentData: DetailModelType) {
const { error, data } = await this._detailRepository!.save(currentData);
if (error || !data) {
throw error?.message ?? 'Repository did not return data after create.';
@@ -258,16 +268,26 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
actionEventContext.dispatchEvent(event);
}
#allowNavigateAway = false;
#onWillNavigate = async (e: CustomEvent) => {
const newUrl = e.detail.url;
if (this.#allowNavigateAway) {
return true;
}
/* TODO: temp removal of discard changes in workspace modals.
The modal closes before the discard changes dialog is resolved.*/
if (newUrl.includes('/modal/umb-modal-workspace/')) {
return true;
}
if (this._checkWillNavigateAway(newUrl) && this._data.getHasUnpersistedChanges()) {
if (this._checkWillNavigateAway(newUrl) && this._getHasUnpersistedChanges()) {
/* Since ours modals are async while events are synchronous, we need to prevent the default behavior of the event, even if the modal hasnt been resolved yet.
Once the modal is resolved (the user accepted to discard the changes and navigate away from the route), we will push a new history state.
This push will make the "willchangestate" event happen again and due to this somewhat "backward" behavior,
we set an "allowNavigateAway"-flag to prevent the "discard-changes" functionality from running in a loop.*/
e.preventDefault();
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
const modal = modalManager.open(this, UMB_DISCARD_CHANGES_MODAL);
@@ -275,8 +295,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
try {
// navigate to the new url when discarding changes
await modal.onSubmit();
// Reset the current data so we don't end in a endless loop of asking to discard changes.
this._data.resetCurrent();
this.#allowNavigateAway = true;
history.pushState({}, '', e.detail.url);
return true;
} catch {
@@ -287,9 +306,18 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
return true;
};
/**
* Check if there are unpersisted changes.
* @returns { boolean } true if there are unpersisted changes.
*/
protected _getHasUnpersistedChanges(): boolean {
return this._data.getHasUnpersistedChanges();
}
override resetState() {
super.resetState();
this._data.clear();
this.#allowNavigateAway = false;
}
#checkIfInitialized() {

View File

@@ -7,99 +7,42 @@ import {
} from '../../paths.js';
import type { UmbDocumentTypeDetailModel } from '../../types.js';
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../../entity.js';
import { UmbDocumentTypeDetailRepository } from '../../repository/detail/document-type-detail.repository.js';
import { UmbDocumentTypeWorkspaceEditorElement } from './document-type-workspace-editor.element.js';
import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContentTypeWorkspaceContextBase } from '@umbraco-cms/backoffice/content-type';
import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import {
UmbSubmittableWorkspaceContextBase,
UmbWorkspaceIsNewRedirectController,
UmbWorkspaceIsNewRedirectControllerAlias,
} from '@umbraco-cms/backoffice/workspace';
import { UmbTemplateDetailRepository } from '@umbraco-cms/backoffice/template';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import type {
UmbContentTypeCompositionModel,
UmbContentTypeSortModel,
UmbContentTypeWorkspaceContext,
} from '@umbraco-cms/backoffice/content-type';
import type { UmbContentTypeSortModel, UmbContentTypeWorkspaceContext } from '@umbraco-cms/backoffice/content-type';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
import type { UmbRoutableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
import type { UmbPathPatternTypeAsEncodedParamsType } from '@umbraco-cms/backoffice/router';
import { UmbValidationContext } from '@umbraco-cms/backoffice/validation';
import { UMB_DOCUMENT_TYPE_WORKSPACE_ALIAS } from './constants.js';
import { UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS } from '../../repository/index.js';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import { UmbTemplateDetailRepository } from '@umbraco-cms/backoffice/template';
type EntityType = UmbDocumentTypeDetailModel;
type DetailModelType = UmbDocumentTypeDetailModel;
export class UmbDocumentTypeWorkspaceContext
extends UmbSubmittableWorkspaceContextBase<EntityType>
implements UmbContentTypeWorkspaceContext<EntityType>, UmbRoutableWorkspaceContext
extends UmbContentTypeWorkspaceContextBase<DetailModelType>
implements UmbContentTypeWorkspaceContext<DetailModelType>, UmbRoutableWorkspaceContext
{
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true;
//
readonly repository = new UmbDocumentTypeDetailRepository(this);
// Data/Draft is located in structure manager
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
// General for content types:
//readonly data;
readonly unique;
readonly entityType;
readonly name;
getName(): string | undefined {
return this.structure.getOwnerContentType()?.name;
}
readonly alias;
readonly description;
readonly icon;
readonly allowedAtRoot;
readonly variesByCulture;
readonly variesBySegment;
readonly isElement;
readonly allowedContentTypes;
readonly compositions;
readonly collection;
// Document type specific:
readonly allowedTemplateIds;
readonly defaultTemplate;
readonly cleanup;
readonly structure = new UmbContentTypeStructureManager<EntityType>(this, this.repository);
createTemplateMode: boolean = false;
#templateRepository = new UmbTemplateDetailRepository(this);
constructor(host: UmbControllerHost) {
super(host, 'Umb.Workspace.DocumentType');
this.addValidationContext(new UmbValidationContext(this));
// General for content types:
//this.data = this.structure.ownerContentType;
this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique);
this.entityType = this.structure.ownerContentTypeObservablePart((data) => data?.entityType);
this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name);
this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias);
this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description);
this.icon = this.structure.ownerContentTypeObservablePart((data) => data?.icon);
this.allowedAtRoot = this.structure.ownerContentTypeObservablePart((data) => data?.allowedAtRoot);
this.variesByCulture = this.structure.ownerContentTypeObservablePart((data) => data?.variesByCulture);
this.variesBySegment = this.structure.ownerContentTypeObservablePart((data) => data?.variesBySegment);
this.isElement = this.structure.ownerContentTypeObservablePart((data) => data?.isElement);
this.allowedContentTypes = this.structure.ownerContentTypeObservablePart((data) => data?.allowedContentTypes);
this.compositions = this.structure.ownerContentTypeObservablePart((data) => data?.compositions);
this.collection = this.structure.ownerContentTypeObservablePart((data) => data?.collection);
super(host, {
workspaceAlias: UMB_DOCUMENT_TYPE_WORKSPACE_ALIAS,
entityType: UMB_DOCUMENT_TYPE_ENTITY_TYPE,
detailRepositoryAlias: UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
});
// Document type specific:
this.allowedTemplateIds = this.structure.ownerContentTypeObservablePart((data) => data?.allowedTemplates);
@@ -117,10 +60,12 @@ export class UmbDocumentTypeWorkspaceContext
const parentEntityType = params.parentEntityType;
const parentUnique = params.parentUnique === 'null' ? null : params.parentUnique;
const presetAlias = params.presetAlias === 'null' ? null : (params.presetAlias ?? null);
if (parentUnique === undefined) {
throw new Error('ParentUnique url parameter is required to create a document type');
}
await this.create({ entityType: parentEntityType, unique: parentUnique }, presetAlias);
await this.#onScaffoldSetup({ entityType: parentEntityType, unique: parentUnique }, presetAlias);
new UmbWorkspaceIsNewRedirectController(
this,
@@ -141,40 +86,6 @@ export class UmbDocumentTypeWorkspaceContext
]);
}
protected override resetState(): void {
super.resetState();
this.#persistedData.setValue(undefined);
}
getData() {
return this.structure.getOwnerContentType();
}
getUnique() {
return this.getData()?.unique;
}
getEntityType() {
return UMB_DOCUMENT_TYPE_ENTITY_TYPE;
}
setName(name: string) {
this.structure.updateOwnerContentType({ name });
}
setAlias(alias: string) {
this.structure.updateOwnerContentType({ alias });
}
setDescription(description: string) {
this.structure.updateOwnerContentType({ description });
}
// TODO: manage setting icon color alias?
setIcon(icon: string) {
this.structure.updateOwnerContentType({ icon });
}
setAllowedAtRoot(allowedAtRoot: boolean) {
this.structure.updateOwnerContentType({ allowedAtRoot });
}
@@ -199,10 +110,6 @@ export class UmbDocumentTypeWorkspaceContext
this.structure.updateOwnerContentType({ cleanup });
}
setCompositions(compositions: Array<UmbContentTypeCompositionModel>) {
this.structure.updateOwnerContentType({ compositions });
}
setCollection(collection: UmbReferenceByUnique) {
this.structure.updateOwnerContentType({ collection });
}
@@ -220,115 +127,69 @@ export class UmbDocumentTypeWorkspaceContext
this.structure.updateOwnerContentType({ defaultTemplate });
}
async create(parent: { entityType: string; unique: string | null }, presetAlias: string | null) {
this.resetState();
this.#parent.setValue(parent);
const { data } = await this.structure.createScaffold();
if (!data) return undefined;
async #onScaffoldSetup(parent: UmbEntityModel, presetAlias: string | null) {
let preset: Partial<DetailModelType> | undefined = undefined;
switch (presetAlias) {
case UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE satisfies UmbCreateDocumentTypeWorkspacePresetType: {
this.setIcon('icon-document-html');
preset = {
icon: 'icon-document-html',
};
this.createTemplateMode = true;
break;
}
case UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT satisfies UmbCreateDocumentTypeWorkspacePresetType: {
this.setIcon('icon-plugin');
this.setIsElement(true);
preset = {
icon: 'icon-plugin',
isElement: true,
};
break;
}
default:
break;
}
this.setIsNew(true);
this.#persistedData.setValue(this.structure.getOwnerContentType());
return data;
this.createScaffold({ parent, preset });
}
async load(unique: string) {
this.resetState();
const { data, asObservable } = await this.structure.loadType(unique);
if (data) {
this.setIsNew(false);
this.#persistedData.update(data);
override async _create(currentData: DetailModelType, parent: UmbEntityModel) {
// TODO: move this responsibility to the template package
if (this.createTemplateMode) {
await this.#createAndAssignTemplate();
}
if (asObservable) {
this.observe(asObservable(), (entity) => this.#onStoreChange(entity), 'umbDocumentTypeStoreObserver');
try {
super._create(currentData, parent);
this.createTemplateMode = false;
} catch (error) {
console.log(error);
}
}
#onStoreChange(entity: EntityType | undefined) {
if (!entity) {
//TODO: This solution is alright for now. But reconsider when we introduce signal-r
history.pushState(null, '', 'section/settings/workspace/document-type-root');
}
// TODO: move this responsibility to the template package
async #createAndAssignTemplate() {
const { data: templateScaffold } = await this.#templateRepository.createScaffold({
name: this.getName(),
alias: this.getAlias(),
});
if (!templateScaffold) throw new Error('Could not create template scaffold');
const { data: template } = await this.#templateRepository.create(templateScaffold, null);
if (!template) throw new Error('Could not create template');
const templateEntity = { id: template.unique };
const allowedTemplates = this.getAllowedTemplateIds() ?? [];
this.setAllowedTemplateIds([templateEntity, ...allowedTemplates]);
this.setDefaultTemplate(templateEntity);
}
/**
* Save or creates the document type, based on wether its a new one or existing.
* @deprecated Use the createScaffold method instead. Will be removed in 17.
* @param {UmbEntityModel} parent
* @memberof UmbMediaTypeWorkspaceContext
*/
async submit() {
const data = this.getData();
if (data === undefined) {
throw new Error('Cannot save, no data');
}
if (this.getIsNew()) {
const parent = this.#parent.getValue();
if (!parent) throw new Error('Parent is not set');
if (this.createTemplateMode) {
const repo = new UmbTemplateDetailRepository(this);
const { data: templateScaffold } = await repo.createScaffold();
if (!templateScaffold) throw new Error('Could not create template scaffold');
templateScaffold.name = data.name;
templateScaffold.alias = data.alias;
const { data: template } = await repo.create(templateScaffold, null);
if (!template) throw new Error('Could not create template');
const templateEntity = { id: template.unique };
const allowedTemplates = this.getAllowedTemplateIds() ?? [];
this.setAllowedTemplateIds([templateEntity, ...allowedTemplates]);
this.setDefaultTemplate(templateEntity);
}
await this.structure.create(parent.unique);
// TODO: this might not be the right place to alert the tree, but it works for now
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadChildrenOfEntityEvent({
entityType: parent.entityType,
unique: parent.unique,
});
eventContext.dispatchEvent(event);
this.setIsNew(false);
this.createTemplateMode = false;
} else {
await this.structure.save();
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.getUnique()!,
entityType: this.getEntityType(),
});
actionEventContext.dispatchEvent(event);
}
}
public override destroy(): void {
this.#persistedData.destroy();
this.structure.destroy();
this.repository.destroy();
super.destroy();
async create(parent: UmbEntityModel, presetAlias: string | null) {
this.#onScaffoldSetup(parent, presetAlias);
}
}

View File

@@ -37,6 +37,7 @@ export class UmbDocumentWorkspaceViewInfoLinksElement extends UmbLitElement {
this.observe(
observeMultiple([context.isNew, context.unique, context.variantOptions]),
([isNew, unique, variantOptions]) => {
if (!unique) return;
this._isNew = isNew === true;
this._unique = unique;
this._variantOptions = variantOptions;

View File

@@ -1,90 +1,39 @@
import { UmbMediaTypeDetailRepository } from '../repository/detail/media-type-detail.repository.js';
import { UMB_MEDIA_TYPE_ENTITY_TYPE } from '../entity.js';
import type { UmbMediaTypeDetailModel } from '../types.js';
import { UmbMediaTypeWorkspaceEditorElement } from './media-type-workspace-editor.element.js';
import {
UmbSubmittableWorkspaceContextBase,
type UmbRoutableWorkspaceContext,
UmbWorkspaceIsNewRedirectController,
} from '@umbraco-cms/backoffice/workspace';
import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import type {
UmbContentTypeCompositionModel,
UmbContentTypeSortModel,
UmbContentTypeWorkspaceContext,
} from '@umbraco-cms/backoffice/content-type';
import { UmbContentTypeWorkspaceContextBase } from '@umbraco-cms/backoffice/content-type';
import type { UmbContentTypeSortModel, UmbContentTypeWorkspaceContext } from '@umbraco-cms/backoffice/content-type';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import { UMB_MEDIA_TYPE_WORKSPACE_ALIAS } from './constants.js';
import { UMB_MEDIA_TYPE_DETAIL_REPOSITORY_ALIAS } from '../repository/index.js';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
type EntityType = UmbMediaTypeDetailModel;
type DetailModelType = UmbMediaTypeDetailModel;
export class UmbMediaTypeWorkspaceContext
extends UmbSubmittableWorkspaceContextBase<EntityType>
implements UmbContentTypeWorkspaceContext<EntityType>, UmbRoutableWorkspaceContext
extends UmbContentTypeWorkspaceContextBase<DetailModelType>
implements UmbContentTypeWorkspaceContext<DetailModelType>, UmbRoutableWorkspaceContext
{
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true;
//
public readonly repository: UmbMediaTypeDetailRepository = new UmbMediaTypeDetailRepository(this);
// Draft is located in structure manager
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
// General for content types:
readonly data;
readonly unique;
readonly entityType;
readonly name;
getName(): string | undefined {
return this.structure.getOwnerContentType()?.name;
}
readonly alias;
readonly description;
readonly icon;
readonly allowedAtRoot;
readonly variesByCulture;
readonly variesBySegment;
readonly allowedContentTypes;
readonly compositions;
readonly collection;
readonly structure = new UmbContentTypeStructureManager<EntityType>(this, this.repository);
constructor(host: UmbControllerHost) {
super(host, 'Umb.Workspace.MediaType');
// General for content types:
this.data = this.structure.ownerContentType;
this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique);
this.entityType = this.structure.ownerContentTypeObservablePart((data) => data?.entityType);
this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name);
this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias);
this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description);
this.icon = this.structure.ownerContentTypeObservablePart((data) => data?.icon);
this.allowedAtRoot = this.structure.ownerContentTypeObservablePart((data) => data?.allowedAtRoot);
this.variesByCulture = this.structure.ownerContentTypeObservablePart((data) => data?.variesByCulture);
this.variesBySegment = this.structure.ownerContentTypeObservablePart((data) => data?.variesBySegment);
this.allowedContentTypes = this.structure.ownerContentTypeObservablePart((data) => data?.allowedContentTypes);
this.compositions = this.structure.ownerContentTypeObservablePart((data) => data?.compositions);
this.collection = this.structure.ownerContentTypeObservablePart((data) => data?.collection);
super(host, {
workspaceAlias: UMB_MEDIA_TYPE_WORKSPACE_ALIAS,
entityType: UMB_MEDIA_TYPE_ENTITY_TYPE,
detailRepositoryAlias: UMB_MEDIA_TYPE_DETAIL_REPOSITORY_ALIAS,
});
this.routes.setRoutes([
{
path: 'create/parent/:entityType/:parentUnique',
path: 'create/parent/:parentEntityType/:parentUnique',
component: UmbMediaTypeWorkspaceEditorElement,
setup: async (_component, info) => {
const parentEntityType = info.match.params.entityType;
const parentEntityType = info.match.params.parentEntityType;
const parentUnique = info.match.params.parentUnique === 'null' ? null : info.match.params.parentUnique;
await this.create({ entityType: parentEntityType, unique: parentUnique });
const parent: UmbEntityModel = { entityType: parentEntityType, unique: parentUnique };
await this.createScaffold({ parent });
new UmbWorkspaceIsNewRedirectController(
this,
@@ -104,40 +53,6 @@ export class UmbMediaTypeWorkspaceContext
]);
}
protected override resetState(): void {
super.resetState();
this.#persistedData.setValue(undefined);
}
getData() {
return this.structure.getOwnerContentType();
}
getUnique() {
return this.getData()?.unique;
}
getEntityType() {
return UMB_MEDIA_TYPE_ENTITY_TYPE;
}
setName(name: string) {
this.structure.updateOwnerContentType({ name });
}
setAlias(alias: string) {
this.structure.updateOwnerContentType({ alias });
}
setDescription(description: string) {
this.structure.updateOwnerContentType({ description });
}
// TODO: manage setting icon color alias?
setIcon(icon: string) {
this.structure.updateOwnerContentType({ icon });
}
setAllowedAtRoot(allowedAtRoot: boolean) {
this.structure.updateOwnerContentType({ allowedAtRoot });
}
@@ -158,86 +73,17 @@ export class UmbMediaTypeWorkspaceContext
this.structure.updateOwnerContentType({ allowedContentTypes });
}
setCompositions(compositions: Array<UmbContentTypeCompositionModel>) {
this.structure.updateOwnerContentType({ compositions });
}
setCollection(collection: UmbReferenceByUnique) {
this.structure.updateOwnerContentType({ collection });
}
async create(parent: { entityType: string; unique: string | null }) {
this.resetState();
this.#parent.setValue(parent);
const { data } = await this.structure.createScaffold();
if (!data) return undefined;
this.setIsNew(true);
this.#persistedData.setValue(data);
return data;
}
async load(unique: string) {
this.resetState();
const { data, asObservable } = await this.structure.loadType(unique);
if (data) {
this.setIsNew(false);
this.#persistedData.update(data);
}
if (asObservable) {
this.observe(asObservable(), (entity) => this.#onStoreChange(entity), 'umbMediaTypeStoreObserver');
}
}
#onStoreChange(entity: EntityType | undefined) {
if (!entity) {
//TODO: This solution is alright for now. But reconsider when we introduce signal-r
history.pushState(null, '', 'section/settings/workspace/media-type-root');
}
}
/**
* Save or creates the media type, based on wether its a new one or existing.
* @deprecated Use the createScaffold method instead. Will be removed in 17.
* @param {UmbEntityModel} parent
* @memberof UmbMediaTypeWorkspaceContext
*/
async submit() {
const data = this.getData();
if (!data) {
throw new Error('Something went wrong, there is no data for media type you want to save...');
}
if (this.getIsNew()) {
const parent = this.#parent.getValue();
if (!parent) throw new Error('Parent is not set');
await this.structure.create(parent.unique);
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadChildrenOfEntityEvent({
entityType: parent.entityType,
unique: parent.unique,
});
eventContext.dispatchEvent(event);
this.setIsNew(false);
} else {
await this.structure.save();
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.getUnique()!,
entityType: this.getEntityType(),
});
actionEventContext.dispatchEvent(event);
}
}
public override destroy(): void {
this.#persistedData.destroy();
this.structure.destroy();
this.repository.destroy();
super.destroy();
async create(parent: UmbEntityModel) {
this.createScaffold({ parent });
}
}

View File

@@ -1,86 +1,40 @@
import { UmbMemberTypeDetailRepository } from '../repository/detail/index.js';
import { UMB_MEMBER_TYPE_DETAIL_REPOSITORY_ALIAS } from '../repository/detail/index.js';
import type { UmbMemberTypeDetailModel } from '../types.js';
import { UMB_MEMBER_TYPE_ENTITY_TYPE } from '../index.js';
import { UmbMemberTypeWorkspaceEditorElement } from './member-type-workspace-editor.element.js';
import {
UmbSubmittableWorkspaceContextBase,
type UmbRoutableWorkspaceContext,
UmbWorkspaceIsNewRedirectController,
} from '@umbraco-cms/backoffice/workspace';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import {
type UmbContentTypeCompositionModel,
UmbContentTypeStructureManager,
type UmbContentTypeWorkspaceContext,
UmbContentTypeWorkspaceContextBase,
} from '@umbraco-cms/backoffice/content-type';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import { UMB_MEMBER_TYPE_WORKSPACE_ALIAS } from './manifests.js';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
type EntityType = UmbMemberTypeDetailModel;
type EntityDetailModel = UmbMemberTypeDetailModel;
export class UmbMemberTypeWorkspaceContext
extends UmbSubmittableWorkspaceContextBase<EntityType>
implements UmbContentTypeWorkspaceContext<EntityType>, UmbRoutableWorkspaceContext
extends UmbContentTypeWorkspaceContextBase<EntityDetailModel>
implements UmbContentTypeWorkspaceContext<EntityDetailModel>, UmbRoutableWorkspaceContext
{
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true;
public readonly repository = new UmbMemberTypeDetailRepository(this);
#parent = new UmbObjectState<{ entityType: string; unique: string | null } | undefined>(undefined);
readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
readonly parentEntityType = this.#parent.asObservablePart((parent) => (parent ? parent.entityType : undefined));
#persistedData = new UmbObjectState<EntityType | undefined>(undefined);
// General for content types:
readonly data;
readonly unique;
readonly name;
getName(): string | undefined {
return this.structure.getOwnerContentType()?.name;
}
readonly alias;
readonly description;
readonly icon;
readonly allowedAtRoot;
readonly variesByCulture;
readonly variesBySegment;
readonly isElement;
readonly allowedContentTypes;
readonly compositions;
readonly structure = new UmbContentTypeStructureManager<EntityType>(this, this.repository);
constructor(host: UmbControllerHost) {
super(host, 'Umb.Workspace.MemberType');
// General for content types:
this.data = this.structure.ownerContentType;
this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique);
this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name);
this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias);
this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description);
this.icon = this.structure.ownerContentTypeObservablePart((data) => data?.icon);
this.allowedAtRoot = this.structure.ownerContentTypeObservablePart((data) => data?.allowedAtRoot);
this.variesByCulture = this.structure.ownerContentTypeObservablePart((data) => data?.variesByCulture);
this.variesBySegment = this.structure.ownerContentTypeObservablePart((data) => data?.variesBySegment);
this.isElement = this.structure.ownerContentTypeObservablePart((data) => data?.isElement);
this.allowedContentTypes = this.structure.ownerContentTypeObservablePart((data) => data?.allowedContentTypes);
this.compositions = this.structure.ownerContentTypeObservablePart((data) => data?.compositions);
super(host, {
workspaceAlias: UMB_MEMBER_TYPE_WORKSPACE_ALIAS,
entityType: UMB_MEMBER_TYPE_ENTITY_TYPE,
detailRepositoryAlias: UMB_MEMBER_TYPE_DETAIL_REPOSITORY_ALIAS,
});
this.routes.setRoutes([
{
path: 'create/parent/:entityType/:parentUnique',
path: 'create/parent/:parentEntityType/:parentUnique',
component: UmbMemberTypeWorkspaceEditorElement,
setup: async (_component, info) => {
const parentEntityType = info.match.params.entityType;
const parentEntityType = info.match.params.parentEntityType;
const parentUnique = info.match.params.parentUnique === 'null' ? null : info.match.params.parentUnique;
await this.create({ entityType: parentEntityType, unique: parentUnique });
const parent: UmbEntityModel = { entityType: parentEntityType, unique: parentUnique };
await this.createScaffold({ parent });
new UmbWorkspaceIsNewRedirectController(
this,
@@ -100,120 +54,27 @@ export class UmbMemberTypeWorkspaceContext
]);
}
set<PropertyName extends keyof EntityType>(propertyName: PropertyName, value: EntityType[PropertyName]) {
/**
* @deprecated Use the individual set methods instead. Will be removed in 17.
* @template PropertyName
* @param {PropertyName} propertyName
* @param {EntityDetailModel[PropertyName]} value
* @memberof UmbMemberTypeWorkspaceContext
*/
set<PropertyName extends keyof EntityDetailModel>(
propertyName: PropertyName,
value: EntityDetailModel[PropertyName],
) {
this.structure.updateOwnerContentType({ [propertyName]: value });
}
protected override resetState(): void {
super.resetState();
this.#persistedData.setValue(undefined);
}
getData() {
return this.structure.getOwnerContentType();
}
getUnique() {
return this.getData()?.unique;
}
getEntityType() {
return UMB_MEMBER_TYPE_ENTITY_TYPE;
}
setName(name: string) {
this.structure.updateOwnerContentType({ name });
}
setAlias(alias: string) {
this.structure.updateOwnerContentType({ alias });
}
setDescription(description: string) {
this.structure.updateOwnerContentType({ description });
}
// TODO: manage setting icon color alias?
setIcon(icon: string) {
this.structure.updateOwnerContentType({ icon });
}
setCompositions(compositions: Array<UmbContentTypeCompositionModel>) {
this.structure.updateOwnerContentType({ compositions });
}
async create(parent: { entityType: string; unique: string | null }) {
this.resetState();
this.#parent.setValue(parent);
const { data } = await this.structure.createScaffold();
if (!data) return undefined;
this.setIsNew(true);
this.#persistedData.setValue(data);
return data;
}
async load(unique: string) {
this.resetState();
const { data, asObservable } = await this.structure.loadType(unique);
if (data) {
this.setIsNew(false);
this.#persistedData.update(data);
}
if (asObservable) {
this.observe(asObservable(), (entity) => this.#onStoreChange(entity), 'umbMemberTypeStoreObserver');
}
}
#onStoreChange(entity: EntityType | undefined) {
if (!entity) {
//TODO: This solution is alright for now. But reconsider when we introduce signal-r
history.pushState(null, '', 'section/settings/workspace/member-type-root');
}
}
/**
* Save or creates the member type, based on wether its a new one or existing.
* @deprecated Use the createScaffold method instead. Will be removed in 17.
* @param {UmbEntityModel} parent
* @memberof UmbMemberTypeWorkspaceContext
*/
async submit() {
const data = this.getData();
if (!data) {
throw new Error('Something went wrong, there is no data for media type you want to save...');
}
if (this.getIsNew()) {
const parent = this.#parent.getValue();
if (!parent) throw new Error('Parent is not set');
await this.structure.create(parent.unique);
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadChildrenOfEntityEvent({
entityType: parent.entityType,
unique: parent.unique,
});
eventContext.dispatchEvent(event);
this.setIsNew(false);
} else {
await this.structure.save();
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.getUnique()!,
entityType: this.getEntityType(),
});
actionEventContext.dispatchEvent(event);
}
}
public override destroy(): void {
this.#persistedData.destroy();
this.structure.destroy();
this.repository.destroy();
super.destroy();
async create(parent: UmbEntityModel) {
this.createScaffold({ parent });
}
}