Skip to content

Commit 419a8e1

Browse files
Feature: media server validation (#17591)
* implement validation for media and prepare for member * remove import * use repository response type --------- Co-authored-by: Mads Rasmussen <[email protected]>
1 parent e87d1fc commit 419a8e1

File tree

17 files changed

+235
-51
lines changed

17 files changed

+235
-51
lines changed

src/Umbraco.Web.UI.Client/src/packages/core/content/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export * from './controller/merge-content-variant-data.controller.js';
88
export * from './manager/index.js';
99
export * from './property-dataset-context/index.js';
1010
export * from './workspace/index.js';
11+
export type * from './repository/index.js';
1112
export type * from './types.js';
1213
export type * from './variant-picker/index.js';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { UmbContentDetailModel } from '../types.js';
2+
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
3+
import type { UmbRepositoryResponse } from '@umbraco-cms/backoffice/repository';
4+
5+
export interface UmbContentValidationRepository<DetailModelType extends UmbContentDetailModel> {
6+
validateCreate(model: DetailModelType, parentUnique: string | null): Promise<UmbRepositoryResponse<string>>;
7+
validateSave(model: DetailModelType, variantIds: Array<UmbVariantId>): Promise<UmbRepositoryResponse<string>>;
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type * from './content-validation-repository.interface.js';

src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { UmbContentWorkspaceDataManager } from '../manager/index.js';
33
import { UmbMergeContentVariantDataController } from '../controller/merge-content-variant-data.controller.js';
44
import type { UmbContentVariantPickerData, UmbContentVariantPickerValue } from '../variant-picker/index.js';
55
import type { UmbContentPropertyDatasetContext } from '../property-dataset-context/index.js';
6+
import type { UmbContentValidationRepository } from '../repository/content-validation-repository.interface.js';
67
import type { UmbContentWorkspaceContext } from './content-workspace-context.interface.js';
78
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
89
import type { UmbDetailRepository, UmbDetailRepositoryConstructor } from '@umbraco-cms/backoffice/repository';
@@ -33,6 +34,7 @@ import {
3334
UMB_VALIDATION_CONTEXT,
3435
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
3536
UmbDataPathVariantQuery,
37+
UmbServerModelValidatorContext,
3638
UmbValidationContext,
3739
UmbVariantsValidationPathTranslator,
3840
UmbVariantValuesValidationPathTranslator,
@@ -44,6 +46,7 @@ import {
4446
UmbRequestReloadChildrenOfEntityEvent,
4547
UmbRequestReloadStructureForEntityEvent,
4648
} from '@umbraco-cms/backoffice/entity-action';
49+
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
4750

4851
export interface UmbContentDetailWorkspaceContextArgs<
4952
DetailModelType extends UmbContentDetailModel<VariantModelType>,
@@ -54,6 +57,8 @@ export interface UmbContentDetailWorkspaceContextArgs<
5457
VariantOptionModelType extends UmbEntityVariantOptionModel = UmbEntityVariantOptionModel<VariantModelType>,
5558
> extends UmbEntityDetailWorkspaceContextArgs {
5659
contentTypeDetailRepository: UmbDetailRepositoryConstructor<ContentTypeDetailModelType>;
60+
contentValidationRepository?: ClassConstructor<UmbContentValidationRepository<DetailModelType>>;
61+
skipValidationOnSubmit?: boolean;
5762
contentVariantScaffold: VariantModelType;
5863
saveModalToken?: UmbModalToken<UmbContentVariantPickerData<VariantOptionModelType>, UmbContentVariantPickerValue>;
5964
}
@@ -118,6 +123,11 @@ export abstract class UmbContentDetailWorkspaceContextBase<
118123
// TODO: fix type error
119124
public readonly variantOptions;
120125

126+
#validateOnSubmit: boolean;
127+
#serverValidation = new UmbServerModelValidatorContext(this);
128+
#validationRepositoryClass?: ClassConstructor<UmbContentValidationRepository<DetailModelType>>;
129+
#validationRepository?: UmbContentValidationRepository<DetailModelType>;
130+
121131
#saveModalToken?: UmbModalToken<UmbContentVariantPickerData<VariantOptionModelType>, UmbContentVariantPickerValue>;
122132

123133
constructor(
@@ -135,6 +145,8 @@ export abstract class UmbContentDetailWorkspaceContextBase<
135145
this.#saveModalToken = args.saveModalToken;
136146

137147
const contentTypeDetailRepository = new args.contentTypeDetailRepository(this);
148+
this.#validationRepositoryClass = args.contentValidationRepository;
149+
this.#validateOnSubmit = args.skipValidationOnSubmit ? !args.skipValidationOnSubmit : true;
138150
this.structure = new UmbContentTypeStructureManager<ContentTypeDetailModelType>(this, contentTypeDetailRepository);
139151
this.variesByCulture = this.structure.ownerContentTypeObservablePart((x) => x?.variesByCulture);
140152
this.variesBySegment = this.structure.ownerContentTypeObservablePart((x) => x?.variesBySegment);
@@ -470,6 +482,36 @@ export abstract class UmbContentDetailWorkspaceContextBase<
470482
}
471483
}
472484

485+
protected async _askServerToValidate(saveData: DetailModelType, variantIds: Array<UmbVariantId>) {
486+
if (this.#validationRepositoryClass) {
487+
// Create the validation repository if it does not exist. (we first create this here when we need it) [NL]
488+
this.#validationRepository ??= new this.#validationRepositoryClass(this);
489+
490+
// We ask the server first to get a concatenated set of validation messages. So we see both front-end and back-end validation messages [NL]
491+
if (this.getIsNew()) {
492+
const parent = this.getParent();
493+
if (!parent) throw new Error('Parent is not set');
494+
await this.#serverValidation.askServerForValidation(
495+
saveData,
496+
this.#validationRepository.validateCreate(saveData, parent.unique),
497+
);
498+
} else {
499+
await this.#serverValidation.askServerForValidation(
500+
saveData,
501+
this.#validationRepository.validateSave(saveData, variantIds),
502+
);
503+
}
504+
}
505+
}
506+
507+
/**
508+
* Request a submit of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be submitted.
509+
* @returns {Promise<void>} a promise which resolves once it has been completed.
510+
*/
511+
public override requestSubmit() {
512+
return this._handleSubmit();
513+
}
514+
473515
public override submit() {
474516
return this._handleSubmit();
475517
}
@@ -513,7 +555,19 @@ export abstract class UmbContentDetailWorkspaceContextBase<
513555

514556
const saveData = await this._data.constructData(variantIds);
515557
await this._runMandatoryValidationForSaveData(saveData);
516-
await this._performCreateOrUpdate(variantIds, saveData);
558+
if (this.#validateOnSubmit) {
559+
await this._askServerToValidate(saveData, variantIds);
560+
return this.validateAndSubmit(
561+
async () => {
562+
return this._performCreateOrUpdate(variantIds, saveData);
563+
},
564+
async () => {
565+
return this.invalidSubmit();
566+
},
567+
);
568+
} else {
569+
await this._performCreateOrUpdate(variantIds, saveData);
570+
}
517571
}
518572

519573
protected async _performCreateOrUpdate(variantIds: Array<UmbVariantId>, saveData: DetailModelType) {

src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.repository.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import { UmbDocumentValidationServerDataSource } from './document-validation.ser
33
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
44
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
55
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
6+
import type { UmbContentValidationRepository } from '@umbraco-cms/backoffice/content';
67

78
type DetailModelType = UmbDocumentDetailModel;
89

9-
export class UmbDocumentValidationRepository extends UmbRepositoryBase {
10+
export class UmbDocumentValidationRepository
11+
extends UmbRepositoryBase
12+
implements UmbContentValidationRepository<DetailModelType>
13+
{
1014
#validationDataSource: UmbDocumentValidationServerDataSource;
1115

1216
constructor(host: UmbControllerHost) {

src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,10 @@ import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
1111

1212
/**
1313
* A server data source for Document Validation
14-
* @class UmbDocumentPublishingServerDataSource
15-
* @implements {DocumentTreeDataSource}
1614
*/
1715
export class UmbDocumentValidationServerDataSource {
1816
//#host: UmbControllerHost;
1917

20-
/**
21-
* Creates an instance of UmbDocumentPublishingServerDataSource.
22-
* @param {UmbControllerHost} host - The controller host for this controller to be appended to
23-
* @memberof UmbDocumentPublishingServerDataSource
24-
*/
2518
// TODO: [v15]: ignoring unused var here here to prevent a breaking change
2619
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2720
constructor(host: UmbControllerHost) {

src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import {
3333
UmbRequestReloadStructureForEntityEvent,
3434
} from '@umbraco-cms/backoffice/entity-action';
3535
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
36-
import { UmbServerModelValidatorContext } from '@umbraco-cms/backoffice/validation';
3736
import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint';
3837
import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification';
3938
import {
@@ -62,9 +61,6 @@ export class UmbDocumentWorkspaceContext
6261
{
6362
public readonly publishingRepository = new UmbDocumentPublishingRepository(this);
6463

65-
#serverValidation = new UmbServerModelValidatorContext(this);
66-
#validationRepository?: UmbDocumentValidationRepository;
67-
6864
readonly isTrashed = this._data.createObservablePartOfCurrent((data) => data?.isTrashed);
6965
readonly contentTypeUnique = this._data.createObservablePartOfCurrent((data) => data?.documentType.unique);
7066
readonly contentTypeHasCollection = this._data.createObservablePartOfCurrent(
@@ -81,6 +77,8 @@ export class UmbDocumentWorkspaceContext
8177
workspaceAlias: UMB_DOCUMENT_WORKSPACE_ALIAS,
8278
detailRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS,
8379
contentTypeDetailRepository: UmbDocumentTypeDetailRepository,
80+
contentValidationRepository: UmbDocumentValidationRepository,
81+
skipValidationOnSubmit: true,
8482
contentVariantScaffold: UMB_DOCUMENT_DETAIL_MODEL_VARIANT_SCAFFOLD,
8583
saveModalToken: UMB_DOCUMENT_SAVE_MODAL,
8684
});
@@ -206,19 +204,6 @@ export class UmbDocumentWorkspaceContext
206204
this._data.updateCurrent({ template: { unique: templateUnique } });
207205
}
208206

209-
/**
210-
* Request a submit of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be submitted.
211-
* @returns {Promise<void>} a promise which resolves once it has been completed.
212-
*/
213-
public override requestSubmit() {
214-
return this._handleSubmit();
215-
}
216-
217-
// Because we do not make validation prevent submission this also submits the workspace. [NL]
218-
public override invalidSubmit() {
219-
return this._handleSubmit();
220-
}
221-
222207
async #handleSaveAndPreview() {
223208
const unique = this.getUnique();
224209
if (!unique) throw new Error('Unique is missing');
@@ -287,23 +272,7 @@ export class UmbDocumentWorkspaceContext
287272
const saveData = await this._data.constructData(variantIds);
288273
await this._runMandatoryValidationForSaveData(saveData);
289274

290-
// Create the validation repository if it does not exist. (we first create this here when we need it) [NL]
291-
this.#validationRepository ??= new UmbDocumentValidationRepository(this);
292-
293-
// We ask the server first to get a concatenated set of validation messages. So we see both front-end and back-end validation messages [NL]
294-
if (this.getIsNew()) {
295-
const parent = this.getParent();
296-
if (!parent) throw new Error('Parent is not set');
297-
this.#serverValidation.askServerForValidation(
298-
saveData,
299-
this.#validationRepository.validateCreate(saveData, parent.unique),
300-
);
301-
} else {
302-
this.#serverValidation.askServerForValidation(
303-
saveData,
304-
this.#validationRepository.validateSave(saveData, variantIds),
305-
);
306-
}
275+
await this._askServerToValidate(saveData, variantIds);
307276

308277
// TODO: Only validate the specified selection.. [NL]
309278
return this.validateAndSubmit(

src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.repository.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import { UmbMediaValidationServerDataSource } from './media-validation.server.da
33
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
44
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
55
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
6+
import type { UmbContentValidationRepository } from '@umbraco-cms/backoffice/content';
67

78
type DetailModelType = UmbMediaDetailModel;
89

9-
export class UmbMediaValidationRepository extends UmbRepositoryBase {
10+
export class UmbMediaValidationRepository
11+
extends UmbRepositoryBase
12+
implements UmbContentValidationRepository<DetailModelType>
13+
{
1014
#validationDataSource: UmbMediaValidationServerDataSource;
1115

1216
constructor(host: UmbControllerHost) {

src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.server.data-source.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,11 @@ import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
1010
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
1111

1212
/**
13-
* A server data source for Media Validatiom
13+
* A server data source for Media Validation
1414
*/
1515
export class UmbMediaValidationServerDataSource {
1616
//#host: UmbControllerHost;
1717

18-
/**
19-
* Creates an instance of UmbDocumentPublishingServerDataSource.
20-
* @param {UmbControllerHost} host - The controller host for this controller to be appended to
21-
* @memberof UmbDocumentPublishingServerDataSource
22-
*/
2318
// TODO: [v15]: ignoring unused var here here to prevent a breaking change
2419
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2520
constructor(host: UmbControllerHost) {

src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js';
44
import { UMB_MEDIA_DETAIL_REPOSITORY_ALIAS } from '../constants.js';
55
import type { UmbMediaDetailModel, UmbMediaVariantModel } from '../types.js';
66
import { UMB_CREATE_MEDIA_WORKSPACE_PATH_PATTERN, UMB_EDIT_MEDIA_WORKSPACE_PATH_PATTERN } from '../paths.js';
7+
import { UmbMediaValidationRepository } from '../repository/validation/media-validation.repository.js';
78
import { UMB_MEDIA_COLLECTION_ALIAS } from '../collection/constants.js';
89
import type { UmbMediaDetailRepository } from '../repository/index.js';
910
import { UMB_MEDIA_WORKSPACE_ALIAS, UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD } from './constants.js';
@@ -48,6 +49,7 @@ export class UmbMediaWorkspaceContext
4849
workspaceAlias: UMB_MEDIA_WORKSPACE_ALIAS,
4950
detailRepositoryAlias: UMB_MEDIA_DETAIL_REPOSITORY_ALIAS,
5051
contentTypeDetailRepository: UmbMediaTypeDetailRepository,
52+
contentValidationRepository: UmbMediaValidationRepository,
5153
contentVariantScaffold: UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD,
5254
});
5355

0 commit comments

Comments
 (0)