2019-02-12 01:18:43 +00:00

277 lines
6.8 KiB
TypeScript

/*!
* @license
* Copyright 2019 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 { SiteBodyCreate, SiteEntry, SitePaging } from '@alfresco/js-api';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { debounceTime, mergeMap, takeUntil } from 'rxjs/operators';
@Component({
selector: 'adf-library-dialog',
styleUrls: ['./library.dialog.scss'],
templateUrl: './library.dialog.html',
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-library-dialog' }
})
export class LibraryDialogComponent implements OnInit, OnDestroy {
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when the new library is created successfully. The
* event parameter is a SiteEntry object with the details of the
* newly-created library.
*/
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
onDestroy$: Subject<boolean> = new Subject<boolean>();
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<LibraryDialogComponent>
) {}
ngOnInit() {
const validators = {
id: [
Validators.required,
Validators.maxLength(72),
this.forbidSpecialCharacters
],
title: [
Validators.required,
this.forbidOnlySpaces,
Validators.minLength(2),
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<SiteEntry> {
const { title, id, description, visibility } = this;
const siteBody = <SiteBodyCreate> {
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) {
let entries = [];
try {
entries = (await this.findLibraryByTitle(libraryTitle)).list.entries;
} catch {
entries = [];
}
if (entries.length) {
this.libraryTitleExists = entries[0].entry.title.toLowerCase() === libraryTitle.toLowerCase();
} else {
this.libraryTitleExists = false;
}
}
private async findLibraryByTitle(libraryTitle: string): Promise<SitePaging> {
return this.alfrescoApiService
.getInstance()
.core.queriesApi.findSites(libraryTitle, {
maxItems: 1,
fields: ['title']
});
}
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);
});
};
}
}