From 686e6c9dbbbdd0290af405482538b06da0a7d0bf Mon Sep 17 00:00:00 2001
From: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com>
Date: Mon, 16 Oct 2023 23:29:19 +0200
Subject: [PATCH] [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
---
projects/aca-content/assets/i18n/en.json | 2 +-
.../library-metadata-form.component.html | 2 +-
.../library-metadata-form.component.spec.ts | 53 +++++++++++++++++++
.../library-metadata-form.component.ts | 42 +++++++++++++--
4 files changed, 92 insertions(+), 7 deletions(-)
diff --git a/projects/aca-content/assets/i18n/en.json b/projects/aca-content/assets/i18n/en.json
index bab98e67e3..a3a48e7ad3 100644
--- a/projects/aca-content/assets/i18n/en.json
+++ b/projects/aca-content/assets/i18n/en.json
@@ -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"
diff --git a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.html b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.html
index 6faceb3f67..2202212058 100644
--- a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.html
+++ b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.html
@@ -92,7 +92,7 @@
/>
{{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }}
- {{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }}
+ {{ titleErrorTranslationKey | translate }}
diff --git a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts
index 988f70d4be..80881c92ed 100644
--- a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts
+++ b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts
@@ -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');
@@ -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({
@@ -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');
+ });
});
diff --git a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts
index 0f14836eae..d443168966 100644
--- a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts
+++ b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts
@@ -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 {
@@ -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;
@@ -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)
});
@@ -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),
@@ -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()
+ })
+ );
}
}
@@ -182,7 +210,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
private findLibraryByTitle(libraryTitle: string): Observable {
return from(
this.queriesApi
- .findSites(libraryTitle, {
+ .findSites(libraryTitle.trim(), {
maxItems: 1,
fields: ['title']
})
@@ -199,4 +227,8 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
)
.subscribe(handle);
}
+
+ private validateEmptyName(control: FormControl): ValidationErrors {
+ return control.value.length && !control.value.trim() ? { empty: true } : null;
+ }
}