From a0b452bf8301bfcb7ee07038dfe1f472f76c3f74 Mon Sep 17 00:00:00 2001 From: Vito Date: Tue, 25 Sep 2018 21:46:54 +0100 Subject: [PATCH] [ADF-3499] adding new dialog for external content file (#3799) * [ADF-3499] start creation of login dialog component with extra auth service feature * [ADF-3499] adding new dialog for external content file * [ADF-3499] fixed condition for pop up and added prefix ticket * [ADF-3499] fixed smartfolder bug for content node selector * [ADF-3499] disabling preview after uploading for external resource files * [ADF-3499] fixed unit test for document list service * [ADF-3499] added unit test to new components * [ADF-3499] added translation and some fix * [ADF-3499] fixed labels * [ADF-3499] fixed problem with node and node entry for smart folders * [ADF-3499] fixed compilation problem --- docs/core/login-dialog.component.md | 84 ++++++++++ .../data/share-datatable-adapter.ts | 9 +- .../services/custom-resources.service.ts | 4 +- .../services/document-list.service.spec.ts | 8 +- .../services/document-list.service.ts | 4 +- lib/core/core.module.ts | 6 +- lib/core/i18n/en.json | 4 + .../login-dialog-component-data.interface.ts | 24 +++ .../login-dialog-panel.component.html | 11 ++ .../login-dialog-panel.component.scss | 9 ++ .../login-dialog-panel.component.spec.ts | 97 +++++++++++ .../login-dialog-panel.component.ts | 47 ++++++ .../components/login-dialog.component.html | 25 +++ .../components/login-dialog.component.scss | 14 ++ .../components/login-dialog.component.ts | 55 +++++++ .../login/components/login.component.html | 6 +- lib/core/login/components/login.component.ts | 13 ++ lib/core/login/login.module.ts | 11 +- lib/core/login/public-api.ts | 3 + .../services/external-alfresco-api.service.ts | 74 +++++++++ .../services/login-dialog.service.spec.ts | 64 ++++++++ lib/core/services/login-dialog.service.ts | 61 +++++++ lib/core/services/public-api.ts | 2 + lib/core/styles/_index.scss | 4 + ...-file-widget-dialog-component.interface.ts | 28 ++++ .../attach-file-widget-dialog.component.html | 37 +++++ .../attach-file-widget-dialog.component.scss | 32 ++++ ...ttach-file-widget-dialog.component.spec.ts | 150 ++++++++++++++++++ .../attach-file-widget-dialog.component.ts | 76 +++++++++ .../attach-file-widget-dialog.service.spec.ts | 64 ++++++++ .../attach-file-widget-dialog.service.ts | 74 +++++++++ .../attach-file-widget.component.html | 3 +- .../attach-file-widget.component.ts | 52 ++++-- .../attach-file-widget.components.spec.ts | 6 +- .../content-widget/content-widget.module.ts | 12 +- .../content-widget/public-api.ts | 3 + lib/process-services/i18n/en.json | 7 + lib/process-services/process.module.ts | 4 +- lib/process-services/styles/_index.scss | 2 + 39 files changed, 1154 insertions(+), 35 deletions(-) create mode 100644 docs/core/login-dialog.component.md create mode 100644 lib/core/login/components/login-dialog-component-data.interface.ts create mode 100644 lib/core/login/components/login-dialog-panel.component.html create mode 100644 lib/core/login/components/login-dialog-panel.component.scss create mode 100644 lib/core/login/components/login-dialog-panel.component.spec.ts create mode 100644 lib/core/login/components/login-dialog-panel.component.ts create mode 100644 lib/core/login/components/login-dialog.component.html create mode 100644 lib/core/login/components/login-dialog.component.scss create mode 100644 lib/core/login/components/login-dialog.component.ts create mode 100644 lib/core/services/external-alfresco-api.service.ts create mode 100644 lib/core/services/login-dialog.service.spec.ts create mode 100644 lib/core/services/login-dialog.service.ts create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog-component.interface.ts create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.component.html create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.component.scss create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.component.spec.ts create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.component.ts create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.service.spec.ts create mode 100644 lib/process-services/content-widget/attach-file-widget-dialog.service.ts diff --git a/docs/core/login-dialog.component.md b/docs/core/login-dialog.component.md new file mode 100644 index 0000000000..92fc12f6ce --- /dev/null +++ b/docs/core/login-dialog.component.md @@ -0,0 +1,84 @@ +--- +Added: v2.0.0 +Status: Active +Last reviewed: 2018-04-18 +--- + +# Content Node Selector component + +Allows a user to perform a login via a dialog. + +## Details + +The [Login Dialog component](../core/login-dialog.component.md) allow you to perform a login via a dialog. + +### Showing the dialog + +Unlike most components, the Login Dialog Component is typically shown in a dialog box +rather than the main page and you are responsible for opening the dialog yourself. You can use the +[Angular Material Dialog](https://material.angular.io/components/dialog/overview) for this, +as shown in the usage example. ADF provides the [`LoginDialogComponentData`](../../lib/core/login/components/login-dialog-component-data.interface.ts) interface +to work with the Dialog's +[data option](https://material.angular.io/components/dialog/overview#sharing-data-with-the-dialog-component-): + +```ts +export interface LoginDialogComponentData { + title: string; + actionName?: string; + logged: Subject; +} +``` + +The properties are described in the table below: + +| Name | Type | Default value | Description | +| ---- | ---- | ------------- | ----------- | +| title | `string` | "" | Dialog title | +| actionName | `string` | "" | Text to appear on the dialog's main action button ("Login", "Access", etc) | +| logged | [`EventEmitter`]| | Event emitted when the login succeeds. | + +If you don't want to manage the dialog yourself then it is easier to use the +[Login Dialog Panel component](login-dialog-panel.component.md), or the +methods of the [Login Dialog service](login-dialog.service.md), which create +the dialog for you. + +### Usage example + +```ts +import { MatDialog } from '@angular/material'; +import { LoginDialogComponentData, LoginDialogComponent} from '@adf/core' +import { Subject } from 'rxjs/Subject'; + ... + +constructor(dialog: MatDialog ... ) {} + +openSelectorDialog() { + data: LoginDialogComponentData = { + title: "Perform a Login", + actionName: "Access", + logged: new Subject() + }; + + this.dialog.open( + LoginDialogComponent, + { + data, panelClass: 'adf-content-node-selector-dialog', + width: '630px' + } + ); + + data.logged.subscribe(() => { + // Action after being logged in... + }, + (error)=>{ + //your error handling + }, + ()=>{ + //action called when an action or cancel is clicked on the dialog + this.dialog.closeAll(); + }); +} +``` + +All the results will be streamed to the logged [subject](http://reactivex.io/rxjs/manual/overview.html#subject) present in the [`LoginDialogComponentData`](../../lib/core/login/components/login-dialog-component-data.interface.ts) object passed to the dialog. +When the dialog action is selected by clicking, the `data.logged` stream will be completed. diff --git a/lib/content-services/document-list/data/share-datatable-adapter.ts b/lib/content-services/document-list/data/share-datatable-adapter.ts index 2fffc326e9..31db55eff5 100644 --- a/lib/content-services/document-list/data/share-datatable-adapter.ts +++ b/lib/content-services/document-list/data/share-datatable-adapter.ts @@ -168,8 +168,13 @@ export class ShareDataTableAdapter implements DataTableAdapter { } isSmartFolder(node: any) { - return node.entry.aspectNames && (node.entry.aspectNames.indexOf('smf:customConfigSmartFolder') > -1 || - (node.entry.aspectNames.indexOf('smf:systemConfigSmartFolder') > -1)); + let nodeAspects = this.getNodeAspectNames(node); + return nodeAspects.indexOf('smf:customConfigSmartFolder') > -1 || + (nodeAspects.indexOf('smf:systemConfigSmartFolder') > -1); + } + + private getNodeAspectNames(node: any): any[] { + return node.entry && node.entry.aspectNames ? node.entry.aspectNames : node.aspectNames ? node.aspectNames : []; } private sortRows(rows: DataRow[], sorting: DataSorting) { diff --git a/lib/content-services/document-list/services/custom-resources.service.ts b/lib/content-services/document-list/services/custom-resources.service.ts index e247e7b368..ffb5f30fc0 100644 --- a/lib/content-services/document-list/services/custom-resources.service.ts +++ b/lib/content-services/document-list/services/custom-resources.service.ts @@ -182,7 +182,7 @@ export class CustomResourcesService { */ loadSites(pagination: PaginationModel): Observable { const options = { - include: ['properties'], + include: ['properties', 'aspectNames'], maxItems: pagination.maxItems, skipCount: pagination.skipCount }; @@ -345,7 +345,7 @@ export class CustomResourcesService { } private getIncludesFields(includeFields: string[]): string[] { - return ['path', 'properties', 'allowableOperations', 'permissions', ...includeFields] + return ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields] .filter((element, index, array) => index === array.indexOf(element)); } diff --git a/lib/content-services/document-list/services/document-list.service.spec.ts b/lib/content-services/document-list/services/document-list.service.spec.ts index b7ac8102d7..044f52e815 100644 --- a/lib/content-services/document-list/services/document-list.service.spec.ts +++ b/lib/content-services/document-list/services/document-list.service.spec.ts @@ -168,7 +168,7 @@ describe('DocumentListService', () => { expect(spyGetNodeInfo).toHaveBeenCalledWith('-root-', { includeSource: true, - include: ['path', 'properties', 'allowableOperations', 'permissions', 'isLocked'], + include: ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', 'isLocked'], relativePath: '/fake-root/fake-name' }); }); @@ -180,7 +180,7 @@ describe('DocumentListService', () => { expect(spyGetNodeInfo).toHaveBeenCalledWith('-root-', { includeSource: true, - include: ['path', 'properties', 'allowableOperations', 'permissions'], + include: ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames'], relativePath: '/fake-root/fake-name' }); }); @@ -192,7 +192,7 @@ describe('DocumentListService', () => { expect(spyGetNodeInfo).toHaveBeenCalledWith('test-id', { includeSource: true, - include: ['path', 'properties', 'allowableOperations', 'permissions', 'isLocked'] + include: ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', 'isLocked'] }); }); @@ -203,7 +203,7 @@ describe('DocumentListService', () => { expect(spyGetNodeInfo).toHaveBeenCalledWith('test-id', { includeSource: true, - include: ['path', 'properties', 'allowableOperations', 'permissions'] + include: ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames'] } ); }); diff --git a/lib/content-services/document-list/services/document-list.service.ts b/lib/content-services/document-list/services/document-list.service.ts index d4247e87b0..248ccc4724 100644 --- a/lib/content-services/document-list/services/document-list.service.ts +++ b/lib/content-services/document-list/services/document-list.service.ts @@ -44,7 +44,7 @@ export class DocumentListService { rootNodeId = opts.rootFolderId; } - let includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', ...includeFields] + let includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields] .filter((element, index, array) => index === array.indexOf(element)); let params: any = { @@ -158,7 +158,7 @@ export class DocumentListService { */ getFolderNode(nodeId: string, includeFields: string[] = []): Observable { - let includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', ...includeFields] + let includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields] .filter((element, index, array) => index === array.indexOf(element)); let opts: any = { diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 08c934f3bf..e38c8051fc 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -96,6 +96,8 @@ import { WidgetVisibilityService } from './form/services/widget-visibility.servi import { EcmUserService } from './userinfo/services/ecm-user.service'; import { BpmUserService } from './userinfo/services/bpm-user.service'; import { ViewUtilService } from './viewer/services/view-util.service'; +import { LoginDialogService } from './services/login-dialog.service'; +import { ExternalAlfrescoApiService } from './services/external-alfresco-api.service'; export function createTranslateLoader(http: HttpClient, logService: LogService) { return new TranslateLoaderService(http, logService); @@ -150,7 +152,9 @@ export function providers() { WidgetVisibilityService, EcmUserService, BpmUserService, - ViewUtilService + ViewUtilService, + LoginDialogService, + ExternalAlfrescoApiService ]; } diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index 643b39839a..ffbeb153c5 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -218,6 +218,10 @@ "ACTION": { "HELP": "NEED HELP?", "REGISTER": "REGISTER" + }, + "DIALOG": { + "CANCEL": "Cancel", + "CHOOSE": "Choose" } }, "ADF-DATATABLE": { diff --git a/lib/core/login/components/login-dialog-component-data.interface.ts b/lib/core/login/components/login-dialog-component-data.interface.ts new file mode 100644 index 0000000000..bec6042ad2 --- /dev/null +++ b/lib/core/login/components/login-dialog-component-data.interface.ts @@ -0,0 +1,24 @@ +/*! + * @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 { Subject } from 'rxjs/Subject'; + +export interface LoginDialogComponentData { + title: string; + actionName?: string; + logged: Subject; +} diff --git a/lib/core/login/components/login-dialog-panel.component.html b/lib/core/login/components/login-dialog-panel.component.html new file mode 100644 index 0000000000..5eac2db555 --- /dev/null +++ b/lib/core/login/components/login-dialog-panel.component.html @@ -0,0 +1,11 @@ +
+ + +
diff --git a/lib/core/login/components/login-dialog-panel.component.scss b/lib/core/login/components/login-dialog-panel.component.scss new file mode 100644 index 0000000000..032a20412a --- /dev/null +++ b/lib/core/login/components/login-dialog-panel.component.scss @@ -0,0 +1,9 @@ +@mixin adf-login-dialog-panel-theme($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $warn: map-get($theme, warn); + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $text-color-primary: mat-color($foreground, text); + +} diff --git a/lib/core/login/components/login-dialog-panel.component.spec.ts b/lib/core/login/components/login-dialog-panel.component.spec.ts new file mode 100644 index 0000000000..ebec56979e --- /dev/null +++ b/lib/core/login/components/login-dialog-panel.component.spec.ts @@ -0,0 +1,97 @@ +/*! + * @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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { AuthenticationService } from '../../services/authentication.service'; +import { LoginDialogPanelComponent } from './login-dialog-panel.component'; +import { of } from 'rxjs'; +import { setupTestBed } from '../../testing/setupTestBed'; +import { CoreTestingModule } from '../../testing/core.testing.module'; + +describe('LoginDialogPanelComponent', () => { + let component: LoginDialogPanelComponent; + let fixture: ComponentFixture; + let element: any; + let usernameInput, passwordInput; + let authService: AuthenticationService; + + setupTestBed({ + imports: [CoreTestingModule] + }); + + beforeEach(async(() => { + fixture = TestBed.createComponent(LoginDialogPanelComponent); + element = fixture.nativeElement; + component = fixture.componentInstance; + authService = TestBed.get(AuthenticationService); + fixture.detectChanges(); + fixture.whenStable().then(() => { + usernameInput = element.querySelector('#username'); + passwordInput = element.querySelector('#password'); + }); + })); + + afterEach(() => { + fixture.destroy(); + }); + + function loginWithCredentials(username, password) { + usernameInput.value = username; + passwordInput.value = password; + + usernameInput.dispatchEvent(new Event('input')); + passwordInput.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + component.submitForm(); + fixture.detectChanges(); + } + + it('should be created', () => { + expect(element.querySelector('#adf-login-form')).not.toBeNull(); + expect(element.querySelector('#adf-login-form')).toBeDefined(); + }); + + it('should be able to login', (done) => { + component.success.subscribe((event) => { + expect(event.token.type).toBe('type'); + expect(event.token.ticket).toBe('ticket'); + done(); + }); + spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' })); + loginWithCredentials('fake-username', 'fake-password'); + }); + + it('should return false when the login form is empty', () => { + usernameInput.value = ''; + passwordInput.value = ''; + usernameInput.dispatchEvent(new Event('input')); + passwordInput.dispatchEvent(new Event('input')); + fixture.detectChanges(); + expect(component.isValid()).toBeFalsy(); + }); + + it('should return true when the login form is empty', () => { + usernameInput.value = 'fake-user'; + passwordInput.value = 'fake-psw'; + usernameInput.dispatchEvent(new Event('input')); + passwordInput.dispatchEvent(new Event('input')); + fixture.detectChanges(); + expect(component.isValid()).toBeTruthy(); + }); + +}); diff --git a/lib/core/login/components/login-dialog-panel.component.ts b/lib/core/login/components/login-dialog-panel.component.ts new file mode 100644 index 0000000000..de609ff89b --- /dev/null +++ b/lib/core/login/components/login-dialog-panel.component.ts @@ -0,0 +1,47 @@ +/*! + * @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 { Component, ViewEncapsulation, ViewChild, Output, EventEmitter } from '@angular/core'; +import { LoginComponent } from './login.component'; +import { LoginSuccessEvent } from '../models/login-success.event'; + +@Component({ + selector: 'adf-login-dialog-panel', + templateUrl: './login-dialog-panel.component.html', + styleUrls: ['./login-dialog-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class LoginDialogPanelComponent { + + @Output() + success = new EventEmitter(); + + @ViewChild('adfLogin') + login: LoginComponent; + + submitForm(): void { + this.login.submit(); + } + + onLoginSuccess(event: LoginSuccessEvent) { + this.success.emit(event); + } + + isValid() { + return this.login && this.login.form ? this.login.form.valid : false; + } +} diff --git a/lib/core/login/components/login-dialog.component.html b/lib/core/login/components/login-dialog.component.html new file mode 100644 index 0000000000..35463e7f9b --- /dev/null +++ b/lib/core/login/components/login-dialog.component.html @@ -0,0 +1,25 @@ +
{{data?.title}} +
+ + + + + + + + diff --git a/lib/core/login/components/login-dialog.component.scss b/lib/core/login/components/login-dialog.component.scss new file mode 100644 index 0000000000..d6a3a2698c --- /dev/null +++ b/lib/core/login/components/login-dialog.component.scss @@ -0,0 +1,14 @@ +@mixin adf-login-dialog-theme($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $warn: map-get($theme, warn); + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $text-color-primary: mat-color($foreground, text); + + + .adf-login-dialog-content adf-login .adf-login-content .adf-login-card-wide { + padding: 0px; + box-shadow: none; + } +} diff --git a/lib/core/login/components/login-dialog.component.ts b/lib/core/login/components/login-dialog.component.ts new file mode 100644 index 0000000000..ca525ef193 --- /dev/null +++ b/lib/core/login/components/login-dialog.component.ts @@ -0,0 +1,55 @@ +/*! + * @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 { Component, Inject, ViewEncapsulation, ViewChild } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material'; +import { LoginDialogComponentData } from './login-dialog-component-data.interface'; +import { LoginDialogPanelComponent } from './login-dialog-panel.component'; +@Component({ + selector: 'adf-login-dialog', + templateUrl: './login-dialog.component.html', + styleUrls: ['./login-dialog.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class LoginDialogComponent { + + @ViewChild('adfLoginPanel') + loginPanel: LoginDialogPanelComponent; + + buttonActionName = ''; + + constructor(@Inject(MAT_DIALOG_DATA) public data: LoginDialogComponentData) { + this.buttonActionName = data.actionName ? `LOGIN.DIALOG.${data.actionName.toUpperCase()}` : 'LOGIN.DIALOG.CHOOSE'; + } + + close() { + this.data.logged.complete(); + } + + submitForm(): void { + this.loginPanel.submitForm(); + } + + onLoginSuccess(event: any) { + this.data.logged.next(event); + this.close(); + } + + isFormValid() { + return this.loginPanel ? this.loginPanel.isValid() : false; + } +} diff --git a/lib/core/login/components/login.component.html b/lib/core/login/components/login.component.html index bfb1d834d2..7e33db26c4 100644 --- a/lib/core/login/components/login.component.html +++ b/lib/core/login/components/login.component.html @@ -5,7 +5,7 @@