From 3adde7eac8d72ae9b3572c6ef1e1db7c2791461f Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Wed, 24 Jan 2024 16:40:16 -0300 Subject: [PATCH 01/17] create binary settings component --- .../dot-binary-settings.component.html | 1 + .../dot-binary-settings.component.scss | 0 .../dot-binary-settings.component.spec.ts | 22 +++++++++++++++++++ .../dot-binary-settings.component.ts | 12 ++++++++++ 4 files changed, 35 insertions(+) create mode 100644 core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html create mode 100644 core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss create mode 100644 core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts create mode 100644 core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html new file mode 100644 index 000000000000..cc3a32f893a3 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -0,0 +1 @@ +

dot-binary-settings works!

diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts new file mode 100644 index 000000000000..d8cff3a8bc04 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DotBinarySettingsComponent } from './dot-binary-settings.component'; + +describe('DotBinarySettingsComponent', () => { + let component: DotBinarySettingsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DotBinarySettingsComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(DotBinarySettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts new file mode 100644 index 000000000000..312fa3061993 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'dot-dot-binary-settings', + standalone: true, + imports: [CommonModule], + templateUrl: './dot-binary-settings.component.html', + styleUrl: './dot-binary-settings.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DotBinarySettingsComponent {} From 91be97603fdc82c6ea8d9983d53a386b36b95896 Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 10:39:50 -0300 Subject: [PATCH 02/17] make the formulary save and delete --- .../dot-binary-settings.component.html | 28 ++- .../dot-binary-settings.component.scss | 21 ++ .../dot-binary-settings.component.ts | 198 +++++++++++++++++- ...ntent-type-fields-drop-zone.component.html | 47 ++--- ...content-type-fields-drop-zone.component.ts | 14 +- .../dot-content-types-edit.module.ts | 2 + 6 files changed, 273 insertions(+), 37 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html index cc3a32f893a3..99fae8e27788 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -1 +1,27 @@ -

dot-binary-settings works!

+
+
+ + + + Link to documentation + +
+ @for( option of systemOptions; track option.key ) { + +
+

+ +
+ + } +
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss index e69de29bb2d1..e9f0c0351fff 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.scss @@ -0,0 +1,21 @@ +@use "variables" as *; + +.wrapper { + padding: $spacing-6; + + .field { + padding: 0 $spacing-1; + } +} + +.horizontal-field { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 $spacing-1; + + .info { + margin: 0; + line-height: 140%; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index 312fa3061993..1e508375a16f 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -1,12 +1,202 @@ +import { forkJoin, of } from 'rxjs'; + import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + inject +} from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { DividerModule } from 'primeng/divider'; +import { InputSwitchModule } from 'primeng/inputswitch'; +import { InputTextModule } from 'primeng/inputtext'; + +import { catchError, tap, take } from 'rxjs/operators'; + +import { DotDialogActions } from '@components/dot-dialog/dot-dialog.component'; +import { DotHttpErrorManagerService, DotMessageService } from '@dotcms/data-access'; +import { DotCMSContentTypeField, DotFieldVariable } from '@dotcms/dotcms-models'; + +import { DotFieldVariablesService } from '../fields/dot-content-type-fields-variables/services/dot-field-variables.service'; @Component({ - selector: 'dot-dot-binary-settings', + selector: 'dot-binary-settings', standalone: true, - imports: [CommonModule], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + InputTextModule, + InputSwitchModule, + DividerModule + ], templateUrl: './dot-binary-settings.component.html', styleUrl: './dot-binary-settings.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) -export class DotBinarySettingsComponent {} +export class DotBinarySettingsComponent implements OnInit, OnChanges { + @Input() field: DotCMSContentTypeField; + @Input() isVisible: boolean = false; + + @Output() changeControls = new EventEmitter(); + @Output() valid = new EventEmitter(); + @Output() save = new EventEmitter(); + + protected form: FormGroup; + + private fb: FormBuilder = inject(FormBuilder); + private fieldVariablesService = inject(DotFieldVariablesService); + private dotMessageService = inject(DotMessageService); + private dotHttpErrorManagerService = inject(DotHttpErrorManagerService); + + private settingsMap = { + accept: { + key: 'accept', + variable: null + }, + systemOptions: { + key: 'systemOptions', + variable: null + } + }; + + get settings() { + return Object.values(this.settingsMap); + } + + protected readonly systemOptions = [ + { + key: 'allowURLImport', + message: 'Allow users to create a file by importing from URL', + variable: null + }, + { + key: 'allowCodeWrite', + message: 'Allow users to create a file by writting code', + variable: null + }, + { + key: 'allowFileNameEdit', + message: 'Allow specifying the file name in the code editor', + variable: null + } + ]; + + ngOnChanges(changes: SimpleChanges) { + const { isVisible } = changes; + if (isVisible?.currentValue) { + this.changeControls.emit(this.dialogActions()); + } + } + + ngOnInit(): void { + this.form = this.fb.group({ + accept: '', + // This is an object called systemOptions, this is going to the backend as a field variable + allowURLImport: false, + allowCodeWrite: false, + allowFileNameEdit: false + }); + + this.form.valueChanges.subscribe(() => { + this.valid.emit(this.form.valid); + }); + + // I have to load here the variables, but first I need to be able to save field variables + this.fieldVariablesService + .load(this.field) + .subscribe((fieldVariables: DotFieldVariable[]) => { + fieldVariables.forEach((variable) => { + const { key, value } = variable; + + if (key === 'accept') { + this.settingsMap.accept.variable = variable; + this.form.get(key)?.setValue(value); + } else if (key === 'systemOptions') { + this.settingsMap.systemOptions.variable = variable; + const systemOptions = JSON.parse(value); + + this.systemOptions.forEach(({ key }) => { + this.form.get(key)?.setValue(systemOptions[key]); + }); + } + }); + }); + } + + saveSettings(): void { + const updateActions = this.settings.map(({ variable, key }) => { + let fieldVariable: DotFieldVariable; + let value: string | boolean; + + if (key === 'accept') { + value = this.form.get(key).value; + fieldVariable = { + ...variable, + key, + value + }; + } else if (key === 'systemOptions') { + value = JSON.stringify( + this.systemOptions.reduce((acc, { key }) => { + acc[key] = this.form.get(key).value; + + return acc; + }, {}) + ); + + fieldVariable = { + ...variable, + key, + value + }; + } + + // This is to prevent endpoints from breaking. + // fieldVariablesService.save -> breaks if there is not current value. + // fieldVariablesService.delete -> breaks if there is not previus exinting variable. + if (!value && !variable) { + return of({}); + } + + return ( + value + ? this.fieldVariablesService.save(this.field, fieldVariable) + : this.fieldVariablesService.delete(this.field, fieldVariable) + ).pipe(tap((variable) => (this.settingsMap[key].variable = variable))); // Update Variable Reference + }); + + forkJoin(updateActions) + .pipe( + take(1), + catchError((err: HttpErrorResponse) => + this.dotHttpErrorManagerService.handle(err).pipe(take(1)) + ) + ) + .subscribe((value: DotFieldVariable[]) => { + this.save.emit(value); + this.form.markAsPristine(); + }); + } + + private dialogActions() { + return { + accept: { + action: () => this.saveSettings(), + label: this.dotMessageService.get('contenttypes.dropzone.action.save'), + disabled: this.form.invalid || this.form.pristine + }, + cancel: { + label: this.dotMessageService.get('contenttypes.dropzone.action.cancel') + } + }; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.html index 9fea69abad9e..37451fbff25d 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.html @@ -3,16 +3,14 @@ class="content-type-fields-drop-zone__container" [dragulaModel]="fieldRows" [attr.disabled]="loading" - dragula="fields-row-bag" - > + dragula="fields-row-bag"> + (removeRow)="removeFieldRow($event, i)"> @@ -20,8 +18,7 @@ class="row-header__drag" [fieldTab]="row" (editTab)="saveFieldsHandler($event)" - (removeTab)="removeTab($event, i)" - > + (removeTab)="removeTab($event, i)"> @@ -37,8 +34,7 @@ [hideButtons]="hideButtons" [header]="currentFieldType?.label" (hide)="removeFieldsWithoutId()" - width="45rem" -> + width="45rem"> + (action)="scrollTo($event)">
+ (valid)="setDialogOkButtonState($event)"> + (convert)="convertWysiwygToBlock($event)">
- + @if(!!currentField?.id && isFieldWithSettings) { + + @switch ( this.currentFieldType?.clazz) { @case + ('com.dotcms.contenttype.model.field.ImmutableStoryBlockField') { + } @case("com.dotcms.contenttype.model.field.ImmutableBinaryField") { + + (changeControls)="changesDialogActions($event)"> + }} + } + [disabled]="!currentField?.id"> + [field]="currentField">
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.ts index 48c272274809..301ebfa7d242 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/content-type-fields-drop-zone/content-type-fields-drop-zone.component.ts @@ -106,11 +106,11 @@ export class ContentTypeFieldsDropZoneComponent implements OnInit, OnChanges, On } } - get isBlockEditorField() { - return ( - this.currentFieldType?.clazz === - 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField' - ); + get isFieldWithSettings() { + return [ + 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField', + 'com.dotcms.contenttype.model.field.ImmutableBinaryField' + ].includes(this.currentFieldType?.clazz); } private static findColumnBreakIndex(fields: DotCMSContentTypeField[]): number { @@ -400,7 +400,7 @@ export class ContentTypeFieldsDropZoneComponent implements OnInit, OnChanges, On this.hideButtons = index !== this.OVERVIEW_TAB_INDEX && - !(index === this.BLOCK_EDITOR_SETTINGS_TAB_INDEX && this.isBlockEditorField); + !(index === this.BLOCK_EDITOR_SETTINGS_TAB_INDEX && this.isFieldWithSettings); } /** @@ -433,7 +433,7 @@ export class ContentTypeFieldsDropZoneComponent implements OnInit, OnChanges, On this.currentFieldType = this.fieldPropertyService.getFieldType(this.currentField.clazz); } - private toggleDialog(): void { + protected toggleDialog(): void { this.dialogActions = this.defaultDialogActions; this.activeTab = this.OVERVIEW_TAB_INDEX; this.displayDialog = !this.displayDialog; diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/dot-content-types-edit.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/dot-content-types-edit.module.ts index 9a905d8533c2..c3ebf9682c3a 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/dot-content-types-edit.module.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/dot-content-types-edit.module.ts @@ -76,6 +76,7 @@ import { } from '@portlets/shared/dot-content-types-edit/components/fields/service'; import { DotDirectivesModule } from '@shared/dot-directives.module'; +import { DotBinarySettingsComponent } from './components/dot-binary-settings/dot-binary-settings.component'; import { DotBlockEditorSettingsComponent } from './components/dot-block-editor-settings/dot-block-editor-settings.component'; import { DotConvertToBlockInfoComponent } from './components/dot-convert-to-block-info/dot-convert-to-block-info.component'; import { DotConvertWysiwygToBlockComponent } from './components/dot-convert-wysiwyg-to-block/dot-convert-wysiwyg-to-block.component'; @@ -131,6 +132,7 @@ import { DotAddToMenuModule } from '../dot-content-types-listing/components/dot- DotSecondaryToolbarModule, DotFieldHelperModule, DotFieldValidationMessageComponent, + DotBinarySettingsComponent, TooltipModule, DotIconModule, DotMaxlengthModule, From 99c4e8a762515c960bb21cab1b0de6ceb09b53d0 Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 10:43:31 -0300 Subject: [PATCH 03/17] add variables in settings to blacklist --- .../dot-content-type-fields-variables.component.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/dot-content-type-fields-variables/dot-content-type-fields-variables.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/dot-content-type-fields-variables/dot-content-type-fields-variables.component.ts index 39b19c238ae6..3d29c3eff847 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/dot-content-type-fields-variables/dot-content-type-fields-variables.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/fields/dot-content-type-fields-variables/dot-content-type-fields-variables.component.ts @@ -25,6 +25,10 @@ export class DotContentTypeFieldsVariablesComponent implements OnChanges, OnDest 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField': { allowedBlocks: true // contentAssets: true + }, + 'com.dotcms.contenttype.model.field.ImmutableBinaryField': { + accept: true, + systemOptions: true } }; From d6facdb8f77a408e854254349b5e08cd698c4c7c Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 12:21:52 -0300 Subject: [PATCH 04/17] apply settings to binary field --- .../dot-binary-field-editor.component.html | 2 +- .../dot-binary-field-editor.component.ts | 1 + .../dot-edit-content-binary-field.component.html | 3 +++ .../dot-edit-content-binary-field.component.scss | 4 ++++ .../dot-edit-content-binary-field.component.ts | 13 +++++++++---- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html index 2e95391f9c8f..6a99e9155f58 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html @@ -1,5 +1,5 @@
-
+
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.ts index 9e5bd1ae121c..3c7b3ed7e3f0 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.ts @@ -72,6 +72,7 @@ const EDITOR_CONFIG: MonacoEditorConstructionOptions = { export class DotBinaryFieldEditorComponent implements OnInit { @Input() fileName = ''; @Input() fileContent = ''; + @Input() allowFileNameEdit = false; @Output() readonly tempFileUploaded = new EventEmitter(); @Output() readonly cancel = new EventEmitter(); diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.html index d4923df36672..7864b4ebd386 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.html +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.html @@ -36,12 +36,14 @@
void; private onTouched: () => void; + private tempId = ''; readonly dialogFullScreenStyles = { height: '90%', width: '90%' }; readonly dialogHeaderMap = { @@ -99,9 +100,11 @@ export class DotEditContentBinaryFieldComponent readonly BinaryFieldStatus = BinaryFieldStatus; readonly BinaryFieldMode = BinaryFieldMode; readonly vm$ = this.dotBinaryFieldStore.vm$; - private tempId = ''; + dialogOpen = false; + protected systemOptions: Record; + private get variable(): string { return this.field.variable; } @@ -158,6 +161,7 @@ export class DotEditContentBinaryFieldComponent ngAfterViewInit() { this.setFieldVariables(); + if (this.value) { this.dotBinaryFieldStore.setFileFromContentlet({ ...this.contentlet, @@ -283,10 +287,9 @@ export class DotEditContentBinaryFieldComponent * Handle file drop * * @param {DropZoneFileEvent} { validity, file } - * @return {*} * @memberof DotBinaryFieldComponent */ - handleFileDrop({ validity, file }: DropZoneFileEvent) { + handleFileDrop({ validity, file }: DropZoneFileEvent): void { if (!validity.valid) { this.handleFileDropError(validity); @@ -303,9 +306,11 @@ export class DotEditContentBinaryFieldComponent * @memberof DotBinaryFieldComponent */ private setFieldVariables() { - const { accept, maxFileSize = 0 } = this.getFieldVariables(); + const { accept, maxFileSize = 0, systemOptions = '{}' } = this.getFieldVariables(); this.DotBinaryFieldValidatorService.setAccept(accept ? accept.split(',') : []); this.DotBinaryFieldValidatorService.setMaxFileSize(Number(maxFileSize)); + + this.systemOptions = JSON.parse(systemOptions); } /** From a59b6e75819b959aec2c13a9c7f8ad7298ed7c0c Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 13:58:57 -0300 Subject: [PATCH 05/17] Update dot-edit-content-binary-field.component.scss --- .../dot-edit-content-binary-field.component.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.scss b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.scss index a9504a2ca831..24294d8c3f97 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.scss +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.scss @@ -16,6 +16,11 @@ padding: $spacing-1; height: 12.5rem; min-width: 12.5rem; + gap: $spacing-1; + + &:has(.binary-field__actions:empty) { + gap: 0; // Remove gap when there are no actions, is cleaner than removing the div by logic + } } .binary-field__container--uploading { @@ -45,7 +50,6 @@ .binary-field__drop-zone { border: $field-border-size dashed $input-border-color; border-radius: $border-radius-md; - margin-right: $spacing-1; height: 100%; flex: 1; overflow: auto; From b9c33b9f5da7dd7f458168edbcae0be76ce4f61a Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 15:27:40 -0300 Subject: [PATCH 06/17] change divider color --- .../dotcms-scss/angular/dotcms-theme/components/_divider.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-web/libs/dotcms-scss/angular/dotcms-theme/components/_divider.scss b/core-web/libs/dotcms-scss/angular/dotcms-theme/components/_divider.scss index f424cf0080f2..a8aecd79714a 100644 --- a/core-web/libs/dotcms-scss/angular/dotcms-theme/components/_divider.scss +++ b/core-web/libs/dotcms-scss/angular/dotcms-theme/components/_divider.scss @@ -5,5 +5,5 @@ } .p-divider.p-component { - color: $color-palette-gray-500; + color: $color-palette-gray-300; } From 88726a0d16ce10e0621e5d30975d42b8e312d57d Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 15:47:21 -0300 Subject: [PATCH 07/17] add test cases --- ...dit-content-binary-field.component.spec.ts | 55 ++++++++++++++++++- ...dot-edit-content-binary-field.component.ts | 1 + 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts index 18ba81a5061f..be59932c7cea 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts @@ -114,10 +114,31 @@ describe('DotEditContentBinaryFieldComponent', () => { }); beforeEach(() => { + const systemOptions = { + allowURLImport: true, + allowCodeWrite: true + }; + + const JSONString = JSON.stringify(systemOptions); + + const newField = { + ...FIELD, + fieldVariables: [ + ...FIELD.fieldVariables, + { + clazz: 'com.dotcms.contenttype.model.field.ImmutableFieldVariable', + fieldId: '5df3f8fc49177c195740bcdc02ec2db7', + id: '1ff1ff05-b9fb-4239-ad3d-b2cfaa9a8406', + key: 'systemOptions', + value: JSONString + } + ] + }; + spectator = createComponent({ detectChanges: false, props: { - field: FIELD, + field: newField, contentlet: null } }); @@ -329,6 +350,38 @@ describe('DotEditContentBinaryFieldComponent', () => { }); }); + describe('No systemOptions', () => { + beforeEach(() => { + spectator = createComponent({ + detectChanges: false, + props: { + field: FIELD, + contentlet: null + } + }); + }); + + it("shouldn't show url import button if not setted in settings", async () => { + store.setStatus(BinaryFieldStatus.INIT); + spectator.detectChanges(); + await spectator.fixture.whenStable(); + + const importFromURLButton = spectator.query(byTestId('action-url-btn')); + + expect(importFromURLButton).toBeNull(); + }); + + it("shouldn't show code editor button if not setted in settings", async () => { + store.setStatus(BinaryFieldStatus.INIT); + spectator.detectChanges(); + await spectator.fixture.whenStable(); + + const codeEditorButton = spectator.query(byTestId('action-editor-btn')); + + expect(codeEditorButton).toBeNull(); + }); + }); + describe('Dialog', () => { beforeEach(async () => { jest.spyOn(store, 'setFileFromContentlet').mockReturnValue(of(null).subscribe()); diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts index 2752cb914da7..366ec0ede37c 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts @@ -307,6 +307,7 @@ export class DotEditContentBinaryFieldComponent */ private setFieldVariables() { const { accept, maxFileSize = 0, systemOptions = '{}' } = this.getFieldVariables(); + this.DotBinaryFieldValidatorService.setAccept(accept ? accept.split(',') : []); this.DotBinaryFieldValidatorService.setMaxFileSize(Number(maxFileSize)); From 5aaa5b4ba05af65a5a1a9ec97c9cc83edc8cbb9b Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 15:48:32 -0300 Subject: [PATCH 08/17] clean tests --- .../dot-edit-content-binary-field.component.spec.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts index be59932c7cea..420cee475673 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts @@ -361,21 +361,13 @@ describe('DotEditContentBinaryFieldComponent', () => { }); }); - it("shouldn't show url import button if not setted in settings", async () => { - store.setStatus(BinaryFieldStatus.INIT); - spectator.detectChanges(); - await spectator.fixture.whenStable(); - + it("shouldn't show url import button if not setted in settings", () => { const importFromURLButton = spectator.query(byTestId('action-url-btn')); expect(importFromURLButton).toBeNull(); }); it("shouldn't show code editor button if not setted in settings", async () => { - store.setStatus(BinaryFieldStatus.INIT); - spectator.detectChanges(); - await spectator.fixture.whenStable(); - const codeEditorButton = spectator.query(byTestId('action-editor-btn')); expect(codeEditorButton).toBeNull(); From 3d7836d76db55545b4f3f5a827ffa33f96cedba6 Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 15:53:08 -0300 Subject: [PATCH 09/17] add test cases --- .../dot-binary-field-editor.component.html | 3 ++- .../dot-binary-field-editor.component.spec.ts | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html index 6a99e9155f58..352aba76bfaf 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/components/dot-binary-field-editor/dot-binary-field-editor.component.html @@ -10,7 +10,8 @@ formControlName="name" autocomplete="off" pInputText - placeholder="Ex. template.html" /> + placeholder="Ex. template.html" + data-testId="editor-file-name" />
{ beforeEach(() => { spectator = createComponent({ - detectChanges: false + detectChanges: false, + props: { + allowFileNameEdit: true + } }); component = spectator.component; @@ -115,13 +118,28 @@ describe('DotBinaryFieldEditorComponent', () => { expect(stopPropagationSpy).toHaveBeenCalled(); }); - describe('label', () => { + describe('input', () => { it('should set label and have css class required', () => { const label = spectator.query(byTestId('editor-label')); expect(label.innerHTML.trim()).toBe('File Name'); expect(label.className).toBe('p-label-input-required'); }); + + it('should show the file name editor', () => { + const input = spectator.query(byTestId('editor-file-name')); + + expect(input).not.toBeNull(); + }); + + it('should not show the file name editor', () => { + spectator.setInput('allowFileNameEdit', false); + spectator.detectChanges(); + + const input = spectator.query(byTestId('editor-file-name')); + + expect(input).toBeNull(); + }); }); describe('Editor', () => { From 23d1ce077078b59cab818b52154bd06f8dd5091c Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 16:33:19 -0300 Subject: [PATCH 10/17] add translations --- .../dot-binary-settings.component.html | 4 ++-- .../dot-binary-settings.component.ts | 11 ++++++----- .../main/webapp/WEB-INF/messages/Language.properties | 6 ++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html index 99fae8e27788..08c4ff38f238 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -1,6 +1,6 @@
- +
-

+

diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index 1e508375a16f..664fc8bfc9e7 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -24,6 +24,7 @@ import { catchError, tap, take } from 'rxjs/operators'; import { DotDialogActions } from '@components/dot-dialog/dot-dialog.component'; import { DotHttpErrorManagerService, DotMessageService } from '@dotcms/data-access'; import { DotCMSContentTypeField, DotFieldVariable } from '@dotcms/dotcms-models'; +import { DotMessagePipe } from '@dotcms/ui'; import { DotFieldVariablesService } from '../fields/dot-content-type-fields-variables/services/dot-field-variables.service'; @@ -36,7 +37,8 @@ import { DotFieldVariablesService } from '../fields/dot-content-type-fields-vari ReactiveFormsModule, InputTextModule, InputSwitchModule, - DividerModule + DividerModule, + DotMessagePipe ], templateUrl: './dot-binary-settings.component.html', styleUrl: './dot-binary-settings.component.scss', @@ -75,17 +77,17 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { protected readonly systemOptions = [ { key: 'allowURLImport', - message: 'Allow users to create a file by importing from URL', + message: 'binary-field.settings.system.options.allow.url.import', variable: null }, { key: 'allowCodeWrite', - message: 'Allow users to create a file by writting code', + message: 'binary-field.settings.system.options.allow.code.write', variable: null }, { key: 'allowFileNameEdit', - message: 'Allow specifying the file name in the code editor', + message: 'binary-field.settings.system.options.allow.file.name.edit', variable: null } ]; @@ -110,7 +112,6 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { this.valid.emit(this.form.valid); }); - // I have to load here the variables, but first I need to be able to save field variables this.fieldVariablesService .load(this.field) .subscribe((fieldVariables: DotFieldVariable[]) => { diff --git a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties index 0c2b30b3e035..954f13f7f6aa 100644 --- a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties +++ b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties @@ -5615,3 +5615,9 @@ block-editor.extension.ai-image.api-error.no-choice-returned=No choices returned editpage.language-change-missing-lang-populate.confirm.header=Create New Language Version? editpage.language-change-missing-lang-populate.confirm.message=Page does not exist in the selected language. Create a new version in {0}? + + +binary-field.settings.allow.type=Allowed File Type +binary-field.settings.system.options.allow.url.import=Allow users to create a file by importing from URL +binary-field.settings.system.options.allow.code.write=Allow users to create a file by writting code +binary-field.settings.system.options.allow.file.name.edit=Allow specifying the file name in the code editor \ No newline at end of file From 8886f322bc859d84903ceb0af330c7b1eed65b8b Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 16:36:55 -0300 Subject: [PATCH 11/17] fix sonarq --- .../dot-binary-settings.component.html | 5 ++++- .../dot-binary-settings.component.ts | 12 ++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html index 08c4ff38f238..d078c8dbc120 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -8,7 +8,10 @@ placeholder="ex: images/*" pInputText /> - Link to documentation diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index 664fc8bfc9e7..d5aaaa3cc7b8 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -183,20 +183,20 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { ) ) .subscribe((value: DotFieldVariable[]) => { - this.save.emit(value); this.form.markAsPristine(); + this.save.emit(value); }); } private dialogActions() { return { - accept: { - action: () => this.saveSettings(), - label: this.dotMessageService.get('contenttypes.dropzone.action.save'), - disabled: this.form.invalid || this.form.pristine - }, cancel: { label: this.dotMessageService.get('contenttypes.dropzone.action.cancel') + }, + accept: { + action: () => this.saveSettings(), + disabled: this.form.invalid || this.form.pristine, + label: this.dotMessageService.get('contenttypes.dropzone.action.save') } }; } From 885d3be6e4a42c43a100904de5b23b28b9ec42c3 Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 17:42:44 -0300 Subject: [PATCH 12/17] add test cases --- .../dot-binary-settings.component.spec.ts | 171 +++++++++++++++++- .../dot-binary-settings.component.ts | 17 +- 2 files changed, 168 insertions(+), 20 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts index d8cff3a8bc04..fb30505874ca 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts @@ -1,22 +1,173 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Spectator, SpyObject, createComponentFactory } from '@ngneat/spectator'; +import { of, throwError } from 'rxjs'; + +import { NgFor } from '@angular/common'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { DividerModule } from 'primeng/divider'; +import { InputSwitchModule } from 'primeng/inputswitch'; +import { InputTextModule } from 'primeng/inputtext'; + +import { DotMessageService, DotHttpErrorManagerService } from '@dotcms/data-access'; +import { DotMessagePipe } from '@dotcms/ui'; +import { MockDotMessageService, mockFieldVariables } from '@dotcms/utils-testing'; import { DotBinarySettingsComponent } from './dot-binary-settings.component'; +import { DotFieldVariablesService } from '../fields/dot-content-type-fields-variables/services/dot-field-variables.service'; + +const messageServiceMock = new MockDotMessageService({ + 'contenttypes.dropzone.action.save': 'Save', + 'contenttypes.dropzone.action.cancel': 'Cancel' +}); + +const systemOptions = JSON.stringify({ + allowURLImport: false, + allowFileNameEdit: false, + allowCodeWrite: true +}); + describe('DotBinarySettingsComponent', () => { + let spectator: Spectator; let component: DotBinarySettingsComponent; - let fixture: ComponentFixture; + let dotFieldVariableService: SpyObject; + let dotHttpErrorManagerService: SpyObject; + + const createComponent = createComponentFactory({ + component: DotBinarySettingsComponent, + imports: [ + NgFor, + FormsModule, + ReactiveFormsModule, + InputTextModule, + InputSwitchModule, + DividerModule, + DotMessagePipe + ], + providers: [ + FormBuilder, + { + provide: DotFieldVariablesService, + useValue: { + load: () => + of([ + { + clazz: 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField', + fieldId: 'f965a51b-130a-435f-b646-41e07d685363', + id: '9671d2c3-793b-41af-a485-e2c5fcba5fb', + key: 'systemOptions', + value: systemOptions + }, + { + clazz: 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField', + fieldId: 'f965a51b-130a-435f-b646-41e07d685363', + id: '9671d2c3-793b-41af-a485-e2c5fcba5fb', + key: 'accept', + value: 'image/*' + } + ]), + save: () => of([]), + delete: () => of([]) + } + }, + { + provide: DotMessageService, + useValue: messageServiceMock + }, + { + provide: DotHttpErrorManagerService, + useValue: { + handle: () => of([]) + } + } + ] + }); beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DotBinarySettingsComponent] - }).compileComponents(); + spectator = createComponent({}); + dotFieldVariableService = spectator.inject(DotFieldVariablesService); + dotHttpErrorManagerService = spectator.inject(DotHttpErrorManagerService); + + component = spectator.component; + }); + + it('should setup form values', async () => { + expect(component.form.get('accept').value).toBe('image/*'); + expect(component.form.get('allowURLImport').value).toBe(false); + expect(component.form.get('allowCodeWrite').value).toBe(true); + expect(component.form.get('allowFileNameEdit').value).toBe(false); + }); + + it('should emit changeControls when isVisible input is true', () => { + spyOn(component.changeControls, 'emit'); + + spectator.setInput('isVisible', true); + + expect(component.changeControls.emit).toHaveBeenCalled(); + }); + + it('should emit valid output on form change', () => { + spyOn(component.valid, 'emit'); + + component.form.get('accept').setValue('text/*'); - fixture = TestBed.createComponent(DotBinarySettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + expect(component.valid.emit).toHaveBeenCalled(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should save properties on saveSettings', () => { + spyOn(dotFieldVariableService, 'save').and.returnValue(of(mockFieldVariables[0])); + spyOn(component.save, 'emit'); + + component.saveSettings(); + + expect(dotFieldVariableService.save).toHaveBeenCalledTimes(2); // One for accept and one for systemOptions + expect(component.save.emit).toHaveBeenCalled(); + expect(component.settingsMap['accept'].variable).toEqual(mockFieldVariables[0]); + }); + + it('should delete properties on saveSettings when is empty', () => { + spyOn(dotFieldVariableService, 'delete').and.returnValue(of(mockFieldVariables[0])); + spyOn(component.save, 'emit'); + + component.form.get('accept').setValue(''); + component.saveSettings(); + + expect(dotFieldVariableService.delete).toHaveBeenCalled(); + expect(component.save.emit).toHaveBeenCalled(); + expect(component.settingsMap['accept'].variable).toEqual(mockFieldVariables[0]); + }); + + it('should handler error if save properties failed', () => { + spyOn(dotFieldVariableService, 'save').and.returnValue(throwError({})); + spyOn(dotHttpErrorManagerService, 'handle').and.returnValue(of()); + spyOn(component.save, 'emit'); + + component.saveSettings(); + + expect(dotHttpErrorManagerService.handle).toHaveBeenCalledTimes(1); + expect(component.save.emit).not.toHaveBeenCalled(); + }); + it('should not call save or delete when is empty and not previous variable exist', () => { + spyOn(dotFieldVariableService, 'load'); + spyOn(dotFieldVariableService, 'delete').and.returnValue(of([])); + spyOn(dotFieldVariableService, 'save').and.returnValue(of([])); + + // Couldn't find a way to force a no variable from load method + component.settingsMap = { + accept: { + key: 'accept', + variable: null + }, + systemOptions: { + key: 'systemOptions', + variable: null + } + }; + + component.form.get('accept').setValue(''); + component.saveSettings(); + + expect(dotFieldVariableService.delete).not.toHaveBeenCalled(); + expect(dotFieldVariableService.save).not.toHaveBeenCalledTimes(2); // One for accept and one for systemOptions, accept should not call save or delete }); }); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index d5aaaa3cc7b8..865676a3fbb0 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -1,6 +1,6 @@ import { forkJoin, of } from 'rxjs'; -import { CommonModule } from '@angular/common'; +import { NgFor } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; import { ChangeDetectionStrategy, @@ -32,7 +32,7 @@ import { DotFieldVariablesService } from '../fields/dot-content-type-fields-vari selector: 'dot-binary-settings', standalone: true, imports: [ - CommonModule, + NgFor, FormsModule, ReactiveFormsModule, InputTextModule, @@ -52,14 +52,14 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { @Output() valid = new EventEmitter(); @Output() save = new EventEmitter(); - protected form: FormGroup; + form: FormGroup; private fb: FormBuilder = inject(FormBuilder); private fieldVariablesService = inject(DotFieldVariablesService); private dotMessageService = inject(DotMessageService); private dotHttpErrorManagerService = inject(DotHttpErrorManagerService); - private settingsMap = { + settingsMap = { accept: { key: 'accept', variable: null @@ -77,18 +77,15 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { protected readonly systemOptions = [ { key: 'allowURLImport', - message: 'binary-field.settings.system.options.allow.url.import', - variable: null + message: 'binary-field.settings.system.options.allow.url.import' }, { key: 'allowCodeWrite', - message: 'binary-field.settings.system.options.allow.code.write', - variable: null + message: 'binary-field.settings.system.options.allow.code.write' }, { key: 'allowFileNameEdit', - message: 'binary-field.settings.system.options.allow.file.name.edit', - variable: null + message: 'binary-field.settings.system.options.allow.file.name.edit' } ]; From 13302279a3b729cf7766ebb861debc582d0f8d40 Mon Sep 17 00:00:00 2001 From: Jalinson Diaz Date: Fri, 26 Jan 2024 17:54:23 -0300 Subject: [PATCH 13/17] add test cases --- .../dot-binary-settings.component.html | 5 ++-- .../dot-binary-settings.component.spec.ts | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html index d078c8dbc120..2449be39ab99 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -6,7 +6,8 @@ [style]="{ width: '100%' }" formControlName="accept" placeholder="ex: images/*" - pInputText /> + pInputText + data-testId="setting-accept" />

- +
} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts index fb30505874ca..9955fe19a558 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts @@ -1,4 +1,4 @@ -import { Spectator, SpyObject, createComponentFactory } from '@ngneat/spectator'; +import { Spectator, SpyObject, byTestId, createComponentFactory } from '@ngneat/spectator'; import { of, throwError } from 'rxjs'; import { NgFor } from '@angular/common'; @@ -170,4 +170,27 @@ describe('DotBinarySettingsComponent', () => { expect(dotFieldVariableService.delete).not.toHaveBeenCalled(); expect(dotFieldVariableService.save).not.toHaveBeenCalledTimes(2); // One for accept and one for systemOptions, accept should not call save or delete }); + + it('should have 3 switches with the corresponding control name', () => { + const switches = spectator.queryAll(byTestId('setting-switch')); + + expect(switches.length).toBe(3); + expect( + switches.find((s) => s.getAttribute('ng-reflect-name') === 'allowURLImport') + ).not.toBeNull(); + expect( + switches.find((s) => s.getAttribute('ng-reflect-name') === 'allowCodeWrite') + ).not.toBeNull(); + expect( + switches.find((s) => s.getAttribute('ng-reflect-name') === 'allowFileNameEdit') + ).not.toBeNull(); + }); + + it('should have 1 input with the control name accept', () => { + const [acceptInput] = spectator.queryAll(byTestId('setting-accept')); + + expect(acceptInput).not.toBeNull(); + + expect(acceptInput.getAttribute('ng-reflect-name')).toBe('accept'); + }); }); From 353a55b1c2acaf44454b5f1d4605e08899b48597 Mon Sep 17 00:00:00 2001 From: KevinDavilaDotCMS <144152756+KevinDavilaDotCMS@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:42:14 -0500 Subject: [PATCH 14/17] Refactor, make PR suggestions --- .../dot-binary-settings.component.html | 6 +- .../dot-binary-settings.component.spec.ts | 54 ++------- .../dot-binary-settings.component.ts | 103 +++++++----------- .../WEB-INF/messages/Language.properties | 3 +- 4 files changed, 55 insertions(+), 111 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html index 2449be39ab99..b3ade5bee5fc 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.html @@ -5,7 +5,7 @@ id="allowed-file-type" [style]="{ width: '100%' }" formControlName="accept" - placeholder="ex: images/*" + placeholder="ex: image/*" pInputText data-testId="setting-accept" /> @@ -13,7 +13,7 @@ href="https://www.dotcms.com/docs/latest/field-variables#BinaryField" target="_blank" rel="noopener" - >Link to documentation
{{ 'binary-field-settings.system.link.to.documentation' | dm }}
@@ -22,7 +22,7 @@ [style]="{ margin: '1rem 0' }"> -
+

diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts index 9955fe19a558..72128a28f4f6 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.spec.ts @@ -10,7 +10,7 @@ import { InputTextModule } from 'primeng/inputtext'; import { DotMessageService, DotHttpErrorManagerService } from '@dotcms/data-access'; import { DotMessagePipe } from '@dotcms/ui'; -import { MockDotMessageService, mockFieldVariables } from '@dotcms/utils-testing'; +import { MockDotMessageService } from '@dotcms/utils-testing'; import { DotBinarySettingsComponent } from './dot-binary-settings.component'; @@ -21,7 +21,7 @@ const messageServiceMock = new MockDotMessageService({ 'contenttypes.dropzone.action.cancel': 'Cancel' }); -const systemOptions = JSON.stringify({ +const SYSTEM_OPTIONS = JSON.stringify({ allowURLImport: false, allowFileNameEdit: false, allowCodeWrite: true @@ -56,7 +56,7 @@ describe('DotBinarySettingsComponent', () => { fieldId: 'f965a51b-130a-435f-b646-41e07d685363', id: '9671d2c3-793b-41af-a485-e2c5fcba5fb', key: 'systemOptions', - value: systemOptions + value: SYSTEM_OPTIONS }, { clazz: 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField', @@ -91,11 +91,13 @@ describe('DotBinarySettingsComponent', () => { component = spectator.component; }); - it('should setup form values', async () => { + it('should setup form values', () => { expect(component.form.get('accept').value).toBe('image/*'); - expect(component.form.get('allowURLImport').value).toBe(false); - expect(component.form.get('allowCodeWrite').value).toBe(true); - expect(component.form.get('allowFileNameEdit').value).toBe(false); + expect(component.form.get('systemOptions').value).toEqual({ + allowURLImport: false, + allowCodeWrite: true, + allowFileNameEdit: false + }); }); it('should emit changeControls when isVisible input is true', () => { @@ -109,34 +111,12 @@ describe('DotBinarySettingsComponent', () => { it('should emit valid output on form change', () => { spyOn(component.valid, 'emit'); - component.form.get('accept').setValue('text/*'); + const acceptInput = spectator.query(byTestId('setting-accept')); + spectator.typeInElement('text/*', acceptInput); expect(component.valid.emit).toHaveBeenCalled(); }); - it('should save properties on saveSettings', () => { - spyOn(dotFieldVariableService, 'save').and.returnValue(of(mockFieldVariables[0])); - spyOn(component.save, 'emit'); - - component.saveSettings(); - - expect(dotFieldVariableService.save).toHaveBeenCalledTimes(2); // One for accept and one for systemOptions - expect(component.save.emit).toHaveBeenCalled(); - expect(component.settingsMap['accept'].variable).toEqual(mockFieldVariables[0]); - }); - - it('should delete properties on saveSettings when is empty', () => { - spyOn(dotFieldVariableService, 'delete').and.returnValue(of(mockFieldVariables[0])); - spyOn(component.save, 'emit'); - - component.form.get('accept').setValue(''); - component.saveSettings(); - - expect(dotFieldVariableService.delete).toHaveBeenCalled(); - expect(component.save.emit).toHaveBeenCalled(); - expect(component.settingsMap['accept'].variable).toEqual(mockFieldVariables[0]); - }); - it('should handler error if save properties failed', () => { spyOn(dotFieldVariableService, 'save').and.returnValue(throwError({})); spyOn(dotHttpErrorManagerService, 'handle').and.returnValue(of()); @@ -152,18 +132,6 @@ describe('DotBinarySettingsComponent', () => { spyOn(dotFieldVariableService, 'delete').and.returnValue(of([])); spyOn(dotFieldVariableService, 'save').and.returnValue(of([])); - // Couldn't find a way to force a no variable from load method - component.settingsMap = { - accept: { - key: 'accept', - variable: null - }, - systemOptions: { - key: 'systemOptions', - variable: null - } - }; - component.form.get('accept').setValue(''); component.saveSettings(); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index 865676a3fbb0..678ee1b1e6cf 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -5,6 +5,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { ChangeDetectionStrategy, Component, + DestroyRef, EventEmitter, Input, OnChanges, @@ -13,13 +14,14 @@ import { SimpleChanges, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DividerModule } from 'primeng/divider'; import { InputSwitchModule } from 'primeng/inputswitch'; import { InputTextModule } from 'primeng/inputtext'; -import { catchError, tap, take } from 'rxjs/operators'; +import { catchError, take } from 'rxjs/operators'; import { DotDialogActions } from '@components/dot-dialog/dot-dialog.component'; import { DotHttpErrorManagerService, DotMessageService } from '@dotcms/data-access'; @@ -58,21 +60,7 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { private fieldVariablesService = inject(DotFieldVariablesService); private dotMessageService = inject(DotMessageService); private dotHttpErrorManagerService = inject(DotHttpErrorManagerService); - - settingsMap = { - accept: { - key: 'accept', - variable: null - }, - systemOptions: { - key: 'systemOptions', - variable: null - } - }; - - get settings() { - return Object.values(this.settingsMap); - } + private readonly destroyRef = inject(DestroyRef); protected readonly systemOptions = [ { @@ -99,77 +87,64 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { ngOnInit(): void { this.form = this.fb.group({ accept: '', - // This is an object called systemOptions, this is going to the backend as a field variable - allowURLImport: false, - allowCodeWrite: false, - allowFileNameEdit: false + systemOptions: this.fb.group({ + allowURLImport: false, + allowCodeWrite: false, + allowFileNameEdit: false + }) }); - this.form.valueChanges.subscribe(() => { + this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.valid.emit(this.form.valid); }); - this.fieldVariablesService - .load(this.field) - .subscribe((fieldVariables: DotFieldVariable[]) => { + this.fieldVariablesService.load(this.field).subscribe({ + next: (fieldVariables: DotFieldVariable[]) => { fieldVariables.forEach((variable) => { const { key, value } = variable; - - if (key === 'accept') { - this.settingsMap.accept.variable = variable; - this.form.get(key)?.setValue(value); - } else if (key === 'systemOptions') { - this.settingsMap.systemOptions.variable = variable; + const control = this.form.get(key); + if (control instanceof FormGroup) { const systemOptions = JSON.parse(value); this.systemOptions.forEach(({ key }) => { - this.form.get(key)?.setValue(systemOptions[key]); + control.get(key)?.setValue(systemOptions[key]); }); + } else { + control.setValue(value); } }); - }); + } + }); } saveSettings(): void { - const updateActions = this.settings.map(({ variable, key }) => { - let fieldVariable: DotFieldVariable; - let value: string | boolean; - - if (key === 'accept') { - value = this.form.get(key).value; - fieldVariable = { - ...variable, - key, - value - }; - } else if (key === 'systemOptions') { - value = JSON.stringify( - this.systemOptions.reduce((acc, { key }) => { - acc[key] = this.form.get(key).value; - - return acc; - }, {}) - ); - - fieldVariable = { - ...variable, - key, - value - }; - } + const updateActions = Object.keys(this.form.controls).map((key) => { + const control = this.form.get(key); + const value = + control instanceof FormGroup + ? JSON.stringify( + this.systemOptions.reduce((acc, { key }) => { + acc[key] = control.get(key).value; + + return acc; + }, {}) + ) + : control.value; + const fieldVariable: DotFieldVariable = { + key, + value + }; // This is to prevent endpoints from breaking. // fieldVariablesService.save -> breaks if there is not current value. // fieldVariablesService.delete -> breaks if there is not previus exinting variable. - if (!value && !variable) { + if (!value) { return of({}); } - return ( - value - ? this.fieldVariablesService.save(this.field, fieldVariable) - : this.fieldVariablesService.delete(this.field, fieldVariable) - ).pipe(tap((variable) => (this.settingsMap[key].variable = variable))); // Update Variable Reference + return value + ? this.fieldVariablesService.save(this.field, fieldVariable) + : this.fieldVariablesService.delete(this.field, fieldVariable); }); forkJoin(updateActions) diff --git a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties index 42502568756d..ca41507df3c5 100644 --- a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties +++ b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties @@ -5625,4 +5625,5 @@ editpage.language-change-missing-lang-populate.confirm.message=Page does not exi binary-field.settings.allow.type=Allowed File Type binary-field.settings.system.options.allow.url.import=Allow users to create a file by importing from URL binary-field.settings.system.options.allow.code.write=Allow users to create a file by writting code -binary-field.settings.system.options.allow.file.name.edit=Allow specifying the file name in the code editor \ No newline at end of file +binary-field.settings.system.options.allow.file.name.edit=Allow specifying the file name in the code editor +binary-field-settings.system.link.to.documentation=Link to documentation \ No newline at end of file From 8ead604091430fb14d15354b448bafc2dba4d89c Mon Sep 17 00:00:00 2001 From: KevinDavilaDotCMS <144152756+KevinDavilaDotCMS@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:42:03 -0500 Subject: [PATCH 15/17] Fixed behavior when dont have value --- .../dot-binary-settings/dot-binary-settings.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index d202a398aebc..6a1f83490305 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -1,4 +1,4 @@ -import { forkJoin } from 'rxjs'; +import { forkJoin, of } from 'rxjs'; import { NgFor } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; @@ -139,6 +139,10 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { value }; + if (!value) { + return of({}); + } + return ( value ? this.fieldVariablesService.save(this.field, fieldVariable) From 0d57259a93c226585827b4d2b553ab6bc7c17055 Mon Sep 17 00:00:00 2001 From: KevinDavilaDotCMS <144152756+KevinDavilaDotCMS@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:45:35 -0500 Subject: [PATCH 16/17] Fixed getter, missed on merge --- .../dot-edit-content-binary-field.component.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts index ee0cb3d9ad75..490f432840df 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts @@ -29,7 +29,6 @@ import { delay, filter, skip, tap } from 'rxjs/operators'; import { DotLicenseService, DotMessageService } from '@dotcms/data-access'; import { - DotCMSBaseTypesContentTypes, DotCMSContentlet, DotCMSContentTypeField, DotCMSContentTypeFieldVariable, @@ -124,10 +123,6 @@ export class DotEditContentBinaryFieldComponent protected systemOptions: Record; - private get variable(): string { - return this.field.variable; - } - constructor( private readonly dotBinaryFieldStore: DotBinaryFieldStore, private readonly dotMessageService: DotMessageService, @@ -155,11 +150,8 @@ export class DotEditContentBinaryFieldComponent return this.DotBinaryFieldValidatorService.accept; } - private get metaDataKey(): string { - const { baseType } = this.contentlet; - const isFileAsset = baseType === DotCMSBaseTypesContentTypes.FILEASSET; - - return isFileAsset ? 'metaData' : this.variable + 'MetaData'; + private get variable(): string { + return this.contentTypeField().variable; } ngOnInit() { From 156e728e56aef262174e6e3e585108f6d1e41a6e Mon Sep 17 00:00:00 2001 From: KevinDavilaDotCMS <144152756+KevinDavilaDotCMS@users.noreply.github.com> Date: Fri, 2 Feb 2024 12:55:10 -0500 Subject: [PATCH 17/17] Removed unnecesary code --- .../dot-binary-settings.component.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts index 6a1f83490305..f783e7dd60c1 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/shared/dot-content-types-edit/components/dot-binary-settings/dot-binary-settings.component.ts @@ -124,15 +124,7 @@ export class DotBinarySettingsComponent implements OnInit, OnChanges { const updateActions = Object.keys(this.form.controls).map((key) => { const control = this.form.get(key); const value = - control instanceof FormGroup - ? JSON.stringify( - this.systemOptions.reduce((acc, { key }) => { - acc[key] = control.get(key).value; - - return acc; - }, {}) - ) - : control.value; + control instanceof FormGroup ? JSON.stringify(control.value) : control.value; const fieldVariable: DotFieldVariable = { ...this.FIELD_VARIABLES[key], key,