diff --git a/docs/extending/application-actions.md b/docs/extending/application-actions.md
index 0d6108763..4e14b50b3 100644
--- a/docs/extending/application-actions.md
+++ b/docs/extending/application-actions.md
@@ -124,6 +124,6 @@ Below is the list of public actions types you can use in the plugin definitions
| 1.8.0 | VIEW_NODE | NodeId<`string`> , [ViewNodeExtras](../features/file-viewer.md#details)<`any`> | Lightweight preview of a node by id. Can be invoked from extensions. For details also see [File Viewer](../features/file-viewer.md#details) |
| 1.8.0 | CLOSE_PREVIEW | n/a | Closes the viewer ( preview of the item ) |
| 1.9.0 | RESET_SELECTION | n/a | Resets active document list selection |
-| 1.10.0 | FILE_FROM_TEMPLATE | n/a | Invoke dialogs flow for creating a file from selected template|
-| 1.10.0 | CREATE_FILE_FROM_TEMPLATE | Node | Copy selected template into current folder |
-| 1.10.0 | CONTEXT_MENU | MouseEvent | Invoke context menu for [DocumentListComponent](https://www.alfresco.com/abn/adf/docs/content-services/components/document-list.component) |
+| 1.10.0 | FILE_FROM_TEMPLATE | n/a | Invoke dialogs flow for creating a file from a template into current folder |
+| 1.10.0 | FOLDER_FROM_TEMPLATE | n/a | Invoke dialogs flow for creating a folder structure from a template into current folder |
+| 1.10.0 | CONTEXT_MENU | MouseEvent | Invoke context menu for [DocumentListComponent](https://www.alfresco.com/abn/adf/docs/content-services/components/document-list.component) |
diff --git a/e2e/components/dialog/create-from-template-dialog.ts b/e2e/components/dialog/create-from-template-dialog.ts
index 66df52621..c98b245b5 100755
--- a/e2e/components/dialog/create-from-template-dialog.ts
+++ b/e2e/components/dialog/create-from-template-dialog.ts
@@ -29,7 +29,7 @@ import { Component } from '../component';
export class CreateFromTemplateDialog extends Component {
private static selectors = {
- root: '.aca-file-from-template-dialog',
+ root: '.aca-create-from-template-dialog',
title: '.mat-dialog-title',
nameInput: 'input[placeholder="Name" i]',
diff --git a/projects/aca-shared/store/src/actions/template.actions.ts b/projects/aca-shared/store/src/actions/template.actions.ts
index 6b8feb285..156d94348 100644
--- a/projects/aca-shared/store/src/actions/template.actions.ts
+++ b/projects/aca-shared/store/src/actions/template.actions.ts
@@ -28,7 +28,9 @@ import { Node } from '@alfresco/js-api';
export enum TemplateActionTypes {
FileFromTemplate = 'FILE_FROM_TEMPLATE',
- CreateFileFromTemplate = 'CREATE_FILE_FROM_TEMPLATE'
+ FolderFromTemplate = 'FOLDER_FROM_TEMPLATE',
+ CreateFromTemplate = 'CREATE_FROM_TEMPLATE',
+ CreateFromTemplateSuccess = 'CREATE_FROM_TEMPLATE_SUCCESS'
}
export class FileFromTemplate implements Action {
@@ -37,8 +39,20 @@ export class FileFromTemplate implements Action {
constructor() {}
}
-export class CreateFileFromTemplate implements Action {
- readonly type = TemplateActionTypes.CreateFileFromTemplate;
+export class FolderFromTemplate implements Action {
+ readonly type = TemplateActionTypes.FolderFromTemplate;
+
+ constructor() {}
+}
+
+export class CreateFromTemplate implements Action {
+ readonly type = TemplateActionTypes.CreateFromTemplate;
constructor(public payload: Node) {}
}
+
+export class CreateFromTemplateSuccess implements Action {
+ readonly type = TemplateActionTypes.CreateFromTemplateSuccess;
+
+ constructor(public node: Node) {}
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index f9edc2cf5..3bbeeb1ed 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -76,7 +76,7 @@ import { AppNodeVersionModule } from './components/node-version/node-version.mod
import { FavoritesComponent } from './components/favorites/favorites.component';
import { RecentFilesComponent } from './components/recent-files/recent-files.component';
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
-import { CreateFileFromTemplateDialogComponent } from './dialogs/node-templates/create-from-template.dialog';
+import { CreateFromTemplateDialogComponent } from './dialogs/node-template/create-from-template.dialog';
import { environment } from '../environments/environment';
import { registerLocaleData } from '@angular/common';
@@ -159,7 +159,7 @@ registerLocaleData(localeSv);
FavoritesComponent,
RecentFilesComponent,
SharedFilesComponent,
- CreateFileFromTemplateDialogComponent
+ CreateFromTemplateDialogComponent
],
providers: [
{ provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy },
@@ -177,7 +177,7 @@ registerLocaleData(localeSv);
NodeVersionsDialogComponent,
NodeVersionUploadDialogComponent,
LibraryDialogComponent,
- CreateFileFromTemplateDialogComponent
+ CreateFromTemplateDialogComponent
],
bootstrap: [AppComponent]
})
diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.html b/src/app/dialogs/node-template/create-from-template.dialog.html
similarity index 69%
rename from src/app/dialogs/node-templates/create-from-template.dialog.html
rename to src/app/dialogs/node-template/create-from-template.dialog.html
index a542bebd2..7fc0a9b55 100644
--- a/src/app/dialogs/node-templates/create-from-template.dialog.html
+++ b/src/app/dialogs/node-template/create-from-template.dialog.html
@@ -1,13 +1,10 @@
-
+
diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.scss b/src/app/dialogs/node-template/create-from-template.dialog.scss
similarity index 93%
rename from src/app/dialogs/node-templates/create-from-template.dialog.scss
rename to src/app/dialogs/node-template/create-from-template.dialog.scss
index 1cc586b47..7cbbb2947 100644
--- a/src/app/dialogs/node-templates/create-from-template.dialog.scss
+++ b/src/app/dialogs/node-template/create-from-template.dialog.scss
@@ -1,10 +1,10 @@
-@mixin app-create-file-from-template-theme($theme) {
+@mixin app-create-from-template-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
- .aca-file-from-template-dialog {
+ .aca-create-from-template-dialog {
ng-component {
overflow: visible;
}
diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.spec.ts b/src/app/dialogs/node-template/create-from-template.dialog.spec.ts
similarity index 73%
rename from src/app/dialogs/node-templates/create-from-template.dialog.spec.ts
rename to src/app/dialogs/node-template/create-from-template.dialog.spec.ts
index 80eb35e2a..cf710cadb 100644
--- a/src/app/dialogs/node-templates/create-from-template.dialog.spec.ts
+++ b/src/app/dialogs/node-template/create-from-template.dialog.spec.ts
@@ -23,19 +23,18 @@
* along with Alfresco. If not, see .
*/
-import { CreateFileFromTemplateDialogComponent } from './create-from-template.dialog';
+import { CreateFromTemplateDialogComponent } from './create-from-template.dialog';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppTestingModule } from '../../testing/app-testing.module';
-import { CoreModule } from '@alfresco/adf-core';
+import { CoreModule, TranslationMock } from '@alfresco/adf-core';
import {
MatDialogModule,
- MatDialogRef,
- MAT_DIALOG_DATA
+ MAT_DIALOG_DATA,
+ MatDialogRef
} from '@angular/material/dialog';
import { Store } from '@ngrx/store';
-import { CreateFileFromTemplate } from '@alfresco/aca-shared/store';
+import { CreateFromTemplate } from '@alfresco/aca-shared/store';
import { Node } from '@alfresco/js-api';
-import { CreateFromTemplateDialogService } from './create-from-template-dialog.service';
function text(length: number) {
return new Array(length)
@@ -48,15 +47,15 @@ function text(length: number) {
}
describe('CreateFileFromTemplateDialogComponent', () => {
- let fixture: ComponentFixture;
- let component: CreateFileFromTemplateDialogComponent;
- let dialogRef: MatDialogRef;
+ let fixture: ComponentFixture;
+ let component: CreateFromTemplateDialogComponent;
let store;
- let createFromTemplateDialogService: CreateFromTemplateDialogService;
const data = {
id: 'node-id',
name: 'node-name',
+ isFolder: false,
+ isFile: true,
properties: {
'cm:title': 'node-title',
'cm:description': ''
@@ -66,36 +65,39 @@ describe('CreateFileFromTemplateDialogComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreModule.forRoot(), AppTestingModule, MatDialogModule],
- declarations: [CreateFileFromTemplateDialogComponent],
+ declarations: [CreateFromTemplateDialogComponent],
providers: [
+ {
+ provide: MatDialogRef,
+ useValue: {
+ close: jasmine.createSpy('close')
+ }
+ },
+ {
+ provide: TranslationMock,
+ useValue: {
+ instant: jasmine.createSpy('instant')
+ }
+ },
{
provide: Store,
useValue: {
dispatch: jasmine.createSpy('dispatch')
}
},
- { provide: MAT_DIALOG_DATA, useValue: data },
- {
- provide: MatDialogRef,
- useValue: {
- close: jasmine.createSpy('close')
- }
- }
+ { provide: MAT_DIALOG_DATA, useValue: {} }
]
});
- fixture = TestBed.createComponent(CreateFileFromTemplateDialogComponent);
- dialogRef = TestBed.get(MatDialogRef);
+ fixture = TestBed.createComponent(CreateFromTemplateDialogComponent);
store = TestBed.get(Store);
- createFromTemplateDialogService = TestBed.get(
- CreateFromTemplateDialogService
- );
component = fixture.componentInstance;
-
- fixture.detectChanges();
+ component.data = data as Node;
});
it('should populate form with provided dialog data', () => {
+ fixture.detectChanges();
+
expect(component.form.controls.name.value).toBe(data.name);
expect(component.form.controls.title.value).toBe(
data.properties['cm:title']
@@ -106,32 +108,47 @@ describe('CreateFileFromTemplateDialogComponent', () => {
});
it('should invalidate form if required `name` field is invalid', () => {
+ fixture.detectChanges();
+
component.form.controls.name.setValue('');
fixture.detectChanges();
+
expect(component.form.invalid).toBe(true);
});
it('should invalidate form if required `name` field has `only spaces`', () => {
+ fixture.detectChanges();
+
component.form.controls.name.setValue(' ');
fixture.detectChanges();
+
expect(component.form.invalid).toBe(true);
});
it('should invalidate form if required `name` field has `ending dot`', () => {
+ fixture.detectChanges();
+
component.form.controls.name.setValue('something.');
fixture.detectChanges();
+
expect(component.form.invalid).toBe(true);
});
it('should invalidate form if `title` text length is long', () => {
+ fixture.detectChanges();
+
component.form.controls.title.setValue(text(260));
fixture.detectChanges();
+
expect(component.form.invalid).toBe(true);
});
it('should invalidate form if `description` text length is long', () => {
+ fixture.detectChanges();
+
component.form.controls.description.setValue(text(520));
fixture.detectChanges();
+
expect(component.form.invalid).toBe(true);
});
@@ -139,11 +156,16 @@ describe('CreateFileFromTemplateDialogComponent', () => {
const newNode = {
id: 'node-id',
name: 'new-node-name',
+ isFolder: false,
+ isFile: true,
properties: {
'cm:title': 'new-node-title',
'cm:description': 'new-node-description'
}
} as Node;
+
+ fixture.detectChanges();
+
component.form.controls.name.setValue('new-node-name');
component.form.controls.title.setValue('new-node-title');
component.form.controls.description.setValue('new-node-description');
@@ -152,27 +174,8 @@ describe('CreateFileFromTemplateDialogComponent', () => {
component.onSubmit();
- expect(store.dispatch).toHaveBeenCalledWith(
- new CreateFileFromTemplate(newNode)
+ expect(store.dispatch['calls'].mostRecent().args[0]).toEqual(
+ new CreateFromTemplate(newNode)
);
});
-
- it('should close dialog on create file from template success', done => {
- const newNode = {
- id: 'node-id',
- name: 'new-node-name',
- properties: {
- 'cm:title': 'new-node-title',
- 'cm:description': 'new-node-description'
- }
- } as Node;
-
- fixture.detectChanges();
- createFromTemplateDialogService.success$.subscribe(node => {
- expect(dialogRef.close).toHaveBeenCalledWith(node);
- done();
- });
-
- createFromTemplateDialogService.success$.next(newNode);
- });
});
diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.ts b/src/app/dialogs/node-template/create-from-template.dialog.ts
similarity index 76%
rename from src/app/dialogs/node-templates/create-from-template.dialog.ts
rename to src/app/dialogs/node-template/create-from-template.dialog.ts
index 9a247a738..2b5ad1f90 100644
--- a/src/app/dialogs/node-templates/create-from-template.dialog.ts
+++ b/src/app/dialogs/node-template/create-from-template.dialog.ts
@@ -33,31 +33,27 @@ import {
FormControl,
ValidationErrors
} from '@angular/forms';
-import { CreateFromTemplateDialogService } from './create-from-template-dialog.service';
import { Store } from '@ngrx/store';
-import { AppStore, CreateFileFromTemplate } from '@alfresco/aca-shared/store';
+import { AppStore, CreateFromTemplate } from '@alfresco/aca-shared/store';
+import { TranslationService } from '@alfresco/adf-core';
@Component({
templateUrl: './create-from-template.dialog.html',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./create-from-template.dialog.scss']
})
-export class CreateFileFromTemplateDialogComponent implements OnInit {
+export class CreateFromTemplateDialogComponent implements OnInit {
public form: FormGroup;
constructor(
- private createFromTemplateDialogService: CreateFromTemplateDialogService,
+ private translationService: TranslationService,
private store: Store,
private formBuilder: FormBuilder,
- private dialogRef: MatDialogRef,
- @Inject(MAT_DIALOG_DATA) public data: any
+ private dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: Node
) {}
ngOnInit() {
- this.createFromTemplateDialogService.success$.subscribe((data: Node) => {
- this.dialogRef.close(data);
- });
-
this.form = this.formBuilder.group({
name: [
this.data.name,
@@ -85,7 +81,21 @@ export class CreateFileFromTemplateDialogComponent implements OnInit {
}
};
const data: Node = Object.assign({}, this.data, update);
- this.store.dispatch(new CreateFileFromTemplate(data));
+ this.store.dispatch(new CreateFromTemplate(data));
+ }
+
+ title(): string {
+ if (this.data.isFolder) {
+ return this.translationService.instant(
+ 'NODE_FROM_TEMPLATE.FOLDER_DIALOG_TITLE',
+ { template: this.data.name }
+ );
+ }
+
+ return this.translationService.instant(
+ 'NODE_FROM_TEMPLATE.FILE_DIALOG_TITLE',
+ { template: this.data.name }
+ );
}
close() {
@@ -101,7 +111,7 @@ export class CreateFileFromTemplateDialogComponent implements OnInit {
return isValid
? null
: {
- message: `FILE_FROM_TEMPLATE.FORM.ERRORS.SPECIAL_CHARACTERS`
+ message: `NODE_FROM_TEMPLATE.FORM.ERRORS.SPECIAL_CHARACTERS`
};
}
@@ -115,7 +125,7 @@ export class CreateFileFromTemplateDialogComponent implements OnInit {
return isValid
? null
: {
- message: `FILE_FROM_TEMPLATE.FORM.ERRORS.ENDING_DOT`
+ message: `NODE_FROM_TEMPLATE.FORM.ERRORS.ENDING_DOT`
};
}
@@ -126,11 +136,11 @@ export class CreateFileFromTemplateDialogComponent implements OnInit {
return isValid
? null
: {
- message: `FILE_FROM_TEMPLATE.FORM.ERRORS.ONLY_SPACES`
+ message: `NODE_FROM_TEMPLATE.FORM.ERRORS.ONLY_SPACES`
};
} else {
return {
- message: `FILE_FROM_TEMPLATE.FORM.ERRORS.REQUIRED`
+ message: `NODE_FROM_TEMPLATE.FORM.ERRORS.REQUIRED`
};
}
}
diff --git a/src/app/dialogs/node-templates/create-from-template-dialog.service.ts b/src/app/dialogs/node-templates/create-from-template-dialog.service.ts
deleted file mode 100644
index c6d95a3bf..000000000
--- a/src/app/dialogs/node-templates/create-from-template-dialog.service.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*!
- * @license
- * Alfresco Example Content Application
- *
- * Copyright (C) 2005 - 2020 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 { Injectable } from '@angular/core';
-import { Subject } from 'rxjs';
-import { Node } from '@alfresco/js-api';
-
-@Injectable({
- providedIn: 'root'
-})
-export class CreateFromTemplateDialogService {
- success$: Subject = new Subject();
-}
diff --git a/src/app/services/create-file-from-template.service.spec.ts b/src/app/services/create-file-from-template.service.spec.ts
deleted file mode 100644
index ca066dfec..000000000
--- a/src/app/services/create-file-from-template.service.spec.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-/*!
- * @license
- * Alfresco Example Content Application
- *
- * Copyright (C) 2005 - 2020 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 { TestBed, fakeAsync, tick } from '@angular/core/testing';
-import { EffectsModule } from '@ngrx/effects';
-import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store';
-import { TemplateEffects } from '../store/effects/template.effects';
-import { AppTestingModule } from '../testing/app-testing.module';
-import { Store } from '@ngrx/store';
-import { MatDialog } from '@angular/material/dialog';
-import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-core';
-import { CreateFileFromTemplateService } from './create-file-from-template.service';
-import { of } from 'rxjs';
-
-describe('CreateFileFromTemplateService', () => {
- let dialog: MatDialog;
- let store: Store;
- let alfrescoApiService: AlfrescoApiService;
- let createFileFromTemplateService: CreateFileFromTemplateService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects])],
- providers: [
- CreateFileFromTemplateService,
- { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
- ]
- });
-
- store = TestBed.get(Store);
- alfrescoApiService = TestBed.get(AlfrescoApiService);
- dialog = TestBed.get(MatDialog);
- createFileFromTemplateService = TestBed.get(CreateFileFromTemplateService);
- });
-
- it('should open dialog with `Node Templates` folder id as data property', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(of({ id: 'templates-folder-id' }));
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- expect(dialog.open['calls'].argsFor(0)[1].data).toEqual(
- jasmine.objectContaining({ currentFolderId: 'templates-folder-id' })
- );
- });
-
- it('should remove parents for templates node breadcrumb path', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(
- of({
- id: 'templates-folder-id',
- path: {
- elements: [],
- name: '/Company Home/Data Dictionary'
- }
- })
- );
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- const breadcrumb = dialog.open['calls']
- .argsFor(0)[1]
- .data.breadcrumbTransform({
- name: 'Node Templates',
- path: {
- elements: [{ name: 'Company Home' }, { name: 'Data Dictionary' }],
- name: '/Company Home/Data Dictionary'
- }
- });
-
- expect(breadcrumb.path.elements).toEqual([]);
- });
-
- it('should return false if selected node is not a template file', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(of({ id: 'templates-folder-id' }));
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- const isSelectionValid = dialog.open['calls']
- .argsFor(0)[1]
- .data.isSelectionValid({
- isFile: false
- });
-
- expect(isSelectionValid).toBe(false);
- });
-
- it('should return true if selected node is a template file', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(of({ id: 'templates-folder-id' }));
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- const isSelectionValid = dialog.open['calls']
- .argsFor(0)[1]
- .data.isSelectionValid({
- isFile: true
- });
-
- expect(isSelectionValid).toBe(true);
- });
-
- it('should raise an error when getNodeInfo fails', fakeAsync(() => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(
- Promise.reject({
- message: `{ "error": { "statusCode": 404 } } `
- })
- );
- spyOn(store, 'dispatch');
-
- createFileFromTemplateService.openTemplatesDialog();
- tick();
-
- expect(store.dispatch).toHaveBeenCalledWith(
- new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')
- );
- }));
-
- it('should return true if row is not a `link` nodeType', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(
- of({
- id: 'templates-folder-id',
- path: {
- elements: [],
- name: '/Company Home/Data Dictionary'
- }
- })
- );
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- expect(
- dialog.open['calls'].argsFor(0)[1].data.rowFilter({
- node: { entry: { nodeType: 'text' } }
- })
- ).toBe(true);
- });
-
- it('should return false if row is a `link` nodeType', () => {
- spyOn(
- alfrescoApiService.getInstance().nodes,
- 'getNodeInfo'
- ).and.returnValue(
- of({
- id: 'templates-folder-id',
- path: {
- elements: [],
- name: '/Company Home/Data Dictionary'
- }
- })
- );
- spyOn(dialog, 'open');
-
- createFileFromTemplateService.openTemplatesDialog();
-
- expect(
- dialog.open['calls'].argsFor(0)[1].data.rowFilter({
- node: { entry: { nodeType: 'app:filelink' } }
- })
- ).toBe(false);
- });
-});
diff --git a/src/app/services/node-template.service.spec.ts b/src/app/services/node-template.service.spec.ts
new file mode 100644
index 000000000..3e94713e5
--- /dev/null
+++ b/src/app/services/node-template.service.spec.ts
@@ -0,0 +1,308 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2020 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 { TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { EffectsModule } from '@ngrx/effects';
+import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store';
+import { TemplateEffects } from '../store/effects/template.effects';
+import { AppTestingModule } from '../testing/app-testing.module';
+import { Store } from '@ngrx/store';
+import { MatDialog } from '@angular/material/dialog';
+import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-core';
+import { NodeTemplateService } from './node-template.service';
+import { of } from 'rxjs';
+
+describe('NodeTemplateService', () => {
+ let dialog: MatDialog;
+ let store: Store;
+ let alfrescoApiService: AlfrescoApiService;
+ let nodeTemplateService: NodeTemplateService;
+ const fileTemplateConfig = {
+ relativePath: 'relative-path/parent-file-templates',
+ selectionType: 'file'
+ };
+ const folderTemplateConfig = {
+ relativePath: 'relative-path/parent-folder-templates',
+ selectionType: 'folder'
+ };
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects])],
+ providers: [
+ NodeTemplateService,
+ { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
+ ]
+ });
+
+ store = TestBed.get(Store);
+ alfrescoApiService = TestBed.get(AlfrescoApiService);
+ dialog = TestBed.get(MatDialog);
+ nodeTemplateService = TestBed.get(NodeTemplateService);
+ });
+
+ it('should open dialog with parent node `id` as data property', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'parent-node-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ expect(dialog.open['calls'].argsFor(0)[1].data).toEqual(
+ jasmine.objectContaining({ currentFolderId: 'parent-node-id' })
+ );
+ });
+
+ it('should remove parents for templates node breadcrumb path', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(
+ of({
+ id: 'parent-node-id',
+ path: {
+ elements: [],
+ name: '/Company Home/Data Dictionary'
+ }
+ })
+ );
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ const breadcrumb = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.breadcrumbTransform({
+ name: 'Node Templates',
+ path: {
+ elements: [{ name: 'Company Home' }, { name: 'Data Dictionary' }],
+ name: '/Company Home/Data Dictionary'
+ }
+ });
+
+ expect(breadcrumb.path.elements).toEqual([]);
+ });
+
+ it('should raise an error when getNodeInfo fails', fakeAsync(() => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(
+ Promise.reject({
+ message: `{ "error": { "statusCode": 404 } } `
+ })
+ );
+ spyOn(store, 'dispatch');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+ tick();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')
+ );
+ }));
+
+ it('should return true if row is not a `link` nodeType', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(
+ of({
+ id: 'templates-folder-id',
+ path: {
+ elements: [],
+ name: '/Company Home/Data Dictionary'
+ }
+ })
+ );
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ expect(
+ dialog.open['calls'].argsFor(0)[1].data.rowFilter({
+ node: { entry: { nodeType: 'text' } }
+ })
+ ).toBe(true);
+ });
+
+ it('should return false if row is a `link` nodeType', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(
+ of({
+ id: 'templates-folder-id',
+ path: {
+ elements: [],
+ name: '/Company Home/Data Dictionary'
+ }
+ })
+ );
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ expect(
+ dialog.open['calls'].argsFor(0)[1].data.rowFilter({
+ node: { entry: { nodeType: 'app:filelink' } }
+ })
+ ).toBe(false);
+ });
+
+ describe('File templates', () => {
+ it('should return false if selected node is not a file', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ const isSelectionValid = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.isSelectionValid({
+ name: 'some-folder-template',
+ isFile: false,
+ isFolder: true
+ });
+
+ expect(isSelectionValid).toBe(false);
+ });
+
+ it('should return true if selected node is a template file', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ const isSelectionValid = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.isSelectionValid({
+ name: 'some-file-template',
+ isFile: true,
+ isFolder: false
+ });
+
+ expect(isSelectionValid).toBe(true);
+ });
+
+ it('should set dialog title for file templates', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
+
+ const title = dialog.open['calls'].argsFor(0)[1].data.title;
+
+ expect(title).toBe('NODE_SELECTOR.SELECT_FILE_TEMPLATE_TITLE');
+ });
+ });
+
+ describe('Folder templates', () => {
+ it('should return false if selected node is not a folder', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(folderTemplateConfig);
+
+ const isSelectionValid = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.isSelectionValid({
+ name: 'some-file-template',
+ isFile: true,
+ isFolder: false
+ });
+
+ expect(isSelectionValid).toBe(false);
+ });
+
+ it('should return false if current node is the parent folder', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(folderTemplateConfig);
+
+ const isSelectionValid = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.isSelectionValid({
+ name: 'parent-folder-templates',
+ isFile: false,
+ isFolder: true
+ });
+
+ expect(isSelectionValid).toBe(false);
+ });
+
+ it('should return true if selected node is a folder template', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(folderTemplateConfig);
+
+ const isSelectionValid = dialog.open['calls']
+ .argsFor(0)[1]
+ .data.isSelectionValid({
+ name: 'some-folder-template',
+ isFile: false,
+ isFolder: true
+ });
+
+ expect(isSelectionValid).toBe(true);
+ });
+
+ it('should set dialog title for folder templates', () => {
+ spyOn(
+ alfrescoApiService.getInstance().nodes,
+ 'getNodeInfo'
+ ).and.returnValue(of({ id: 'templates-folder-id' }));
+ spyOn(dialog, 'open');
+
+ nodeTemplateService.selectTemplateDialog(folderTemplateConfig);
+
+ const title = dialog.open['calls'].argsFor(0)[1].data.title;
+
+ expect(title).toBe('NODE_SELECTOR.SELECT_FOLDER_TEMPLATE_TITLE');
+ });
+ });
+});
diff --git a/src/app/services/create-file-from-template.service.ts b/src/app/services/node-template.service.ts
similarity index 77%
rename from src/app/services/create-file-from-template.service.ts
rename to src/app/services/node-template.service.ts
index 761c550ce..e13a66838 100644
--- a/src/app/services/create-file-from-template.service.ts
+++ b/src/app/services/node-template.service.ts
@@ -25,7 +25,7 @@
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material';
-import { CreateFileFromTemplateDialogComponent } from '../dialogs/node-templates/create-from-template.dialog';
+import { CreateFromTemplateDialogComponent } from '../dialogs/node-template/create-from-template.dialog';
import { Subject, from, of } from 'rxjs';
import { Node, MinimalNode, MinimalNodeEntryEntity } from '@alfresco/js-api';
import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core';
@@ -38,10 +38,17 @@ import {
ShareDataRow
} from '@alfresco/adf-content-services';
+export interface TemplateDialogConfig {
+ relativePath: string;
+ selectionType: string;
+}
+
@Injectable({
providedIn: 'root'
})
-export class CreateFileFromTemplateService {
+export class NodeTemplateService {
+ private currentTemplateConfig: TemplateDialogConfig = null;
+
constructor(
private store: Store,
private alfrescoApiService: AlfrescoApiService,
@@ -49,14 +56,16 @@ export class CreateFileFromTemplateService {
public dialog: MatDialog
) {}
- openTemplatesDialog(): Subject {
+ selectTemplateDialog(config: TemplateDialogConfig): Subject {
+ this.currentTemplateConfig = config;
+
const select = new Subject();
select.subscribe({
complete: this.close.bind(this)
});
const data: ContentNodeSelectorComponentData = {
- title: this.title,
+ title: this.title(config.selectionType),
actionName: 'NEXT',
dropdownHideMyFiles: true,
currentFolderId: null,
@@ -69,7 +78,7 @@ export class CreateFileFromTemplateService {
from(
this.alfrescoApiService.getInstance().nodes.getNodeInfo('-root-', {
- relativePath: 'Data Dictionary/Node Templates'
+ relativePath: config.relativePath
})
)
.pipe(
@@ -100,10 +109,10 @@ export class CreateFileFromTemplateService {
createTemplateDialog(
node: Node
- ): MatDialogRef {
- return this.dialog.open(CreateFileFromTemplateDialogComponent, {
+ ): MatDialogRef {
+ return this.dialog.open(CreateFromTemplateDialogComponent, {
data: node,
- panelClass: 'aca-file-from-template-dialog',
+ panelClass: 'aca-create-from-template-dialog',
width: '630px'
});
}
@@ -123,6 +132,14 @@ export class CreateFileFromTemplateService {
}
private isSelectionValid(node: Node): boolean {
+ if (node.name === this.currentTemplateConfig.relativePath.split('/')[1]) {
+ return false;
+ }
+
+ if (this.currentTemplateConfig.selectionType === 'folder') {
+ return node.isFolder;
+ }
+
return node.isFile;
}
@@ -130,8 +147,16 @@ export class CreateFileFromTemplateService {
this.dialog.closeAll();
}
- private get title() {
- return this.translation.instant('NODE_SELECTOR.SELECT_TEMPLATE_TITLE');
+ private title(selectionType: string) {
+ if (selectionType === 'file') {
+ return this.translation.instant(
+ 'NODE_SELECTOR.SELECT_FILE_TEMPLATE_TITLE'
+ );
+ }
+
+ return this.translation.instant(
+ 'NODE_SELECTOR.SELECT_FOLDER_TEMPLATE_TITLE'
+ );
}
private rowFilter(row: ShareDataRow): boolean {
diff --git a/src/app/store/effects/template.effects.spec.ts b/src/app/store/effects/template.effects.spec.ts
index 6f4cb9808..e0d522d00 100644
--- a/src/app/store/effects/template.effects.spec.ts
+++ b/src/app/store/effects/template.effects.spec.ts
@@ -29,25 +29,27 @@ import { TemplateEffects } from './template.effects';
import { EffectsModule } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import {
- CreateFileFromTemplate,
+ CreateFromTemplate,
+ CreateFromTemplateSuccess,
FileFromTemplate,
+ FolderFromTemplate,
SnackbarErrorAction
} from '@alfresco/aca-shared/store';
-import { CreateFileFromTemplateService } from '../../services/create-file-from-template.service';
+import { NodeTemplateService } from '../../services/node-template.service';
import { of } from 'rxjs';
import { AlfrescoApiServiceMock, AlfrescoApiService } from '@alfresco/adf-core';
import { ContentManagementService } from '../../services/content-management.service';
import { Node, NodeEntry } from '@alfresco/js-api';
-import { CreateFromTemplateDialogService } from '../../dialogs/node-templates/create-from-template-dialog.service';
+import { MatDialog } from '@angular/material/dialog';
describe('TemplateEffects', () => {
let store: Store;
- let createFileFromTemplateService: CreateFileFromTemplateService;
+ let nodeTemplateService: NodeTemplateService;
let alfrescoApiService: AlfrescoApiService;
let contentManagementService: ContentManagementService;
- let createFromTemplateDialogService: CreateFromTemplateDialogService;
let copyNodeSpy;
let updateNodeSpy;
+ let matDialog: MatDialog;
const node: Node = {
name: 'node-name',
id: 'node-id',
@@ -63,29 +65,41 @@ describe('TemplateEffects', () => {
'cm:description': 'description'
}
};
+ const fileTemplateConfig = {
+ relativePath: 'Data Dictionary/Node Templates',
+ selectionType: 'file'
+ };
+
+ const folderTemplateConfig = {
+ relativePath: 'Data Dictionary/Space Templates',
+ selectionType: 'folder'
+ };
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects])],
providers: [
- CreateFileFromTemplateService,
- { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
+ NodeTemplateService,
+ { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
+ {
+ provide: MatDialog,
+ useValue: {
+ closeAll: jasmine.createSpy('closeAll')
+ }
+ }
]
});
store = TestBed.get(Store);
- createFileFromTemplateService = TestBed.get(CreateFileFromTemplateService);
+ nodeTemplateService = TestBed.get(NodeTemplateService);
alfrescoApiService = TestBed.get(AlfrescoApiService);
- createFromTemplateDialogService = TestBed.get(
- CreateFromTemplateDialogService
- );
contentManagementService = TestBed.get(ContentManagementService);
+ matDialog = TestBed.get(MatDialog);
spyOn(store, 'dispatch').and.callThrough();
- spyOn(createFromTemplateDialogService.success$, 'next');
spyOn(contentManagementService.reload, 'next');
spyOn(store, 'select').and.returnValue(of({ id: 'parent-id' }));
- spyOn(createFileFromTemplateService, 'openTemplatesDialog').and.returnValue(
+ spyOn(nodeTemplateService, 'selectTemplateDialog').and.returnValue(
of([{ id: 'template-id' }])
);
@@ -98,41 +112,43 @@ describe('TemplateEffects', () => {
updateNodeSpy.calls.reset();
});
- it('should reload content on create file from template', fakeAsync(() => {
- spyOn(
- createFileFromTemplateService,
- 'createTemplateDialog'
- ).and.returnValue({ afterClosed: () => of(node) });
+ it('should open dialog to select template files', fakeAsync(() => {
+ spyOn(nodeTemplateService, 'createTemplateDialog').and.returnValue({
+ afterClosed: () => of(node)
+ });
store.dispatch(new FileFromTemplate());
- tick(300);
+ tick();
- expect(contentManagementService.reload.next).toHaveBeenCalled();
+ expect(nodeTemplateService.selectTemplateDialog).toHaveBeenCalledWith(
+ fileTemplateConfig
+ );
}));
- it('should not reload content if no file was created', fakeAsync(() => {
- spyOn(
- createFileFromTemplateService,
- 'createTemplateDialog'
- ).and.returnValue({ afterClosed: () => of(null) });
+ it('should open dialog to select template folders', fakeAsync(() => {
+ spyOn(nodeTemplateService, 'createTemplateDialog').and.returnValue({
+ afterClosed: () => of(node)
+ });
- store.dispatch(new FileFromTemplate());
- tick(300);
+ store.dispatch(new FolderFromTemplate());
+ tick();
- expect(contentManagementService.reload.next).not.toHaveBeenCalled();
+ expect(nodeTemplateService.selectTemplateDialog).toHaveBeenCalledWith(
+ folderTemplateConfig
+ );
}));
- it('should call dialog service success event on create file from template', fakeAsync(() => {
+ it('should create node from template successful', fakeAsync(() => {
copyNodeSpy.and.returnValue(
of({ entry: { id: 'node-id', properties: {} } })
);
updateNodeSpy.and.returnValue(of({ entry: node }));
- store.dispatch(new CreateFileFromTemplate(node));
+ store.dispatch(new CreateFromTemplate(node));
tick();
- expect(createFromTemplateDialogService.success$.next).toHaveBeenCalledWith(
- node
+ expect(store.dispatch['calls'].mostRecent().args[0]).toEqual(
+ new CreateFromTemplateSuccess(node)
);
}));
@@ -143,12 +159,12 @@ describe('TemplateEffects', () => {
})
);
- store.dispatch(new CreateFileFromTemplate(node));
+ store.dispatch(new CreateFromTemplate(node));
tick();
- expect(
- createFromTemplateDialogService.success$.next
- ).not.toHaveBeenCalledWith();
+ expect(store.dispatch['calls'].mostRecent().args[0]).not.toEqual(
+ new CreateFromTemplateSuccess(node)
+ );
expect(store.dispatch['calls'].argsFor(1)[0]).toEqual(
new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')
);
@@ -161,12 +177,12 @@ describe('TemplateEffects', () => {
})
);
- store.dispatch(new CreateFileFromTemplate(node));
+ store.dispatch(new CreateFromTemplate(node));
tick();
- expect(
- createFromTemplateDialogService.success$.next
- ).not.toHaveBeenCalledWith();
+ expect(store.dispatch['calls'].mostRecent().args[0]).not.toEqual(
+ new CreateFromTemplateSuccess(node)
+ );
expect(store.dispatch['calls'].argsFor(1)[0]).toEqual(
new SnackbarErrorAction('APP.MESSAGES.ERRORS.CONFLICT')
);
@@ -190,11 +206,26 @@ describe('TemplateEffects', () => {
})
);
- store.dispatch(new CreateFileFromTemplate(test_node.entry));
+ store.dispatch(new CreateFromTemplate(test_node.entry));
tick();
- expect(createFromTemplateDialogService.success$.next).toHaveBeenCalledWith(
- test_node.entry
+ expect(store.dispatch['calls'].mostRecent().args[0]).toEqual(
+ new CreateFromTemplateSuccess(test_node.entry)
+ );
+ }));
+
+ it('should close dialog on create template success', fakeAsync(() => {
+ store.dispatch(new CreateFromTemplateSuccess({} as Node));
+ tick();
+ expect(matDialog.closeAll).toHaveBeenCalled();
+ }));
+
+ it('should should reload content on create template success', fakeAsync(() => {
+ const test_node = { id: 'test-node-id' } as Node;
+ store.dispatch(new CreateFromTemplateSuccess(test_node));
+ tick();
+ expect(contentManagementService.reload.next).toHaveBeenCalledWith(
+ test_node
);
}));
});
diff --git a/src/app/store/effects/template.effects.ts b/src/app/store/effects/template.effects.ts
index 713ad2574..ab6a778de 100644
--- a/src/app/store/effects/template.effects.ts
+++ b/src/app/store/effects/template.effects.ts
@@ -25,65 +25,64 @@
import { Effect, Actions, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
-import {
- map,
- switchMap,
- debounceTime,
- flatMap,
- take,
- catchError
-} from 'rxjs/operators';
+import { map, switchMap, debounceTime, take, catchError } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import {
FileFromTemplate,
- CreateFileFromTemplate,
+ FolderFromTemplate,
+ CreateFromTemplate,
+ CreateFromTemplateSuccess,
TemplateActionTypes,
getCurrentFolder,
AppStore,
SnackbarErrorAction
} from '@alfresco/aca-shared/store';
-import { CreateFileFromTemplateService } from '../../services/create-file-from-template.service';
+import {
+ NodeTemplateService,
+ TemplateDialogConfig
+} from '../../services/node-template.service';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { ContentManagementService } from '../../services/content-management.service';
import { from, Observable, of } from 'rxjs';
import { NodeEntry, NodeBodyUpdate, Node } from '@alfresco/js-api';
-import { CreateFromTemplateDialogService } from '../../dialogs/node-templates/create-from-template-dialog.service';
+import { MatDialog } from '@angular/material/dialog';
+
@Injectable()
export class TemplateEffects {
constructor(
+ private matDialog: MatDialog,
private content: ContentManagementService,
private store: Store,
private apiService: AlfrescoApiService,
private actions$: Actions,
- private createFromTemplateDialogService: CreateFromTemplateDialogService,
- private createFileFromTemplateService: CreateFileFromTemplateService
+ private nodeTemplateService: NodeTemplateService
) {}
@Effect({ dispatch: false })
fileFromTemplate$ = this.actions$.pipe(
ofType(TemplateActionTypes.FileFromTemplate),
map(() => {
- this.createFileFromTemplateService
- .openTemplatesDialog()
- .pipe(
- debounceTime(300),
- flatMap(([node]) =>
- this.createFileFromTemplateService
- .createTemplateDialog(node)
- .afterClosed()
- )
- )
- .subscribe((node: NodeEntry | null) => {
- if (node) {
- this.content.reload.next(node);
- }
- });
+ this.openDialog({
+ relativePath: 'Data Dictionary/Node Templates',
+ selectionType: 'file'
+ });
})
);
@Effect({ dispatch: false })
- createFileFromTemplate$ = this.actions$.pipe(
- ofType(TemplateActionTypes.CreateFileFromTemplate),
+ folderFromTemplate$ = this.actions$.pipe(
+ ofType(TemplateActionTypes.FolderFromTemplate),
+ map(() =>
+ this.openDialog({
+ relativePath: 'Data Dictionary/Space Templates',
+ selectionType: 'folder'
+ })
+ )
+ );
+
+ @Effect({ dispatch: false })
+ createFromTemplate$ = this.actions$.pipe(
+ ofType(TemplateActionTypes.CreateFromTemplate),
map(action => {
this.store
.select(getCurrentFolder)
@@ -95,12 +94,32 @@ export class TemplateEffects {
)
.subscribe((node: NodeEntry | null) => {
if (node) {
- this.createFromTemplateDialogService.success$.next(node.entry);
+ this.store.dispatch(new CreateFromTemplateSuccess(node.entry));
}
});
})
);
+ @Effect({ dispatch: false })
+ createFromTemplateSuccess$ = this.actions$.pipe(
+ ofType(
+ TemplateActionTypes.CreateFromTemplateSuccess
+ ),
+ map(payload => {
+ this.matDialog.closeAll();
+ this.content.reload.next(payload.node);
+ })
+ );
+
+ private openDialog(config: TemplateDialogConfig) {
+ this.nodeTemplateService
+ .selectTemplateDialog(config)
+ .pipe(debounceTime(300))
+ .subscribe(([node]) =>
+ this.nodeTemplateService.createTemplateDialog(node)
+ );
+ }
+
private copyNode(source: Node, parentId: string): Observable {
return from(
this.apiService.getInstance().nodes.copyNode(source.id, {
diff --git a/src/app/ui/custom-theme.scss b/src/app/ui/custom-theme.scss
index 3059f4a95..fa20674ca 100644
--- a/src/app/ui/custom-theme.scss
+++ b/src/app/ui/custom-theme.scss
@@ -10,7 +10,7 @@
@import '../dialogs/node-versions/node-versions.dialog.theme';
@import '../components/create-menu/create-menu.component.scss';
@import '../components/layout/layout.theme.scss';
-@import '../dialogs/node-templates/create-from-template.dialog.scss';
+@import '../dialogs/node-template/create-from-template.dialog.scss';
@import './overrides/adf-style-fixes.theme';
@@ -68,7 +68,7 @@ $warn: map-get($custom-theme, warn);
@include sidenav-component-theme($theme);
@include aca-current-user-theme($theme);
@include aca-context-menu-theme($theme);
- @include app-create-file-from-template-theme($theme);
+ @include app-create-from-template-theme($theme);
@include app-create-menu-theme($theme);
@include adf-style-fixes($theme);
diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json
index 7f5aa5857..8c4301175 100644
--- a/src/assets/app.extensions.json
+++ b/src/assets/app.extensions.json
@@ -121,6 +121,20 @@
"rules": {
"enabled": "app.navigation.folder.canUpload"
}
+ },
+ {
+ "id": "app.create.folderFromTemplate",
+ "order": 800,
+ "icon": "create_new_folder",
+ "title": "APP.NEW_MENU.MENU_ITEMS.FOLDER_TEMPLATE",
+ "description": "APP.NEW_MENU.MENU_ITEMS.FOLDER_TEMPLATE",
+ "description-disabled": "APP.NEW_MENU.TOOLTIPS.CREATE_FOLDER_NOT_ALLOWED",
+ "actions": {
+ "click": "FOLDER_FROM_TEMPLATE"
+ },
+ "rules": {
+ "enabled": "app.navigation.folder.canUpload"
+ }
}
],
"navbar": [
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index e31b9392f..8825d3e58 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -58,7 +58,8 @@
"UPLOAD_FILE": "Upload File",
"UPLOAD_FOLDER": "Upload Folder",
"CREATE_LIBRARY": "Create Library",
- "FILE_TEMPLATE": "Create file from template"
+ "FILE_TEMPLATE": "Create file from template",
+ "FOLDER_TEMPLATE": "Create folder from template"
},
"TOOLTIPS": {
"CREATE_FOLDER": "Create new folder",
@@ -359,12 +360,14 @@
"MOVE_ITEMS": "Move {{ number }} items to...",
"SEARCH": "Search",
"NEXT": "Next",
- "SELECT_TEMPLATE_TITLE": "Select a document template"
+ "SELECT_FILE_TEMPLATE_TITLE": "Select a document template",
+ "SELECT_FOLDER_TEMPLATE_TITLE": "Select a folder template"
},
- "FILE_FROM_TEMPLATE": {
+ "NODE_FROM_TEMPLATE": {
"CANCEL": "CANCEL",
"CREATE": "Create",
- "TITLE": "Create new document from '{{ template }}'",
+ "FOLDER_DIALOG_TITLE": "Create new folder from '{{ template }}'",
+ "FILE_DIALOG_TITLE": "Create new document from '{{ template }}'",
"FORM": {
"PLACEHOLDER": {
"NAME": "Name",