mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
[ACS-6085] user is not prevented from renaming library to name containing only spaces (#3476)
* ACS-6085 Prevent user from renaming library to name containing only spaces * ACS-6085 Trimmed value * ACS-6085 Unit tests for titleErrorTranslationKey and update function * ACS-6085 Unit tests * ACS-6085 Removed redundant code * ACS-6085 Set type for validateEmptyName function * ACS-6085 Rename error translation key for required error * ACS-6085 Empty commit
This commit is contained in:
@@ -488,7 +488,7 @@
|
|||||||
"CONFLICT": "This Library ID is already used. Check the trashcan.",
|
"CONFLICT": "This Library ID is already used. Check the trashcan.",
|
||||||
"ID_TOO_LONG": "Use 72 characters or less for the URL name",
|
"ID_TOO_LONG": "Use 72 characters or less for the URL name",
|
||||||
"DESCRIPTION_TOO_LONG": "Use 512 characters or less for description",
|
"DESCRIPTION_TOO_LONG": "Use 512 characters or less for description",
|
||||||
"TITLE_TOO_LONG": "Use 256 characters or less for title",
|
"TITLE_TOO_LONG_OR_MISSING": "Use 256 characters or less for title",
|
||||||
"ILLEGAL_CHARACTERS": "Use numbers and letters only",
|
"ILLEGAL_CHARACTERS": "Use numbers and letters only",
|
||||||
"ONLY_SPACES": "Library name can't contain only spaces",
|
"ONLY_SPACES": "Library name can't contain only spaces",
|
||||||
"LIBRARY_UPDATE_ERROR": "There was an error updating library properties"
|
"LIBRARY_UPDATE_ERROR": "There was an error updating library properties"
|
||||||
|
@@ -92,7 +92,7 @@
|
|||||||
/>
|
/>
|
||||||
<mat-hint *ngIf="libraryTitleExists">{{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }}</mat-hint>
|
<mat-hint *ngIf="libraryTitleExists">{{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }}</mat-hint>
|
||||||
<mat-error>
|
<mat-error>
|
||||||
{{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }}
|
{{ titleErrorTranslationKey | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
@@ -167,6 +167,21 @@ describe('LibraryMetadataFormComponent', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledWith(new UpdateLibraryAction(siteEntryModel));
|
expect(store.dispatch).toHaveBeenCalledWith(new UpdateLibraryAction(siteEntryModel));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update library node with trimmed title', () => {
|
||||||
|
component.node.entry.role = Site.RoleEnum.SiteManager;
|
||||||
|
siteEntryModel.title = ' some title ';
|
||||||
|
component.node.entry.title = siteEntryModel.title;
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.update();
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(
|
||||||
|
new UpdateLibraryAction({
|
||||||
|
...siteEntryModel,
|
||||||
|
title: siteEntryModel.title.trim()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should call markAsPristine on form when updating valid form and has permission to update', () => {
|
it('should call markAsPristine on form when updating valid form and has permission to update', () => {
|
||||||
component.node.entry.role = Site.RoleEnum.SiteManager;
|
component.node.entry.role = Site.RoleEnum.SiteManager;
|
||||||
spyOn(component.form, 'markAsPristine');
|
spyOn(component.form, 'markAsPristine');
|
||||||
@@ -264,6 +279,23 @@ describe('LibraryMetadataFormComponent', () => {
|
|||||||
expect(component.libraryTitleExists).toBe(true);
|
expect(component.libraryTitleExists).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should call findSites on queriesApi with trimmed title', fakeAsync(() => {
|
||||||
|
const title = ' test ';
|
||||||
|
spyOn(component.queriesApi, 'findSites').and.returnValue(
|
||||||
|
Promise.resolve({
|
||||||
|
list: { entries: [{ entry: { title } }] }
|
||||||
|
} as SitePaging)
|
||||||
|
);
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.form.controls.title.setValue(title);
|
||||||
|
tick(300);
|
||||||
|
expect(component.queriesApi.findSites).toHaveBeenCalledWith(title.trim(), {
|
||||||
|
maxItems: 1,
|
||||||
|
fields: ['title']
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should not warn if library name input is the same with library node data', fakeAsync(() => {
|
it('should not warn if library name input is the same with library node data', fakeAsync(() => {
|
||||||
spyOn(component['queriesApi'], 'findSites').and.returnValue(
|
spyOn(component['queriesApi'], 'findSites').and.returnValue(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@@ -293,4 +325,25 @@ describe('LibraryMetadataFormComponent', () => {
|
|||||||
tick(500);
|
tick(500);
|
||||||
expect(component.libraryTitleExists).toBe(false);
|
expect(component.libraryTitleExists).toBe(false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should set proper titleErrorTranslationKey when there is error for empty title', () => {
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.form.controls.title.setValue(' ');
|
||||||
|
expect(component.titleErrorTranslationKey).toBe('LIBRARY.ERRORS.ONLY_SPACES');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set proper titleErrorTranslationKey when there is error for too long title', () => {
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.form.controls.title.setValue('t'.repeat(257));
|
||||||
|
expect(component.titleErrorTranslationKey).toBe('LIBRARY.ERRORS.TITLE_TOO_LONG_OR_MISSING');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set proper titleErrorTranslationKey when there is error for missing title', () => {
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.form.controls.title.setValue('');
|
||||||
|
expect(component.titleErrorTranslationKey).toBe('LIBRARY.ERRORS.TITLE_TOO_LONG_OR_MISSING');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -23,7 +23,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { UntypedFormGroup, UntypedFormControl, Validators, FormGroupDirective, NgForm, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import {
|
||||||
|
UntypedFormGroup,
|
||||||
|
UntypedFormControl,
|
||||||
|
Validators,
|
||||||
|
FormGroupDirective,
|
||||||
|
NgForm,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormControl,
|
||||||
|
ValidationErrors
|
||||||
|
} from '@angular/forms';
|
||||||
import { QueriesApi, SiteEntry, SitePaging } from '@alfresco/js-api';
|
import { QueriesApi, SiteEntry, SitePaging } from '@alfresco/js-api';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import {
|
import {
|
||||||
@@ -77,11 +87,17 @@ export class InstantErrorStateMatcher implements ErrorStateMatcher {
|
|||||||
})
|
})
|
||||||
export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestroy {
|
export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
private _queriesApi: QueriesApi;
|
private _queriesApi: QueriesApi;
|
||||||
|
private _titleErrorTranslationKey: string;
|
||||||
|
|
||||||
get queriesApi(): QueriesApi {
|
get queriesApi(): QueriesApi {
|
||||||
this._queriesApi = this._queriesApi ?? new QueriesApi(this.alfrescoApiService.getInstance());
|
this._queriesApi = this._queriesApi ?? new QueriesApi(this.alfrescoApiService.getInstance());
|
||||||
return this._queriesApi;
|
return this._queriesApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get titleErrorTranslationKey(): string {
|
||||||
|
return this._titleErrorTranslationKey;
|
||||||
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
node: SiteEntry;
|
node: SiteEntry;
|
||||||
|
|
||||||
@@ -96,7 +112,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
|
|||||||
|
|
||||||
form: UntypedFormGroup = new UntypedFormGroup({
|
form: UntypedFormGroup = new UntypedFormGroup({
|
||||||
id: new UntypedFormControl({ value: '', disabled: true }),
|
id: new UntypedFormControl({ value: '', disabled: true }),
|
||||||
title: new UntypedFormControl({ value: '' }, [Validators.required, Validators.maxLength(256)]),
|
title: new UntypedFormControl({ value: '' }, [Validators.required, Validators.maxLength(256), this.validateEmptyName]),
|
||||||
description: new UntypedFormControl({ value: '' }, [Validators.maxLength(512)]),
|
description: new UntypedFormControl({ value: '' }, [Validators.maxLength(512)]),
|
||||||
visibility: new UntypedFormControl(this.libraryType[0].value)
|
visibility: new UntypedFormControl(this.libraryType[0].value)
|
||||||
});
|
});
|
||||||
@@ -124,7 +140,14 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.updateForm(this.node);
|
this.updateForm(this.node);
|
||||||
|
this.form.controls.title.statusChanges
|
||||||
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe(
|
||||||
|
() =>
|
||||||
|
(this._titleErrorTranslationKey = this.form.controls.title.errors?.empty
|
||||||
|
? 'LIBRARY.ERRORS.ONLY_SPACES'
|
||||||
|
: 'LIBRARY.ERRORS.TITLE_TOO_LONG_OR_MISSING')
|
||||||
|
);
|
||||||
this.form.controls['title'].valueChanges
|
this.form.controls['title'].valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
@@ -164,7 +187,12 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
|
|||||||
update() {
|
update() {
|
||||||
if (this.canUpdateLibrary && this.form.valid) {
|
if (this.canUpdateLibrary && this.form.valid) {
|
||||||
this.form.markAsPristine();
|
this.form.markAsPristine();
|
||||||
this.store.dispatch(new UpdateLibraryAction(this.form.value));
|
this.store.dispatch(
|
||||||
|
new UpdateLibraryAction({
|
||||||
|
...this.form.value,
|
||||||
|
title: this.form.value.title.trim()
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +210,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
|
|||||||
private findLibraryByTitle(libraryTitle: string): Observable<SitePaging | { list: { entries: any[] } }> {
|
private findLibraryByTitle(libraryTitle: string): Observable<SitePaging | { list: { entries: any[] } }> {
|
||||||
return from(
|
return from(
|
||||||
this.queriesApi
|
this.queriesApi
|
||||||
.findSites(libraryTitle, {
|
.findSites(libraryTitle.trim(), {
|
||||||
maxItems: 1,
|
maxItems: 1,
|
||||||
fields: ['title']
|
fields: ['title']
|
||||||
})
|
})
|
||||||
@@ -199,4 +227,8 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
|
|||||||
)
|
)
|
||||||
.subscribe(handle);
|
.subscribe(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateEmptyName(control: FormControl<string>): ValidationErrors {
|
||||||
|
return control.value.length && !control.value.trim() ? { empty: true } : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user