@umbraco-cms/backoffice 17.1.0-rc → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cms/packages/content/content/workspace/content-detail-workspace-base.d.ts +1 -5
- package/dist-cms/packages/content/content/workspace/content-detail-workspace-base.js +5 -70
- package/dist-cms/packages/content/content/workspace/content-detail-workspace-type-transform.controller.d.ts +12 -0
- package/dist-cms/packages/content/content/workspace/content-detail-workspace-type-transform.controller.js +72 -0
- package/dist-cms/packages/core/property/property-value-preset/property-value-preset-variant-builder.controller.js +2 -30
- package/dist-cms/packages/core/workspace/entity-detail/entity-detail-workspace-base.d.ts +5 -0
- package/dist-cms/packages/core/workspace/entity-detail/entity-detail-workspace-base.js +11 -3
- package/dist-cms/tsconfig.build.tsbuildinfo +1 -1
- package/dist-cms/umbraco-package.json +1 -1
- package/package.json +1 -1
|
@@ -75,6 +75,7 @@ export declare abstract class UmbContentDetailWorkspaceContextBase<DetailModelTy
|
|
|
75
75
|
* @internal
|
|
76
76
|
*/
|
|
77
77
|
readonly languages: Observable<UmbLanguageDetailModel[]>;
|
|
78
|
+
getLanguages(): Array<UmbLanguageDetailModel>;
|
|
78
79
|
protected readonly _segments: UmbArrayState<UmbSegmentModel, unknown>;
|
|
79
80
|
readonly variantOptions: Observable<VariantOptionModelType[]>;
|
|
80
81
|
protected _variantOptionsFilter: (variantOption: VariantOptionModelType) => boolean;
|
|
@@ -235,10 +236,5 @@ export declare abstract class UmbContentDetailWorkspaceContextBase<DetailModelTy
|
|
|
235
236
|
resetState(): void;
|
|
236
237
|
abstract getContentTypeUnique(): string | undefined;
|
|
237
238
|
abstract createPropertyDatasetContext(host: UmbControllerHost, variantId: UmbVariantId): UmbContentPropertyDatasetContext<DetailModelType, ContentTypeDetailModelType, VariantModelType>;
|
|
238
|
-
/**
|
|
239
|
-
* Override reload to process incoming data through the value migration pipeline.
|
|
240
|
-
* This ensures property values are properly transformed when variation settings change.
|
|
241
|
-
*/
|
|
242
|
-
reload(): Promise<void>;
|
|
243
239
|
destroy(): void;
|
|
244
240
|
}
|
|
@@ -3,6 +3,7 @@ import { UmbContentWorkspaceDataManager } from '../manager/index.js';
|
|
|
3
3
|
import { UmbMergeContentVariantDataController } from '../controller/merge-content-variant-data.controller.js';
|
|
4
4
|
import { UmbContentDetailValidationPathTranslator } from './content-detail-validation-path-translator.js';
|
|
5
5
|
import { UmbContentValidationToHintsManager } from './content-validation-to-hints.manager.js';
|
|
6
|
+
import { UmbContentDetailWorkspaceTypeTransformController } from './content-detail-workspace-type-transform.controller.js';
|
|
6
7
|
import { appendToFrozenArray, mergeObservables, observeMultiple, UmbArrayState, } from '../../../../libs/observable-api/index.js';
|
|
7
8
|
import { firstValueFrom, map } from '../../../../external/rxjs/index.js';
|
|
8
9
|
import { umbOpenModal } from '../../../core/modal/index.js';
|
|
@@ -43,6 +44,9 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
43
44
|
// TODO: Optimize this so it uses either a App Language Context? [NL]
|
|
44
45
|
#languageRepository;
|
|
45
46
|
#languages;
|
|
47
|
+
getLanguages() {
|
|
48
|
+
return this.#languages.getValue();
|
|
49
|
+
}
|
|
46
50
|
#variantValidationContexts;
|
|
47
51
|
getVariantValidationContext(variantId) {
|
|
48
52
|
return this.#variantValidationContexts.find((x) => x.getVariantId()?.compare(variantId));
|
|
@@ -54,11 +58,6 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
54
58
|
#validationRepository;
|
|
55
59
|
#saveModalToken;
|
|
56
60
|
#contentTypePropertyName;
|
|
57
|
-
/**
|
|
58
|
-
* Cache of property variation settings to detect changes when structure is updated.
|
|
59
|
-
* Used to trigger value migration when properties change from invariant to variant or vice versa.
|
|
60
|
-
*/
|
|
61
|
-
#propertyVariationCache;
|
|
62
61
|
constructor(host, args) {
|
|
63
62
|
super(host, args);
|
|
64
63
|
this.IS_CONTENT_WORKSPACE_CONTEXT = true;
|
|
@@ -93,11 +92,6 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
93
92
|
this._variantOptionsFilter = (variantOption) => true;
|
|
94
93
|
this.#variantValidationContexts = [];
|
|
95
94
|
this.#serverValidation = new UmbServerModelValidatorContext(this);
|
|
96
|
-
/**
|
|
97
|
-
* Cache of property variation settings to detect changes when structure is updated.
|
|
98
|
-
* Used to trigger value migration when properties change from invariant to variant or vice versa.
|
|
99
|
-
*/
|
|
100
|
-
this.#propertyVariationCache = new Map();
|
|
101
95
|
this.finishPropertyValueChange = () => {
|
|
102
96
|
this._data.finishPropertyValueChange();
|
|
103
97
|
};
|
|
@@ -120,6 +114,7 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
120
114
|
this.varies = this.structure.ownerContentTypeObservablePart((x) => x ? x.variesByCulture || x.variesBySegment : undefined);
|
|
121
115
|
this.collection = new UmbContentCollectionManager(this, this.structure, args.collectionAlias);
|
|
122
116
|
new UmbContentValidationToHintsManager(this, this.structure, this.validationContext, this.view.hints);
|
|
117
|
+
new UmbContentDetailWorkspaceTypeTransformController(this);
|
|
123
118
|
this.variantOptions = mergeObservables([this.variesByCulture, this.variesBySegment, this.variants, this.languages, this._segments.asObservable()], ([variesByCulture, variesBySegment, variants, languages, segments]) => {
|
|
124
119
|
if ((variesByCulture || variesBySegment) === undefined) {
|
|
125
120
|
return [];
|
|
@@ -235,11 +230,6 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
235
230
|
this.observe(this.structure.contentTypeDataTypeUniques, (dataTypeUniques) => {
|
|
236
231
|
this.#dataTypeItemManager.setUniques(dataTypeUniques);
|
|
237
232
|
}, null);
|
|
238
|
-
// Observe property variation changes to trigger value migration when properties change
|
|
239
|
-
// from invariant to variant (or vice versa) via Infinite Editing
|
|
240
|
-
this.observe(this.structure.contentTypeProperties, (properties) => {
|
|
241
|
-
this.#handlePropertyVariationChanges(properties);
|
|
242
|
-
}, null);
|
|
243
233
|
this.loadLanguages();
|
|
244
234
|
}
|
|
245
235
|
async loadLanguages() {
|
|
@@ -804,61 +794,6 @@ export class UmbContentDetailWorkspaceContextBase extends UmbEntityDetailWorkspa
|
|
|
804
794
|
this.propertyViewGuard.fallbackToPermitted();
|
|
805
795
|
this.propertyWriteGuard.fallbackToPermitted();
|
|
806
796
|
}
|
|
807
|
-
/**
|
|
808
|
-
* Handles property variation changes when the document type is updated via Infinite Editing.
|
|
809
|
-
* When a property's variation setting changes (e.g., from shared/invariant to variant or vice versa),
|
|
810
|
-
* this method reloads the document to get properly migrated values from the server.
|
|
811
|
-
*/
|
|
812
|
-
#handlePropertyVariationChanges(properties) {
|
|
813
|
-
// Skip if no current data or if this is initial load
|
|
814
|
-
const currentData = this._data.getCurrent();
|
|
815
|
-
if (!currentData || this.#propertyVariationCache.size === 0) {
|
|
816
|
-
// Initial load - just populate the cache
|
|
817
|
-
for (const property of properties) {
|
|
818
|
-
this.#propertyVariationCache.set(property.alias, {
|
|
819
|
-
variesByCulture: property.variesByCulture,
|
|
820
|
-
variesBySegment: property.variesBySegment,
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
// Check for variation setting changes
|
|
826
|
-
let hasChanges = false;
|
|
827
|
-
for (const property of properties) {
|
|
828
|
-
const cached = this.#propertyVariationCache.get(property.alias);
|
|
829
|
-
if (cached) {
|
|
830
|
-
if (cached.variesByCulture !== property.variesByCulture ||
|
|
831
|
-
cached.variesBySegment !== property.variesBySegment) {
|
|
832
|
-
hasChanges = true;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
// Update cache
|
|
836
|
-
this.#propertyVariationCache.set(property.alias, {
|
|
837
|
-
variesByCulture: property.variesByCulture,
|
|
838
|
-
variesBySegment: property.variesBySegment,
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
// If variation settings changed, reload to get properly migrated values
|
|
842
|
-
if (hasChanges) {
|
|
843
|
-
this.reload();
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Override reload to process incoming data through the value migration pipeline.
|
|
848
|
-
* This ensures property values are properly transformed when variation settings change.
|
|
849
|
-
*/
|
|
850
|
-
async reload() {
|
|
851
|
-
const unique = this.getUnique();
|
|
852
|
-
if (!unique)
|
|
853
|
-
throw new Error('Unique is not set');
|
|
854
|
-
const { data } = await this._detailRepository.requestByUnique(unique);
|
|
855
|
-
if (data) {
|
|
856
|
-
// Process the data through _processIncomingData to handle value migration
|
|
857
|
-
const processedData = await this._processIncomingData(data);
|
|
858
|
-
this._data.setPersisted(processedData);
|
|
859
|
-
this._data.setCurrent(processedData);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
797
|
destroy() {
|
|
863
798
|
this.structure.destroy();
|
|
864
799
|
this.#languageRepository.destroy();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UmbContentDetailModel } from '../index.js';
|
|
2
|
+
import type { UmbContentDetailWorkspaceContextBase } from './content-detail-workspace-base.js';
|
|
3
|
+
import { UmbControllerBase } from '../../../../libs/class-api/index.js';
|
|
4
|
+
import type { UmbEntityVariantModel } from '../../../core/variant/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* @class UmbContentDetailWorkspaceTypeTransformController
|
|
7
|
+
* @description - Controller to handle content detail workspace type transformations, such as property variation changes.
|
|
8
|
+
*/
|
|
9
|
+
export declare class UmbContentDetailWorkspaceTypeTransformController<DetailModelType extends UmbContentDetailModel<UmbEntityVariantModel>> extends UmbControllerBase {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(host: UmbContentDetailWorkspaceContextBase<DetailModelType, any, any, any>);
|
|
12
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { UmbControllerBase } from '../../../../libs/class-api/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* @class UmbContentDetailWorkspaceTypeTransformController
|
|
4
|
+
* @description - Controller to handle content detail workspace type transformations, such as property variation changes.
|
|
5
|
+
*/
|
|
6
|
+
export class UmbContentDetailWorkspaceTypeTransformController extends UmbControllerBase {
|
|
7
|
+
#workspace;
|
|
8
|
+
// Current property types, currently used to detect variation changes:
|
|
9
|
+
#propertyTypes;
|
|
10
|
+
constructor(host) {
|
|
11
|
+
super(host);
|
|
12
|
+
this.#workspace = host;
|
|
13
|
+
// Observe property variation changes to trigger value migration when properties change
|
|
14
|
+
// from invariant to variant (or vice versa) via Infinite Editing
|
|
15
|
+
this.observe(host.structure.contentTypeProperties, (propertyTypes) => {
|
|
16
|
+
this.#handlePropertyTypeVariationChanges(this.#propertyTypes, propertyTypes);
|
|
17
|
+
this.#propertyTypes = propertyTypes;
|
|
18
|
+
}, null);
|
|
19
|
+
}
|
|
20
|
+
async #handlePropertyTypeVariationChanges(oldPropertyTypes, newPropertyTypes) {
|
|
21
|
+
if (!oldPropertyTypes || !newPropertyTypes) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Skip if no current data or if this is initial load
|
|
25
|
+
const currentData = this.#workspace.getData();
|
|
26
|
+
if (!currentData) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const defaultLanguage = this.#getDefaultLanguage();
|
|
30
|
+
// To spare a bit of energy we keep awareness of whether this brings any changes:
|
|
31
|
+
let hasChanges = false;
|
|
32
|
+
const values = currentData.values.map((v) => {
|
|
33
|
+
const transformation = this.#transformValueForVariationChange(v, oldPropertyTypes, newPropertyTypes, defaultLanguage);
|
|
34
|
+
if (transformation.changed) {
|
|
35
|
+
hasChanges = true;
|
|
36
|
+
}
|
|
37
|
+
return transformation.value;
|
|
38
|
+
});
|
|
39
|
+
if (hasChanges) {
|
|
40
|
+
this.#workspace.setData({ ...currentData, values });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
#getDefaultLanguage() {
|
|
44
|
+
const languages = this.#workspace.getLanguages();
|
|
45
|
+
const defaultLanguage = languages.find((lang) => lang.isDefault)?.unique;
|
|
46
|
+
if (!defaultLanguage) {
|
|
47
|
+
throw new Error('Default language not found');
|
|
48
|
+
}
|
|
49
|
+
return defaultLanguage;
|
|
50
|
+
}
|
|
51
|
+
#transformValueForVariationChange(value, oldPropertyTypes, newPropertyTypes, defaultLanguage) {
|
|
52
|
+
const oldType = oldPropertyTypes.find((p) => p.alias === value.alias);
|
|
53
|
+
const newType = newPropertyTypes.find((p) => p.alias === value.alias);
|
|
54
|
+
// If we cant find both, we do not dare changing anything. Notice a composition may not have been loaded yet.
|
|
55
|
+
if (!oldType || !newType) {
|
|
56
|
+
return { value, changed: false };
|
|
57
|
+
}
|
|
58
|
+
// If variation hasn't changed, return unchanged
|
|
59
|
+
if (oldType.variesByCulture === newType.variesByCulture) {
|
|
60
|
+
return { value, changed: false };
|
|
61
|
+
}
|
|
62
|
+
// Variation has changed, migrate the value
|
|
63
|
+
if (newType.variesByCulture) {
|
|
64
|
+
// If it now varies by culture, set to default language
|
|
65
|
+
return { value: { ...value, culture: defaultLanguage }, changed: true };
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// If it no longer varies by culture, set to invariant
|
|
69
|
+
return { value: { ...value, culture: null }, changed: true };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -72,42 +72,14 @@ export class UmbPropertyValuePresetVariantBuilderController extends UmbPropertyV
|
|
|
72
72
|
};
|
|
73
73
|
return args;
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Finds an existing value for a property, with fallback logic for variation setting changes.
|
|
77
|
-
* When a property changes from invariant to variant (or vice versa), the existing values
|
|
78
|
-
* may have a different culture/segment than what we're looking for. This method handles
|
|
79
|
-
* value migration by trying fallback lookups.
|
|
80
|
-
*/
|
|
81
75
|
#findExistingValue(alias, variantId) {
|
|
82
|
-
if (!this._existingValues)
|
|
76
|
+
if (!this._existingValues) {
|
|
83
77
|
return undefined;
|
|
84
|
-
|
|
78
|
+
}
|
|
85
79
|
const exactMatch = this._existingValues.find((x) => x.alias === alias && variantId.compare(x));
|
|
86
80
|
if (exactMatch) {
|
|
87
81
|
return exactMatch.value;
|
|
88
82
|
}
|
|
89
|
-
// No exact match - try fallback for variation setting changes
|
|
90
|
-
// Get all values for this property alias
|
|
91
|
-
const valuesForAlias = this._existingValues.filter((x) => x.alias === alias);
|
|
92
|
-
if (valuesForAlias.length === 0) {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
// Fallback 1: If looking for a culture-specific value, try to use the invariant value
|
|
96
|
-
// This handles: invariant → variant migration
|
|
97
|
-
if (variantId.culture !== null) {
|
|
98
|
-
const invariantValue = valuesForAlias.find((x) => x.culture === null && x.segment === variantId.segment);
|
|
99
|
-
if (invariantValue) {
|
|
100
|
-
return invariantValue.value;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// Fallback 2: If looking for an invariant value, try to use the first culture-specific value
|
|
104
|
-
// This handles: variant → invariant migration
|
|
105
|
-
if (variantId.culture === null) {
|
|
106
|
-
const anyVariantValue = valuesForAlias.find((x) => x.culture !== null && x.segment === variantId.segment);
|
|
107
|
-
if (anyVariantValue) {
|
|
108
|
-
return anyVariantValue.value;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
83
|
return undefined;
|
|
112
84
|
}
|
|
113
85
|
}
|
|
@@ -62,6 +62,11 @@ export declare abstract class UmbEntityDetailWorkspaceContextBase<DetailModelTyp
|
|
|
62
62
|
* @returns { DetailModelType | undefined } The entity context
|
|
63
63
|
*/
|
|
64
64
|
getData(): DetailModelType | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Get the current data
|
|
67
|
+
* @param {DetailModelType | undefined} data - New data of this workspace.
|
|
68
|
+
*/
|
|
69
|
+
setData(data: DetailModelType | undefined): void;
|
|
65
70
|
/**
|
|
66
71
|
* Get the persisted data
|
|
67
72
|
* @returns { DetailModelType | undefined } The persisted data
|
|
@@ -145,6 +145,13 @@ export class UmbEntityDetailWorkspaceContextBase extends UmbSubmittableWorkspace
|
|
|
145
145
|
getData() {
|
|
146
146
|
return this._data.getCurrent();
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Get the current data
|
|
150
|
+
* @param {DetailModelType | undefined} data - New data of this workspace.
|
|
151
|
+
*/
|
|
152
|
+
setData(data) {
|
|
153
|
+
this._data.setCurrent(data);
|
|
154
|
+
}
|
|
148
155
|
/**
|
|
149
156
|
* Get the persisted data
|
|
150
157
|
* @returns { DetailModelType | undefined } The persisted data
|
|
@@ -231,7 +238,7 @@ export class UmbEntityDetailWorkspaceContextBase extends UmbSubmittableWorkspace
|
|
|
231
238
|
}
|
|
232
239
|
}
|
|
233
240
|
else if (data) {
|
|
234
|
-
const processedData = await this.
|
|
241
|
+
const processedData = await this._processIncomingData(data);
|
|
235
242
|
this._data.setPersisted(processedData);
|
|
236
243
|
this._data.setCurrent(processedData);
|
|
237
244
|
this.observe(asObservable?.(), (entity) => this.#onDetailStoreChange(entity), 'umbEntityDetailTypeStoreObserver');
|
|
@@ -249,8 +256,9 @@ export class UmbEntityDetailWorkspaceContextBase extends UmbSubmittableWorkspace
|
|
|
249
256
|
throw new Error('Unique is not set');
|
|
250
257
|
const { data } = await this._detailRepository.requestByUnique(unique);
|
|
251
258
|
if (data) {
|
|
252
|
-
this.
|
|
253
|
-
this._data.
|
|
259
|
+
const processedData = await this._processIncomingData(data);
|
|
260
|
+
this._data.setPersisted(processedData);
|
|
261
|
+
this._data.setCurrent(processedData);
|
|
254
262
|
}
|
|
255
263
|
}
|
|
256
264
|
/**
|