Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbContentCollectionManager } from './manager/content-collection-manager.controller.js';
import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
import type { UmbEntityWorkspaceContext } from '@umbraco-cms/backoffice/workspace';

export interface UmbContentCollectionWorkspaceContext<T extends UmbContentTypeModel> extends UmbEntityWorkspaceContext {
contentTypeHasCollection: Observable<boolean>;
getCollectionAlias(): string;
collection: UmbContentCollectionManager;
structure: UmbContentTypeStructureManager<T>;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT } from './content-collection-workspace.context-token.js';
import { customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace';
import type { UmbCollectionConfiguration } from '@umbraco-cms/backoffice/collection';
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace';

const elementName = 'umb-content-collection-workspace-view';
@customElement('umb-content-collection-workspace-view')
export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement implements UmbWorkspaceViewElement {
@state()
Expand All @@ -19,59 +15,25 @@ export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement impl
@state()
private _collectionAlias?: string;

@state()
private _documentUnique?: string;

#dataTypeDetailRepository = new UmbDataTypeDetailRepository(this);

constructor() {
super();
this.#observeConfig();
}

async #observeConfig() {
this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => {
this._collectionAlias = workspaceContext?.getCollectionAlias();
this._documentUnique = workspaceContext?.getUnique() ?? '';
this._collectionAlias = workspaceContext?.collection.getCollectionAlias();

this.observe(
workspaceContext?.structure.ownerContentType,
async (contentType) => {
if (!contentType || !contentType.collection) return;

const dataTypeUnique = contentType.collection.unique;

if (dataTypeUnique) {
await this.#dataTypeDetailRepository.requestByUnique(dataTypeUnique);
this.observe(
await this.#dataTypeDetailRepository.byUnique(dataTypeUnique),
(dataType) => {
if (!dataType) return;
this._config = this.#mapDataTypeConfigToCollectionConfig(dataType);
this._loading = false;
},
'_observeConfigDataType',
);
workspaceContext?.collection.collectionConfig,
(config) => {
if (config) {
this._config = config;
this._loading = false;
}
},
'_observeConfigContentType',
);
});
}

#mapDataTypeConfigToCollectionConfig(dataType: UmbDataTypeDetailModel): UmbCollectionConfiguration {
const config = new UmbPropertyEditorConfigCollection(dataType.values);
const pageSize = Number(config.getValueByAlias('pageSize'));
return {
unique: this._documentUnique,
layouts: config?.getValueByAlias('layouts'),
orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate',
orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc',
pageSize: isNaN(pageSize) ? 50 : pageSize,
userDefinedProperties: config?.getValueByAlias('includeProperties'),
};
}

override render() {
if (this._loading) return nothing;
return html`<umb-collection .alias=${this._collectionAlias} .config=${this._config}></umb-collection>`;
Expand All @@ -82,6 +44,6 @@ export { UmbContentCollectionWorkspaceViewElement as element };

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbContentCollectionWorkspaceViewElement;
'umb-content-collection-workspace-view': UmbContentCollectionWorkspaceViewElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export const UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT = new UmbContextToken<
'UmbWorkspaceContext',
undefined,
(context): context is UmbContentCollectionWorkspaceContext<UmbContentTypeModel> =>
(context as UmbContentCollectionWorkspaceContext<UmbContentTypeModel>).contentTypeHasCollection !== undefined,
(context as UmbContentCollectionWorkspaceContext<UmbContentTypeModel>).collection.hasCollection !== undefined,
);
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type * from './content-collection-workspace-context.interface.js';
export * from './content-collection-workspace.context-token.js';
export * from './manager/content-collection-manager.controller.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbDataTypeDetailRepository, type UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import type { ManifestWorkspaceView, UmbEntityWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
import type { UmbCollectionConfiguration } from '@umbraco-cms/backoffice/collection';
import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

type partialManifestWorkspaceView = Omit<Partial<ManifestWorkspaceView>, 'meta'> & {
meta: Partial<ManifestWorkspaceView['meta']>;
};

export class UmbContentCollectionManager<
ContentTypeDetailModelType extends UmbContentTypeModel = UmbContentTypeModel,
> extends UmbControllerBase {
#host: UmbEntityWorkspaceContext & UmbControllerHost;

#collectionAlias?: string;

#collectionConfig = new UmbObjectState<UmbCollectionConfiguration | undefined>(undefined);
readonly collectionConfig = this.#collectionConfig.asObservable();

#manifestOverrides = new UmbObjectState<partialManifestWorkspaceView | undefined>(undefined);
readonly manifestOverrides = this.#manifestOverrides.asObservable();

#hasCollection = new UmbBooleanState(false);
readonly hasCollection = this.#hasCollection.asObservable();

#dataTypeDetailRepository = new UmbDataTypeDetailRepository(this);

constructor(
host: UmbEntityWorkspaceContext & UmbControllerHost,
structureManager: UmbContentTypeStructureManager<ContentTypeDetailModelType>,
collectionAlias?: string,
) {
super(host);

this.#host = host;
this.#collectionAlias = collectionAlias;

this.observe(
collectionAlias ? structureManager.ownerContentType : undefined,
async (contentType) => {
this.#hasCollection.setValue(!!contentType?.collection);

const dataTypeUnique = contentType?.collection?.unique;
if (dataTypeUnique) {
this.#dataTypeDetailRepository.requestByUnique(dataTypeUnique);
this.observe(
await this.#dataTypeDetailRepository.byUnique(dataTypeUnique),
(dataType) => {
this.#gotDataType(dataType);
},
'_observeConfigDataType',
);
}
},
null,
);
}

getCollectionAlias() {
return this.#collectionAlias;
}

#gotDataType(dataType?: UmbDataTypeDetailModel): void {
if (!dataType) {
this.#collectionConfig.setValue(undefined);
this.#manifestOverrides.setValue(undefined);
return;
}

const config = new UmbPropertyEditorConfigCollection(dataType.values);
const pageSize = Number(config.getValueByAlias('pageSize'));

this.#collectionConfig.setValue({
unique: this.#host.getUnique(),
layouts: config?.getValueByAlias('layouts'),
orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate',
orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc',
pageSize: isNaN(pageSize) ? 50 : pageSize,
userDefinedProperties: config?.getValueByAlias('includeProperties'),
});

const overrides: partialManifestWorkspaceView = {
alias: 'Umb.WorkspaceView.Content.Collection',
meta: {},
};

const overrideIcon = config?.getValueByAlias<string | undefined>('icon');
if (overrideIcon && overrideIcon !== '') {
overrides.meta!.icon = overrideIcon;
}

const overrideLabel = config?.getValueByAlias<string | undefined>('tabName');
if (overrideLabel && overrideLabel !== '') {
overrides.meta!.label = overrideLabel;
}

const showFirst = config?.getValueByAlias<boolean | undefined>('showContentFirst');
if (showFirst === true) {
overrides.weight = 150;
}

this.#manifestOverrides.setValue(overrides);
}

Check warning on line 107 in src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manager/content-collection-manager.controller.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

UmbContentCollectionManager.gotDataType has a cyclomatic complexity of 15, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS } from './workspace-has-content-collection/constants.js';
import { manifests as workspaceHasContentCollectionManifests } from './workspace-has-content-collection/manifests.js';
import { UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace';
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
Expand All @@ -19,5 +21,26 @@ export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> =
},
},
},
{
type: 'workspaceView',
kind: 'contentCollection',
alias: 'Umb.WorkspaceView.Content.Collection',
name: 'Content Workspace Collection View',
weight: 1000,
meta: {
label: 'Collection',
pathname: 'collection',
icon: 'icon-grid',
},
conditions: [
{
alias: UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS,
},
{
alias: UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS,
match: false,
},
],
},
...workspaceHasContentCollectionManifests,
];
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class UmbWorkspaceHasContentCollectionCondition

this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (context) => {
this.observe(
context?.contentTypeHasCollection,
context?.collection.hasCollection,
(hasCollection) => {
this.permitted = hasCollection ?? false;
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,58 @@
import type { UmbContentDetailModel, UmbElementValueModel } from '../types.js';
import { UmbContentCollectionManager } from '../collection/index.js';
import { UmbContentWorkspaceDataManager } from '../manager/index.js';
import { UmbMergeContentVariantDataController } from '../controller/merge-content-variant-data.controller.js';
import type { UmbContentVariantPickerData, UmbContentVariantPickerValue } from '../variant-picker/index.js';
import type { UmbContentPropertyDatasetContext } from '../property-dataset-context/index.js';
import type { UmbContentValidationRepository } from '../repository/content-validation-repository.interface.js';
import type { UmbContentCollectionWorkspaceContext } from '../collection/content-collection-workspace-context.interface.js';
import type { UmbContentWorkspaceContext } from './content-workspace-context.interface.js';
import { UmbContentDetailValidationPathTranslator } from './content-detail-validation-path-translator.js';
import { UmbContentValidationToHintsManager } from './content-validation-to-hints.manager.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbDetailRepository, UmbDetailRepositoryConstructor } from '@umbraco-cms/backoffice/repository';
import {
UmbEntityDetailWorkspaceContextBase,
UmbWorkspaceSplitViewManager,
type UmbEntityDetailWorkspaceContextArgs,
type UmbEntityDetailWorkspaceContextCreateArgs,
type UmbSaveableWorkspaceContext,
} from '@umbraco-cms/backoffice/workspace';
import {
UmbContentTypeStructureManager,
type UmbContentTypeModel,
type UmbPropertyTypeModel,
} from '@umbraco-cms/backoffice/content-type';
import {
UmbVariantId,
type UmbEntityVariantModel,
type UmbEntityVariantOptionModel,
} from '@umbraco-cms/backoffice/variant';
import { UmbDeprecation, UmbReadOnlyVariantGuardManager } from '@umbraco-cms/backoffice/utils';
import { UmbDataTypeDetailRepository, UmbDataTypeItemRepositoryManager } from '@umbraco-cms/backoffice/data-type';
import { appendToFrozenArray, mergeObservables, UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import { firstValueFrom, map } from '@umbraco-cms/backoffice/external/rxjs';
import {
UMB_VALIDATION_CONTEXT,
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
UmbDataPathVariantQuery,
UmbServerModelValidatorContext,
UmbValidationController,
} from '@umbraco-cms/backoffice/validation';
import type { UmbModalToken } from '@umbraco-cms/backoffice/modal';
import { umbOpenModal } from '@umbraco-cms/backoffice/modal';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
import { UmbDataTypeDetailRepository, UmbDataTypeItemRepositoryManager } from '@umbraco-cms/backoffice/data-type';
import { UmbDeprecation, UmbReadOnlyVariantGuardManager } from '@umbraco-cms/backoffice/utils';
import { UmbEntityDetailWorkspaceContextBase, UmbWorkspaceSplitViewManager } from '@umbraco-cms/backoffice/workspace';
import {
UmbEntityUpdatedEvent,
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
import { UmbHintContext } from '@umbraco-cms/backoffice/hint';
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import {
UmbPropertyValuePresetVariantBuilderController,
UmbVariantPropertyGuardManager,
type UmbPropertyTypePresetModel,
type UmbPropertyTypePresetModelTypeModel,
} from '@umbraco-cms/backoffice/property';
import { UmbSegmentCollectionRepository, type UmbSegmentCollectionItemModel } from '@umbraco-cms/backoffice/segment';
import { UmbHintContext, type UmbVariantHint } from '@umbraco-cms/backoffice/hint';
import { UmbSegmentCollectionRepository } from '@umbraco-cms/backoffice/segment';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import {
UMB_VALIDATION_CONTEXT,
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
UmbDataPathVariantQuery,
UmbServerModelValidatorContext,
UmbValidationController,
} from '@umbraco-cms/backoffice/validation';
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbContentTypeModel, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbDetailRepository, UmbDetailRepositoryConstructor } from '@umbraco-cms/backoffice/repository';
import type {
UmbEntityDetailWorkspaceContextArgs,
UmbEntityDetailWorkspaceContextCreateArgs,
UmbSaveableWorkspaceContext,
} from '@umbraco-cms/backoffice/workspace';
import type { UmbEntityVariantModel, UmbEntityVariantOptionModel } from '@umbraco-cms/backoffice/variant';
import type { UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
import type { UmbPropertyTypePresetModel, UmbPropertyTypePresetModelTypeModel } from '@umbraco-cms/backoffice/property';
import type { UmbModalToken } from '@umbraco-cms/backoffice/modal';
import type { UmbSegmentCollectionItemModel } from '@umbraco-cms/backoffice/segment';
import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint';

export interface UmbContentDetailWorkspaceContextArgs<
DetailModelType extends UmbContentDetailModel<VariantModelType>,
Expand All @@ -71,6 +68,7 @@
ignoreValidationResultOnSubmit?: boolean;
contentVariantScaffold: VariantModelType;
contentTypePropertyName: string;
collectionAlias?: string;
saveModalToken?: UmbModalToken<UmbContentVariantPickerData<VariantOptionModelType>, UmbContentVariantPickerValue>;
}

Expand Down Expand Up @@ -102,7 +100,8 @@
extends UmbEntityDetailWorkspaceContextBase<DetailModelType, DetailRepositoryType, CreateArgsType>
implements
UmbContentWorkspaceContext<DetailModelType, ContentTypeDetailModelType, VariantModelType>,
UmbSaveableWorkspaceContext
UmbSaveableWorkspaceContext,
UmbContentCollectionWorkspaceContext<ContentTypeDetailModelType>
{
public readonly IS_CONTENT_WORKSPACE_CONTEXT = true as const;

Expand Down Expand Up @@ -140,6 +139,8 @@
/* Split View */
readonly splitView = new UmbWorkspaceSplitViewManager();

readonly collection: UmbContentCollectionManager;

/* Hints */
readonly hints = new UmbHintContext<UmbVariantHint>(this);

Expand Down Expand Up @@ -210,6 +211,12 @@
x ? x.variesByCulture || x.variesBySegment : undefined,
);

this.collection = new UmbContentCollectionManager<ContentTypeDetailModelType>(
this,
this.structure,
args.collectionAlias,
);

Check warning on line 219 in src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-detail-workspace-base.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ Getting worse: Complex Method

constructor already has high cyclomatic complexity, and now it increases in Lines of Code from 160 to 165. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
new UmbContentValidationToHintsManager<ContentTypeDetailModelType>(
this,
this.structure,
Expand Down
Loading
Loading