Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACS-6085] user is not prevented from renaming library to name containing only spaces #3476

2 changes: 1 addition & 1 deletion projects/aca-content/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@
"CONFLICT": "This Library ID is already used. Check the trashcan.",
"ID_TOO_LONG": "Use 72 characters or less for the URL name",
"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",
"ONLY_SPACES": "Library name can't contain only spaces",
"LIBRARY_UPDATE_ERROR": "There was an error updating library properties"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
/>
<mat-hint *ngIf="libraryTitleExists">{{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }}</mat-hint>
<mat-error>
{{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }}
{{ titleErrorTranslationKey | translate }}
</mat-error>
</mat-form-field>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ describe('LibraryMetadataFormComponent', () => {
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', () => {
component.node.entry.role = Site.RoleEnum.SiteManager;
spyOn(component.form, 'markAsPristine');
Expand Down Expand Up @@ -264,6 +279,23 @@ describe('LibraryMetadataFormComponent', () => {
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(() => {
spyOn(component['queriesApi'], 'findSites').and.returnValue(
Promise.resolve({
Expand Down Expand Up @@ -293,4 +325,25 @@ describe('LibraryMetadataFormComponent', () => {
tick(500);
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');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@
*/

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 { Store } from '@ngrx/store';
import {
Expand Down Expand Up @@ -77,11 +87,17 @@ export class InstantErrorStateMatcher implements ErrorStateMatcher {
})
export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestroy {
private _queriesApi: QueriesApi;
private _titleErrorTranslationKey: string;

get queriesApi(): QueriesApi {
this._queriesApi = this._queriesApi ?? new QueriesApi(this.alfrescoApiService.getInstance());
return this._queriesApi;
}

get titleErrorTranslationKey(): string {
return this._titleErrorTranslationKey;
}

@Input()
node: SiteEntry;

Expand All @@ -96,7 +112,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro

form: UntypedFormGroup = new UntypedFormGroup({
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)]),
visibility: new UntypedFormControl(this.libraryType[0].value)
});
Expand Down Expand Up @@ -124,7 +140,14 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro

ngOnInit() {
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
.pipe(
debounceTime(300),
Expand Down Expand Up @@ -164,7 +187,12 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
update() {
if (this.canUpdateLibrary && this.form.valid) {
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()
})
);
}
}

Expand All @@ -182,7 +210,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
private findLibraryByTitle(libraryTitle: string): Observable<SitePaging | { list: { entries: any[] } }> {
return from(
this.queriesApi
.findSites(libraryTitle, {
.findSites(libraryTitle.trim(), {
maxItems: 1,
fields: ['title']
})
Expand All @@ -199,4 +227,8 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
)
.subscribe(handle);
}

private validateEmptyName(control: FormControl<string>): ValidationErrors {
return control.value.length && !control.value.trim() ? { empty: true } : null;
}
}
Loading