From 71074b2bf1f69ba44b8493ac47cc9ca885ab0fed Mon Sep 17 00:00:00 2001 From: Cilibiu Bogdan Date: Wed, 9 Jan 2019 10:19:09 +0200 Subject: [PATCH] [ACA] Create Library - switch to ADF component (#893) * use adf component * fix component name locator --- .../dialog/create-library-dialog.ts | 2 +- src/app/app.module.ts | 5 +- src/app/dialogs/library/library.dialog.html | 89 ------ src/app/dialogs/library/library.dialog.scss | 29 -- .../dialogs/library/library.dialog.spec.ts | 248 ----------------- src/app/dialogs/library/library.dialog.ts | 262 ------------------ .../services/content-management.service.ts | 4 +- 7 files changed, 5 insertions(+), 634 deletions(-) delete mode 100644 src/app/dialogs/library/library.dialog.html delete mode 100644 src/app/dialogs/library/library.dialog.scss delete mode 100644 src/app/dialogs/library/library.dialog.spec.ts delete mode 100644 src/app/dialogs/library/library.dialog.ts diff --git a/e2e/components/dialog/create-library-dialog.ts b/e2e/components/dialog/create-library-dialog.ts index 074d5beeb..305ccdf70 100755 --- a/e2e/components/dialog/create-library-dialog.ts +++ b/e2e/components/dialog/create-library-dialog.ts @@ -30,7 +30,7 @@ import { Utils } from '../../utilities/utils'; export class CreateLibraryDialog extends Component { private static selectors = { - root: 'app-library-dialog', + root: 'adf-library-dialog', title: '.mat-dialog-title', nameInput: 'input[placeholder="Name" i]', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2de688991..98391279c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -39,6 +39,7 @@ import { TranslateLoaderService } from '@alfresco/adf-core'; import { + LibraryDialogComponent, ContentModule, CustomResourcesService } from '@alfresco/adf-content-services'; @@ -50,7 +51,6 @@ import { FilesComponent } from './components/files/files.component'; import { LibrariesComponent } from './components/libraries/libraries.component'; import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component'; import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog'; -import { LibraryDialogComponent } from './dialogs/library/library.dialog'; import { AppStoreModule } from './store/app-store.module'; import { MaterialModule } from './material.module'; @@ -118,8 +118,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; FilesComponent, LibrariesComponent, FavoriteLibrariesComponent, - NodeVersionsDialogComponent, - LibraryDialogComponent + NodeVersionsDialogComponent ], providers: [ { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, diff --git a/src/app/dialogs/library/library.dialog.html b/src/app/dialogs/library/library.dialog.html deleted file mode 100644 index 20437bcd7..000000000 --- a/src/app/dialogs/library/library.dialog.html +++ /dev/null @@ -1,89 +0,0 @@ -

{{ createTitle | translate }}

- - -
- - - - {{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }} - - - {{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }} - - - - {{ form.controls['title'].errors?.message | translate }} - - - - - - - - {{ form.controls['id'].errors?.message | translate }} - - - - {{ 'LIBRARY.ERRORS.ID_TOO_LONG' | translate }} - - - - - - - - {{ 'LIBRARY.ERRORS.DESCRIPTION_TOO_LONG' | translate }} - - - - - - {{ option.label | translate }} - - -
-
- - - - - - diff --git a/src/app/dialogs/library/library.dialog.scss b/src/app/dialogs/library/library.dialog.scss deleted file mode 100644 index 5c8abdba9..000000000 --- a/src/app/dialogs/library/library.dialog.scss +++ /dev/null @@ -1,29 +0,0 @@ -.app-library-dialog { - .mat-radio-group { - display: flex; - flex-direction: column; - margin: 0 0 20px 0; - } - - .mat-radio-group .mat-radio-button { - margin: 10px 0; - } - - .mat-form-field { - width: 100%; - } - - mat-form-field { - padding-top: 20px; - } - - .actions-buttons { - display: flex; - flex-direction: row; - justify-content: flex-end; - - .mat-button { - text-transform: uppercase; - } - } -} diff --git a/src/app/dialogs/library/library.dialog.spec.ts b/src/app/dialogs/library/library.dialog.spec.ts deleted file mode 100644 index 01808c145..000000000 --- a/src/app/dialogs/library/library.dialog.spec.ts +++ /dev/null @@ -1,248 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2018 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ReactiveFormsModule } from '@angular/forms'; -import { CoreModule } from '@alfresco/adf-core'; -import { LibraryDialogComponent } from './library.dialog'; -import { TestBed, fakeAsync, tick, flush } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; -import { - AlfrescoApiService, - AlfrescoApiServiceMock, - setupTestBed -} from '@alfresco/adf-core'; - -describe('LibraryDialogComponent', () => { - let fixture; - let component; - let alfrescoApi; - const dialogRef = { - close: jasmine.createSpy('close') - }; - - setupTestBed({ - imports: [NoopAnimationsModule, CoreModule, ReactiveFormsModule], - declarations: [LibraryDialogComponent], - providers: [ - { - provide: AlfrescoApiService, - useClass: AlfrescoApiServiceMock - }, - { provide: MatDialogRef, useValue: dialogRef } - ], - schemas: [NO_ERRORS_SCHEMA] - }); - - beforeEach(() => { - fixture = TestBed.createComponent(LibraryDialogComponent); - component = fixture.componentInstance; - alfrescoApi = TestBed.get(AlfrescoApiService); - - spyOn( - alfrescoApi.getInstance().core.queriesApi, - 'findSites' - ).and.returnValue( - Promise.resolve({ - list: { entries: [] } - }) - ); - }); - - it('should set library id automatically on title input', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('libraryTitle'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe('libraryTitle'); - })); - - it('should translate library title space character to dash for library id', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('library title'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe('library-title'); - })); - - it('should not translate library title if value is not a valid id', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('@@@####'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe(null); - })); - - it('should translate library title partially for library id', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('@@@####library'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe('library'); - })); - - it('should translate library title multiple space character to one dash for library id', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('library title'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe('library-title'); - })); - - it('should not change custom library id on title input', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.id.setValue('custom-id'); - component.form.controls.id.markAsDirty(); - tick(500); - flush(); - fixture.detectChanges(); - - component.form.controls.title.setValue('library title'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.value).toBe('custom-id'); - })); - - it('should invalidate form when library id already exists', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'getSite').and.returnValue(Promise.resolve()); - - fixture.detectChanges(); - component.form.controls.id.setValue('existingLibrary'); - tick(500); - flush(); - fixture.detectChanges(); - - expect(component.form.controls.id.errors).toEqual({ - message: 'LIBRARY.ERRORS.EXISTENT_SITE' - }); - expect(component.form.valid).toBe(false); - })); - - it('should create site when form is valid', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'createSite').and.returnValue( - Promise.resolve() - ); - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('library title'); - tick(500); - flush(); - fixture.detectChanges(); - - component.submit(); - fixture.detectChanges(); - flush(); - - expect(alfrescoApi.sitesApi.createSite).toHaveBeenCalledWith({ - id: 'library-title', - title: 'library title', - description: '', - visibility: 'PUBLIC' - }); - })); - - it('should not create site when form is invalid', fakeAsync(() => { - spyOn(alfrescoApi.sitesApi, 'createSite').and.returnValue( - Promise.resolve({}) - ); - spyOn(alfrescoApi.sitesApi, 'getSite').and.returnValue(Promise.resolve()); - - fixture.detectChanges(); - component.form.controls.title.setValue('existingLibrary'); - tick(500); - flush(); - fixture.detectChanges(); - - component.submit(); - fixture.detectChanges(); - flush(); - - expect(alfrescoApi.sitesApi.createSite).not.toHaveBeenCalled(); - })); - - it('should notify on 409 conflict error (might be in trash)', fakeAsync(() => { - const error = { message: '{ "error": { "statusCode": 409 } }' }; - spyOn(alfrescoApi.sitesApi, 'createSite').and.callFake(() => { - return new Promise((resolve, reject) => reject(error)); - }); - spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { - return new Promise((resolve, reject) => reject()); - }); - - fixture.detectChanges(); - component.form.controls.title.setValue('test'); - tick(500); - flush(); - fixture.detectChanges(); - - component.submit(); - fixture.detectChanges(); - flush(); - - expect(component.form.controls.id.errors).toEqual({ - message: 'LIBRARY.ERRORS.CONFLICT' - }); - })); -}); diff --git a/src/app/dialogs/library/library.dialog.ts b/src/app/dialogs/library/library.dialog.ts deleted file mode 100644 index 7bfcbf8e9..000000000 --- a/src/app/dialogs/library/library.dialog.ts +++ /dev/null @@ -1,262 +0,0 @@ -/*! - * @license - * Copyright 2016 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Observable, Subject, from } from 'rxjs'; -import { - Component, - OnInit, - Output, - EventEmitter, - OnDestroy, - ViewEncapsulation -} from '@angular/core'; -import { - FormBuilder, - FormGroup, - Validators, - FormControl, - AbstractControl -} from '@angular/forms'; -import { MatDialogRef } from '@angular/material'; -import { SiteBody, SiteEntry, SitePaging } from 'alfresco-js-api'; -import { AlfrescoApiService } from '@alfresco/adf-core'; -import { debounceTime, mergeMap, takeUntil } from 'rxjs/operators'; - -@Component({ - selector: 'app-library-dialog', - styleUrls: ['./library.dialog.scss'], - templateUrl: './library.dialog.html', - encapsulation: ViewEncapsulation.None, - host: { class: 'app-library-dialog' } -}) -export class LibraryDialogComponent implements OnInit, OnDestroy { - @Output() - error: EventEmitter = new EventEmitter(); - - @Output() - success: EventEmitter = new EventEmitter(); - - onDestroy$: Subject = new Subject(); - - createTitle = 'LIBRARY.DIALOG.CREATE_TITLE'; - libraryTitleExists = false; - form: FormGroup; - visibilityOption: any; - visibilityOptions = [ - { value: 'PUBLIC', label: 'LIBRARY.VISIBILITY.PUBLIC', disabled: false }, - { value: 'PRIVATE', label: 'LIBRARY.VISIBILITY.PRIVATE', disabled: false }, - { - value: 'MODERATED', - label: 'LIBRARY.VISIBILITY.MODERATED', - disabled: false - } - ]; - - constructor( - private alfrescoApiService: AlfrescoApiService, - private formBuilder: FormBuilder, - private dialog: MatDialogRef - ) {} - - ngOnInit() { - const validators = { - id: [ - Validators.required, - Validators.maxLength(72), - this.forbidSpecialCharacters - ], - title: [ - Validators.required, - this.forbidOnlySpaces, - Validators.maxLength(256) - ], - description: [Validators.maxLength(512)] - }; - - this.form = this.formBuilder.group({ - title: [null, validators.title], - id: [null, validators.id, this.createSiteIdValidator()], - description: ['', validators.description] - }); - - this.visibilityOption = this.visibilityOptions[0].value; - - this.form.controls['title'].valueChanges - .pipe( - debounceTime(300), - mergeMap(title => this.checkLibraryNameExists(title), title => title), - takeUntil(this.onDestroy$) - ) - .subscribe((title: string) => { - if (!this.form.controls['id'].dirty && this.canGenerateId(title)) { - this.form.patchValue({ id: this.sanitize(title.trim()) }); - this.form.controls['id'].markAsTouched(); - } - }); - } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - - get title(): string { - const { title } = this.form.value; - - return (title || '').trim(); - } - - get id(): string { - const { id } = this.form.value; - - return (id || '').trim(); - } - - get description(): string { - const { description } = this.form.value; - - return (description || '').trim(); - } - - get visibility(): string { - return this.visibilityOption || ''; - } - - submit() { - const { form, dialog } = this; - - if (!form.valid) { - return; - } - - this.create().subscribe( - (node: SiteEntry) => { - this.success.emit(node); - dialog.close(node); - }, - error => this.handleError(error) - ); - } - - visibilityChangeHandler(event) { - this.visibilityOption = event.value; - } - - private create(): Observable { - const { title, id, description, visibility } = this; - const siteBody = { - id, - title, - description, - visibility - }; - - return from(this.alfrescoApiService.sitesApi.createSite(siteBody)); - } - - private sanitize(input: string) { - return input.replace(/[\s\s]+/g, '-').replace(/[^A-Za-z0-9-]/g, ''); - } - - private canGenerateId(title) { - return Boolean(title.replace(/[^A-Za-z0-9-]/g, '').length); - } - - private handleError(error: any): any { - const { - error: { statusCode } - } = JSON.parse(error.message); - - if (statusCode === 409) { - this.form.controls['id'].setErrors({ - message: 'LIBRARY.ERRORS.CONFLICT' - }); - } - - return error; - } - - private async checkLibraryNameExists(libraryTitle: string) { - const { entries } = (await this.findLibraryByTitle(libraryTitle)).list; - - if (entries.length) { - this.libraryTitleExists = entries[0].entry.title === libraryTitle; - } else { - this.libraryTitleExists = false; - } - } - - private findLibraryByTitle(libraryTitle: string): Promise { - return this.alfrescoApiService - .getInstance() - .core.queriesApi.findSites(libraryTitle, { - maxItems: 1, - fields: ['title'] - }) - .catch(() => ({ list: { entries: [] } })); - } - - private forbidSpecialCharacters({ value }: FormControl) { - if (value === null || value.length === 0) { - return null; - } - - const validCharacters: RegExp = /[^A-Za-z0-9-]/; - const isValid: boolean = !validCharacters.test(value); - - return isValid - ? null - : { - message: 'LIBRARY.ERRORS.ILLEGAL_CHARACTERS' - }; - } - - private forbidOnlySpaces({ value }: FormControl) { - if (value === null || value.length === 0) { - return null; - } - - const isValid: boolean = !!(value || '').trim(); - - return isValid - ? null - : { - message: 'LIBRARY.ERRORS.ONLY_SPACES' - }; - } - - private createSiteIdValidator() { - let timer; - - return (control: AbstractControl) => { - if (timer) { - clearTimeout(timer); - } - - return new Promise(resolve => { - timer = setTimeout(() => { - return from( - this.alfrescoApiService.sitesApi.getSite(control.value) - ).subscribe( - () => resolve({ message: 'LIBRARY.ERRORS.EXISTENT_SITE' }), - () => resolve(null) - ); - }, 300); - }); - }; - } -} diff --git a/src/app/services/content-management.service.ts b/src/app/services/content-management.service.ts index 31c02520c..f8aa5a146 100644 --- a/src/app/services/content-management.service.ts +++ b/src/app/services/content-management.service.ts @@ -28,9 +28,9 @@ import { Injectable } from '@angular/core'; import { MatDialog, MatSnackBar } from '@angular/material'; import { FolderDialogComponent, - ConfirmDialogComponent + ConfirmDialogComponent, + LibraryDialogComponent } from '@alfresco/adf-content-services'; -import { LibraryDialogComponent } from '../dialogs/library/library.dialog'; import { SnackbarErrorAction, SnackbarInfoAction,