From 40d78a11c0591c97122169b75144538e32b510f0 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 13 Feb 2019 19:17:40 +0000 Subject: [PATCH 001/223] update versions to 3.0.0 --- demo-shell/package.json | 2 +- lib/content-services/package.json | 6 +++--- lib/core/package.json | 4 ++-- lib/extensions/package.json | 4 ++-- lib/insights/package.json | 8 ++++---- lib/process-services-cloud/package.json | 6 +++--- lib/process-services/package.json | 8 ++++---- package.json | 16 ++++++++-------- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/demo-shell/package.json b/demo-shell/package.json index f5f11d076e..b593782ea4 100644 --- a/demo-shell/package.json +++ b/demo-shell/package.json @@ -1,7 +1,7 @@ { "name": "Alfresco-ADF-Angular-Demo", "description": "Demo shell for Alfresco Angular components", - "version": "3.0.0-beta1", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "repository": { "type": "git", diff --git a/lib/content-services/package.json b/lib/content-services/package.json index 617a38d241..c1f64ca600 100644 --- a/lib/content-services/package.json +++ b/lib/content-services/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-content-services", "description": "Alfresco ADF content services", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-content-services.js", "repository": { @@ -25,9 +25,9 @@ "@angular/platform-browser": ">=7.0.3", "@angular/platform-browser-dynamic": ">=7.0.3", "@angular/router": ">=7.0.3", - "@alfresco/js-api": "3.0.0-delta18", + "@alfresco/js-api": "3.0.0", "rxjs": ">=6.2.2", - "@alfresco/adf-core": ">=2.7.0-beta5", + "@alfresco/adf-core": "3.0.0", "@ngx-translate/core": ">=11.0.0", "hammerjs": ">=2.0.8", "moment": ">=2.22.2", diff --git a/lib/core/package.json b/lib/core/package.json index 07b1201fc7..5f77a2e67f 100644 --- a/lib/core/package.json +++ b/lib/core/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-core", "description": "Alfresco ADF core", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-core.js", "repository": { @@ -27,7 +27,7 @@ "@angular/router": ">=7.0.3", "@mat-datetimepicker/core": ">=2.0.1", "@mat-datetimepicker/moment": ">=2.0.1", - "@alfresco/js-api": "3.0.0-delta18", + "@alfresco/js-api": "3.0.0", "rxjs": ">=6.2.2", "@ngx-translate/core": ">=11.0.0", "core-js": ">=2.5.4", diff --git a/lib/extensions/package.json b/lib/extensions/package.json index 2ed7da8340..42386f7d42 100644 --- a/lib/extensions/package.json +++ b/lib/extensions/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-extensions", "description": "Provides extensibility support for ADF applications.", - "version": "2.7.0-beta5", + "version": "3.0.0", "license": "Apache-2.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-extensions.js", @@ -16,6 +16,6 @@ "@angular/common": "^6.0.0", "@angular/core": "^6.0.0", "@angular/http": "^6.1.4", - "@alfresco/js-api": "3.0.0-delta18" + "@alfresco/js-api": "3.0.0" } } diff --git a/lib/insights/package.json b/lib/insights/package.json index aca24cf69f..4717d8ac06 100644 --- a/lib/insights/package.json +++ b/lib/insights/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-insights", "description": "Alfresco ADF insights", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-insights.js", "repository": { @@ -25,10 +25,10 @@ "@angular/platform-browser": ">=7.0.3", "@angular/platform-browser-dynamic": ">=7.0.3", "@angular/router": ">=7.0.3", - "@alfresco/js-api": "3.0.0-delta18", + "@alfresco/js-api": "3.0.0", "rxjs": ">=6.2.2", - "@alfresco/adf-core": ">=2.7.0-beta5", - "@alfresco/adf-content-services": ">=2.7.0-beta5", + "@alfresco/adf-core": "3.0.0", + "@alfresco/adf-content-services": "3.0.0", "@ngx-translate/core": ">=11.0.0", "chart.js": ">=2.5.0", "core-js": ">=2.5.4", diff --git a/lib/process-services-cloud/package.json b/lib/process-services-cloud/package.json index 86f296b7e9..2989b652dd 100644 --- a/lib/process-services-cloud/package.json +++ b/lib/process-services-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-process-services-cloud", "description": "Alfresco ADF process services cloud", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-process-services-cloud.js", "repository": { @@ -25,9 +25,9 @@ "@angular/platform-browser": ">=7.0.3", "@angular/platform-browser-dynamic": ">=7.0.3", "@angular/router": ">=7.0.3", - "@alfresco/js-api": "3.0.0-delta18", + "@alfresco/js-api": "3.0.0", "rxjs": ">=6.2.2", - "@alfresco/adf-core": ">=2.7.0-beta5", + "@alfresco/adf-core": "3.0.0", "@ngx-translate/core": ">=11.0.0", "hammerjs": ">=2.0.8", "moment": ">=2.22.2", diff --git a/lib/process-services/package.json b/lib/process-services/package.json index 3f61370ddd..02ae6d1e47 100644 --- a/lib/process-services/package.json +++ b/lib/process-services/package.json @@ -1,7 +1,7 @@ { "name": "@alfresco/adf-process-services", "description": "Alfresco ADF process services", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-process-services.js", "repository": { @@ -25,10 +25,10 @@ "@angular/platform-browser": ">=7.0.3", "@angular/platform-browser-dynamic": ">=7.0.3", "@angular/router": ">=7.0.3", - "@alfresco/js-api": "3.0.0-delta18", + "@alfresco/js-api": "3.0.0", "rxjs": ">=6.2.2", - "@alfresco/adf-core": ">=2.7.0-beta5", - "@alfresco/adf-content-services": ">=2.7.0-beta5", + "@alfresco/adf-core": "3.0.0", + "@alfresco/adf-content-services": "3.0.0", "@ngx-translate/core": ">=11.0.0", "core-js": ">=2.5.4", "hammerjs": ">=2.0.8", diff --git a/package.json b/package.json index 1f2fc04226..9c62bbf02e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "alfresco-components", "description": "Alfresco Angular components", - "version": "2.7.0-beta5", + "version": "3.0.0", "author": "Alfresco Software, Ltd.", "main": "./index.js", "scripts": { @@ -55,13 +55,13 @@ "process services-cloud" ], "dependencies": { - "@alfresco/adf-content-services": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/adf-core": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/adf-extensions": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/adf-insights": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/adf-process-services": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/adf-process-services-cloud": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "@alfresco/js-api": "^3.0.0-d7850f421268e21861e2cd219441b7343efd27ba", + "@alfresco/adf-content-services": "3.0.0", + "@alfresco/adf-core": "3.0.0", + "@alfresco/adf-extensions": "3.0.0", + "@alfresco/adf-insights": "3.0.0", + "@alfresco/adf-process-services": "3.0.0", + "@alfresco/adf-process-services-cloud": "3.0.0", + "@alfresco/js-api": "3.0.0", "@angular/animations": "7.0.3", "@angular/cdk": "7.0.3", "@angular/common": "7.0.3", From e318f3607a0d228aa63a983dbd413929c2057d92 Mon Sep 17 00:00:00 2001 From: Andy Stark <30621568+therealandeeee@users.noreply.github.com> Date: Wed, 13 Feb 2019 20:49:29 +0000 Subject: [PATCH 002/223] [ADF-3745] Doc review updates (#4316) * [ADF-3745] Updated compatibility page based on feedback * [ADF-3745] Renamed node perm directive and fixed broken links * [ADF-3745] Updated version index with Check allowable op directive * [ADF-3745] Reviewed other files affected by node perm directive name change --- docs/README.md | 6 ++-- docs/compatibility.md | 15 +++++++--- .../content-node-selector-panel.component.md | 2 +- .../permissions-style.model.md | 3 +- docs/core/README.md | 2 +- ...=> check-allowable-operation.directive.md} | 28 +++++++++---------- docs/core/content.service.md | 10 ++----- docs/versionIndex.md | 2 +- 8 files changed, 35 insertions(+), 33 deletions(-) rename docs/core/{node-permission.directive.md => check-allowable-operation.directive.md} (76%) diff --git a/docs/README.md b/docs/README.md index 504d6aa73a..240e3045e6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -116,12 +116,12 @@ for more information about installing and using the source code. | Name | Description | Source link | | ---- | ----------- | ----------- | | [Context menu directive](core/context-menu.directive.md) | Adds a context menu to a component. | [Source](../lib/core/context-menu/context-menu.directive.ts) | +| [Check allowable operation directive](core/check-allowable-operation.directive.md) | Selectively disables an HTML element or Angular component. | [Source](../lib/core/directives/check-allowable-operation.directive.ts) | | [Highlight directive](core/highlight.directive.md) | Adds highlighting to selected sections of an HTML element's content. | [Source](../lib/core/directives/highlight.directive.ts) | | [Logout directive](core/logout.directive.md) | Logs the user out when the decorated element is clicked. | [Source](../lib/core/directives/logout.directive.ts) | | [Node delete directive](core/node-delete.directive.md) | Deletes multiple files and folders. | [Source](../lib/core/directives/node-delete.directive.ts) | | [Node download directive](core/node-download.directive.md) | Allows folders and/or files to be downloaded, with multiple nodes packed as a '.ZIP' archive. | [Source](../lib/core/directives/node-download.directive.ts) | | [Node favorite directive](core/node-favorite.directive.md) | Selectively toggles nodes as favorites. | [Source](../lib/core/directives/node-favorite.directive.ts) | -| [Node permission directive](core/node-permission.directive.md) | Selectively disables an HTML element or Angular component. | [Source](../lib/core/directives/node-permission.directive.ts) | | [Node restore directive](core/node-restore.directive.md) | Restores deleted nodes to their original location. | [Source](../lib/core/directives/node-restore.directive.ts) | | [Upload directive](core/upload.directive.md) | Uploads content in response to file drag and drop. | [Source](../lib/core/directives/upload.directive.ts) | @@ -209,7 +209,7 @@ for more information about installing and using the source code. | Name | Description | Source link | | ---- | ----------- | ----------- | | [Data Table Adapter interface](core/datatable-adapter.interface.md) | Defines how table data is supplied to DataTable and Tasklist components. | [Source](../lib/core/datatable/data/datatable-adapter.ts) | -| [Form Field Validator interface](core/form-field-validator.interface.md) | Defines how the input fields of [`Form`](../../lib/process-services/task-list/models/form.model.ts) and Task Details components are validated. | [Source](../lib/core/form/components/widgets/core/form-field-validator.ts) | +| [Form Field Validator interface](core/form-field-validator.interface.md) | Defines how the input fields of Form and Task Details components are validated. | [Source](../lib/core/form/components/widgets/core/form-field-validator.ts) | | [Search Configuration interface](core/search-configuration.interface.md) | Provides fine control of parameters to a search. | [Source](../lib/core/services/search-configuration.service.ts) | [(Back to Contents)](#contents) @@ -319,7 +319,7 @@ for more information about installing and using the source code. | Name | Description | Source link | | ---- | ----------- | ----------- | -| [Search Widget interface](content-services/search-widget.interface.md) | Specifies required properties for [Search filter component](../content-services/search-filter.component.md) widgets. | [Source](../lib/content-services/search/search-widget.interface.ts) | +| [Search Widget interface](content-services/search-widget.interface.md) | Specifies required properties for Search filter component widgets. | [Source](../lib/content-services/search/search-widget.interface.ts) | [(Back to Contents)](#contents) diff --git a/docs/compatibility.md b/docs/compatibility.md index 986f2dd647..c3e37a63bc 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -5,14 +5,21 @@ Title: Version compatibility # Version compatibility The table below shows the versions of Alfresco's backend services that -have been tested with latest versions of ADF. Note that *smoke testing* -means that the version should be safe to use (ie, it won't "catch fire") -but it is not guaranteed to be free of minor issues or missing features. +have been tested with latest versions of ADF. + +Our goal is to support ADF for *all* versions of Content Services +and Process Services. However, we only test against a subset of those +versions that believe to be representative. If a bug is reported for a +version that we don't test against then the bug will still be fixed in +the next release (ie, the fact that a version is not tested doesn't mean that it isn't supported). + +Note that [*smoke testing*](https://en.wikipedia.org/wiki/Smoke_testing_%28software%29) implies that the product will not "go up in smoke" when +used but the tests are quicker and not as thorough as with *full testing*. You can find further information about released versions of ADF in the [version index](versionIndex.md) and the [release notes](release-notes/README.md). -| ADF version | ACS versions | APS versions | +| ADF version | Content Services | Process Services | | -- | -- | -- | | [3.0.0](versionIndex.md#v300) | **Full test:** v6.1.0 RC7
**Smoke test:** v5.2.4 | **Full test:** v2.0.0 (latest CI pipeline build), v1.9.0
**Smoke test:** v1.8.1 | | [2.6.0](versionIndex.md#v260) | **Full test:** v6.0.0, v5.2.4
**Smoke test:** v5.2.3 | **Full test:** v1.9.0
**Smoke test:** v1.8.1, v1.7.0, v1.6.4 | diff --git a/docs/content-services/content-node-selector-panel.component.md b/docs/content-services/content-node-selector-panel.component.md index 4f5480429e..a5d2e708e0 100644 --- a/docs/content-services/content-node-selector-panel.component.md +++ b/docs/content-services/content-node-selector-panel.component.md @@ -7,7 +7,7 @@ Last reviewed: 2019-01-16 # [Content Node Selector Panel component](../../lib/content-services/content-node-selector/content-node-selector-panel.component.ts "Defined in content-node-selector-panel.component.ts") -Opens a [Content Node Selector](content-node-selecctor.component.md) in its own dialog window. +Opens a [Content Node Selector](content-node-selector.component.md) in its own dialog window. ![Content Node Selector screenshot](../docassets/images/ContentNodeSelector.png) diff --git a/docs/content-services/permissions-style.model.md b/docs/content-services/permissions-style.model.md index db5c8f5d41..8846814b4c 100644 --- a/docs/content-services/permissions-style.model.md +++ b/docs/content-services/permissions-style.model.md @@ -2,6 +2,7 @@ Title: Permission Style model Added: v2.0.0 Status: Active +Last reviewed: 2019-02-13 --- # [Permission Style model](../../lib/content-services/document-list/models/permissions-style.model.ts "Defined in permissions-style.model.ts") @@ -28,7 +29,7 @@ and can be applied separately to files and folders by setting `isFile` and `isFo ### Permissions enum -The [Permissions](https://github.com/Alfresco/alfresco-ng2-components/blob/development/lib/core/models/allowable-operations.enum.ts) +The [Allowable Operations](https://github.com/Alfresco/alfresco-ng2-components/blob/development/lib/core/models/allowable-operations.enum.ts) enum contains all the valid permissions for which you can apply custom styles: **DELETE**, **UPDATE**, **CREATE**, **UPDATEPERMISSIONS**, **NOT_DELETE**, **NOT_UPDATE**, **NOT_CREATE**, **NOT_UPDATEPERMISSIONS**. diff --git a/docs/core/README.md b/docs/core/README.md index 5cd40bc383..251f8282b0 100644 --- a/docs/core/README.md +++ b/docs/core/README.md @@ -56,12 +56,12 @@ for more information about installing and using the source code. | Name | Description | Source link | | ---- | ----------- | ----------- | | [Context menu directive](context-menu.directive.md) | Adds a context menu to a component. | [Source](../../lib/core/context-menu/context-menu.directive.ts) | +| [Check allowable operation directive](check-allowable-operation.directive.md) | Selectively disables an HTML element or Angular component. | [Source](../../lib/core/directives/check-allowable-operation.directive.ts) | | [Highlight directive](highlight.directive.md) | Adds highlighting to selected sections of an HTML element's content. | [Source](../../lib/core/directives/highlight.directive.ts) | | [Logout directive](logout.directive.md) | Logs the user out when the decorated element is clicked. | [Source](../../lib/core/directives/logout.directive.ts) | | [Node delete directive](node-delete.directive.md) | Deletes multiple files and folders. | [Source](../../lib/core/directives/node-delete.directive.ts) | | [Node download directive](node-download.directive.md) | Allows folders and/or files to be downloaded, with multiple nodes packed as a '.ZIP' archive. | [Source](../../lib/core/directives/node-download.directive.ts) | | [Node favorite directive](node-favorite.directive.md) | Selectively toggles nodes as favorites. | [Source](../../lib/core/directives/node-favorite.directive.ts) | -| [Node permission directive](node-permission.directive.md) | Selectively disables an HTML element or Angular component. | [Source](../../lib/core/directives/node-permission.directive.ts) | | [Node restore directive](node-restore.directive.md) | Restores deleted nodes to their original location. | [Source](../../lib/core/directives/node-restore.directive.ts) | | [Upload directive](upload.directive.md) | Uploads content in response to file drag and drop. | [Source](../../lib/core/directives/upload.directive.ts) | diff --git a/docs/core/node-permission.directive.md b/docs/core/check-allowable-operation.directive.md similarity index 76% rename from docs/core/node-permission.directive.md rename to docs/core/check-allowable-operation.directive.md index 7bc898b462..1af8171908 100644 --- a/docs/core/node-permission.directive.md +++ b/docs/core/check-allowable-operation.directive.md @@ -1,11 +1,11 @@ --- -Title: Node Permission directive +Title: Check Allowable Operation directive Added: v2.0.0 Status: Active -Last reviewed: 2018-11-20 +Last reviewed: 2019-02-13 --- -# [Node Permission directive](../../lib/core/directives/node-permission.directive.ts "Defined in node-permission.directive.ts") +# [Check Allowable Operation directive](../../lib/core/directives/check-allowable-operation.directive.ts "Defined in check-allowable-operation.directive.ts") Selectively disables an HTML element or Angular component. @@ -17,7 +17,7 @@ Selectively disables an HTML element or Angular component. - [Details](#details) - [HTML element example](#html-element-example) - [Angular component example](#angular-component-example) - - [Implementing the NodePermissionSubject interface](#implementing-the-nodepermissionsubject-interface) + - [Implementing the NodeAllowableOperationSubject interface](#implementing-the-nodeallowableoperationsubject-interface) - [Defining your component as an EXTENDIBLE_COMPONENT parent component](#defining-your-component-as-an-extendible_component-parent-component) ## Basic Usage @@ -47,7 +47,7 @@ Selectively disables an HTML element or Angular component. ## Details -The [Node Permission directive](../core/node-permission.directive.md) lets you disable an HTML element or Angular component +The [Check Allowable Operation Directive](../core/check-allowable-operation.directive.md) lets you disable an HTML element or Angular component by taking a collection of [`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md) instances and checking their permissions. The decorated element will be disabled if: @@ -59,8 +59,7 @@ The decorated element will be disabled if: A typical use case is to bind a [Document List](../content-services/document-list.component.md) selection property to a toolbar button. In the following example, the "Delete" button should -be disabled if no selection is present or if user does not have permission to delete at least one -node in the selection: +be disabled if no selection is present or if user does not have permission to delete at least one node in the selection: ```html @@ -81,7 +80,7 @@ one or more documents that they have permission to delete. ### Angular component example -You can add the directive to any Angular component that implements the [`NodePermissionSubject`](../../lib/core/directives/node-permission.directive.ts) +You can add the directive to any Angular component that implements the [`NodeAllowableOperationSubject`](../../lib/core/directives/check-allowable-operation.directive.ts) interface (the [Upload Drag Area component](../content-services/upload-drag-area.component.md), for example). You can also use it in much the same way as you would with an HTML element: @@ -96,21 +95,21 @@ for example). You can also use it in much the same way as you would with an HTML ``` To enable your own component to work with this directive, you need to implement the -[`NodePermissionSubject`](../../lib/core/directives/node-permission.directive.ts) interface and also define it as an +[`NodeAllowableOperationSubject`](../../lib/core/directives/check-allowable-operation.directive.ts) interface and also define it as an [`EXTENDIBLE_COMPONENT`](../../lib/core/interface/injection.tokens.ts) parent component, as described in the following sections. -### Implementing the NodePermissionSubject interface +### Implementing the NodeAllowableOperationSubject interface -The component must implement the [`NodePermissionSubject`](../../lib/core/directives/node-permission.directive.ts) interface which means it must have a +The component must implement the [`NodeAllowableOperationSubject`](../../lib/core/directives/check-allowable-operation.directive.ts) interface which means it must have a boolean `disabled` property. This is the property that will be set by the directive: ```js import { NodePermissionSubject } from '@alfresco/adf-core'; @Component({...}) -export class UploadDragAreaComponent implements NodePermissionSubject { +export class UploadDragAreaComponent implements NodeAllowableOperationSubject { public disabled: boolean = false; } ``` @@ -134,8 +133,7 @@ import { EXTENDIBLE_COMPONENT } from '@alfresco/adf-core'; { provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent)} ] }) -export class UploadDragAreaComponent implements NodePermissionSubject { ... } +export class UploadDragAreaComponent implements NodeAllowableOperationSubject { ... } ``` -**Note:** the usage of **viewProviders** (instead of **providers**) is very important, especially -if you want to use this directive on a transcluded component. +**Note:** the usage of **viewProviders** (instead of **providers**) is very important, especially if you want to use this directive on a transcluded component. diff --git a/docs/core/content.service.md b/docs/core/content.service.md index d9286b186d..6cb789b7ca 100644 --- a/docs/core/content.service.md +++ b/docs/core/content.service.md @@ -2,7 +2,7 @@ Title: Content service Added: v2.0.0 Status: Active -Last reviewed: 2018-11-13 +Last reviewed: 2019-02-13 --- # [Content service](../../lib/core/services/content.service.ts "Defined in content.service.ts") @@ -56,14 +56,10 @@ Accesses app-generated data objects via URLs and file downloads. Gets content for the given node. - _nodeId:_ `string` - ID of the target node - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`` - Content data -- **hasAllowableOperations**(node: `any`): `boolean`
- Checks if the node has the properties allowableOperations - - _node:_ `any` - Node to check allowableOperations - - **Returns** `boolean` - True if the node has the property, false otherwise -- **hasAllowableOperations**(node: `Node`, permission: [`AllowableOperationsEnum`](../../lib/core/models/allowable-operations.enum.ts)`|string`): `boolean`
+- **hasAllowableOperations**(node: `Node`, allowableOperation: [`AllowableOperationsEnum`](../../lib/core/models/allowable-operations.enum.ts)`|string`): `boolean`
Checks if the user has permissions on that node - _node:_ `Node` - Node to check allowableOperations - - _permission:_ [`AllowableOperationsEnum`](../../lib/core/models/allowable-operations.enum.ts)`|string` - Create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions + - _allowableOperation:_ [`AllowableOperationsEnum`](../../lib/core/models/allowable-operations.enum.ts)`|string` - Create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions - **Returns** `boolean` - True if the user has the required permissions, false otherwise ## Details diff --git a/docs/versionIndex.md b/docs/versionIndex.md index 4419fcaca8..8d4c312fa9 100644 --- a/docs/versionIndex.md +++ b/docs/versionIndex.md @@ -232,6 +232,7 @@ backend services have been tested with each released version of ADF. - [Card item types service](core/card-item-types.service.md) - [Card view update service](core/card-view-update.service.md) - [Card view component](core/card-view.component.md) +- [Check allowable operation directive](core/check-allowable-operation.directive.md) - [Checklist component](process-services/checklist.component.md) - [Comment list component](core/comment-list.component.md) - [Comment process service](core/comment-process.service.md) @@ -286,7 +287,6 @@ backend services have been tested with each released version of ADF. - [Node delete directive](core/node-delete.directive.md) - [Node favorite directive](core/node-favorite.directive.md) - [Node name tooltip pipe](core/node-name-tooltip.pipe.md) -- [Node permission directive](core/node-permission.directive.md) - [Node permission service](content-services/node-permission.service.md) - [Node restore directive](core/node-restore.directive.md) - [Node service](core/node.service.md) From 64058d7f0360e978f93d9c55f09d5e7e92edc358 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 13 Feb 2019 21:12:20 +0000 Subject: [PATCH 003/223] Update RelNote300.md --- docs/release-notes/RelNote300.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/RelNote300.md b/docs/release-notes/RelNote300.md index bb5d548d69..e1ba04cb3c 100644 --- a/docs/release-notes/RelNote300.md +++ b/docs/release-notes/RelNote300.md @@ -44,9 +44,9 @@ If you want to be updated on the [ADF roadmap](../roadmap.md), check the public ## Goals for this release -This is a major release of the Alfresco Application Development Framework, developed to speed up the applications in production environments, thanks to a porting of the [JS-API](https://github.com/Alfresco/alfresco-js-api) to Typescript. All the benefits of [Extensibility](../user-guide/app-extensions.md), initially introduced in [Alfresco Content Application (ACA)](https://github.com/Alfresco/alfresco-content-app) and the Alfresco Digital Workspace (ADW), are now available at the framework level and more enhancements are planned to be introduced in the following releases. +This is a major release of the Alfresco Application Development Framework, developed to speed up the applications running in production environments, thanks to a porting of the [JS-API](https://github.com/Alfresco/alfresco-js-api) to Typescript. All the benefits of [Extensibility](../user-guide/app-extensions.md), initially introduced in [Alfresco Content Application (ACA)](https://github.com/Alfresco/alfresco-content-app) and the Alfresco Digital Workspace (ADW), are now available at the framework level and more enhancements are planned to be introduced in the following releases. -This new major version of ADF has been updated to the latest and greatest [Angular 7](https://angular.io/), together with a first iteration on supporting [Activiti 7](https://www.activiti.org/) and the next generation of Alfresco BPM Engines. Also the [Yeoman App Generator](https://github.com/Alfresco/generator-ng2-alfresco-app) has been updated to let you create five small [Angular CLI](https://cli.angular.io/) based applications to help get you started: one on content only, two on process only (on the current generation of Alfresco BPM Engine and one with the new ones), two on content and process (again, using the current and next generations of the Alfresco BPM Engines). +This new major version of ADF has been updated to the latest and greatest [Angular 7](https://angular.io/), together with a first iteration of support for [Activiti 7](https://www.activiti.org/), the next generation Cloud Native implementation of Activiti. Also the [Yeoman App Generator](https://github.com/Alfresco/generator-ng2-alfresco-app) has been updated to let you create five small [Angular CLI](https://cli.angular.io/) based applications to help get you started: one on content only, two on process only (using APS 1.6.4 or higher or using Activiti 7), two on content and process. Please report issues with this release in the [issue tracker](https://github.com/Alfresco/alfresco-ng2-components/issues/new). You can collaborate on this release or share feedback by using the discussion tools on [Gitter](http://gitter.im/Alfresco/alfresco-ng2-components). @@ -160,11 +160,11 @@ Shows the folder and subfolders of a node as a tree view. For more information a Following the [SEMVER 2.0](https://semver.org/) strategy, ADF 3.0.0 introduces breaking changes only in major versions like this one. For this reason, all the features, components and services, marked as deprecated in ADF 2.X, have been removed. -### Activiti 7 and APS 2 support (Experimental) +### Activiti 7 support (Experimental) -From ADF 3.0.0, Alfresco is introducing support for the new generation of BPM Engines, in both the **Open Source** and **Enterprise** editions. This is a first iteration and more coverage of the services will be added in the following versions of ADF. This is the reason why this support is marked as **experimental** for this release. +From ADF 3.0.0, Alfresco is excited to announce ADF support for [Activiti 7](https://www.activiti.org/). This is a first iteration and more coverage of the services will be added in the following versions of ADF. This is the reason why this support is marked as **experimental** for this release. The next release (3.1) plans on moving from experimental to standard ADF components with more complete coverage. -This release of ADF introduces a collection of brand new components re-designed and implemented from scratch to be compliant with [Activiti 7](https://www.activiti.org/) and the new Enterprise Alfresco BPM Engine (powered by [Activiti 7](https://www.activiti.org/)). +This ADF release introduces a collection of brand new components re-designed and implemented from scratch for [Activiti 7](https://www.activiti.org/). Below is a list of all new components services and pipes added along with links to documentation and source code: From 1aac31ec066711648c4d3d258ca5b60091cbe9a8 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 13 Feb 2019 21:15:12 +0000 Subject: [PATCH 004/223] Update RelNote300.md --- docs/release-notes/RelNote300.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/RelNote300.md b/docs/release-notes/RelNote300.md index e1ba04cb3c..74f513e3b4 100644 --- a/docs/release-notes/RelNote300.md +++ b/docs/release-notes/RelNote300.md @@ -22,7 +22,7 @@ If you want to be updated on the [ADF roadmap](../roadmap.md), check the public - [JS-API in Typescript](#js-api-in-typescript) - [Angular 7](#angular-7) - [Deprecation removal](#deprecation-removal) - - [Activiti 7 and APS 2 support](#activiti-7-and-aps-2-support-experimental) + - [Activiti 7 support](#activiti-7-support-experimental) - [Icon Component](#icon-component) - [Localization](#localization) - [References](#references) @@ -60,7 +60,7 @@ Below are the most important new features of this release: - [Create Library Component](#create-library-component) - [Tree view component](#tree-view-component) - [Deprecation removal](#deprecation-removal) -- [Activiti 7 and APS 2 support (experimental)](#activiti-7-and-aps-2-support-experimental) +- [Activiti 7 support (experimental)](#activiti-7-support-experimental) ### Extensibility From 743db053d805d658be77ddd6073015dcaf1a19a4 Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Thu, 14 Feb 2019 13:35:33 +0200 Subject: [PATCH 005/223] [ADF-3861] Document List - aria-label on checkbox and actions menu (#4147) * Add aria-label on checkbox and actions menu * lint * [ADF-3861] - Change Document List checkbox tags for accesibility --- .../datatable/components/datatable/datatable.component.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/datatable/components/datatable/datatable.component.html b/lib/core/datatable/components/datatable/datatable.component.html index 3bde9af91b..65154e94a8 100644 --- a/lib/core/datatable/components/datatable/datatable.component.html +++ b/lib/core/datatable/components/datatable/datatable.component.html @@ -78,6 +78,8 @@
@@ -174,6 +176,7 @@ diff --git a/lib/core/pagination/pagination.component.html b/lib/core/pagination/pagination.component.html index e257a3fc64..4d2f0b4807 100644 --- a/lib/core/pagination/pagination.component.html +++ b/lib/core/pagination/pagination.component.html @@ -43,6 +43,7 @@ - + diff --git a/demo-shell/src/app/components/app-layout/cloud/cloud-layout.component.ts b/demo-shell/src/app/components/app-layout/cloud/cloud-layout.component.ts index 35b16f6894..f3513c1629 100644 --- a/demo-shell/src/app/components/app-layout/cloud/cloud-layout.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/cloud-layout.component.ts @@ -26,7 +26,7 @@ import { CloudLayoutService } from './services/cloud-layout.service'; }) export class CloudLayoutComponent implements OnInit { displayMenu = true; - applicationName: string; + appName: string; constructor( private router: Router, @@ -37,7 +37,7 @@ export class CloudLayoutComponent implements OnInit { ngOnInit() { let root: string = ''; this.route.params.subscribe((params) => { - this.applicationName = params.applicationName; + this.appName = params.appName; }); if (this.route.snapshot && this.route.snapshot.firstChild) { @@ -56,10 +56,10 @@ export class CloudLayoutComponent implements OnInit { } onStartTask() { - this.router.navigate([`/cloud/${this.applicationName}/start-task/`]); + this.router.navigate([`/cloud/${this.appName}/start-task/`]); } onStartProcess() { - this.router.navigate([`/cloud/${this.applicationName}/start-process/`]); + this.router.navigate([`/cloud/${this.appName}/start-process/`]); } } diff --git a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html index 55a46b99b2..02aa4fca19 100644 --- a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html @@ -1,31 +1,29 @@
- + - diff --git a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts index ef0ddf682e..dd7e78f06a 100644 --- a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts @@ -42,7 +42,7 @@ export class ProcessesCloudDemoComponent implements OnInit { @ViewChild('processFiltersCloud') processFiltersCloud: ProcessFiltersCloudComponent; - applicationName: string = ''; + appName: string = ''; isFilterLoaded: boolean; filterId: string = ''; @@ -71,7 +71,7 @@ export class ProcessesCloudDemoComponent implements OnInit { ngOnInit() { this.isFilterLoaded = false; this.route.parent.params.subscribe((params) => { - this.applicationName = params.applicationName; + this.appName = params.appName; }); this.route.queryParams.subscribe((params) => { @@ -116,7 +116,7 @@ export class ProcessesCloudDemoComponent implements OnInit { onProcessFilterAction(filterAction: any) { this.cloudLayoutService.setCurrentProcessFilterParam({ id: filterAction.filter.id }); if (filterAction.actionType === ProcessesCloudDemoComponent.ACTION_SAVE_AS) { - this.router.navigate([`/cloud/${this.applicationName}/processes/`], { queryParams: filterAction.filter }); + this.router.navigate([`/cloud/${this.appName}/processes/`], { queryParams: filterAction.filter }); } } diff --git a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.html index 8b8c2e805c..981810289d 100644 --- a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.html @@ -1,7 +1,7 @@ - diff --git a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts index 9a7891fd9f..ed1e24bb0a 100644 --- a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts @@ -25,7 +25,7 @@ import { CloudLayoutService } from './services/cloud-layout.service'; }) export class StartProcessCloudDemoComponent implements OnInit { - applicationName; + appName; processName: string; constructor(private appConfig: AppConfigService, @@ -37,7 +37,7 @@ export class StartProcessCloudDemoComponent implements OnInit { ngOnInit() { this.route.parent.params.subscribe((params) => { - this.applicationName = params.applicationName; + this.appName = params.appName; }); this.processName = this.appConfig.get('adf-start-process.name'); @@ -45,12 +45,12 @@ export class StartProcessCloudDemoComponent implements OnInit { onStartProcessSuccess() { this.cloudLayoutService.setCurrentProcessFilterParam({ key: 'running-processes' }); - this.router.navigate([`/cloud/${this.applicationName}/processes`]); + this.router.navigate([`/cloud/${this.appName}/processes`]); } onCancelStartProcess() { this.cloudLayoutService.setCurrentProcessFilterParam({ key: 'all-processes' }); - this.router.navigate([`/cloud/${this.applicationName}/processes`]); + this.router.navigate([`/cloud/${this.appName}/processes`]); } openSnackMessage(event: any) { diff --git a/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.html index f77a9e6619..adf11967eb 100644 --- a/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.html @@ -1,5 +1,5 @@ diff --git a/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.ts index 391460bc0f..516bd9bda2 100644 --- a/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/start-task-cloud-demo.component.ts @@ -25,7 +25,7 @@ import { CloudLayoutService } from './services/cloud-layout.service'; }) export class StartTaskCloudDemoComponent implements OnInit { - applicationName; + appName; constructor( private cloudLayoutService: CloudLayoutService, @@ -36,18 +36,18 @@ export class StartTaskCloudDemoComponent implements OnInit { ngOnInit() { this.route.parent.params.subscribe((params) => { - this.applicationName = params.applicationName; + this.appName = params.appName; }); } onStartTaskSuccess() { this.cloudLayoutService.setCurrentTaskFilterParam({key: 'my-tasks'}); - this.router.navigate([`/cloud/${this.applicationName}/tasks`]); + this.router.navigate([`/cloud/${this.appName}/tasks`]); } onCancelStartTask() { this.cloudLayoutService.setCurrentTaskFilterParam({key: 'my-tasks'}); - this.router.navigate([`/cloud/${this.applicationName}/tasks`]); + this.router.navigate([`/cloud/${this.appName}/tasks`]); } openSnackMessage(event: any) { diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html index 3ed80ff5f4..fbf0675087 100644 --- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html @@ -3,10 +3,10 @@ arrow_back Go Back -

Simple page to show the taskId: {{ taskId }} of the app: {{ applicationName }}

+

Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}

diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts index 6746612b47..c5ade39624 100644 --- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.ts @@ -25,7 +25,7 @@ import { ActivatedRoute, Router } from '@angular/router'; export class TaskDetailsCloudDemoComponent { taskId: string; - applicationName: string; + appName: string; readOnly = false; constructor(private route: ActivatedRoute, private router: Router) { @@ -33,12 +33,12 @@ export class TaskDetailsCloudDemoComponent { this.taskId = params.taskId; }); this.route.parent.params.subscribe((params) => { - this.applicationName = params.applicationName; + this.appName = params.appName; }); } onGoBack() { - this.router.navigate([`/cloud/${this.applicationName}/`]); + this.router.navigate([`/cloud/${this.appName}/`]); } } diff --git a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.html index bbc849baef..6cc82275b1 100644 --- a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.html @@ -1,16 +1,14 @@
- - { - this.applicationName = params.applicationName; + this.appName = params.appName; }); this.route.queryParams.subscribe((params) => { @@ -100,7 +100,7 @@ export class TasksCloudDemoComponent implements OnInit { onRowClick(taskId) { if (!this.multiselect && this.selectionMode !== 'multiple') { - this.router.navigate([`/cloud/${this.applicationName}/task-details/${taskId}`]); + this.router.navigate([`/cloud/${this.appName}/task-details/${taskId}`]); } } @@ -117,7 +117,7 @@ export class TasksCloudDemoComponent implements OnInit { onTaskFilterAction(filterAction: any) { this.cloudLayoutService.setCurrentTaskFilterParam({ id: filterAction.filter.id }); if (filterAction.actionType === TasksCloudDemoComponent.ACTION_SAVE_AS) { - this.router.navigate([`/cloud/${this.applicationName}/tasks/`], { queryParams: filterAction.filter }); + this.router.navigate([`/cloud/${this.appName}/tasks/`], { queryParams: filterAction.filter }); } } } diff --git a/docs/process-services-cloud/edit-process-filter-cloud.component.md b/docs/process-services-cloud/edit-process-filter-cloud.component.md index 5caee8dccb..b584f6c63f 100644 --- a/docs/process-services-cloud/edit-process-filter-cloud.component.md +++ b/docs/process-services-cloud/edit-process-filter-cloud.component.md @@ -27,8 +27,8 @@ Shows Process Filter Details. ```html @@ -62,13 +62,13 @@ Use the `appName` and `id` properties to choose which process filter to edit: ```html + [appName]="appName"> ``` ### Filter properties -You can supply various _filter properties_ to edit that will determine +You can supply various _filter properties_ to edit that will determine which processes are found by a filter. The full set of properties is given below: diff --git a/docs/process-services-cloud/edit-task-filter-cloud.component.md b/docs/process-services-cloud/edit-task-filter-cloud.component.md index 41ac7e39d0..225e58a10c 100644 --- a/docs/process-services-cloud/edit-task-filter-cloud.component.md +++ b/docs/process-services-cloud/edit-task-filter-cloud.component.md @@ -16,7 +16,7 @@ Edits Task Filter Details. ```html @@ -51,13 +51,13 @@ Use the `appName` and `id` properties to choose which task filter to edit: ```html + [appName]="appName"> ``` ### Filter properties -You can supply various *filter properties* to edit that will determine +You can supply various *filter properties* to edit that will determine which tasks are found by a filter. The full set of properties is given below: diff --git a/docs/process-services-cloud/process-list-cloud.component.md b/docs/process-services-cloud/process-list-cloud.component.md index 1b1daeb7e8..05114b5db0 100644 --- a/docs/process-services-cloud/process-list-cloud.component.md +++ b/docs/process-services-cloud/process-list-cloud.component.md @@ -27,7 +27,7 @@ Renders a list containing all the process instances matched by the parameters sp ```html + [appName]="'app-name'"> ``` @@ -51,7 +51,7 @@ when the process list is empty: | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | | appVersion | `string` | "" | The related application version. | -| applicationName | `string` | "" | The name of the application. | +| appName | `string` | "" | The name of the application. | | businessKey | `string` | "" | Filter the tasks to display only the ones with this businessKey value. | | id | `string` | "" | Filter the processes to display only the ones with this ID. | | initiator | `string` | "" | Name of the initiator of the process. | @@ -100,7 +100,7 @@ You can define a custom schema for the list in the `app.config.json` file and ac ```html + [appName]="'appName'"> ``` @@ -133,7 +133,7 @@ information defined in `app.config.json` as in the example below: ```html + [appName]="'appName'" > @@ -157,7 +157,7 @@ let sorting = [{ orderBy: 'status', direction: 'desc' }]; ```html ``` diff --git a/docs/process-services-cloud/task-header-cloud.component.md b/docs/process-services-cloud/task-header-cloud.component.md index cc1a16fa5a..822ff317e4 100644 --- a/docs/process-services-cloud/task-header-cloud.component.md +++ b/docs/process-services-cloud/task-header-cloud.component.md @@ -15,7 +15,7 @@ Shows all the information related to a task. ```html ``` @@ -39,12 +39,12 @@ Shows all the information related to a task. ## Details -The component populates an internal array of +The component populates an internal array of [CardViewModel](../core/card-view.component.md) with the information that we want to display. By default all properties are displayed: -**_assignee_**, **_status_**, **_priority_**, **_dueDate_**, **_category_**, **_parentName_**, **_created_**, **_id_**, **_description_**, **_formName_**. +**_assignee_**, **_status_**, **_priority_**, **_dueDate_**, **_category_**, **_parentName_**, **_created_**, **_id_**, **_description_**, **_formName_**. However, you can also choose which properties to show using a configuration in `app.config.json`: diff --git a/docs/process-services-cloud/task-list-cloud.component.md b/docs/process-services-cloud/task-list-cloud.component.md index f15c77c634..5f976268c2 100644 --- a/docs/process-services-cloud/task-list-cloud.component.md +++ b/docs/process-services-cloud/task-list-cloud.component.md @@ -28,7 +28,7 @@ Renders a list containing all the tasks matched by the parameters specified. ```html + [appName]="'APPLICATION-NAME'" > ``` @@ -51,7 +51,7 @@ when the task list is empty: | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | -| applicationName | `string` | "" | The name of the application. | +| appName | `string` | "" | The name of the application. | | assignee | `string` | "" | The assignee of the process. Possible values are: "assignee" (the current user is the assignee), "candidate" (the current user is a task candidate", "group_x" (the task is assigned to a group where the current user is a member, no value (the current user is involved). | | createdDate | `string` | "" | Filter the tasks. Display only tasks created on the supplied date. | | dueDate | `string` | "" | Filter the tasks. Display only tasks with dueDate equal to the supplied date. | @@ -153,7 +153,7 @@ You can use an HTML-based schema and an `app.config.json` custom schema declarat ```html + [appName]="'appName'"> @@ -187,7 +187,7 @@ The Tasklist also supports pagination as shown in the example below: ```html + [appName]="'APPLICATION-NAME'"> diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts index 9e3f42207c..de7fc7f58b 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts @@ -146,13 +146,13 @@ describe('ProcessListCloudComponent', () => { done(); }); - component.applicationName = appName.currentValue; + component.appName = appName.currentValue; component.ngOnChanges({ 'appName': appName }); fixture.detectChanges(); }); it('should reload tasks when reload() is called', (done) => { - component.applicationName = 'fake'; + component.appName = 'fake'; spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList)); component.success.subscribe((res) => { expect(res).toBeDefined(); diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts index d59ab56d4c..196b233cd2 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts @@ -43,7 +43,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan /** The name of the application. */ @Input() - applicationName: string = ''; + appName: string = ''; /** The related application version. */ @Input() @@ -221,7 +221,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan private createRequestNode(): ProcessQueryCloudRequestModel { let requestNode = { - appName: this.applicationName, + appName: this.appName, appVersion: this.appVersion, maxItems: this.size, skipCount: this.skipCount, diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts index 3c04e1d660..c173209509 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts @@ -155,13 +155,13 @@ describe('TaskListCloudComponent', () => { expect(component.rows[0].entry['standAlone']).toBeTruthy(); done(); }); - component.applicationName = appName.currentValue; + component.appName = appName.currentValue; component.ngOnChanges({ 'appName': appName }); fixture.detectChanges(); }); it('should reload tasks when reload() is called', (done) => { - component.applicationName = 'fake'; + component.appName = 'fake'; spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTask)); component.success.subscribe((res) => { expect(res).toBeDefined(); @@ -204,7 +204,7 @@ describe('TaskListCloudComponent', () => { it('should reload the task list when input parameters changed', () => { const getTaskByRequestSpy = spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTask)); - component.applicationName = 'mock-app-name'; + component.appName = 'mock-app-name'; component.priority = 1; component.status = 'mock-status'; component.lastModifiedFrom = 'mock-lastmodified-date'; diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts index 76eb11c0fe..e8fd279100 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts @@ -42,7 +42,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges /** The name of the application. */ @Input() - applicationName: string = ''; + appName: string = ''; /** * The assignee of the process. Possible values are: "assignee" (the current user is the assignee), @@ -251,7 +251,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges private createRequestNode() { let requestNode = { - appName: this.applicationName, + appName: this.appName, assignee: this.assignee, id: this.id, name: this.name, From bfec78aec92e3b0edc06d8d21e3462ef705bb0af Mon Sep 17 00:00:00 2001 From: Suzana Dirla Date: Thu, 21 Feb 2019 14:49:47 +0200 Subject: [PATCH 023/223] [ADF-4083] search facetFields - empty spaced labels support (#4322) * [ADF-4083] ignore wrapping quotes for facet labels - bucket selection needs fixing * [ADF-4083] fix bucket selection - generate missing filterQuery values * [ADF-4083] fix label display * [ADF-4083] renaming * [ADF-4083] support also single quotes label wrappings - update documentation - tests * [ADF-4083] automatically escape facet field labels having spaces - better fix * [ADF-4083] remove unneeded mentions from documentation * [ADF-4083] clean-up code * [ADF-4083] better naming param * [ADF-4083] small refactoring --- .../search-filter.component.md | 21 +++++---- docs/docassets/images/search-facet-fields.png | Bin 19660 -> 39016 bytes .../search-filter/search-filter.component.ts | 12 ++++- .../search-query-builder.service.spec.ts | 43 ++++++++++++++++++ .../search/search-query-builder.service.ts | 12 ++++- 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/docs/content-services/search-filter.component.md b/docs/content-services/search-filter.component.md index b4af1f0de0..9666a0f693 100644 --- a/docs/content-services/search-filter.component.md +++ b/docs/content-services/search-filter.component.md @@ -66,7 +66,7 @@ A typical configuration is shown below: "expanded": true, "fields": [ { "field": "content.mimetype", "mincount": 1, "label": "Type" }, - { "field": "content.size", "mincount": 1, "label": "Size" }, + { "field": "content.size", "mincount": 1, "label": "File Size" }, { "field": "creator", "mincount": 1, "label": "Creator" }, { "field": "modifier", "mincount": 1, "label": "Modifier" } ] @@ -254,13 +254,16 @@ export interface SearchWidgetSettings { ```json { "search": { - "facetFields": [ - { "field": "content.mimetype", "mincount": 1, "label": "Type" }, - { "field": "content.size", "mincount": 1, "label": "Size" }, - { "field": "creator", "mincount": 1, "label": "Creator" }, - { "field": "modifier", "mincount": 1, "label": "Modifier" }, - { "field": "created", "mincount": 1, "label": "Created" } - ] + "facetFields": { + "expanded": true, + "fields": [ + { "field": "content.mimetype", "mincount": 1, "label": "Type" }, + { "field": "content.size", "mincount": 1, "label": "File Size" }, + { "field": "creator", "mincount": 1, "label": "Creator" }, + { "field": "modifier", "mincount": 1, "label": "Modifier" }, + { "field": "created", "mincount": 1, "label": "Created" } + ] + } } } ``` @@ -268,7 +271,7 @@ export interface SearchWidgetSettings { Every field declared within the `facetFields` group is presented by a separate collapsible category at runtime. By default, users see only the top 5 entries. -If there are more than 5 entries, the "Show more" button is displayed to let the user move to +If there are more than 5 entries, a button to show more items is displayed to let the user move to the next block of results. ![Facet Fields](../docassets/images/search-facet-fields.png) diff --git a/docs/docassets/images/search-facet-fields.png b/docs/docassets/images/search-facet-fields.png index 3e87f851b783b5cebbcf56547b88fcbe3087dbfc..ac39e551d6aeb021e83e6f8fbf3a5358c0f46906 100644 GIT binary patch literal 39016 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 literal 19660 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 diff --git a/lib/content-services/search/components/search-filter/search-filter.component.ts b/lib/content-services/search/components/search-filter/search-filter.component.ts index b618447c8b..57010bb9a2 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.ts @@ -192,7 +192,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] { return configFacetFields.map((field) => { const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {}; - const responseBuckets = this.getResponseBuckets(responseField) + const responseBuckets = this.getResponseBuckets(responseField, field) .filter(this.getFilterByMinCount(field.mincount)); const bucketList = new SearchFilterList(responseBuckets, field.pageSize); @@ -270,10 +270,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return result; } - private getResponseBuckets(responseField: GenericFacetResponse): FacetFieldBucket[] { + private getResponseBuckets(responseField: GenericFacetResponse, configField: FacetField): FacetFieldBucket[] { return ((responseField && responseField.buckets) || []).map((respBucket) => { respBucket['count'] = this.getCountValue(respBucket); + respBucket.filterQuery = respBucket.filterQuery || this.getCorrespondingFilterQuery(configField, respBucket.label); return { ...respBucket, checked: false, @@ -312,4 +313,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return bucket.count >= mincount; }; } + + private getCorrespondingFilterQuery (configFacetItem: FacetField, bucketLabel: string): string { + if (!configFacetItem.field || !bucketLabel) { + return null; + } + return `${configFacetItem.field}:"${bucketLabel}"`; + } } diff --git a/lib/content-services/search/search-query-builder.service.spec.ts b/lib/content-services/search/search-query-builder.service.spec.ts index 79e2df7434..96c97f916a 100644 --- a/lib/content-services/search/search-query-builder.service.spec.ts +++ b/lib/content-services/search/search-query-builder.service.spec.ts @@ -216,6 +216,20 @@ describe('SearchQueryBuilder', () => { expect(field).toBeFalsy(); }); + it('should fetch facets from the config by label with spaces and return field with request compatible label (escaped)', () => { + const config: SearchConfiguration = { + categories: [], + facetFields: { 'fields': [ + { 'field': 'content.size', 'mincount': 1, 'label': 'Label with spaces' } + ]} + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + const field = builder.getFacetField('Label with spaces'); + + expect(field.label).toBe('"Label with spaces"'); + expect(field.field).toBe('content.size'); + }); + xit('should build query and raise an event on update', async () => { const builder = new SearchQueryBuilderService(buildConfig({}), null); const query = {}; @@ -371,6 +385,35 @@ describe('SearchQueryBuilder', () => { expect(compiled.facetFields.facets).toEqual(jasmine.objectContaining(config.facetFields.fields)); }); + it('should build query with custom facet fields automatically getting their request compatible labels', () => { + const spacesLabel = { + configValue: 'label with spaces', + requestCompatibleValue: '"label with spaces"' + }; + const noSpacesLabel = { + configValue: 'label', + requestCompatibleValue: 'label' + }; + + const config: SearchConfiguration = { + categories: [ + { id: 'cat1', enabled: true } + ], + facetFields: { fields: [ + { field: 'field1', label: spacesLabel.configValue, mincount: 1, limit: null, offset: 0, prefix: null }, + { field: 'field2', label: noSpacesLabel.configValue, mincount: 1, limit: null, offset: 0, prefix: null } + ]} + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + builder.queryFragments['cat1'] = 'cm:name:test'; + + const compiled = builder.buildQuery(); + expect(compiled.facetFields.facets[0].label).toEqual(spacesLabel.requestCompatibleValue); + expect(compiled.facetFields.facets[0].label).not.toEqual(spacesLabel.configValue); + expect(compiled.facetFields.facets[1].label).toEqual(noSpacesLabel.requestCompatibleValue); + expect(compiled.facetFields.facets[1].label).toEqual(noSpacesLabel.configValue); + }); + it('should build query with custom facet intervals', () => { const config: SearchConfiguration = { categories: [ diff --git a/lib/content-services/search/search-query-builder.service.ts b/lib/content-services/search/search-query-builder.service.ts index c6037242c3..3385632d4a 100644 --- a/lib/content-services/search/search-query-builder.service.ts +++ b/lib/content-services/search/search-query-builder.service.ts @@ -175,6 +175,7 @@ export class SearchQueryBuilderService { const fields = this.config.facetFields.fields || []; const result = fields.find((field) => field.label === label); if (result) { + result.label = this.getSupportedLabel(result.label); return { ...result }; } } @@ -352,6 +353,7 @@ export class SearchQueryBuilderService { if (this.userFacetBuckets) { Object.keys(this.userFacetBuckets).forEach((key) => { const subQuery = (this.userFacetBuckets[key] || []) + .filter((bucket) => bucket.filterQuery) .map((bucket) => bucket.filterQuery) .join(' OR '); if (subQuery) { @@ -374,7 +376,7 @@ export class SearchQueryBuilderService { facets: facetFields.map((facet) => { field: facet.field, mincount: facet.mincount, - label: facet.label, + label: this.getSupportedLabel(facet.label), limit: facet.limit, offset: facet.offset, prefix: facet.prefix @@ -384,4 +386,12 @@ export class SearchQueryBuilderService { return null; } + + getSupportedLabel(configLabel: string): string { + const spaceInsideLabelIndex = configLabel.search(/\s/g); + if (spaceInsideLabelIndex > -1) { + return `"${configLabel}"`; + } + return configLabel; + } } From b967b77946b4d051280d8b955ebc5dd129ae76d7 Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Fri, 22 Feb 2019 03:48:22 +0200 Subject: [PATCH 024/223] [ADF-4068] StartTaskComponent - add description empty space validator (#4341) * [ADF-4068] StartTaskComponent - add description empty space validator * [ADF-4068] StartTaskCloudComponent - add unit tests * [ADF-4068] - lint --- .../components/start-task-cloud.component.spec.ts | 10 ++++++++++ .../components/start-task-cloud.component.ts | 5 ++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts index 7c3a4c14cc..e033b20fb0 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts @@ -213,4 +213,14 @@ describe('StartTaskCloudComponent', () => { fixture.detectChanges(); expect(name.valid).toBeTruthy(); }); + it('should emit error when description have only white spaces', () => { + fixture.detectChanges(); + let description = component.taskForm.controls['description']; + description.setValue(' '); + fixture.detectChanges(); + expect(description.valid).toBeFalsy(); + description.setValue(''); + fixture.detectChanges(); + expect(description.valid).toBeTruthy(); + }); }); diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts index 7f18cd8476..53087ac2b3 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts @@ -131,7 +131,7 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy { this.taskForm = this.formBuilder.group({ name: new FormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength()), this.whitespaceValidator]), priority: new FormControl(), - description: new FormControl() + description: new FormControl('', [this.whitespaceValidator]) }); } @@ -206,7 +206,7 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy { public whitespaceValidator(control: FormControl) { const isWhitespace = (control.value || '').trim().length === 0; - const isValid = !isWhitespace; + const isValid = control.value.length === 0 || !isWhitespace; return isValid ? null : { 'whitespace': true }; } @@ -217,5 +217,4 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy { get priorityController(): AbstractControl { return this.taskForm.get('priority'); } - } From bf4d1a28067735df19f555d6d2a169bb89b82c5c Mon Sep 17 00:00:00 2001 From: cristinaj Date: Fri, 22 Feb 2019 12:50:37 +0200 Subject: [PATCH 025/223] [ADF-4121] Fix tasks-custom-filters test (#4329) * Fix tasks-custom-filters test * Changed the locators for edit task cloud component to be unique. --- .../adf/process-cloud/editTaskFilterCloudComponent.ts | 8 ++++---- .../edit-task-filters-component.e2e.ts | 1 - e2e/process-services-cloud/tasks-custom-filters.e2e.ts | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts b/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts index 3653172760..11de3de411 100644 --- a/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts +++ b/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts @@ -51,7 +51,7 @@ export class EditTaskFilterCloudComponent { } getStateFilterDropDownValue() { - return element(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-state'] span")).getText(); + return element.all(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-state'] span")).first().getText(); } setSortFilterDropDown(option) { @@ -65,7 +65,7 @@ export class EditTaskFilterCloudComponent { } getSortFilterDropDownValue() { - let elementSort = element(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-sort'] span")); + let elementSort = element.all(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-sort'] span")).first(); Util.waitUntilElementIsVisible(elementSort); return elementSort.getText(); } @@ -81,11 +81,11 @@ export class EditTaskFilterCloudComponent { } getOrderFilterDropDownValue() { - return element(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-order'] span")).getText(); + return element.all(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-order'] span")).first().getText(); } clickOnDropDownArrow(option) { - let dropDownArrow = element(by.css("mat-form-field[data-automation-id='" + option + "'] div[class*='arrow']")); + let dropDownArrow = element.all(by.css("mat-form-field[data-automation-id='" + option + "'] div[class*='arrow']")).first(); Util.waitUntilElementIsVisible(dropDownArrow); dropDownArrow.click(); Util.waitUntilElementIsVisible(this.selectedOption); diff --git a/e2e/process-services-cloud/edit-task-filters-component.e2e.ts b/e2e/process-services-cloud/edit-task-filters-component.e2e.ts index 04f15cbcf3..3c43922e2a 100644 --- a/e2e/process-services-cloud/edit-task-filters-component.e2e.ts +++ b/e2e/process-services-cloud/edit-task-filters-component.e2e.ts @@ -149,7 +149,6 @@ describe('Edit task filters cloud', () => { expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getSortFilterDropDownValue()).toEqual('PRIORITY'); tasksCloudDemoPage.editTaskFilterCloudComponent().clickDeleteButton(); tasksCloudDemoPage.customTaskFilter('custom-new').clickTaskFilter(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader(); expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getSortFilterDropDownValue()).toEqual('ID'); diff --git a/e2e/process-services-cloud/tasks-custom-filters.e2e.ts b/e2e/process-services-cloud/tasks-custom-filters.e2e.ts index e3f320ce26..4cc97626b0 100644 --- a/e2e/process-services-cloud/tasks-custom-filters.e2e.ts +++ b/e2e/process-services-cloud/tasks-custom-filters.e2e.ts @@ -113,7 +113,7 @@ describe('Task filters cloud', () => { }); it('[C290139] Should display only tasks with all states when All is selected from state dropdown', () => { - tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().setAssignment(' ') + tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().clearAssignment() .setStateFilterDropDown('ALL'); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(deletedTaskName); From bda7e07b5259bd2f8a88afd01ef889e3c95bced1 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Fri, 22 Feb 2019 14:19:41 +0000 Subject: [PATCH 026/223] [ADF-4125] simplify extension load in extension module (#4214) * add extension load in extension module * add viewer extensions * fix license header * fix node passed in the viewer extension * fix node passed in the viewer extension * startup factory extension * startup factory extension * fix script * fix beta tag build * fix build * fix build * refactoring configuration files * extension using map * fix build * fix config * fix test * fix test --- .travis.yml | 25 +- angular.json | 16 +- demo-shell/src/app/app-extension.module.ts | 27 ++ demo-shell/src/app/app.module.ts | 7 +- .../app/components/about/about.component.ts | 3 +- .../start-process-cloud-demo.component.ts | 1 + .../file-view/file-view.component.html | 2 +- .../file-view/file-view.component.scss | 9 + .../file-view/file-view.component.ts | 7 +- .../components/form/form-list.component.ts | 3 +- .../task-attachments.component.ts | 3 +- .../src/app/extensions/extensions.module.ts | 51 --- demo-shell/src/assets/.gitkeep | 0 demo-shell/src/assets/app.extensions.json | 12 +- .../src/assets/plugins/monaco-extension.json | 17 + demo-shell/src/assets/plugins/plugin1.json | 7 +- demo-shell/src/assets/plugins/plugin2.json | 7 +- demo-shell/src/tsconfig.app.json | 25 +- .../card-view/aspect-oriented-config.e2e.ts | 19 +- .../content-node-selector.component.spec.ts | 33 +- .../content-node-share.dialog.spec.ts | 25 -- .../document-list.component.spec.ts | 2 +- .../folder-create.directive.spec.ts | 19 -- lib/content-services/ng-package.json | 2 +- .../services/tree-view.service.spec.ts | 2 +- lib/content-services/tsconfig.json | 41 --- lib/content-services/tsconfig.lib.json | 6 + lib/content-services/tsconfig.spec.json | 6 + lib/core/app-config/schema.json | 1 - lib/core/core.module.ts | 2 + .../form/components/form.component.spec.ts | 6 +- lib/core/ng-package.json | 5 +- lib/core/tsconfig.json | 46 --- lib/core/tsconfig.lib.json | 6 + lib/core/tsconfig.spec.json | 6 + .../viewer/components/viewer.component.html | 19 +- .../components/viewer.component.spec.ts | 4 +- .../viewer/components/viewer.component.ts | 18 +- lib/core/viewer/viewer.module.ts | 3 + lib/extensions/ng-package.json | 18 +- lib/extensions/package.json | 17 +- .../src/lib/components/public-api.ts | 21 ++ .../viewer/preview-extension.component.ts | 99 ++++++ .../src/lib/config/extension-utils.ts | 2 +- .../config/schema/app-extension.schema.json | 18 + .../schema/plugin-extension.schema.json | 5 +- lib/extensions/src/lib/extensions.module.ts | 33 +- .../src/lib/services/app-extension.service.ts | 14 +- .../lib/services/extension-loader.service.ts | 59 ++-- .../src/lib/services/extension.service.ts | 10 +- .../lib/services/startup-extension-factory.ts | 22 ++ lib/extensions/src/public-api.ts | 5 +- lib/extensions/tsconfig.lib.json | 35 +- lib/extensions/tsconfig.spec.json | 13 +- lib/extensions/tslint.json | 17 - lib/insights/ng-package.json | 2 +- lib/insights/tsconfig.json | 42 --- lib/insights/tsconfig.lib.json | 6 + lib/insights/tsconfig.spec.json | 6 + lib/process-services-cloud/ng-package.json | 18 +- .../src/lib/app/app-list-cloud.module.ts | 2 +- .../src/lib/group/group-cloud.module.ts | 2 +- .../src/lib/process-services-cloud.module.ts | 2 +- .../src/lib/process/process-cloud.module.ts | 2 +- .../process-filters-cloud.module.ts | 2 +- .../process-list/process-list-cloud.module.ts | 2 +- .../start-process-cloud.module.ts | 2 +- .../start-task/start-task-cloud.module.ts | 2 +- .../task-filters/task-filters-cloud.module.ts | 2 +- .../task-header/task-header-cloud.module.ts | 2 +- .../task/task-list/task-list-cloud.module.ts | 2 +- lib/process-services-cloud/src/public-api.ts | 1 + lib/process-services-cloud/tsconfig.lib.json | 33 +- lib/process-services-cloud/tsconfig.spec.json | 13 +- lib/process-services-cloud/tslint.json | 2 +- lib/process-services/ng-package.json | 2 +- .../components/process-list.component.spec.ts | 2 +- .../components/task-list.component.spec.ts | 2 +- lib/process-services/tsconfig.json | 42 --- lib/process-services/tsconfig.lib.json | 6 + lib/process-services/tsconfig.spec.json | 6 + lib/testing/tsconfig.lib.json | 32 +- lib/testing/tsconfig.spec.json | 13 +- lib/tsconfig.spec.json | 15 + package-lock.json | 315 +++++++++--------- package.json | 7 +- scripts/affected-libs.sh | 43 ++- scripts/build-extensions.sh | 2 +- scripts/build-process-services-cloud.sh | 2 +- scripts/ng-packagr.sh | 29 +- scripts/pr-reminder.js | 260 +++++++++++++++ scripts/smart-build.sh | 16 +- 92 files changed, 1035 insertions(+), 785 deletions(-) create mode 100644 demo-shell/src/app/app-extension.module.ts delete mode 100644 demo-shell/src/app/extensions/extensions.module.ts delete mode 100644 demo-shell/src/assets/.gitkeep create mode 100644 demo-shell/src/assets/plugins/monaco-extension.json delete mode 100644 lib/content-services/tsconfig.json create mode 100644 lib/content-services/tsconfig.lib.json create mode 100644 lib/content-services/tsconfig.spec.json delete mode 100644 lib/core/tsconfig.json create mode 100644 lib/core/tsconfig.lib.json create mode 100644 lib/core/tsconfig.spec.json create mode 100644 lib/extensions/src/lib/components/public-api.ts create mode 100644 lib/extensions/src/lib/components/viewer/preview-extension.component.ts create mode 100644 lib/extensions/src/lib/config/schema/app-extension.schema.json rename demo-shell/src/assets/extension.schema.json => lib/extensions/src/lib/config/schema/plugin-extension.schema.json (99%) rename demo-shell/src/app/extensions/extension.service.ts => lib/extensions/src/lib/services/app-extension.service.ts (77%) create mode 100644 lib/extensions/src/lib/services/startup-extension-factory.ts delete mode 100644 lib/extensions/tslint.json delete mode 100644 lib/insights/tsconfig.json create mode 100644 lib/insights/tsconfig.lib.json create mode 100644 lib/insights/tsconfig.spec.json delete mode 100644 lib/process-services/tsconfig.json create mode 100644 lib/process-services/tsconfig.lib.json create mode 100644 lib/process-services/tsconfig.spec.json create mode 100644 lib/tsconfig.spec.json create mode 100644 scripts/pr-reminder.js diff --git a/.travis.yml b/.travis.yml index ccdd4814c3..9bd21020d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ branches: only: - master - development + - /.*beta.*/ # TRAVIS_PULL_REQUEST == false means is running on dev branch and is not a PR stages: @@ -62,7 +63,7 @@ jobs: - stage: Unit test name: core script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "core$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test core --watch=false || exit 1; @@ -70,7 +71,7 @@ jobs: - stage: Unit test name: extensions script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "extensions$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test extensions --watch=false || exit 1; @@ -78,7 +79,7 @@ jobs: - stage: Unit test name: process-services script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "process-services$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test process-services --watch=false || exit 1; @@ -86,7 +87,7 @@ jobs: - stage: Unit test name: insights script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "insights$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test insights --watch=false || exit 1; @@ -94,7 +95,7 @@ jobs: - stage: Unit test name: content-services script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "content-services$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test content-services --watch=false || exit 1; @@ -102,7 +103,7 @@ jobs: - stage: Unit test name: process-services-cloud script: - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "process-services-cloud$" || $TRAVIS_PULL_REQUEST == "false" ]]; then ng test process-services-cloud --watch=false || exit 1; @@ -133,7 +134,7 @@ jobs: name: core script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -gnu -b $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "core$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 --host_sso "$E2E_HOST_SSO" --host_bpm "$E2E_HOST_BPM" --host_identity "$E2E_HOST_IDENTITY" -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder core --skip-lint --use-dist || exit 1;); @@ -142,7 +143,7 @@ jobs: name: process-services script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -gnu -b $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "process-services$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder process-services --skip-lint --use-dist || exit 1;); @@ -151,7 +152,7 @@ jobs: name: content-services script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -gnu -b $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "content-services$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder content-services --skip-lint --use-dist || exit 1;); @@ -160,7 +161,7 @@ jobs: name: search script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "content-services$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder search --skip-lint --use-dist || exit 1;); @@ -169,7 +170,7 @@ jobs: name: process-services-cloud script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "process-services-cloud$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 --host_sso "$E2E_HOST_SSO" --host_bpm "$E2E_HOST_BPM" --host_identity "$E2E_HOST_IDENTITY" -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder process-services-cloud --skip-lint --use-dist || exit 1;); @@ -178,7 +179,7 @@ jobs: name: insights script: AFFECTED_E2E="$(./scripts/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e")"; - AFFECTED_LIBS="$(./scripts/affected-libs.sh -b $TRAVIS_BRANCH)"; + AFFECTED_LIBS="$(./scripts/affected-libs.sh -b -gnu $TRAVIS_BRANCH)"; if [[ $AFFECTED_LIBS =~ "process-services-cloud$" || $AFFECTED_E2E = "e2e" || $TRAVIS_PULL_REQUEST == "false" ]]; then (./scripts/test-e2e-lib.sh -host localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e $E2E_EMAIL -b --folder insights --skip-lint --use-dist || exit 1;); diff --git a/angular.json b/angular.json index 3b79b6cd61..ccbbd5959d 100644 --- a/angular.json +++ b/angular.json @@ -540,7 +540,7 @@ "build": { "builder": "@angular-devkit/build-ng-packagr:build", "options": { - "tsConfig": "lib/core/tsconfig.json", + "tsConfig": "lib/core/tsconfig.lib.json", "project": "lib/core/ng-package.json" }, "configurations": { @@ -553,7 +553,7 @@ "builder": "@angular-devkit/build-angular:karma", "options": { "main": "lib/core/test.ts", - "tsConfig": "lib/core/tsconfig.json", + "tsConfig": "lib/core/tsconfig.spec.json", "karmaConfig": "lib/core/karma.conf.js", "codeCoverage": true, "sourceMap": true @@ -582,7 +582,7 @@ "build": { "builder": "@angular-devkit/build-ng-packagr:build", "options": { - "tsConfig": "lib/content-services/tsconfig.json", + "tsConfig": "lib/content-services/tsconfig.lib.json", "project": "lib/content-services/ng-package.json" }, "configurations": { @@ -595,7 +595,7 @@ "builder": "@angular-devkit/build-angular:karma", "options": { "main": "lib/content-services/test.ts", - "tsConfig": "lib/content-services/tsconfig.json", + "tsConfig": "lib/content-services/tsconfig.spec.json", "karmaConfig": "lib/content-services/karma.conf.js", "codeCoverage": true, "sourceMap": true @@ -624,7 +624,7 @@ "build": { "builder": "@angular-devkit/build-ng-packagr:build", "options": { - "tsConfig": "lib/process-services/tsconfig.json", + "tsConfig": "lib/process-services/tsconfig.lib.json", "project": "lib/process-services/ng-package.json" }, "configurations": { @@ -637,7 +637,7 @@ "builder": "@angular-devkit/build-angular:karma", "options": { "main": "lib/process-services/test.ts", - "tsConfig": "lib/process-services/tsconfig.json", + "tsConfig": "lib/process-services/tsconfig.spec.json", "karmaConfig": "lib/process-services/karma.conf.js", "codeCoverage": true, "sourceMap": true @@ -708,7 +708,7 @@ "build": { "builder": "@angular-devkit/build-ng-packagr:build", "options": { - "tsConfig": "lib/insights/tsconfig.json", + "tsConfig": "lib/extensions/tsconfig.lib.json", "project": "lib/insights/ng-package.json" }, "configurations": { @@ -721,7 +721,7 @@ "builder": "@angular-devkit/build-angular:karma", "options": { "main": "lib/insights/test.ts", - "tsConfig": "lib/insights/tsconfig.json", + "tsConfig": "lib/extensions/tsconfig.spec.json", "karmaConfig": "lib/insights/karma.conf.js", "codeCoverage": true, "sourceMap": true diff --git a/demo-shell/src/app/app-extension.module.ts b/demo-shell/src/app/app-extension.module.ts new file mode 100644 index 0000000000..63a3017383 --- /dev/null +++ b/demo-shell/src/app/app-extension.module.ts @@ -0,0 +1,27 @@ +/*! + * @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 { NgModule } from '@angular/core'; +import { AcaMonacoModule } from 'adf-monaco-extension'; + +@NgModule({ + imports: [ + AcaMonacoModule + ] +}) +export class AppExtensionsModule { +} diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts index 16a29c4618..84914b8208 100644 --- a/demo-shell/src/app/app.module.ts +++ b/demo-shell/src/app/app.module.ts @@ -24,6 +24,7 @@ import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppConfigService, TRANSLATION_PROVIDER, DebugAppConfigService, CoreModule } from '@alfresco/adf-core'; +import { ExtensionsModule } from '@alfresco/adf-extensions'; import { AppComponent } from './app.component'; import { MaterialModule } from './material.module'; import { LoginComponent } from './components/login/login.component'; @@ -65,7 +66,6 @@ import { ProcessModule } from '@alfresco/adf-process-services'; import { AuthBearerInterceptor } from './services'; import { ProcessServicesCloudModule, GroupCloudModule } from '@alfresco/adf-process-services-cloud'; import { TreeViewSampleComponent } from './components/tree-view/tree-view-sample.component'; -import { AppExtensionsModule } from './extensions/extensions.module'; import { CloudLayoutComponent } from './components/app-layout/cloud/cloud-layout.component'; import { AppsCloudDemoComponent } from './components/app-layout/cloud/apps-cloud-demo.component'; import { ProcessesCloudDemoComponent } from './components/app-layout/cloud/processes-cloud-demo.component'; @@ -78,6 +78,7 @@ import { StartProcessCloudDemoComponent } from './components/app-layout/cloud/st import { TemplateDemoComponent } from './components/template-list/template-demo.component'; import { PeopleGroupCloudDemoComponent } from './components/app-layout/cloud/people-groups-cloud-demo.component'; import { CloudSettingsComponent } from './components/app-layout/cloud/cloud-settings.component'; +import { AppExtensionsModule } from './app-extension.module'; @NgModule({ imports: [ @@ -88,16 +89,18 @@ import { CloudSettingsComponent } from './components/app-layout/cloud/cloud-sett FormsModule, HttpClientModule, MaterialModule, + AppExtensionsModule, FlexLayoutModule, CoreModule.forRoot(), ContentModule.forRoot(), InsightsModule.forRoot(), ProcessModule.forRoot(), + ProcessServicesCloudModule, + ExtensionsModule.forRoot(), ThemePickerModule, ChartsModule, MonacoEditorModule.forRoot(), ProcessServicesCloudModule, - AppExtensionsModule.forRoot(), GroupCloudModule ], declarations: [ diff --git a/demo-shell/src/app/components/about/about.component.ts b/demo-shell/src/app/components/about/about.component.ts index 68b61f2d6b..9e450a476c 100644 --- a/demo-shell/src/app/components/about/about.component.ts +++ b/demo-shell/src/app/components/about/about.component.ts @@ -16,8 +16,7 @@ */ import { Component } from '@angular/core'; -import { ExtensionRef } from '@alfresco/adf-extensions'; -import { AppExtensionService } from '../../extensions/extension.service'; +import { AppExtensionService, ExtensionRef } from '@alfresco/adf-extensions'; import { Observable } from 'rxjs'; @Component({ diff --git a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts index ed1e24bb0a..9fff604ef6 100644 --- a/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/start-process-cloud-demo.component.ts @@ -19,6 +19,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NotificationService, AppConfigService } from '@alfresco/adf-core'; import { CloudLayoutService } from './services/cloud-layout.service'; + @Component({ templateUrl: './start-process-cloud-demo.component.html', styleUrls: ['./start-process-cloud-demo.component.scss'] diff --git a/demo-shell/src/app/components/file-view/file-view.component.html b/demo-shell/src/app/components/file-view/file-view.component.html index 3488d76c8f..0fa9cb5a31 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.html +++ b/demo-shell/src/app/components/file-view/file-view.component.html @@ -4,7 +4,7 @@ - + diff --git a/demo-shell/src/app/components/file-view/file-view.component.scss b/demo-shell/src/app/components/file-view/file-view.component.scss index a834b0602b..2be390eb61 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.scss +++ b/demo-shell/src/app/components/file-view/file-view.component.scss @@ -1,3 +1,12 @@ .adf-viewer__sidebar { width: 380px !important; } + +/* stylelint-disable */ +.monaco-scrollable-element { + width: 600px; +} + +adf-preview-extension { + width: 600px; +} diff --git a/demo-shell/src/app/components/file-view/file-view.component.ts b/demo-shell/src/app/components/file-view/file-view.component.ts index 86ad27b4c2..2fcb5aa68c 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.ts +++ b/demo-shell/src/app/components/file-view/file-view.component.ts @@ -17,7 +17,7 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ContentService, PermissionsEnum, NodesApiService } from '@alfresco/adf-core'; +import { ContentService, AllowableOperationsEnum, PermissionsEnum, NodesApiService } from '@alfresco/adf-core'; import { MatSnackBar } from '@angular/material'; @Component({ @@ -52,7 +52,7 @@ export class FileViewComponent implements OnInit { showLeftSidebar = null; showRightSidebar = false; customToolbar = false; - isCommentDisabled = false; + isCommentEnabled = false; constructor(private router: Router, private route: ActivatedRoute, @@ -68,7 +68,8 @@ export class FileViewComponent implements OnInit { this.nodeApiService.getNode(id).subscribe( (node) => { if (node && node.isFile) { - this.isCommentDisabled = this.contentServices.hasPermissions(node, PermissionsEnum.CONSUMER); + this.isCommentEnabled = this.contentServices.hasPermissions(node, PermissionsEnum.NOT_CONSUMER) || + this.contentServices.hasAllowableOperations(node, AllowableOperationsEnum.UPDATE); this.nodeId = id; return; } diff --git a/demo-shell/src/app/components/form/form-list.component.ts b/demo-shell/src/app/components/form/form-list.component.ts index 494c3c5910..5bc660b9e8 100644 --- a/demo-shell/src/app/components/form/form-list.component.ts +++ b/demo-shell/src/app/components/form/form-list.component.ts @@ -16,8 +16,7 @@ */ import { Component, ViewChild } from '@angular/core'; -import { FormComponent, FormModel, FormService, LogService } from '@alfresco/adf-core'; -import { FormOutcomeEvent } from '../../../../../lib/core/form/components/widgets/core'; +import { FormComponent, FormModel, FormService, LogService, FormOutcomeEvent } from '@alfresco/adf-core'; @Component({ selector: 'app-form-list', diff --git a/demo-shell/src/app/components/process-service/task-attachments.component.ts b/demo-shell/src/app/components/process-service/task-attachments.component.ts index dc5abe30ab..f235452b58 100644 --- a/demo-shell/src/app/components/process-service/task-attachments.component.ts +++ b/demo-shell/src/app/components/process-service/task-attachments.component.ts @@ -22,10 +22,9 @@ import { TaskDetailsModel, TaskUploadService } from '@alfresco/adf-process-services'; -import { UploadService, AlfrescoApiService, AppConfigService } from '@alfresco/adf-core'; +import { UploadService, AlfrescoApiService, AppConfigService, FileUploadCompleteEvent } from '@alfresco/adf-core'; import { PreviewService } from '../../services/preview.service'; import { Subscription } from 'rxjs'; -import { FileUploadCompleteEvent } from '../../../../../lib/core/events/file.event'; export function taskUploadServiceFactory(api: AlfrescoApiService, config: AppConfigService) { return new TaskUploadService(api, config); diff --git a/demo-shell/src/app/extensions/extensions.module.ts b/demo-shell/src/app/extensions/extensions.module.ts deleted file mode 100644 index 24d68622c8..0000000000 --- a/demo-shell/src/app/extensions/extensions.module.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * @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 { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { CoreModule } from '@alfresco/adf-core'; -import { AppExtensionService } from './extension.service'; -import { ExtensionsModule } from '@alfresco/adf-extensions'; - -export function setupExtensions(service: AppExtensionService): Function { - return () => service.load(); -} - -@NgModule({ - imports: [CommonModule, CoreModule.forChild(), ExtensionsModule] -}) -export class AppExtensionsModule { - static forRoot(): ModuleWithProviders { - return { - ngModule: AppExtensionsModule, - providers: [ - { - provide: APP_INITIALIZER, - useFactory: setupExtensions, - deps: [AppExtensionService], - multi: true - } - ] - }; - } - - static forChild(): ModuleWithProviders { - return { - ngModule: AppExtensionsModule - }; - } -} diff --git a/demo-shell/src/assets/.gitkeep b/demo-shell/src/assets/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/demo-shell/src/assets/app.extensions.json b/demo-shell/src/assets/app.extensions.json index ecda91eb59..a7e0183e9d 100644 --- a/demo-shell/src/assets/app.extensions.json +++ b/demo-shell/src/assets/app.extensions.json @@ -1,15 +1,9 @@ { - "$schema": "./extension.schema.json", - "$id": "app.1.0.0", - "$name": "app", - "$version": "1.0.0", - "$vendor": "Alfresco", - "$license": "Apache-2.0", - "$runtime": "2.7.0", - "$description": "base application extensions", + "$schema": "../../lib/extensions/config/schema/app-extension.schema.json", "$references": [ "plugin1.json", - "plugin2.json" + "plugin2.json", + "monaco-extension.json" ], "$dependencies": [] } diff --git a/demo-shell/src/assets/plugins/monaco-extension.json b/demo-shell/src/assets/plugins/monaco-extension.json new file mode 100644 index 0000000000..1b5cc0d874 --- /dev/null +++ b/demo-shell/src/assets/plugins/monaco-extension.json @@ -0,0 +1,17 @@ +{ + "$schema": "../../../lib/extensions/config/schema/plugin-extension.schema.json", + "$version": "1.0.0", + "$name": "monaco extension", + "$description": "monaco plugin", + "features": { + "viewer": { + "content": [ + { + "id": "dev.tools.viewer", + "fileExtension": "js", + "component": "monaco-extension.main.component" + } + ] + } + } +} diff --git a/demo-shell/src/assets/plugins/plugin1.json b/demo-shell/src/assets/plugins/plugin1.json index 72d2095dac..63991be786 100644 --- a/demo-shell/src/assets/plugins/plugin1.json +++ b/demo-shell/src/assets/plugins/plugin1.json @@ -1,16 +1,11 @@ { - "$schema": "../extension.schema.json", - "$id": "plugin1.1.0.0", + "$schema": "../../../lib/extensions/config/schema/plugin-extension.schema.json", "$name": "plugin1", "$version": "1.0.0", "$vendor": "Alfresco", "$license": "Apache-2.0", "$runtime": "2.7.0", "$description": "example plugin", - "$dependencies": [ - "@some/library1" - ], - "features": { "custom": { "key": "value" diff --git a/demo-shell/src/assets/plugins/plugin2.json b/demo-shell/src/assets/plugins/plugin2.json index 7afe124bb5..508f7c82ec 100644 --- a/demo-shell/src/assets/plugins/plugin2.json +++ b/demo-shell/src/assets/plugins/plugin2.json @@ -1,16 +1,11 @@ { - "$schema": "../extension.schema.json", - "$id": "plugin2.1.1.0", + "$schema": "../../../lib/extensions/config/schema/plugin-extension.schema.json", "$name": "plugin2", "$version": "1.1.0", "$vendor": "Alfresco", "$license": "Apache-2.0", "$runtime": "2.7.0", "$description": "another example plugin", - "$dependencies": [ - "@some/library2" - ], - "features": { "extra": { "key": "value" diff --git a/demo-shell/src/tsconfig.app.json b/demo-shell/src/tsconfig.app.json index 66f72873a2..7166e04043 100644 --- a/demo-shell/src/tsconfig.app.json +++ b/demo-shell/src/tsconfig.app.json @@ -4,30 +4,7 @@ "outDir": "../out-tsc/app", "module": "es2015", "baseUrl": ".", - "types": [], - "paths": { - "@alfresco/adf-extensions": [ - "../../lib/extensions" - ], - "@alfresco/adf-core": [ - "../../lib/core" - ], - "@alfresco/adf-process-services-cloud": [ - "../../lib/process-services-cloud" - ], - "@alfresco/adf-testing": [ - "../../lib/testing" - ], - "@alfresco/adf-content-services": [ - "../../lib/content-services" - ], - "@alfresco/adf-process-services": [ - "../../lib/process-services" - ], - "@alfresco/adf-insights": [ - "../../lib/insights" - ] - } + "types": [] }, "exclude": [ "test.ts", diff --git a/e2e/core/card-view/aspect-oriented-config.e2e.ts b/e2e/core/card-view/aspect-oriented-config.e2e.ts index b4070736df..bf38664232 100644 --- a/e2e/core/card-view/aspect-oriented-config.e2e.ts +++ b/e2e/core/card-view/aspect-oriented-config.e2e.ts @@ -88,7 +88,7 @@ describe('Aspect oriented config', () => { it('[C261117] Should be possible restrict the display properties of one an aspect', () => { - configEditorPage.enterConfiguration('{ "presets": {' + + configEditorPage.enterBigConfigurationText('{ "presets": {' + ' "default": [{' + ' "title": "IMAGE",' + ' "items": [' + @@ -123,7 +123,7 @@ describe('Aspect oriented config', () => { it('[C260185] Should ignore not existing aspect when present in the configuration', () => { - configEditorPage.enterConfiguration(' {' + + configEditorPage.enterBigConfigurationText(' {' + ' "presets": {' + ' "default": {' + ' "exif:exif": "*",' + @@ -152,7 +152,7 @@ describe('Aspect oriented config', () => { it('[C260183] Should show all the aspect if the content-metadata configuration is NOT provided', () => { - configEditorPage.enterConfiguration('{ }'); + configEditorPage.enterBigConfigurationText('{ }'); configEditorPage.clickSaveButton(); @@ -176,7 +176,7 @@ describe('Aspect oriented config', () => { it('[C260182] Should show all the aspects if the default configuration contains the star symbol', () => { - configEditorPage.enterConfiguration('{' + + configEditorPage.enterBigConfigurationText('{' + ' "presets": {' + ' "default": "*"' + ' }' + @@ -205,7 +205,7 @@ describe('Aspect oriented config', () => { it('[C268899] Should be possible use a Translation key as Title of a metadata group', () => { - configEditorPage.enterConfiguration('{' + + configEditorPage.enterBigConfigurationText('{' + ' "presets": {' + ' "default": [' + ' {' + @@ -221,11 +221,8 @@ describe('Aspect oriented config', () => { ' "title": "GROUP-TITLE2-TRANSLATION-KEY",' + ' "items": [' + ' {' + - ' "aspect": "kitten:food",' + - ' "properties": [' + - ' "kitten:favourite-food",' + - ' "kitten:recommended-food"' + - ' ]' + + ' "aspect": "exif:exif"' + + ' "properties": "*"' + ' }' + ' ]' + ' }' + @@ -255,7 +252,7 @@ describe('Aspect oriented config', () => { it('[C279968] Should be possible use a custom preset', () => { - configEditorPage.enterConfiguration('{' + + configEditorPage.enterBigConfigurationText('{' + ' "presets": {' + ' "custom-preset": {' + ' "exif:exif": "*",' + diff --git a/lib/content-services/content-node-selector/content-node-selector.component.spec.ts b/lib/content-services/content-node-selector/content-node-selector.component.spec.ts index 628a35a1ef..75ede2ffe0 100644 --- a/lib/content-services/content-node-selector/content-node-selector.component.spec.ts +++ b/lib/content-services/content-node-selector/content-node-selector.component.spec.ts @@ -27,6 +27,7 @@ import { of } from 'rxjs'; import { ContentTestingModule } from '../testing/content.testing.module'; import { DocumentListService } from '../document-list/services/document-list.service'; import { DocumentListComponent } from '../document-list/components/document-list.component'; +import { ShareDataRow } from '../document-list'; describe('ContentNodeSelectorDialogComponent', () => { @@ -36,7 +37,10 @@ describe('ContentNodeSelectorDialogComponent', () => { title: 'Move along citizen...', actionName: 'move', select: new EventEmitter(), - rowFilter: () => { + rowFilter: (shareDataRow: ShareDataRow) => { + if (shareDataRow.node.entry.name === 'impossible-name') { + return true; + } }, imageResolver: () => 'piccolo', currentFolderId: 'cat-girl-nuku-nuku' @@ -88,14 +92,25 @@ describe('ContentNodeSelectorDialogComponent', () => { expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); }); - xit('should pass through the injected rowFilter to the documentList', (done) => { - fixture.whenStable().then(() => { - let documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter); - done(); - }); - + it('should pass through the injected rowFilter to the documentList', () => { + let documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.rowFilter({ + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + })) + .toBe(data.rowFilter( { + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + })); }); it('should pass through the injected imageResolver to the documentList', () => { diff --git a/lib/content-services/content-node-share/content-node-share.dialog.spec.ts b/lib/content-services/content-node-share/content-node-share.dialog.spec.ts index eb82ba6f5f..73b16865c6 100644 --- a/lib/content-services/content-node-share/content-node-share.dialog.spec.ts +++ b/lib/content-services/content-node-share/content-node-share.dialog.spec.ts @@ -120,31 +120,6 @@ describe('ShareDialogComponent', () => { }); })); - xit(`should copy shared link and notify on button event`, (done) => { - node.entry.properties['qshare:sharedId'] = 'sharedId'; - spyOn(document, 'execCommand').and.callThrough(); - - component.data = { - node, - baseShareUrl: 'some-url/' - }; - - fixture.detectChanges(); - - fixture.whenStable().then(() => { - fixture.detectChanges(); - - fixture.nativeElement.querySelector('.adf-input-action') - .dispatchEvent(new MouseEvent('click')); - - fixture.detectChanges(); - - expect(document.execCommand).toHaveBeenCalledWith('copy'); - expect(notificationServiceMock.openSnackMessage).toHaveBeenCalledWith('SHARE.CLIPBOARD-MESSAGE'); - done(); - }); - }); - it('should open a confirmation dialog when unshare button is triggered', () => { spyOn(matDialog, 'open').and.returnValue({ beforeClose: () => of(false) }); spyOn(sharedLinksApiService, 'deleteSharedLink').and.callThrough(); diff --git a/lib/content-services/document-list/components/document-list.component.spec.ts b/lib/content-services/document-list/components/document-list.component.spec.ts index 194e350b7c..7c3471087c 100644 --- a/lib/content-services/document-list/components/document-list.component.spec.ts +++ b/lib/content-services/document-list/components/document-list.component.spec.ts @@ -1219,7 +1219,7 @@ describe('DocumentList', () => { documentList.loadFolderByNodeId('-recent-'); }); - xit('should emit error when fetch recent fails on search call', (done) => { + it('should emit error when fetch recent fails on search call', (done) => { spyOn(customResourcesService, 'loadFolderByNodeId').and.returnValue(throwError('error')); let disposableError = documentList.error.subscribe((val) => { diff --git a/lib/content-services/folder-directive/folder-create.directive.spec.ts b/lib/content-services/folder-directive/folder-create.directive.spec.ts index 1c7c1817a8..f7462d82a2 100644 --- a/lib/content-services/folder-directive/folder-create.directive.spec.ts +++ b/lib/content-services/folder-directive/folder-create.directive.spec.ts @@ -55,7 +55,6 @@ class TestComponent { describe('FolderCreateDirective', () => { let fixture: ComponentFixture; let element; - let node: any; let dialog: MatDialog; let contentService: ContentService; let dialogRefMock; @@ -85,8 +84,6 @@ describe('FolderCreateDirective', () => { }); beforeEach(() => { - node = { entry: { id: 'nodeId' } }; - dialogRefMock = { afterClosed: (val) => of(val), componentInstance: { @@ -106,22 +103,6 @@ describe('FolderCreateDirective', () => { spyOn(dialog, 'open').and.returnValue(dialogRefMock); }); - xit('should emit folderCreate event when input value is not undefined', (done) => { - spyOn(dialogRefMock, 'afterClosed').and.returnValue(of(node)); - spyOn(contentService.folderCreate, 'next'); - - contentService.folderCreate.subscribe((val) => { - expect(val).toBe(node); - done(); - }); - - fixture.detectChanges(); - - fixture.whenStable().then(() => { - element.nativeElement.click(); - }); - }); - it('should not emit folderCreate event when input value is undefined', () => { spyOn(dialogRefMock, 'afterClosed').and.returnValue(of(null)); spyOn(contentService.folderCreate, 'next'); diff --git a/lib/content-services/ng-package.json b/lib/content-services/ng-package.json index c5f43efa53..ae3b199144 100644 --- a/lib/content-services/ng-package.json +++ b/lib/content-services/ng-package.json @@ -9,7 +9,7 @@ "entryFile": "./public-api.ts", "flatModuleFile": "adf-content-services", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap", + "@alfresco/js-api": "@alfresco/js-api", "minimatch": "minimatch", "@angular/platform-browser/animations": "@angular/platform-browser/animations", "@angular/material": "@angular/material", diff --git a/lib/content-services/tree-view/services/tree-view.service.spec.ts b/lib/content-services/tree-view/services/tree-view.service.spec.ts index a6e7a2f660..85e2dd7cd8 100644 --- a/lib/content-services/tree-view/services/tree-view.service.spec.ts +++ b/lib/content-services/tree-view/services/tree-view.service.spec.ts @@ -20,7 +20,7 @@ import { TreeViewService } from './tree-view.service'; import { TestBed } from '@angular/core/testing'; import { ContentTestingModule } from '../../testing/content.testing.module'; import { of } from 'rxjs'; -import { TreeBaseNode } from 'tree-view/models/tree-view.model'; +import { TreeBaseNode } from '../models/tree-view.model'; describe('TreeViewService', () => { diff --git a/lib/content-services/tsconfig.json b/lib/content-services/tsconfig.json deleted file mode 100644 index c62a7416e0..0000000000 --- a/lib/content-services/tsconfig.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "skipLibCheck": false, - "noLib": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noImplicitUseStrict": false, - "noFallthroughCasesInSwitch": true, - "removeComments": true, - "declaration": true, - "outDir": "../dist/content-services/", - "baseUrl" : "./", - "paths": { - "@alfresco/adf-process-services": ["../process-services"], - "@alfresco/adf-core": ["../core"] - }, - "lib": [ - "es2016", - "dom" - ], - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - }, - "exclude": [ - "demo", - "node_modules", - "dist" - ], - "angularCompilerOptions": { - "strictMetadataEmit": false, - "skipTemplateCodegen": true - } -} diff --git a/lib/content-services/tsconfig.lib.json b/lib/content-services/tsconfig.lib.json new file mode 100644 index 0000000000..7cffb50412 --- /dev/null +++ b/lib/content-services/tsconfig.lib.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../dist/content-services/" + } +} diff --git a/lib/content-services/tsconfig.spec.json b/lib/content-services/tsconfig.spec.json new file mode 100644 index 0000000000..ab9099eca3 --- /dev/null +++ b/lib/content-services/tsconfig.spec.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.spec.json", + "files": [ + "test.ts" + ] +} diff --git a/lib/core/app-config/schema.json b/lib/core/app-config/schema.json index 8eaf04ee1a..c29f531bb1 100644 --- a/lib/core/app-config/schema.json +++ b/lib/core/app-config/schema.json @@ -1106,7 +1106,6 @@ }, "categories": { "type": "array", - "minItems": 1, "items": { "type": "object", "required": [ diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 2f6d394d11..742a3f3eb9 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -52,10 +52,12 @@ import { startupServiceFactory } from './services/startup-service-factory'; import { SortingPickerModule } from './sorting-picker/sorting-picker.module'; import { IconModule } from './icon/icon.module'; import { TranslateLoaderService } from './services/translate-loader.service'; +import { ExtensionsModule } from '@alfresco/adf-extensions'; @NgModule({ imports: [ TranslateModule, + ExtensionsModule.forChild(), AboutModule, ViewerModule, SidenavLayoutModule, diff --git a/lib/core/form/components/form.component.spec.ts b/lib/core/form/components/form.component.spec.ts index 86a414f78f..56d1e44d22 100644 --- a/lib/core/form/components/form.component.spec.ts +++ b/lib/core/form/components/form.component.spec.ts @@ -627,14 +627,14 @@ describe('FormComponent', () => { it('should parse form from json', () => { let form = formComponent.parseForm({ - id: '', + id: 1, fields: [ { id: 'field1', type: FormFieldTypes.CONTAINER } ] }); expect(form).toBeDefined(); - expect(form.id).toBe(''); + expect(form.id).toBe(1); expect(form.fields.length).toBe(1); expect(form.fields[0].id).toBe('field1'); }); @@ -642,7 +642,7 @@ describe('FormComponent', () => { it('should provide outcomes for form definition', () => { spyOn(formComponent, 'getFormDefinitionOutcomes').and.callThrough(); - let form = formComponent.parseForm({ id: '' }); + let form = formComponent.parseForm({ id: 1 }); expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form); }); diff --git a/lib/core/ng-package.json b/lib/core/ng-package.json index 4f21e42b7b..8337c7246a 100644 --- a/lib/core/ng-package.json +++ b/lib/core/ng-package.json @@ -9,7 +9,7 @@ "entryFile": "./public-api.ts", "flatModuleFile": "adf-core", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap", + "@alfresco/js-api": "@alfresco/js-api", "minimatch": "minimatch-browser", "@angular/platform-browser/animations": "@angular/platform-browser/animations", "@angular/material": "@angular/material", @@ -23,7 +23,8 @@ "moment": "moment", "moment-es6": "moment-es6", "moment/src/moment": "moment/src/moment", - "@ngx-translate/core": "@ngx-translate/core" + "@ngx-translate/core": "@ngx-translate/core", + "rxjs": "rxjs" } } } diff --git a/lib/core/tsconfig.json b/lib/core/tsconfig.json deleted file mode 100644 index e45eb0a69b..0000000000 --- a/lib/core/tsconfig.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "skipLibCheck": false, - "noLib": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noImplicitUseStrict": false, - "noFallthroughCasesInSwitch": true, - "removeComments": true, - "declaration": true, - "outDir": "../dist/core/", - "baseUrl": "./", - "allowSyntheticDefaultImports": true, - "paths": { - "@alfresco/adf-process-services": [ - "../process-services" - ], - "@alfresco/adf-content-services": [ - "../content-services" - ] - }, - "lib": [ - "es2016", - "dom" - ], - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - }, - "exclude": [ - "demo", - "node_modules", - "dist" - ], - "angularCompilerOptions": { - "strictMetadataEmit": false, - "skipTemplateCodegen": true - } -} diff --git a/lib/core/tsconfig.lib.json b/lib/core/tsconfig.lib.json new file mode 100644 index 0000000000..049b4da4fc --- /dev/null +++ b/lib/core/tsconfig.lib.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../dist/core/" + } +} diff --git a/lib/core/tsconfig.spec.json b/lib/core/tsconfig.spec.json new file mode 100644 index 0000000000..ab9099eca3 --- /dev/null +++ b/lib/core/tsconfig.spec.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.spec.json", + "files": [ + "test.ts" + ] +} diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index d919585579..c7c820da77 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -76,7 +76,7 @@ mat-icon-button title="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}" data-automation-id="adf-toolbar-download" - [adfNodeDownload]="node"> + [adfNodeDownload]="nodeEntry"> file_download @@ -169,6 +169,7 @@
+ @@ -195,8 +196,22 @@ + + + + + + - + diff --git a/lib/core/viewer/components/viewer.component.spec.ts b/lib/core/viewer/components/viewer.component.spec.ts index 08cef2a41a..a1d4cd31e4 100644 --- a/lib/core/viewer/components/viewer.component.spec.ts +++ b/lib/core/viewer/components/viewer.component.spec.ts @@ -600,10 +600,10 @@ describe('ViewerComponent', () => { }); it('should get and assign node for download', (done) => { - const node = { id: 'fake-node' }; component.nodeId = '12'; component.urlFile = ''; const displayName = 'the-name'; + const node = new NodeEntry({ entry: { name: displayName, id: '12', content: { mimeType: 'txt' } } }); const nodeDetails = { name: displayName, id: '12', content: { mimeType: 'txt' } }; const contentUrl = '/content/url/path'; const alfrescoApiInstanceMock = { @@ -618,7 +618,7 @@ describe('ViewerComponent', () => { component.ngOnChanges(null); fixture.whenStable().then(() => { fixture.detectChanges(); - expect(component.node).toBe(node); + expect(component.nodeEntry).toBe(node); done(); }); }); diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index 601b95b186..d92b84df61 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -31,6 +31,7 @@ import { ViewerSidebarComponent } from './viewer-sidebar.component'; import { ViewerToolbarComponent } from './viewer-toolbar.component'; import { Subscription } from 'rxjs'; import { ViewUtilService } from '../services/view-util.service'; +import { ExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; @Component({ selector: 'adf-viewer', @@ -201,7 +202,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { viewerType = 'unknown'; isLoading = false; - node: NodeEntry; + nodeEntry: NodeEntry; extensionTemplates: { template: TemplateRef, isVisible: boolean }[] = []; externalExtensions: string[] = []; @@ -211,6 +212,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { sidebarRightTemplateContext: { node: Node } = { node: null }; sidebarLeftTemplateContext: { node: Node } = { node: null }; fileTitle: string; + viewerExtensions: Array = []; private cacheBusterNumber; @@ -220,7 +222,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { private extensions = { image: ['png', 'jpg', 'jpeg', 'gif', 'bpm', 'svg'], media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'], - text: ['txt', 'xml', 'js', 'html', 'json', 'ts', 'css', 'md'], + text: ['txt', 'xml', 'html', 'json', 'ts', 'css', 'md'], pdf: ['pdf'] }; @@ -236,6 +238,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { private viewUtils: ViewUtilService, private logService: LogService, private location: Location, + private extensionService: ExtensionService, private el: ElementRef) { } @@ -247,6 +250,15 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.subscriptions.push( this.apiService.nodeUpdated.subscribe((node) => this.onNodeUpdated(node)) ); + + this.extensionLoad(); + } + + private extensionLoad() { + this.viewerExtensions = this.extensionService.getFeature('viewer.content'); + this.viewerExtensions.forEach((currentViewerExtension: ViewerExtensionRef) => { + this.externalExtensions.push(currentViewerExtension.fileExtension); + }); } ngOnDestroy() { @@ -280,7 +292,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { } else if (this.nodeId) { this.apiService.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] }).then( (node: NodeEntry) => { - this.node = node; + this.nodeEntry = node; this.setUpNodeFile(node.entry).then(() => { this.isLoading = false; }); diff --git a/lib/core/viewer/viewer.module.ts b/lib/core/viewer/viewer.module.ts index 2717e49729..56d410f6d8 100644 --- a/lib/core/viewer/viewer.module.ts +++ b/lib/core/viewer/viewer.module.ts @@ -21,6 +21,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { FlexLayoutModule } from '@angular/flex-layout'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ExtensionsModule } from '@alfresco/adf-extensions'; + import { MaterialModule } from '../material.module'; import { ToolbarModule } from '../toolbar/toolbar.module'; import { PipeModule } from '../pipes/pipe.module'; @@ -43,6 +45,7 @@ import { DirectiveModule } from '../directives/directive.module'; @NgModule({ imports: [ + ExtensionsModule, CommonModule, MaterialModule, TranslateModule.forChild(), diff --git a/lib/extensions/ng-package.json b/lib/extensions/ng-package.json index 5fcc2568b7..5eb0fcd86c 100644 --- a/lib/extensions/ng-package.json +++ b/lib/extensions/ng-package.json @@ -7,7 +7,23 @@ "entryFile": "src/public-api.ts", "flatModuleFile": "adf-extensions", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap" + "@alfresco/js-api": "@alfresco/js-api", + "minimatch": "minimatch", + "@angular/platform-browser/animations": "@angular/platform-browser/animations", + "@angular/material": "@angular/material", + "@mat-datetimepicker": "@mat-datetimepicker", + "@angular/flex-layout": "@angular/flex-layout", + "@alfresco/adf-core": "@alfresco/adf-core", + "@alfresco/adf-content-services": "@alfresco/adf-content-services", + "@angular/material-moment-adapter": "@angular/material-moment-adapter", + "@angular/animations": "@angular/animations", + "@angular/cdk/platform": "@angular/cdk/platform", + "@angular/material/core": "@angular/material/core", + "moment": "moment", + "moment-es6": "moment-es6", + "moment/src/moment": "moment/src/moment", + "@ngx-translate/core": "@ngx-translate/core", + "rxjs": "rxjs" } } } diff --git a/lib/extensions/package.json b/lib/extensions/package.json index 42386f7d42..985e7e71c9 100644 --- a/lib/extensions/package.json +++ b/lib/extensions/package.json @@ -2,7 +2,6 @@ "name": "@alfresco/adf-extensions", "description": "Provides extensibility support for ADF applications.", "version": "3.0.0", - "license": "Apache-2.0", "author": "Alfresco Software, Ltd.", "main": "bundles/adf-extensions.js", "repository": { @@ -13,9 +12,17 @@ "url": "https://github.com/Alfresco/alfresco-ng2-components/issues" }, "peerDependencies": { - "@angular/common": "^6.0.0", - "@angular/core": "^6.0.0", - "@angular/http": "^6.1.4", + "@angular/common": ">=7.0.3", + "@angular/core": ">=7.0.3", + "@angular/http": ">=7.0.3", "@alfresco/js-api": "3.0.0" - } + }, + "keywords": [ + "extensions", + "alfresco-component", + "angular", + "activiti", + "components" + ], + "license": "Apache-2.0" } diff --git a/lib/extensions/src/lib/components/public-api.ts b/lib/extensions/src/lib/components/public-api.ts new file mode 100644 index 0000000000..519440acf6 --- /dev/null +++ b/lib/extensions/src/lib/components/public-api.ts @@ -0,0 +1,21 @@ +/*! + * @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. + */ + +export * from './viewer/preview-extension.component'; +export * from './dynamic-column/dynamic-column.component'; +export * from './dynamic-component/dynamic.component'; +export * from './dynamic-tab/dynamic-tab.component'; diff --git a/lib/extensions/src/lib/components/viewer/preview-extension.component.ts b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts new file mode 100644 index 0000000000..43eedff097 --- /dev/null +++ b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts @@ -0,0 +1,99 @@ +/*! + * @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 { + Component, + Input, + ComponentRef, + OnInit, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, + OnDestroy, + OnChanges +} from '@angular/core'; +import { ExtensionService } from '../../services/extension.service'; +import { Node } from '@alfresco/js-api'; + +@Component({ + selector: 'adf-preview-extension', + template: ` +
+ ` +}) +export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { + @ViewChild('content', { read: ViewContainerRef }) + content: ViewContainerRef; + + @Input() + id: string; + + @Input() + url: string; + + @Input() + extension: string; + + @Input() + node: Node; + + private componentRef: ComponentRef; + + constructor( + private extensionService: ExtensionService, + private componentFactoryResolver: ComponentFactoryResolver + ) {} + + ngOnInit() { + if (!this.id) { + return; + } + + const componentType = this.extensionService.getComponentById(this.id); + if (componentType) { + const factory = this.componentFactoryResolver.resolveComponentFactory( + componentType + ); + if (factory) { + this.content.clear(); + this.componentRef = this.content.createComponent(factory, 0); + this.updateInstance(); + } + } + } + + ngOnChanges() { + this.updateInstance(); + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + this.componentRef = null; + } + } + + private updateInstance() { + if (this.componentRef && this.componentRef.instance) { + const instance = this.componentRef.instance; + + instance.node = this.node; + instance.url = this.url; + instance.extension = this.extension; + } + } +} diff --git a/lib/extensions/src/lib/config/extension-utils.ts b/lib/extensions/src/lib/config/extension-utils.ts index ae277837b3..ddf3f0146a 100644 --- a/lib/extensions/src/lib/config/extension-utils.ts +++ b/lib/extensions/src/lib/config/extension-utils.ts @@ -146,5 +146,5 @@ export function mergeArrays(left: any[], right: any[]): any[] { } }); - return Object.values(map).concat(result); + return Object.keys(map).map((key) => map[key]).concat(result); } diff --git a/lib/extensions/src/lib/config/schema/app-extension.schema.json b/lib/extensions/src/lib/config/schema/app-extension.schema.json new file mode 100644 index 0000000000..2d0d90b45e --- /dev/null +++ b/lib/extensions/src/lib/config/schema/app-extension.schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "id": "https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/extensions/config/schema/app-extension.schema.json", + "title": "ACA Extension Schema", + "description": "Provides a validation schema for extensions", + "type": "object", + "properties": { + "$references": { + "description": "References to external files", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 0, + "uniqueItems": true + } + } +} diff --git a/demo-shell/src/assets/extension.schema.json b/lib/extensions/src/lib/config/schema/plugin-extension.schema.json similarity index 99% rename from demo-shell/src/assets/extension.schema.json rename to lib/extensions/src/lib/config/schema/plugin-extension.schema.json index d5e3a1b230..b8e576e901 100644 --- a/demo-shell/src/assets/extension.schema.json +++ b/lib/extensions/src/lib/config/schema/plugin-extension.schema.json @@ -1,9 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://github.com/Alfresco/alfresco-content-app/blob/development/extension.schema.json", + "id": "https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/extensions/config/schema/extension.schema.json", "title": "ACA Extension Schema", - "description": "Provides a validation schema for ACA extensions", - + "description": "Provides a validation schema for extensions plugin JSON", "definitions": { "ruleRef": { "type": "object", diff --git a/lib/extensions/src/lib/extensions.module.ts b/lib/extensions/src/lib/extensions.module.ts index 29faf04f4f..ae39e3a8e1 100644 --- a/lib/extensions/src/lib/extensions.module.ts +++ b/lib/extensions/src/lib/extensions.module.ts @@ -15,21 +15,46 @@ * limitations under the License. */ -import { NgModule } from '@angular/core'; import { DynamicExtensionComponent } from './components/dynamic-component/dynamic.component'; import { DynamicTabComponent } from './components/dynamic-tab/dynamic-tab.component'; import { DynamicColumnComponent } from './components/dynamic-column/dynamic-column.component'; +import { PreviewExtensionComponent } from './components/viewer/preview-extension.component'; +import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core'; +import { AppExtensionService } from './services/app-extension.service'; +import { setupExtensions } from './services/startup-extension-factory'; @NgModule({ declarations: [ DynamicExtensionComponent, DynamicTabComponent, - DynamicColumnComponent + DynamicColumnComponent, + PreviewExtensionComponent ], exports: [ DynamicExtensionComponent, DynamicTabComponent, - DynamicColumnComponent + DynamicColumnComponent, + PreviewExtensionComponent ] }) -export class ExtensionsModule {} +export class ExtensionsModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: ExtensionsModule, + providers: [ + { + provide: APP_INITIALIZER, + useFactory: setupExtensions, + deps: [AppExtensionService], + multi: true + } + ] + }; + } + + static forChild(): ModuleWithProviders { + return { + ngModule: ExtensionsModule + }; + } +} diff --git a/demo-shell/src/app/extensions/extension.service.ts b/lib/extensions/src/lib/services/app-extension.service.ts similarity index 77% rename from demo-shell/src/app/extensions/extension.service.ts rename to lib/extensions/src/lib/services/app-extension.service.ts index a11a1e0388..5599e975c5 100644 --- a/demo-shell/src/app/extensions/extension.service.ts +++ b/lib/extensions/src/lib/services/app-extension.service.ts @@ -16,11 +16,8 @@ */ import { Injectable } from '@angular/core'; -import { - ExtensionService, - ExtensionConfig, - ExtensionRef -} from '@alfresco/adf-extensions'; +import { ExtensionConfig, ExtensionRef } from '../config/extension.config'; +import { ExtensionService } from '../services/extension.service'; import { Observable, BehaviorSubject } from 'rxjs'; @Injectable({ @@ -31,22 +28,19 @@ export class AppExtensionService { references$: Observable; - constructor(private extensions: ExtensionService) { + constructor(protected extensionService: ExtensionService) { this.references$ = this._references.asObservable(); } async load() { - const config = await this.extensions.load(); + const config = await this.extensionService.load(); this.setup(config); } setup(config: ExtensionConfig) { if (!config) { - console.error('Extension configuration not found'); return; } - // tslint:disable-next-line:no-console - console.log('loaded extension config', config); const references = (config.$references || []) .filter((entry) => typeof entry === 'object') diff --git a/lib/extensions/src/lib/services/extension-loader.service.ts b/lib/extensions/src/lib/services/extension-loader.service.ts index efbd591365..0e918a9ebd 100644 --- a/lib/extensions/src/lib/services/extension-loader.service.ts +++ b/lib/extensions/src/lib/services/extension-loader.service.ts @@ -34,38 +34,40 @@ export class ExtensionLoaderService { load(configPath: string, pluginsPath: string): Promise { return new Promise((resolve) => { this.loadConfig(configPath, 0).then((result) => { - let config = result.config; + if (result) { + let config = result.config; - const override = sessionStorage.getItem('app.extension.config'); - if (override) { - config = JSON.parse(override); - } + const override = sessionStorage.getItem('app.extension.config'); + if (override) { + config = JSON.parse(override); + } - if (config.$references && config.$references.length > 0) { - const plugins = config.$references.map((name, idx) => - this.loadConfig(`${pluginsPath}/${name}`, idx) - ); + if (config.$references && config.$references.length > 0) { + const plugins = config.$references.map((name, idx) => + this.loadConfig(`${pluginsPath}/${name}`, idx) + ); - Promise.all(plugins).then((results) => { - const configs = results - .filter((entry) => entry) - .sort(sortByOrder) - .map((entry) => entry.config); + Promise.all(plugins).then((results) => { + const configs = results + .filter((entry) => entry) + .sort(sortByOrder) + .map((entry) => entry.config); - if (configs.length > 0) { - config = mergeObjects(config, ...configs); - } + if (configs.length > 0) { + config = mergeObjects(config, ...configs); + } - config = { - ...config, - ...this.getMetadata(result.config), - $references: configs.map((ext) => this.getMetadata(ext)) - }; + config = { + ...config, + ...this.getMetadata(result.config), + $references: configs.map((ext) => this.getMetadata(ext)) + }; + resolve(config); + }); + } else { resolve(config); - }); - } else { - resolve(config); + } } }); }); @@ -140,6 +142,13 @@ export class ExtensionLoaderService { return []; } + getFeatures(config: ExtensionConfig): any { + if (config) { + return config.features || []; + } + return []; + } + protected setActionDefaults(action: ContentActionRef): ContentActionRef { if (action) { action.type = action.type || ContentActionType.default; diff --git a/lib/extensions/src/lib/services/extension.service.ts b/lib/extensions/src/lib/services/extension.service.ts index cde2fbce36..f57c8ffe77 100644 --- a/lib/extensions/src/lib/services/extension.service.ts +++ b/lib/extensions/src/lib/services/extension.service.ts @@ -34,6 +34,7 @@ export class ExtensionService { rules: Array = []; routes: Array = []; actions: Array = []; + features: Array = []; authGuards: { [key: string]: Type<{}> } = {}; evaluators: { [key: string]: RuleEvaluator } = {}; @@ -41,7 +42,8 @@ export class ExtensionService { constructor( private loader: ExtensionLoaderService, private componentRegister: ComponentRegisterService - ) {} + ) { + } /** * Loads and registers an extension config file and plugins (specified by path properties). @@ -75,6 +77,12 @@ export class ExtensionService { this.rules = this.loader.getRules(config); this.actions = this.loader.getActions(config); this.routes = this.loader.getRoutes(config); + this.features = this.loader.getFeatures(config); + } + + getFeature(key: string): any[] { + let properties: string[] = Array.isArray(key) ? [key] : key.split('.'); + return properties.reduce((prev, curr) => prev && prev[curr], this.features) || []; } /** diff --git a/lib/extensions/src/lib/services/startup-extension-factory.ts b/lib/extensions/src/lib/services/startup-extension-factory.ts new file mode 100644 index 0000000000..e938f78d5f --- /dev/null +++ b/lib/extensions/src/lib/services/startup-extension-factory.ts @@ -0,0 +1,22 @@ +/*! + * @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 { AppExtensionService } from './app-extension.service'; + +export function setupExtensions(appExtensionService: AppExtensionService): Function { + return () => appExtensionService.load(); +} diff --git a/lib/extensions/src/public-api.ts b/lib/extensions/src/public-api.ts index a13cb00071..c7acbcf4fb 100644 --- a/lib/extensions/src/public-api.ts +++ b/lib/extensions/src/public-api.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +export * from './lib/extensions.module'; + export * from './lib/config/action.extensions'; export * from './lib/config/extension-element'; export * from './lib/config/extension-utils'; @@ -29,10 +31,11 @@ export * from './lib/config/viewer.extensions'; export * from './lib/services/extension-loader.service'; export * from './lib/services/extension.service'; export * from './lib/services/component-register.service'; +export * from './lib/services/app-extension.service'; export * from './lib/store/states/navigation.state'; export * from './lib/store/states/profile.state'; export * from './lib/store/states/selection.state'; export * from './lib/store/states/repository.state'; -export * from './lib/extensions.module'; +export * from './lib/components/public-api'; diff --git a/lib/extensions/tsconfig.lib.json b/lib/extensions/tsconfig.lib.json index bd8c4dab32..a551dbfa82 100644 --- a/lib/extensions/tsconfig.lib.json +++ b/lib/extensions/tsconfig.lib.json @@ -1,34 +1,7 @@ { - "extends": "../../tsconfig.json", + "extends": "../tsconfig.json", "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "module": "es2015", - "moduleResolution": "node", - "declaration": true, - "sourceMap": true, - "inlineSources": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "importHelpers": true, - "types": [], - "lib": [ - "dom", - "es2015" - ] - }, - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "flatModuleId": "AUTOGENERATED", - "flatModuleOutFile": "AUTOGENERATED", - "enableResourceInlining": true - }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "outDir": "../dist/extensions/", + "baseUrl": "src" + } } diff --git a/lib/extensions/tsconfig.spec.json b/lib/extensions/tsconfig.spec.json index 16da33db07..ef8eb83f35 100644 --- a/lib/extensions/tsconfig.spec.json +++ b/lib/extensions/tsconfig.spec.json @@ -1,17 +1,6 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] - }, + "extends": "../tsconfig.spec.json", "files": [ "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" ] } diff --git a/lib/extensions/tslint.json b/lib/extensions/tslint.json deleted file mode 100644 index 65bb2a595e..0000000000 --- a/lib/extensions/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "adf", - "camelCase" - ], - "component-selector": [ - true, - "element", - "adf", - "kebab-case" - ] - } -} diff --git a/lib/insights/ng-package.json b/lib/insights/ng-package.json index aa4bfefd2e..8a16e250e3 100644 --- a/lib/insights/ng-package.json +++ b/lib/insights/ng-package.json @@ -9,7 +9,7 @@ "entryFile": "./public-api.ts", "flatModuleFile": "adf-insights", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap", + "@alfresco/js-api": "@alfresco/js-api", "minimatch": "minimatch", "@angular/platform-browser/animations": "@angular/platform-browser/animations", "@angular/material": "@angular/material", diff --git a/lib/insights/tsconfig.json b/lib/insights/tsconfig.json deleted file mode 100644 index ddee748298..0000000000 --- a/lib/insights/tsconfig.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "skipLibCheck": false, - "noLib": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noImplicitUseStrict": false, - "noFallthroughCasesInSwitch": true, - "removeComments": true, - "declaration": true, - "outDir": "../dist/insights/", - "baseUrl" : "./", - "paths": { - "@alfresco/adf-process-services": ["../process-services"], - "@alfresco/adf-content-services": ["../content-services"], - "@alfresco/adf-core": ["../core"] - }, - "lib": [ - "es2016", - "dom" - ], - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - }, - "exclude": [ - "demo", - "node_modules", - "dist" - ], - "angularCompilerOptions": { - "strictMetadataEmit": false, - "skipTemplateCodegen": true - } -} diff --git a/lib/insights/tsconfig.lib.json b/lib/insights/tsconfig.lib.json new file mode 100644 index 0000000000..6dd77ad2d1 --- /dev/null +++ b/lib/insights/tsconfig.lib.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../dist/insights/" + } +} diff --git a/lib/insights/tsconfig.spec.json b/lib/insights/tsconfig.spec.json new file mode 100644 index 0000000000..ab9099eca3 --- /dev/null +++ b/lib/insights/tsconfig.spec.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.spec.json", + "files": [ + "test.ts" + ] +} diff --git a/lib/process-services-cloud/ng-package.json b/lib/process-services-cloud/ng-package.json index a60b5b9d38..77b6dc0b48 100644 --- a/lib/process-services-cloud/ng-package.json +++ b/lib/process-services-cloud/ng-package.json @@ -6,7 +6,23 @@ "entryFile": "src/public-api.ts", "flatModuleFile": "adf-process-services-cloud", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap" + "@alfresco/js-api": "@alfresco/js-api", + "minimatch": "minimatch", + "@angular/platform-browser/animations": "@angular/platform-browser/animations", + "@angular/material": "@angular/material", + "@mat-datetimepicker": "@mat-datetimepicker", + "@angular/flex-layout": "@angular/flex-layout", + "@alfresco/adf-core": "@alfresco/adf-core", + "@alfresco/adf-content-services": "@alfresco/adf-content-services", + "@angular/material-moment-adapter": "@angular/material-moment-adapter", + "@angular/animations": "@angular/animations", + "@angular/cdk/platform": "@angular/cdk/platform", + "@angular/material/core": "@angular/material/core", + "moment": "moment", + "moment-es6": "moment-es6", + "moment/src/moment": "moment/src/moment", + "@ngx-translate/core": "@ngx-translate/core", + "rxjs": "rxjs" } } } diff --git a/lib/process-services-cloud/src/lib/app/app-list-cloud.module.ts b/lib/process-services-cloud/src/lib/app/app-list-cloud.module.ts index c9903c2876..7bf67346b2 100644 --- a/lib/process-services-cloud/src/lib/app/app-list-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/app/app-list-cloud.module.ts @@ -30,7 +30,7 @@ import { TemplateModule, CoreModule } from '@alfresco/adf-core'; TemplateModule, MaterialModule, FlexLayoutModule, - CoreModule.forChild() + CoreModule ], declarations: [ AppListCloudComponent, diff --git a/lib/process-services-cloud/src/lib/group/group-cloud.module.ts b/lib/process-services-cloud/src/lib/group/group-cloud.module.ts index 3ecd481947..4c28ab8262 100644 --- a/lib/process-services-cloud/src/lib/group/group-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/group/group-cloud.module.ts @@ -35,7 +35,7 @@ import { InitialGroupNamePipe } from './pipe/group-initial.pipe'; FormsModule, ReactiveFormsModule, FormModule, - CoreModule.forChild() + CoreModule ], declarations: [GroupCloudComponent, InitialGroupNamePipe], exports: [GroupCloudComponent, InitialGroupNamePipe] diff --git a/lib/process-services-cloud/src/lib/process-services-cloud.module.ts b/lib/process-services-cloud/src/lib/process-services-cloud.module.ts index 9a2d6363c7..2d06817264 100644 --- a/lib/process-services-cloud/src/lib/process-services-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/process-services-cloud.module.ts @@ -24,7 +24,7 @@ import { GroupCloudModule } from './group/group-cloud.module'; @NgModule({ imports: [ - CoreModule.forChild(), + CoreModule, AppListCloudModule, ProcessCloudModule, TaskCloudModule, diff --git a/lib/process-services-cloud/src/lib/process/process-cloud.module.ts b/lib/process-services-cloud/src/lib/process/process-cloud.module.ts index 39a0f269ea..8a9bf46ac1 100644 --- a/lib/process-services-cloud/src/lib/process/process-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/process/process-cloud.module.ts @@ -26,7 +26,7 @@ import { CoreModule } from '@alfresco/adf-core'; ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudModule, - CoreModule.forChild() + CoreModule ], exports: [ ProcessFiltersCloudModule, diff --git a/lib/process-services-cloud/src/lib/process/process-filters/process-filters-cloud.module.ts b/lib/process-services-cloud/src/lib/process/process-filters/process-filters-cloud.module.ts index 1cb86d990c..99efb0ba5f 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/process-filters-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/process-filters-cloud.module.ts @@ -36,7 +36,7 @@ import { AppListCloudModule } from './../../app/app-list-cloud.module'; FlexLayoutModule, MaterialModule, AppListCloudModule, - CoreModule.forChild() + CoreModule ], declarations: [ProcessFiltersCloudComponent, EditProcessFilterCloudComponent, ProcessFilterDialogCloudComponent], diff --git a/lib/process-services-cloud/src/lib/process/process-list/process-list-cloud.module.ts b/lib/process-services-cloud/src/lib/process/process-list/process-list-cloud.module.ts index ced1e7e2f8..244985b2fc 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/process-list-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/process-list-cloud.module.ts @@ -28,7 +28,7 @@ import { ProcessListCloudService } from './services/process-list-cloud.service'; MaterialModule, DataTableModule, TemplateModule, - CoreModule.forChild() + CoreModule ], declarations: [ProcessListCloudComponent], diff --git a/lib/process-services-cloud/src/lib/process/start-process/start-process-cloud.module.ts b/lib/process-services-cloud/src/lib/process/start-process/start-process-cloud.module.ts index fd6309204f..bfbc4885c5 100644 --- a/lib/process-services-cloud/src/lib/process/start-process/start-process-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/start-process-cloud.module.ts @@ -30,7 +30,7 @@ import { CoreModule } from '@alfresco/adf-core'; MaterialModule, FlexLayoutModule, ReactiveFormsModule, - CoreModule.forChild() + CoreModule ], declarations: [ StartProcessCloudComponent diff --git a/lib/process-services-cloud/src/lib/task/start-task/start-task-cloud.module.ts b/lib/process-services-cloud/src/lib/task/start-task/start-task-cloud.module.ts index 416384963b..73138d8666 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/start-task-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/start-task-cloud.module.ts @@ -38,7 +38,7 @@ import { GroupCloudModule } from '../../group/group-cloud.module'; GroupCloudModule, FormModule, GroupCloudModule, - CoreModule.forChild() + CoreModule ], declarations: [StartTaskCloudComponent, PeopleCloudComponent], providers: [ diff --git a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts index f21a88d20f..fabcd581c2 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts @@ -37,7 +37,7 @@ import { AppListCloudModule } from './../../app/app-list-cloud.module'; FlexLayoutModule, MaterialModule, AppListCloudModule, - CoreModule.forChild() + CoreModule ], declarations: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent, TaskFilterDialogCloudComponent], exports: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent], diff --git a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts index 06df6f6232..6571c48891 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/task-header-cloud.module.ts @@ -29,7 +29,7 @@ import { TaskHeaderCloudService } from './services/task-header-cloud.service'; DataTableModule, TemplateModule, CardViewModule, - CoreModule.forChild() + CoreModule ], declarations: [ TaskHeaderCloudComponent diff --git a/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts index 3026b3894c..5daefde043 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts @@ -28,7 +28,7 @@ import { TaskListCloudService } from './services/task-list-cloud.service'; MaterialModule, DataTableModule, TemplateModule, - CoreModule.forChild() + CoreModule ], declarations: [TaskListCloudComponent], exports: [TaskListCloudComponent], diff --git a/lib/process-services-cloud/src/public-api.ts b/lib/process-services-cloud/src/public-api.ts index eb73264245..187f0cbcae 100644 --- a/lib/process-services-cloud/src/public-api.ts +++ b/lib/process-services-cloud/src/public-api.ts @@ -16,6 +16,7 @@ */ export * from './lib/process-services-cloud.module'; + export * from './lib/app/public-api'; export * from './lib/process/public-api'; export * from './lib/task/public-api'; diff --git a/lib/process-services-cloud/tsconfig.lib.json b/lib/process-services-cloud/tsconfig.lib.json index 89a9991c06..e65ed8e99a 100644 --- a/lib/process-services-cloud/tsconfig.lib.json +++ b/lib/process-services-cloud/tsconfig.lib.json @@ -1,32 +1,7 @@ { - "extends": "../../tsconfig.json", + "extends": "../tsconfig.json", "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "module": "es2015", - "moduleResolution": "node", - "declaration": true, - "sourceMap": true, - "inlineSources": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "importHelpers": true, - "types": [], - "lib": [ - "dom", - "es2015" - ] - }, - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "outDir": "../dist/process-services-cloud/", + "baseUrl": "src" + } } diff --git a/lib/process-services-cloud/tsconfig.spec.json b/lib/process-services-cloud/tsconfig.spec.json index 1e6bebd1a2..ef8eb83f35 100644 --- a/lib/process-services-cloud/tsconfig.spec.json +++ b/lib/process-services-cloud/tsconfig.spec.json @@ -1,17 +1,6 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] - }, + "extends": "../tsconfig.spec.json", "files": [ "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" ] } diff --git a/lib/process-services-cloud/tslint.json b/lib/process-services-cloud/tslint.json index 3af9ab1abd..ba3c56d2fb 100644 --- a/lib/process-services-cloud/tslint.json +++ b/lib/process-services-cloud/tslint.json @@ -1,5 +1,5 @@ { - "extends": "../../tslint.json", + "extends": "../tslint.json", "rules": { "directive-selector": [ true, diff --git a/lib/process-services/ng-package.json b/lib/process-services/ng-package.json index f52550deaf..3adb1b4924 100644 --- a/lib/process-services/ng-package.json +++ b/lib/process-services/ng-package.json @@ -9,7 +9,7 @@ "entryFile": "./public-api.ts", "flatModuleFile": "adf-process-services", "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-ap", + "@alfresco/js-api": "@alfresco/js-api", "minimatch": "minimatch", "@angular/platform-browser/animations": "@angular/platform-browser/animations", "@angular/material": "@angular/material", diff --git a/lib/process-services/process-list/components/process-list.component.spec.ts b/lib/process-services/process-list/components/process-list.component.spec.ts index 62f63eb879..74bf129e59 100644 --- a/lib/process-services/process-list/components/process-list.component.spec.ts +++ b/lib/process-services/process-list/components/process-list.component.spec.ts @@ -29,7 +29,7 @@ import { fakeProcessInstance, fakeProcessInstancesWithNoName, fakeProcessInstanc import { ProcessService } from '../services/process.service'; import { ProcessTestingModule } from '../../testing/process.testing.module'; import { fakeProcessCustomSchema } from '../../mock'; -import { ProcessListModule } from 'process-list/process-list.module'; +import { ProcessListModule } from '../process-list.module'; describe('ProcessInstanceListComponent', () => { diff --git a/lib/process-services/task-list/components/task-list.component.spec.ts b/lib/process-services/task-list/components/task-list.component.spec.ts index 708172ffe8..c81c344491 100644 --- a/lib/process-services/task-list/components/task-list.component.spec.ts +++ b/lib/process-services/task-list/components/task-list.component.spec.ts @@ -26,7 +26,7 @@ import { ProcessTestingModule } from '../../testing/process.testing.module'; import { fakeGlobalTask, fakeCustomSchema, fakeEmptyTask } from '../../mock'; import { TranslateService } from '@ngx-translate/core'; import { of } from 'rxjs'; -import { TaskListModule } from 'task-list/task-list.module'; +import { TaskListModule } from '../task-list.module'; declare let jasmine: any; diff --git a/lib/process-services/tsconfig.json b/lib/process-services/tsconfig.json deleted file mode 100644 index 4832dfc68f..0000000000 --- a/lib/process-services/tsconfig.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "skipLibCheck": false, - "noLib": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noImplicitUseStrict": false, - "noFallthroughCasesInSwitch": true, - "removeComments": true, - "declaration": true, - "outDir": "../dist/process-services/", - "baseUrl" : "./", - "paths": { - "@alfresco/adf-content-services": ["../content-services"], - "@alfresco/adf-insights": ["../analytics"], - "@alfresco/adf-core": ["../core"] - }, - "lib": [ - "es2016", - "dom" - ], - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - }, - "exclude": [ - "demo", - "node_modules", - "dist" - ], - "angularCompilerOptions": { - "strictMetadataEmit": false, - "skipTemplateCodegen": true - } -} diff --git a/lib/process-services/tsconfig.lib.json b/lib/process-services/tsconfig.lib.json new file mode 100644 index 0000000000..acc2f6c692 --- /dev/null +++ b/lib/process-services/tsconfig.lib.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../dist/process-services/" + } +} diff --git a/lib/process-services/tsconfig.spec.json b/lib/process-services/tsconfig.spec.json new file mode 100644 index 0000000000..ab9099eca3 --- /dev/null +++ b/lib/process-services/tsconfig.spec.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.spec.json", + "files": [ + "test.ts" + ] +} diff --git a/lib/testing/tsconfig.lib.json b/lib/testing/tsconfig.lib.json index 3fe337fcf5..fe8317959f 100644 --- a/lib/testing/tsconfig.lib.json +++ b/lib/testing/tsconfig.lib.json @@ -1,32 +1,6 @@ { - "extends": "../../tsconfig.json", + "extends": "../tsconfig.json", "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "module": "es2015", - "moduleResolution": "node", - "declaration": true, - "sourceMap": true, - "inlineSources": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "importHelpers": true, - "types": [], - "lib": [ - "dom", - "es2018" - ] - }, - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "outDir": "../dist/testing/" + } } diff --git a/lib/testing/tsconfig.spec.json b/lib/testing/tsconfig.spec.json index 16da33db07..ef8eb83f35 100644 --- a/lib/testing/tsconfig.spec.json +++ b/lib/testing/tsconfig.spec.json @@ -1,17 +1,6 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] - }, + "extends": "../tsconfig.spec.json", "files": [ "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" ] } diff --git a/lib/tsconfig.spec.json b/lib/tsconfig.spec.json new file mode 100644 index 0000000000..3c0b6a01f9 --- /dev/null +++ b/lib/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "include": [ + "**/*.spec.ts", + "**/*.d.ts", + "**/test.ts" + ] +} diff --git a/package-lock.json b/package-lock.json index ffc5f01010..42710dfe8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,61 +1,61 @@ { "name": "alfresco-components", - "version": "2.7.0-beta5", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@alfresco/adf-content-services": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-TJzQ0fSUkuix7WDkgapQH4jSg60JKPrniNIaE3oPePXN68HA7fSeCPGaZEm3UcGgsvTu8xaVKAgiuLdRnvyOCg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-3.0.0.tgz", + "integrity": "sha512-fslg/adVczwvrDJV4i/9zWgsrYv+zvqhmiFXubS+vORDvl48fqf3575OIfDSoeRblPrINzyoupH61DHWzAKa/w==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-core": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-gbs0L/iLAQzFzznrX9xmDmnuaeURl393Dhjiz4tluKUN7PsmxJJ2HFvg/NnFszWccLo2OpqmhogNrm87tCGqtg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-3.0.0.tgz", + "integrity": "sha512-3WbOGNPDM1YIG0fSQC2NiEwoNVoBU555yiW6jcNIMI6tSMVw13zuDyA9+wOmo6KY4FZlABah3hGcD/DfqBfNLg==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-extensions": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-N9X8aGrR3V8x60jlLE4vYXm1LO+S2kbSxScElXjC/WJ7W0mu7zBLaNzSeY31oSIoXp+LT8GR/ZZyS8Os1Y6tNw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-3.0.0.tgz", + "integrity": "sha512-piQvPQbG6Zx6J+M8U4Qv6m42gUOOOy9W3Zwgq+DdVCjP60RfhXcS+HW8wUMUxFOTgjonFOIyNA26BddIOoUhEQ==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-insights": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-insights/-/adf-insights-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-7m982iBcLvvPXYmBrmPPjy5Pkr0lVXm97T529EoYMea55h8EmW7C81adbcQiGUy6S9FQzb3iqIoBW2fL8vCPgg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-insights/-/adf-insights-3.0.0.tgz", + "integrity": "sha512-rjdESqKT+BFeJXLGqN+LyWUEIujocKOmH+mWaRIY8oeWFnUPrb3vguc3YGoC0eCN6fhNJSGq6v4LUpKcT/M3Eg==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-process-services": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-process-services/-/adf-process-services-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-pZyHyZU4Ye8IFVBgWKEu9BG679Vx8Q1yHznhMybKTentvZZQY+atpL+WK6ZyxGlLJyCd3kxKEmUChgDOttV/qQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-process-services/-/adf-process-services-3.0.0.tgz", + "integrity": "sha512-PqHXne7tERofGAVxzQyNmZKqepOwtNeC4V/ZTcEib3qL44E/LGQJoy+ega3XlrXCqJ3s7eeMtjg6BN7PiarW0A==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-process-services-cloud": { - "version": "3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b", - "resolved": "https://registry.npmjs.org/@alfresco/adf-process-services-cloud/-/adf-process-services-cloud-3.0.0-1c25b50b1a15635da1689fdb9f38449a5714630b.tgz", - "integrity": "sha512-bKjDSNV6RaIQtFYoPvzG9QrsrJGhUM/cNAVrcMOiYT4tuFoDWQatUHBx6SRpC/q/8FlQi9qkhwtSjyRxikF7gQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/adf-process-services-cloud/-/adf-process-services-cloud-3.0.0.tgz", + "integrity": "sha512-gYPhtzWVpML4IAYdlRKo4DPR+yorRF8KQdyKq5beH9pakjgKsJbaF0KN3cbpuQ+fz8wE2d6bnSBK7QRNB1f4Mw==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/js-api": { - "version": "3.0.0-d7850f421268e21861e2cd219441b7343efd27ba", - "resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-3.0.0-d7850f421268e21861e2cd219441b7343efd27ba.tgz", - "integrity": "sha512-glHDIbJX5xoOT1SBlmryJ1aDEH99a/826KnJK5M/ybLfv5yBg+LFL8aMRRMkz26wzKn9YxRwxh14a+6u2u9o1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-3.0.0.tgz", + "integrity": "sha512-eiJQRJH+GHw9OszNoHWyGrc/llcI5qQBult5G3TfmRDK3c1YcCLHXbQmaN7hgoOuk0i88oPziXC6ZFS/rfi+fA==", "requires": { "event-emitter": "0.3.4", "superagent": "3.8.2" @@ -1922,6 +1922,14 @@ "dev": true, "optional": true }, + "adf-monaco-extension": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/adf-monaco-extension/-/adf-monaco-extension-0.0.8.tgz", + "integrity": "sha512-GvbrgvYhLgCLKN9tvgK1JpQW32F2LRSfukNtga9coPNkoDZeYcXEAOSy8ZPtyI7fXROAy8sYI0G4gD94g4SXPw==", + "requires": { + "tslib": "^1.9.0" + } + }, "adf-tslint-rules": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/adf-tslint-rules/-/adf-tslint-rules-0.0.6.tgz", @@ -5906,6 +5914,12 @@ "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", "dev": true }, + "electron-to-chromium": { + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", + "dev": true + }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", @@ -8244,7 +8258,7 @@ }, "got": { "version": "6.7.1", - "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -10631,7 +10645,7 @@ "dependencies": { "promise": { "version": "7.0.4", - "resolved": "http://registry.npmjs.org/promise/-/promise-7.0.4.tgz", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.0.4.tgz", "integrity": "sha1-Nj6EpMNsg1a4kP7WLJHOhdAu1Tk=", "dev": true, "requires": { @@ -12457,9 +12471,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "ng-packagr": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.4.5.tgz", - "integrity": "sha512-O2s2j6c54HguKLX183zQtey/hcVY7+XVJ5ynpB/LEaiGmkhxFXAku7K/7lLdCO5GkE8YaYw55M/Cmt8O+AqPsQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.7.1.tgz", + "integrity": "sha512-MIPKxyrnV22fS3wSfst2XjwWOonFKujVVEnIehYJhiu8GOg37bCdbbr9plsE1jRDmDAUz6M1MvdKibUrJyRp6Q==", "dev": true, "requires": { "@ngtools/json-schema": "^1.1.0", @@ -12476,7 +12490,7 @@ "less-plugin-npm-import": "^2.1.0", "node-sass": "^4.9.3", "node-sass-tilde-importer": "^1.0.0", - "opencollective": "^1.0.3", + "opencollective-postinstall": "^2.0.1", "postcss": "^7.0.0", "postcss-url": "^8.0.0", "read-pkg-up": "^4.0.0", @@ -12484,7 +12498,7 @@ "rollup": "^0.67.0", "rollup-plugin-commonjs": "^9.1.3", "rollup-plugin-json": "^3.1.0", - "rollup-plugin-node-resolve": "^3.0.0", + "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-sourcemaps": "^0.4.2", "rxjs": "^6.0.0", "stylus": "^0.54.5", @@ -12505,23 +12519,36 @@ "dev": true }, "autoprefixer": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.2.tgz", - "integrity": "sha512-tYQYJvZvqlJCzF+BLC//uAcdT/Yy4ik9bwZRXr/EehUJ/bjjpTthsWTy8dpowdoIE1sLCDf1ch4Eb2cOSzZC9w==", + "version": "9.4.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.8.tgz", + "integrity": "sha512-DIhd0KMi9Nql3oJkJ2HCeOVihrXFPtWXc6ckwaUNwliDOt9OGr0fk8vV8jCLWXnZc1EXvQ2uLUzGpcPxFAQHEQ==", "dev": true, "requires": { - "browserslist": "^4.3.5", - "caniuse-lite": "^1.0.30000914", + "browserslist": "^4.4.1", + "caniuse-lite": "^1.0.30000938", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.6", + "postcss": "^7.0.14", "postcss-value-parser": "^3.3.1" + }, + "dependencies": { + "browserslist": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz", + "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000929", + "electron-to-chromium": "^1.3.103", + "node-releases": "^1.1.3" + } + } } }, "caniuse-lite": { - "version": "1.0.30000918", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000918.tgz", - "integrity": "sha512-CAZ9QXGViBvhHnmIHhsTPSWFBujDaelKnUj7wwImbyQRxmXynYqKGi3UaZTSz9MoVh+1EVxOS/DFIkrJYgR3aw==", + "version": "1.0.30000938", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", + "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", "dev": true }, "cross-spawn": { @@ -12605,7 +12632,7 @@ "dependencies": { "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -12619,9 +12646,9 @@ } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -12675,7 +12702,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -12824,6 +12851,15 @@ } } }, + "node-releases": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz", + "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, "node-sass": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz", @@ -13508,6 +13544,12 @@ } } }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "dev": true + }, "opener": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", @@ -14194,21 +14236,52 @@ "dev": true }, "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", "dev": true, "requires": { - "chalk": "^2.4.1", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.5.0" + "supports-color": "^6.1.0" }, "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -15331,9 +15404,9 @@ "optional": true }, "reflect-metadata": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "regenerate": { "version": "1.4.0", @@ -15791,12 +15864,12 @@ }, "dependencies": { "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "^1.0.6" } } } @@ -15811,21 +15884,30 @@ } }, "rollup-plugin-node-resolve": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.4.0.tgz", - "integrity": "sha512-PJcd85dxfSBWih84ozRtBkB731OjXk0KnzN0oGp7WOWcarAFkVa71cV5hTJg2qpVsV2U8EUwrzHP3tvy9vS3qg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.0.tgz", + "integrity": "sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw==", "dev": true, "requires": { - "builtin-modules": "^2.0.0", + "builtin-modules": "^3.0.0", "is-module": "^1.0.0", - "resolve": "^1.1.6" + "resolve": "^1.8.1" }, "dependencies": { "builtin-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", - "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", + "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==", "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, @@ -15840,103 +15922,20 @@ } }, "rollup-pluginutils": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz", - "integrity": "sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.4.1.tgz", + "integrity": "sha512-wesMQ9/172IJDIW/lYWm0vW0LiKe5Ekjws481R7z9WTRtmO59cqyM/2uUlxvf6yzm/fElFmHUobeQOYz46dZJw==", "dev": true, "requires": { - "estree-walker": "^0.5.2", - "micromatch": "^2.3.11" + "estree-walker": "^0.6.0", + "micromatch": "^3.1.10" }, "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "estree-walker": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.0.tgz", + "integrity": "sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==", "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } } } }, @@ -17037,6 +17036,12 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==" }, + "spdx-license-list": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-5.0.0.tgz", + "integrity": "sha512-N5u9tEFRBUzQDjMKRRt8SHxC/UaqYApPmdF4MMFnICQg3z52onNbnneuro/sWw2rd+eGu9agQOzUbD671Xia7Q==", + "dev": true + }, "spdx-ranges": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.0.tgz", diff --git a/package.json b/package.json index 9c62bbf02e..e9e77ab604 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "@ngx-translate/core": "^11.0.0", "@nrwl/nx": "7.1.1", "@nrwl/schematics": "7.1.1", + "adf-monaco-extension": "0.0.8", "adf-tslint-rules": "0.0.6", "alfresco-js-api-node": "2.7.0-beta5", "chart.js": "2.5.0", @@ -91,11 +92,11 @@ "moment": "2.22.2", "moment-es6": "^1.0.0", "ng2-charts": "1.6.0", - "ngx-monaco-editor": "6.0.0", + "ngx-monaco-editor": "^6.0.0", "pdfjs-dist": "^2.0.489", "protractor-retry": "^1.2.0", "raphael": "2.2.7", - "reflect-metadata": "0.1.10", + "reflect-metadata": "0.1.13", "rxjs": "^6.2.2", "systemjs": "0.19.27", "web-animations-js": "2.3.1", @@ -154,7 +155,7 @@ "merge-stream": "1.0.1", "nconf": "^0.10.0", "ncp": "^2.0.0", - "ng-packagr": "4.4.5", + "ng-packagr": "4.7.1", "node-sass": "4.9.2", "protractor": "^5.4.0", "protractor-html-reporter-2": "^1.0.4", diff --git a/scripts/affected-libs.sh b/scripts/affected-libs.sh index aac1fbb015..89dc7c0c73 100755 --- a/scripts/affected-libs.sh +++ b/scripts/affected-libs.sh @@ -4,11 +4,18 @@ eval BRANCH_NAME="" eval HEAD_SHA_BRANCH="" eval SHA_2="HEAD" eval DIRECTORY="tmp" +eval GNU=false show_help() { - echo "Usage: smart-build.sh" + echo "Usage: affected-libs.sh" echo "" echo "-b branch name" + echo "-gnu for gnu" +} + +gnu_mode() { + echo "====== GNU MODE =====" + GNU=true } branch_name(){ @@ -18,10 +25,18 @@ branch_name(){ while [[ $1 == -* ]]; do case "$1" in -b) branch_name $2; shift 2;; + -gnu) gnu_mode; shift;; -*) echo "invalid option: $1" 1>&2; show_help; exit 0;; esac done +if $GNU; then + sedi='-i' +else + sedi=('-i' '') +fi + + if [[ "$BRANCH_NAME" == "" ]] then echo "The branch name is mandatory" @@ -47,10 +62,10 @@ cat $DIRECTORY/deps.txt #echo "extensions" > deps.txt #clean file -sed -i '/^$/d' ./$DIRECTORY/deps.txt -sed -i '/alfresco-components/d' ./$DIRECTORY/deps.txt -sed -i '/nx affected:libs/d' ./$DIRECTORY/deps.txt -sed -i '/^$/d' ./$DIRECTORY/deps.txt +sed "${sedi[@]}" '/^$/d' ./$DIRECTORY/deps.txt +sed "${sedi[@]}" '/alfresco-components/d' ./$DIRECTORY/deps.txt +sed "${sedi[@]}" '/nx affected:libs/d' ./$DIRECTORY/deps.txt +sed "${sedi[@]}" '/^$/d' ./$DIRECTORY/deps.txt #read result from file while IFS= read -r var @@ -61,6 +76,14 @@ done < "./$DIRECTORY/deps.txt" #transform string to array libs=(`echo $fileLine | sed 's/^$/\n/g'`) +#extensions +for i in "${libs[@]}" +do + if [ "$i" == "extensions" ] ; then + AFFECTED_LIBS=$AFFECTED_LIBS" extensions$" + fi +done + #core for i in "${libs[@]}" do @@ -95,14 +118,6 @@ do fi done -#extensions -for i in "${libs[@]}" -do - if [ "$i" == "extensions" ] ; then - AFFECTED_LIBS=$AFFECTED_LIBS" extensions$" - fi -done - #process-services-cloud for i in "${libs[@]}" do @@ -111,4 +126,4 @@ do fi done -echo "${AFFECTED_LIBS}" +echo "AFFECTED LIBS => ${AFFECTED_LIBS}" diff --git a/scripts/build-extensions.sh b/scripts/build-extensions.sh index 431584b1bd..827d6fd34f 100755 --- a/scripts/build-extensions.sh +++ b/scripts/build-extensions.sh @@ -6,7 +6,7 @@ cd $DIR/.. echo "====== Extensions ======" echo "====== Build ======" -ng build extensions || exit 1 +npm run ng-packagr -- -p ./lib/extensions/ || exit 1 echo "====== Move to node_modules ======" rm -rf ./node_modules/@alfresco/adf-extensions/ && \ diff --git a/scripts/build-process-services-cloud.sh b/scripts/build-process-services-cloud.sh index 98397e97c7..67ccd05a29 100755 --- a/scripts/build-process-services-cloud.sh +++ b/scripts/build-process-services-cloud.sh @@ -6,7 +6,7 @@ cd $DIR/.. echo "====== Process Services Cloud ======" echo "====== Build ======" -ng build process-services-cloud || exit 1 +npm run ng-packagr -- -p ./lib/process-services-cloud/ || exit 1 echo "====== Build style ======" node ./lib/config/bundle-process-services-cloud-scss.js || exit 1 diff --git a/scripts/ng-packagr.sh b/scripts/ng-packagr.sh index 58d34cef0f..4900014123 100755 --- a/scripts/ng-packagr.sh +++ b/scripts/ng-packagr.sh @@ -3,24 +3,25 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $DIR -echo "====== run lib =====" -echo "------ run core -----" -./build-core.sh +echo "====== Run lib =====" -echo "------ run content-services -----" -./build-content-services.sh - -echo "------ Build process-services -----" -./build-process-services.sh - -echo "------ run insights -----" -./build-insights.sh - -echo "------ run extensions -----" +echo "====== Run extensions =====" ./build-extensions.sh -echo "------ run process-services-cloud -----" +echo "====== run core =====" +./build-core.sh + +echo "====== Run content-services =====" +./build-content-services.sh + +echo "====== Run process-services =====" +./build-process-services.sh + +echo "====== Run insights =====" +./build-insights.sh + +echo "====== Run process-services-cloud =====" ./build-process-services-cloud.sh echo "====== Copy schema =====" diff --git a/scripts/pr-reminder.js b/scripts/pr-reminder.js new file mode 100644 index 0000000000..aa297ce29c --- /dev/null +++ b/scripts/pr-reminder.js @@ -0,0 +1,260 @@ +var program = require('commander'); +var request = require('request'); + +function asyncRequest(option) { + return new Promise(function (resolve, reject) { + request(option, function (error, res, body) { + if (!error && (res.statusCode == 200 || res.statusCode == 201)) { + resolve(body); + } else { + console.log("Error " + JSON.stringify(body)); + reject(error + JSON.stringify(body)); + throw "Error"; + } + }); + }); +} + +async function main() { + + program + .version('0.1.0') + .option('-n, --name [type]', 'Name to give at the service in rancher') + .option('-r, --remote [type]', 'Remote environment host adf.lab.com ') + .option('-e, --env [type]', 'Name to give at the service in rancher') + .option('-i, --image [type]', 'Docker image to load') + .option('-s, --server [type]', 'Server RANCHER_SERVER URL') + .option('-p, --password [type]', 'password RANCHER') + .option('-u, --username [type]', 'username RANCHER') + .parse(process.argv); + + auth = 'Basic ' + new Buffer(program.username + ':' + program.password).toString('base64'); + + var project = await asyncRequest({ + url: program.server + `/v1/project?name=${program.env}`, + method: 'GET', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + }, + body: "" + }).catch((error) => { + console.log('Project name error'+ error); + }); + + var stacks = await asyncRequest({ + url: `${program.server}/v2-beta/projects/${project.data[0].id}/stacks?limit=-1&sort=name`, + method: 'GET', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + }, + body: "" + }).catch((error) => { + console.log('Stacks error'+ error); + }); + + + var stackId = stacks.data[0].id; + var environmentId = project.data[0].id; + + console.log("StackId " + stackId); + console.log("ID environment " + environmentId); + console.log("image to Load " + program.image); + + var postData = { + "scale": 1, + "assignServiceIpAddress": false, + "startOnCreate": true, + "type": "service", + "stackId": stackId, + "launchConfig": { + "instanceTriggeredStop": "stop", + "kind": "container", + "networkMode": "managed", + "privileged": false, + "publishAllPorts": false, + "readOnly": false, + "runInit": false, + "startOnCreate": true, + "stdinOpen": true, + "tty": true, + "vcpu": 1, + "drainTimeoutMs": 0, + "type": "launchConfig", + "labels": {"io.rancher.container.pull_image": "always"}, + "restartPolicy": {"name": "always"}, + "secrets": [], + "dataVolumes": [], + "dataVolumesFrom": [], + "dns": [], + "dnsSearch": [], + "capAdd": [], + "capDrop": [], + "devices": [], + "logConfig": {"driver": "", "config": {}}, + "dataVolumesFromLaunchConfigs": [], + "imageUuid": program.image, + "ports": [], + "blkioWeight": null, + "cgroupParent": null, + "count": null, + "cpuCount": null, + "cpuPercent": null, + "cpuPeriod": null, + "cpuQuota": null, + "cpuRealtimePeriod": null, + "cpuRealtimeRuntime": null, + "cpuSet": null, + "cpuSetMems": null, + "cpuShares": null, + "createIndex": null, + "created": null, + "deploymentUnitUuid": null, + "description": null, + "diskQuota": null, + "domainName": null, + "externalId": null, + "firstRunning": null, + "healthInterval": null, + "healthRetries": null, + "healthState": null, + "healthTimeout": null, + "hostname": null, + "ioMaximumBandwidth": null, + "ioMaximumIOps": null, + "ip": null, + "ip6": null, + "ipcMode": null, + "isolation": null, + "kernelMemory": null, + "memory": null, + "memoryMb": null, + "memoryReservation": null, + "memorySwap": null, + "memorySwappiness": null, + "milliCpuReservation": null, + "oomScoreAdj": null, + "pidMode": null, + "pidsLimit": null, + "removed": null, + "requestedIpAddress": null, + "shmSize": null, + "startCount": null, + "stopSignal": null, + "stopTimeout": null, + "user": null, + "userdata": null, + "usernsMode": null, + "uts": null, + "uuid": null, + "volumeDriver": null, + "workingDir": null, + "networkLaunchConfig": null + }, + "secondaryLaunchConfigs": [], + "name": program.name, + "createIndex": null, + "created": null, + "description": null, + "externalId": null, + "healthState": null, + "kind": null, + "removed": null, + "selectorContainer": null, + "selectorLink": null, + "uuid": null, + "vip": null, + "fqdn": null + }; + + var createService = await asyncRequest({ + url: `${program.server}/v2-beta/projects/${environmentId}/service`, + method: 'POST', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + }, + body: postData, + }).catch((error) => { + console.log('Error createService'+ error); + }); + + if (!createService) { + return; + } + + console.log("New environment ID " + createService.id); + + var loadBalancer = await asyncRequest({ + url: `${program.server}/v1/projects/${environmentId}/loadbalancerservices`, + method: 'GET', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + }, + body: postData, + }).catch((error) => { + console.log('Error loadBalancer'+ error); + }); + + if (!loadBalancer) { + return; + } + + var loadBalancerId = loadBalancer.data[0].id; + console.log("Load balancer ID " + loadBalancerId); + + var loadBalancerGet = await asyncRequest({ + url: `${program.server}/v2-beta/projects/${environmentId}/loadbalancerservices/${loadBalancerId}`, + method: 'GET', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + } + }).catch((error) => { + console.log('Error get load balancer'+ error); + }); + + //console.log("Load balancer ID " + JSON.stringify(loadBalancerGet.lbConfig.portRules)); + + var newRule = { + "type": "portRule", + "hostname": program.remote, + "path": `/${program.name}`, + "priority": 1, + "protocol": "http", + "serviceId": createService.id, + "sourcePort": 80, + "targetPort": 80 + }; + + loadBalancerGet.lbConfig.portRules.push(newRule); + + await asyncRequest({ + url: `${program.server}/v2-beta/projects/${environmentId}/loadbalancerservices/${loadBalancerId}`, + method: 'PUT', + json: true, + headers: { + "content-type": "application/json", + "accept": "application/json", + "Authorization": auth + }, + body: loadBalancerGet + }).catch((error) => { + console.log('Error Update load balancer'+ error); + }); + +} + +main(); diff --git a/scripts/smart-build.sh b/scripts/smart-build.sh index 65a42b6f4f..00cda50608 100755 --- a/scripts/smart-build.sh +++ b/scripts/smart-build.sh @@ -31,6 +31,14 @@ affected="$(./scripts/affected-libs.sh -b "$BRANCH_NAME")" echo $affected libs=(`echo $affected | sed 's/^$/\n/g'`) +#extensions +for i in "${libs[@]}" +do + if [ "$i" == "extensions$" ] ; then + ./scripts/build-extensions.sh || exit 1; + fi +done + #core for i in "${libs[@]}" do @@ -70,11 +78,3 @@ do ./scripts/build-insights.sh || exit 1; fi done - -#extensions -for i in "${libs[@]}" -do - if [ "$i" == "extensions$" ] ; then - ./scripts/build-extensions.sh || exit 1; - fi -done From c066a2f36ba1fc35cfc14aee774245c856cca07a Mon Sep 17 00:00:00 2001 From: gmandakini <45559635+gmandakini@users.noreply.github.com> Date: Fri, 22 Feb 2019 14:28:56 +0000 Subject: [PATCH 027/223] [ADF-4100] Automate Permission Role dropdown for User (#4331) * in progress * in progress * ADF-4012 pull request ready * crc's * crc's - moved the methods ProcessListCloudComponent to dataTablePage, to keep it common. * crc's - moved the methods ProcessListCloudComponent to dataTablePage, to keep it common. * changed the locator to use data-automation-id * linting fix * test automated - C268974 * test automated - C268974 * crc's * crc's * crc's * crc's * linting fix * added automation for C276980 - Duplicate User or Group * merged the overlapping testcases * Changed checkUserHasRoleSelected to use datatable methods * changed hte usernames to something more relevant and readable. --- .../permissions-component.e2e.ts | 192 ++++++++++++------ e2e/pages/adf/dataTablePage.ts | 4 + e2e/pages/adf/permissionsPage.ts | 35 ++++ 3 files changed, 171 insertions(+), 60 deletions(-) diff --git a/e2e/content-services/permissions-component.e2e.ts b/e2e/content-services/permissions-component.e2e.ts index 554beaab56..468e34fca5 100644 --- a/e2e/content-services/permissions-component.e2e.ts +++ b/e2e/content-services/permissions-component.e2e.ts @@ -25,7 +25,6 @@ import { ContentListPage } from '../pages/adf/dialog/contentListPage'; import AlfrescoApi = require('alfresco-js-api-node'); import { FileModel } from '../models/ACS/fileModel'; import { UploadActions } from '../actions/ACS/upload.actions'; -import { DataTablePage } from '../pages/adf/dataTablePage'; import { Util } from '../util/util'; import { browser } from 'protractor'; @@ -36,8 +35,7 @@ describe('Permissions Component', function () { let permissionsPage = new PermissionsPage(); let uploadActions = new UploadActions(); let contentList = new ContentListPage(); - let dataTablePage = new DataTablePage(); - let acsUser, file; + let fileOwnerUser, filePermissionUser, file; let fileModel = new FileModel({ 'name': resources.Files.ADF_DOCUMENTS.TXT_0B.file_name, @@ -50,20 +48,25 @@ describe('Permissions Component', function () { }; let groupId; + let alfrescoJsApi = new AlfrescoApi({ + provider: 'ECM', + hostEcm: TestConfig.adf.url + }); + + fileOwnerUser = new AcsUserModel(); + + filePermissionUser = new AcsUserModel(); + const duplicateUserPermissionMessage = 'One or more of the permissions you have set is already present : authority -> ' + filePermissionUser.getId() + ' / role -> Contributor'; beforeAll(async (done) => { - this.alfrescoJsApi = new AlfrescoApi({ - provider: 'ECM', - hostEcm: TestConfig.adf.url - }); - acsUser = new AcsUserModel(); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - await this.alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.core.peopleApi.addPerson(fileOwnerUser); - await this.alfrescoJsApi.core.peopleApi.addPerson(acsUser); + await alfrescoJsApi.core.peopleApi.addPerson(filePermissionUser); - let group = await this.alfrescoJsApi.core.groupsApi.createGroup(groupBody); + let group = await alfrescoJsApi.core.groupsApi.createGroup(groupBody); groupId = group.entry.id; @@ -72,63 +75,132 @@ describe('Permissions Component', function () { done(); }); - beforeEach(async (done) => { - await this.alfrescoJsApi.login(acsUser.id, acsUser.password); - - file = await uploadActions.uploadFile(this.alfrescoJsApi, fileModel.location, fileModel.name, '-my-'); - - loginPage.loginToContentServicesUsingUserModel(acsUser); - contentServicesPage.goToDocumentList(); - - contentList.checkContentIsDisplayed(fileModel.name); - contentList.rightClickOnRowNamed(fileModel.name); - contentList.pressContextMenuActionNamed('Permission'); - - done(); - }); - - afterEach(async (done) => { - await uploadActions.deleteFilesOrFolder(this.alfrescoJsApi, file.entry.id); - - done(); - }); - afterAll(async (done) => { - await this.alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - await this.alfrescoJsApi.core.groupsApi.deleteGroup(groupId); + await alfrescoJsApi.core.groupsApi.deleteGroup(groupId); done(); }); - it('[C286272] Should be able to see results when searching for a user', () => { - permissionsPage.checkAddPermissionButtonIsDisplayed(); - permissionsPage.clickAddPermissionButton(); - permissionsPage.checkAddPermissionDialogIsDisplayed(); - permissionsPage.checkSearchUserInputIsDisplayed(); - permissionsPage.searchUserOrGroup('a'); - permissionsPage.checkResultListIsDisplayed(); + describe('Inherit and assigning permissions', function () { + + beforeEach(async (done) => { + await alfrescoJsApi.login(fileOwnerUser.id, fileOwnerUser.password); + + file = await uploadActions.uploadFile(alfrescoJsApi, fileModel.location, fileModel.name, '-my-'); + + loginPage.loginToContentServicesUsingUserModel(fileOwnerUser); + contentServicesPage.goToDocumentList(); + + contentList.checkContentIsDisplayed(fileModel.name); + contentList.rightClickOnRowNamed(fileModel.name); + contentList.pressContextMenuActionNamed('Permission'); + + done(); + }); + + afterEach(async (done) => { + await uploadActions.deleteFilesOrFolder(alfrescoJsApi, file.entry.id); + + done(); + }); + + it('[C286272] Should be able to see results when searching for a user', () => { + permissionsPage.checkAddPermissionButtonIsDisplayed(); + permissionsPage.clickAddPermissionButton(); + permissionsPage.checkAddPermissionDialogIsDisplayed(); + permissionsPage.checkSearchUserInputIsDisplayed(); + permissionsPage.searchUserOrGroup('a'); + permissionsPage.checkResultListIsDisplayed(); + }); + + it('[C276979] Should be able to give permissions to a group of people', () => { + permissionsPage.checkAddPermissionButtonIsDisplayed(); + permissionsPage.clickAddPermissionButton(); + permissionsPage.checkAddPermissionDialogIsDisplayed(); + permissionsPage.checkSearchUserInputIsDisplayed(); + permissionsPage.searchUserOrGroup('GROUP_' + groupBody.id); + permissionsPage.clickUserOrGroup('GROUP_' + groupBody.id); + permissionsPage.checkUserOrGroupIsAdded('GROUP_' + groupBody.id); + }); + + it('[C268974] Inherit Permission', () => { + permissionsPage.checkPermissionInheritedButtonIsDisplayed(); + expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Permission Inherited'); + permissionsPage.checkPermissionsDatatableIsDisplayed(); + permissionsPage.clickPermissionInheritedButton(); + expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Inherit Permission'); + permissionsPage.checkNoPermissionsIsDisplayed(); + permissionsPage.clickPermissionInheritedButton(); + expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Permission Inherited'); + permissionsPage.checkPermissionsDatatableIsDisplayed(); + }); }); - it('[C276979] Should be able to give permissions to a group of people', () => { - permissionsPage.checkAddPermissionButtonIsDisplayed(); - permissionsPage.clickAddPermissionButton(); - permissionsPage.checkAddPermissionDialogIsDisplayed(); - permissionsPage.checkSearchUserInputIsDisplayed(); - permissionsPage.searchUserOrGroup('GROUP_' + groupBody.id); - permissionsPage.clickUserOrGroup('GROUP_' + groupBody.id); - permissionsPage.checkUserOrGroupIsAdded('GROUP_' + groupBody.id); - }); + describe('Changing and duplicate Permissions', function () { + + beforeEach(async (done) => { + await alfrescoJsApi.login(fileOwnerUser.id, fileOwnerUser.password); + + file = await uploadActions.uploadFile(alfrescoJsApi, fileModel.location, fileModel.name, '-my-'); + + loginPage.loginToContentServicesUsingUserModel(fileOwnerUser); + contentServicesPage.goToDocumentList(); + + contentList.checkContentIsDisplayed(fileModel.name); + contentList.rightClickOnRowNamed(fileModel.name); + contentList.pressContextMenuActionNamed('Permission'); + permissionsPage.checkAddPermissionButtonIsDisplayed(); + permissionsPage.clickAddPermissionButton(); + permissionsPage.checkAddPermissionDialogIsDisplayed(); + permissionsPage.checkSearchUserInputIsDisplayed(); + permissionsPage.searchUserOrGroup(filePermissionUser.getId()); + permissionsPage.clickUserOrGroup(filePermissionUser.getFirstName()); + permissionsPage.checkUserOrGroupIsAdded(filePermissionUser.getId()); + done(); + }); + + afterEach(async (done) => { + await uploadActions.deleteFilesOrFolder(alfrescoJsApi, file.entry.id); + + done(); + }); + + it('[C274691] Should be able to add a new User with permission to the file and also change locally set permissions', () => { + + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Contributor'); + permissionsPage.clickRoleDropdown(); + expect(permissionsPage.getRoleDropdownOptions().count()).toBe(5); + expect(permissionsPage.getRoleDropdownOptions().get(0).getText()).toBe('Contributor'); + expect(permissionsPage.getRoleDropdownOptions().get(1).getText()).toBe('Collaborator'); + expect(permissionsPage.getRoleDropdownOptions().get(2).getText()).toBe('Coordinator'); + expect(permissionsPage.getRoleDropdownOptions().get(3).getText()).toBe('Editor'); + expect(permissionsPage.getRoleDropdownOptions().get(4).getText()).toBe('Consumer'); + permissionsPage.selectOption('Collaborator'); + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Collaborator'); + permissionsPage.clickRoleDropdown(); + permissionsPage.selectOption('Coordinator'); + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Coordinator'); + permissionsPage.clickRoleDropdown(); + permissionsPage.selectOption('Editor'); + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Editor'); + permissionsPage.clickRoleDropdown(); + permissionsPage.selectOption('Consumer'); + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Consumer'); + + }); + + it('[C276980] Should not be able to duplicate User or Group to the locally set permissions', () => { + + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Contributor'); + permissionsPage.clickAddPermissionButton(); + permissionsPage.checkAddPermissionDialogIsDisplayed(); + permissionsPage.checkSearchUserInputIsDisplayed(); + permissionsPage.searchUserOrGroup(filePermissionUser.getId()); + permissionsPage.clickUserOrGroup(filePermissionUser.getFirstName()); + expect(permissionsPage.getAssignPermissionErrorText()).toBe(duplicateUserPermissionMessage); + }); - it('[C268974] Inherit Permission', () => { - permissionsPage.checkPermissionInheritedButtonIsDisplayed(); - expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Permission Inherited'); - permissionsPage.checkPermissionsDatatableIsDisplayed(); - permissionsPage.clickPermissionInheritedButton(); - expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Inherit Permission'); - permissionsPage.checkNoPermissionsIsDisplayed(); - permissionsPage.clickPermissionInheritedButton(); - expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Permission Inherited'); - permissionsPage.checkPermissionsDatatableIsDisplayed(); }); }); diff --git a/e2e/pages/adf/dataTablePage.ts b/e2e/pages/adf/dataTablePage.ts index 6f355536b5..9057415c00 100644 --- a/e2e/pages/adf/dataTablePage.ts +++ b/e2e/pages/adf/dataTablePage.ts @@ -274,4 +274,8 @@ export class DataTablePage { return this.allColumns.count(); } + getCellByNameAndColumn(content, columnName) { + return this.getRowByRowName(content).element(by.css(`div[title='${columnName}']`)); + } + } diff --git a/e2e/pages/adf/permissionsPage.ts b/e2e/pages/adf/permissionsPage.ts index 2bc404fd86..8ed15e4f82 100644 --- a/e2e/pages/adf/permissionsPage.ts +++ b/e2e/pages/adf/permissionsPage.ts @@ -20,6 +20,10 @@ import { element, by } from 'protractor'; import { Util } from '../../util/util'; import { DataTablePage } from './dataTablePage'; +let column = { + role: 'Role' +}; + export class PermissionsPage { addPermissionButton = element(by.css("button[data-automation-id='adf-add-permission-button']")); @@ -30,6 +34,9 @@ export class PermissionsPage { permissionInheritedButton = element.all(by.css("div[class='adf-inherit_permission_button'] button")).first(); permissionInheritedButtonText = this.permissionInheritedButton.element(by.css('span')); noPermissions = element(by.css('div[id="adf-no-permissions-template"]')); + roleDropdown = element(by.id('adf-select-role-permission')); + roleDropdownOptions = element.all(by.css('.mat-option-text')); + assignPermissionError = element(by.css('simple-snack-bar')); checkAddPermissionButtonIsDisplayed() { Util.waitUntilElementIsVisible(this.addPermissionButton); @@ -92,4 +99,32 @@ export class PermissionsPage { return new DataTablePage(element(by.css('[class*="adf-datatable-permission"]'))); } + getRoleCellValue(rowName) { + let locator = new DataTablePage().getCellByNameAndColumn(rowName, column.role); + Util.waitUntilElementIsVisible(locator); + return locator.getText(); + } + + clickRoleDropdown() { + Util.waitUntilElementIsVisible(this.roleDropdown); + return this.roleDropdown.click(); + } + + getRoleDropdownOptions() { + Util.waitUntilElementIsVisible(this.roleDropdownOptions); + return this.roleDropdownOptions; + } + + selectOption(name) { + let selectProcessDropdown = element(by.cssContainingText('.mat-option-text', name)); + Util.waitUntilElementIsVisible(selectProcessDropdown); + Util.waitUntilElementIsClickable(selectProcessDropdown); + selectProcessDropdown.click(); + return this; + } + + getAssignPermissionErrorText() { + Util.waitUntilElementIsVisible(this.assignPermissionError); + return this.assignPermissionError.getText(); + } } From 5d024cf22a8e5a4b6df6512067fca41ecae0ce9d Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Fri, 22 Feb 2019 16:30:38 +0200 Subject: [PATCH 028/223] [ADF-3995] - EditProcessFilterCloud - fix processInstanceId filter property (#4339) --- .../components/edit-process-filter-cloud.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts index a705c6f20c..6039372a58 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts @@ -320,7 +320,7 @@ export class EditProcessFilterCloudComponent implements OnChanges { label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.PROCESS_INS_ID', type: 'text', key: 'processInstanceId', - value: '' + value: currentProcessFilter.processInstanceId || '' }), new ProcessFilterProperties({ label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.PROCESS_NAME', From ae1fbd523a2f954d4f966081187fb418029a17c6 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Sun, 24 Feb 2019 22:43:42 +0000 Subject: [PATCH 029/223] fix aspect orient config e2e core --- e2e/core/card-view/aspect-oriented-config.e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/core/card-view/aspect-oriented-config.e2e.ts b/e2e/core/card-view/aspect-oriented-config.e2e.ts index bf38664232..346911a5bb 100644 --- a/e2e/core/card-view/aspect-oriented-config.e2e.ts +++ b/e2e/core/card-view/aspect-oriented-config.e2e.ts @@ -221,7 +221,7 @@ describe('Aspect oriented config', () => { ' "title": "GROUP-TITLE2-TRANSLATION-KEY",' + ' "items": [' + ' {' + - ' "aspect": "exif:exif"' + + ' "aspect": "exif:exif",' + ' "properties": "*"' + ' }' + ' ]' + From 687257e45ee0cd78006fa5beb041ff8847166e75 Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Mon, 25 Feb 2019 12:27:32 +0200 Subject: [PATCH 030/223] [ADF-4143] EditTaskFilterComponent - change date format (#4342) * [ADF-4143] EditTaskFilterComponent - change date format * [ADF-4143] EditTaskFilter - fix date filtering * [ADF-4143] - lint * [ADF-4143] - refractor * [ADF-4143] - fix failing unit test * [ADF-4143] EditTaskFilter - replace datepicker form-control with ngModel --- .../edit-task-filter-cloud.component.html | 3 +- .../edit-task-filter-cloud.component.ts | 29 +++++++++++-------- .../task-filters/task-filters-cloud.module.ts | 11 +++++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html index de51e41fec..084c9a9fc5 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html @@ -47,7 +47,8 @@ (dateChange)="onDateChanged($event.value, taskFilterProperty)" [matDatepicker]="dateController" placeholder="{{taskFilterProperty.label | translate}}" - [formControlName]="taskFilterProperty.key" + [(ngModel)]="dateFilter[taskFilterProperty.key]" + [ngModelOptions]="{standalone: true}" [attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key"> diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts index b8a89b53ac..5315fa8e5d 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts @@ -15,24 +15,25 @@ * limitations under the License. */ -import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; +import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges, OnInit } from '@angular/core'; import { AbstractControl, FormGroup, FormBuilder } from '@angular/forms'; import { TaskFilterCloudModel, FilterActionType, TaskFilterProperties } from './../models/filter-cloud.model'; import { TaskFilterCloudService } from '../services/task-filter-cloud.service'; -import { MatDialog } from '@angular/material'; +import { MatDialog, DateAdapter } from '@angular/material'; import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component'; -import { TranslationService } from '@alfresco/adf-core'; +import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { debounceTime, filter } from 'rxjs/operators'; import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service'; import { ApplicationInstanceModel } from '../../../app/models/application-instance.model'; import moment from 'moment-es6'; +import { Moment } from 'moment'; @Component({ selector: 'adf-cloud-edit-task-filter', templateUrl: './edit-task-filter-cloud.component.html', styleUrls: ['./edit-task-filter-cloud.component.scss'] }) -export class EditTaskFilterCloudComponent implements OnChanges { +export class EditTaskFilterCloudComponent implements OnInit, OnChanges { public static ACTION_SAVE = 'SAVE'; public static ACTION_SAVE_AS = 'SAVE_AS'; @@ -98,6 +99,7 @@ export class EditTaskFilterCloudComponent implements OnChanges { ]; applicationNames: any[] = []; + dateFilter: any[] = []; formHasBeenChanged = false; editTaskFilterForm: FormGroup; taskFilterProperties: any[] = []; @@ -108,9 +110,17 @@ export class EditTaskFilterCloudComponent implements OnChanges { public dialog: MatDialog, private translateService: TranslationService, private taskFilterCloudService: TaskFilterCloudService, + private dateAdapter: DateAdapter, + private userPreferencesService: UserPreferencesService, private appsProcessCloudService: AppsProcessCloudService) { } + ngOnInit() { + this.userPreferencesService.select(UserPreferenceValues.Locale).subscribe((locale) => { + this.dateAdapter.setLocale(locale); + }); + } + ngOnChanges(changes: SimpleChanges) { const id = changes['id']; if (id && id.currentValue !== id.previousValue) { @@ -185,18 +195,13 @@ export class EditTaskFilterCloudComponent implements OnChanges { onDateChanged(newDateValue: any, dateProperty: TaskFilterProperties) { if (newDateValue) { - let momentDate; - - if (typeof newDateValue === 'string') { - momentDate = moment(newDateValue, this.FORMAT_DATE, true); - } else { - momentDate = newDateValue; - } + let momentDate = moment(newDateValue, this.FORMAT_DATE, true); if (momentDate.isValid()) { this.getPropertyController(dateProperty).setValue(momentDate.toDate()); + this.getPropertyController(dateProperty).setErrors(null); } else { - this.getPropertyController(dateProperty).setErrors({ invalid: true }); + this.getPropertyController(dateProperty).setErrors({invalid: true}); } } } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts index fabcd581c2..6aca8c8285 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts @@ -21,12 +21,13 @@ import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { TaskFiltersCloudComponent } from './components/task-filters-cloud.component'; import { MaterialModule } from '../../material.module'; -import { LogService, StorageService, CoreModule } from '@alfresco/adf-core'; +import { LogService, StorageService, CoreModule, MomentDateAdapter, MOMENT_DATE_FORMATS } from '@alfresco/adf-core'; import { TaskFilterCloudService } from './services/task-filter-cloud.service'; import { HttpClientModule } from '@angular/common/http'; import { EditTaskFilterCloudComponent } from './components/edit-task-filter-cloud.component'; import { TaskFilterDialogCloudComponent } from './components/task-filter-dialog-cloud.component'; import { AppListCloudModule } from './../../app/app-list-cloud.module'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material'; @NgModule({ imports: [ @@ -41,7 +42,13 @@ import { AppListCloudModule } from './../../app/app-list-cloud.module'; ], declarations: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent, TaskFilterDialogCloudComponent], exports: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent], - providers: [TaskFilterCloudService, LogService, StorageService], + providers: [ + TaskFilterCloudService, + LogService, + StorageService, + { provide: DateAdapter, useClass: MomentDateAdapter }, + { provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS } + ], entryComponents: [ TaskFilterDialogCloudComponent ] From 70c523546ab997eddaf738ebd24f1bc4f446c040 Mon Sep 17 00:00:00 2001 From: gmandakini <45559635+gmandakini@users.noreply.github.com> Date: Mon, 25 Feb 2019 11:35:55 +0000 Subject: [PATCH 031/223] [ADF-4066] Start task form with no assignee is saved as unassigned task (#4347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * in progress * in progress * ADF-4012 pull request ready * crc's * crc's - moved the methods ProcessListCloudComponent to dataTablePage, to keep it common. * crc's - moved the methods ProcessListCloudComponent to dataTablePage, to keep it common. * changed the locator to use data-automation-id * linting fix * test automated - C268974 * test automated - C268974 * crc's * crc's * crc's * crc's * linting fix * added automation for C276980 - Duplicate User or Group * merged the overlapping testcases * Changed checkUserHasRoleSelected to use datatable methods * changed hte usernames to something more relevant and readable. * [ADF-4066] Should create a task unassigned when assignee field is empty … (#4334) * C297675 Should create a task unassigned when assignee field is empty in Start Task form automated * linting fixes and updated some locators which are returning more than 1 result back when running this test, as part of best practices. * using hte new method added as it's more efficient, getIdCellValue * linting fixes * updates * updated the taskname variables to more meaningful ones. * added data-automation-id for better test locators. --- .../task-details-cloud-demo.component.html | 2 +- .../process-services/taskDetailsPage.ts | 46 +++++++++++++++++++ .../process-cloud/taskListCloudComponent.ts | 14 +++++- .../start-task-custom-app-cloud.e2e.ts | 42 +++++++++++++---- 4 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 e2e/pages/adf/demo-shell/process-services/taskDetailsPage.ts diff --git a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html index fbf0675087..c3f4a4d399 100644 --- a/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/task-details-cloud-demo.component.html @@ -3,7 +3,7 @@ arrow_back Go Back -

Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}

+

Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}

{ const settingsPage = new SettingsPage(); const loginSSOPage = new LoginSSOPage(); + const taskDetailsPage = new TaskDetailsPage(); const navigationBarPage = new NavigationBarPage(); const appListCloudComponent = new AppListCloudComponent(); const tasksCloudDemoPage = new TasksCloudDemoPage(); const startTask = new StartTasksCloudComponent(); - const standaloneTaskName1 = Util.generateRandomString(5); - const standaloneTaskName2 = Util.generateRandomString(5); + const standaloneTaskName = Util.generateRandomString(5); + const unassignedTaskName = Util.generateRandomString(5); const taskName255Characters = Util.generateRandomString(255); const taskNameBiggerThen255Characters = Util.generateRandomString(256); const lengthValidationError = 'Length exceeded, 255 characters max.'; @@ -61,22 +63,22 @@ describe('Start Task', () => { startTask.checkStartButtonIsDisabled() .blur(startTask.name) .checkValidationErrorIsDisplayed(requiredError); - startTask.addName(standaloneTaskName1) + startTask.addName(standaloneTaskName) .addDescription('descriptions') .addDueDate('12/12/2018'); startTask.checkStartButtonIsEnabled(); startTask.clickCancelButton(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(standaloneTaskName1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(standaloneTaskName); }); it('[C290180] Should be able to create a new standalone task', () => { tasksCloudDemoPage.openNewTaskForm(); - startTask.addName(standaloneTaskName1) + startTask.addName(standaloneTaskName) .addDescription('descriptions') .addDueDate('12/12/2018') .addPriority('50') .clickStartButton(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(standaloneTaskName1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(standaloneTaskName); }); it('[C290181] Should be displayed an error message if task name exceed 255 characters', () => { @@ -103,12 +105,12 @@ describe('Start Task', () => { it('[C290182] Should be possible to assign the task to another user', () => { tasksCloudDemoPage.openNewTaskForm(); - startTask.addName(standaloneTaskName1) + startTask.addName(standaloneTaskName) .addAssignee('Super Admin') .clickStartButton(); tasksCloudDemoPage.myTasksFilter().clickTaskFilter(); expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(standaloneTaskName1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(standaloneTaskName); }); it('[C291953] Assignee field should display the logged user as default', () => { @@ -119,14 +121,34 @@ describe('Start Task', () => { it('[C291956] Should be able to create a new standalone task without assignee', () => { tasksCloudDemoPage.openNewTaskForm(); - startTask.addName(standaloneTaskName2); + startTask.addName(unassignedTaskName); startTask.clearField(startTask.assignee); startTask.clickStartButton(); tasksCloudDemoPage.editTaskFilterCloudComponent() .clickCustomiseFilterHeader() .setStateFilterDropDown('CREATED') .clearAssignment(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(standaloneTaskName2); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(unassignedTaskName); + }); + + it('[C297675] Should create a task unassigned when assignee field is empty in Start Task form', () => { + + tasksCloudDemoPage.openNewTaskForm(); + startTask.addName(unassignedTaskName); + startTask.clearField(startTask.assignee); + startTask.clickStartButton(); + tasksCloudDemoPage.editTaskFilterCloudComponent() + .clickCustomiseFilterHeader() + .setStateFilterDropDown('CREATED') + .clearAssignment(); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().waitForTableBody(); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(unassignedTaskName); + let taskId = tasksCloudDemoPage.taskListCloudComponent().getIdCellValue(unassignedTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByContentName(unassignedTaskName); + expect(taskDetailsPage.getTaskDetailsHeader()).toContain(taskId); + expect(taskDetailsPage.getPropertyLabel('assignee')).toBe('Assignee'); + expect(taskDetailsPage.getPropertyValue('assignee')).toBe('No assignee'); + }); }); From aca5fd10aaf0991411f2a60908255143ec937d56 Mon Sep 17 00:00:00 2001 From: gmandakini <45559635+gmandakini@users.noreply.github.com> Date: Mon, 25 Feb 2019 11:37:54 +0000 Subject: [PATCH 032/223] ADF-4103 automated (#4348) --- e2e/content-services/permissions-component.e2e.ts | 7 +++++++ e2e/pages/adf/permissionsPage.ts | 11 +++++++++++ .../permission-list/permission-list.component.html | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/e2e/content-services/permissions-component.e2e.ts b/e2e/content-services/permissions-component.e2e.ts index 468e34fca5..22953889ba 100644 --- a/e2e/content-services/permissions-component.e2e.ts +++ b/e2e/content-services/permissions-component.e2e.ts @@ -201,6 +201,13 @@ describe('Permissions Component', function () { expect(permissionsPage.getAssignPermissionErrorText()).toBe(duplicateUserPermissionMessage); }); + it('[C276982] Should be able to remove User or Group from the locally set permissions', () => { + + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('Contributor'); + permissionsPage.clickDeletePermissionButton(); + permissionsPage.checkUserOrGroupIsDeleted(filePermissionUser.getId()); + }); + }); }); diff --git a/e2e/pages/adf/permissionsPage.ts b/e2e/pages/adf/permissionsPage.ts index 8ed15e4f82..44acd67f97 100644 --- a/e2e/pages/adf/permissionsPage.ts +++ b/e2e/pages/adf/permissionsPage.ts @@ -37,6 +37,7 @@ export class PermissionsPage { roleDropdown = element(by.id('adf-select-role-permission')); roleDropdownOptions = element.all(by.css('.mat-option-text')); assignPermissionError = element(by.css('simple-snack-bar')); + deletePermissionButton = element(by.css(`button[data-automation-id='adf-delete-permission-button']`)); checkAddPermissionButtonIsDisplayed() { Util.waitUntilElementIsVisible(this.addPermissionButton); @@ -77,6 +78,11 @@ export class PermissionsPage { Util.waitUntilElementIsVisible(userOrGroupName); } + checkUserOrGroupIsDeleted(name) { + let userOrGroupName = element(by.css('div[data-automation-id="text_' + name + '"]')); + Util.waitUntilElementIsNotVisible(userOrGroupName); + } + checkPermissionInheritedButtonIsDisplayed() { Util.waitUntilElementIsVisible(this.permissionInheritedButton); } @@ -86,6 +92,11 @@ export class PermissionsPage { return this.permissionInheritedButton.click(); } + clickDeletePermissionButton() { + Util.waitUntilElementIsClickable(this.deletePermissionButton); + return this.deletePermissionButton.click(); + } + checkNoPermissionsIsDisplayed() { Util.waitUntilElementIsVisible(this.noPermissions); } diff --git a/lib/content-services/permission-manager/components/permission-list/permission-list.component.html b/lib/content-services/permission-manager/components/permission-list/permission-list.component.html index 5508ed81ed..759dc502cb 100644 --- a/lib/content-services/permission-manager/components/permission-list/permission-list.component.html +++ b/lib/content-services/permission-manager/components/permission-list/permission-list.component.html @@ -51,7 +51,7 @@ - From 475a2211fc47f416308d088fbe61edff24a7e2f7 Mon Sep 17 00:00:00 2001 From: Venu Date: Mon, 25 Feb 2019 07:37:13 -0500 Subject: [PATCH 033/223] 4345- Text File Viewer withCredentials (#4349) * 4345-Text File Viewer not passing withCredentials * added new line --- lib/core/viewer/components/txtViewer.component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/viewer/components/txtViewer.component.ts b/lib/core/viewer/components/txtViewer.component.ts index e344360e39..23f4331442 100644 --- a/lib/core/viewer/components/txtViewer.component.ts +++ b/lib/core/viewer/components/txtViewer.component.ts @@ -18,6 +18,7 @@ import { HttpClient } from '@angular/common/http'; import { Component, Input, OnChanges, ViewEncapsulation } from '@angular/core'; import { SimpleChanges } from '@angular/core'; +import { AppConfigService } from './../../app-config/app-config.service'; @Component({ selector: 'adf-txt-viewer', @@ -36,7 +37,7 @@ export class TxtViewerComponent implements OnChanges { content: string | ArrayBuffer; - constructor(private http: HttpClient) { + constructor(private http: HttpClient, private appConfigService: AppConfigService) { } ngOnChanges(changes: SimpleChanges): Promise { @@ -57,8 +58,10 @@ export class TxtViewerComponent implements OnChanges { } private getUrlContent(url: string): Promise { + let withCredentialsMode = this.appConfigService.get('auth.withCredentials', false); + return new Promise((resolve, reject) => { - this.http.get(url, { responseType: 'text' }).subscribe((res) => { + this.http.get(url, { responseType: 'text', withCredentials: withCredentialsMode }).subscribe((res) => { this.content = res; resolve(); }, (event) => { From dcc509532667cd26dcb2801744425a736858df74 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Mon, 25 Feb 2019 15:29:24 +0000 Subject: [PATCH 034/223] remove duplicate branches only travis conf --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9bd21020d6..4f735e37ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,11 +40,6 @@ before_install: - sh -e /etc/init.d/xvfb start - sleep 3 # give xvfb some time to start -branches: - only: - - master - - development - jobs: include: - stage: Warm Up Cache From 5a4a2bd9fe6d2f0bcf01b5eac5cbff6c02bbbd95 Mon Sep 17 00:00:00 2001 From: Andy Stark <30621568+therealandeeee@users.noreply.github.com> Date: Mon, 25 Feb 2019 15:38:51 +0000 Subject: [PATCH 035/223] [ADF-4008] Added column keys to en.json for proc list cloud (#4354) --- lib/process-services-cloud/src/lib/i18n/en.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/process-services-cloud/src/lib/i18n/en.json b/lib/process-services-cloud/src/lib/i18n/en.json index d4013d9ece..01a53d480e 100644 --- a/lib/process-services-cloud/src/lib/i18n/en.json +++ b/lib/process-services-cloud/src/lib/i18n/en.json @@ -10,7 +10,16 @@ "CREATED": "Created", "STATUS": "Status", "START_DATE": "Start Date", - "ID": "Id" + "ID": "Id", + "INITIATOR": "Initiator", + "APP_NAME": "Application Name", + "BUSINESS_KEY": "Business Key", + "DESCRIPTION": "Description", + "LAST_MODIFIED": "Last Modified", + "PROCESS_NAME": "Process Name", + "PARENT_TASK_ID": "Parent Task Id", + "PROCESS_DEF_ID": "Process Definition Id", + "PROCESS_DEF_KEY": "Process Definition Key" }, "ADF_CLOUD_START_PROCESS": { "BUTTON": "Start Process", From 777a15f92d4954ae602cfcddbcd606196f45dfad Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Mon, 25 Feb 2019 15:50:43 +0000 Subject: [PATCH 036/223] [ADF-4147] Add task details redirection toggle in cloud settings page (#4350) --- demo-shell/resources/i18n/en.json | 3 ++- .../cloud/cloud-settings.component.html | 13 ++++++++----- .../app-layout/cloud/cloud-settings.component.ts | 16 ++++++++++------ .../cloud/processes-cloud-demo.component.ts | 6 +----- .../cloud/services/cloud-layout.service.ts | 1 + .../cloud/tasks-cloud-demo.component.ts | 10 ++++------ 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index 750c165e43..2148b7f76c 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -310,6 +310,7 @@ "SETTINGS_CLOUD": { "MULTISELECTION": "Multiselection", "TESTING_MODE": "Testing Mode", - "SELECTION_MODE": "Selection Mode" + "SELECTION_MODE": "Selection Mode", + "TASK_DETAILS_REDIRECTION": "Display task details on task click" } } diff --git a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html index 467b00715d..a52dc0782f 100644 --- a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html @@ -1,10 +1,13 @@
- + {{ 'SETTINGS_CLOUD.MULTISELECTION' | translate }} - - + + {{ 'SETTINGS_CLOUD.TESTING_MODE' | translate }} - + + + {{ 'SETTINGS_CLOUD.TASK_DETAILS_REDIRECTION' | translate }} + {{ 'SETTINGS_CLOUD.SELECTION_MODE' | translate }} @@ -15,4 +18,4 @@ -
\ No newline at end of file +
diff --git a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.ts b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.ts index 6690bb354d..7d2932b258 100644 --- a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.ts @@ -28,6 +28,7 @@ export class CloudSettingsComponent implements OnInit { multiselect: boolean; selectionMode: string; testingMode: boolean; + taskDetailsRedirection: boolean; selectionModeOptions = [ { value: '', title: 'None' }, @@ -43,14 +44,11 @@ export class CloudSettingsComponent implements OnInit { } setCurrentSettings(settings) { - if (settings.multiselect !== undefined) { + if (settings) { this.multiselect = settings.multiselect; - } - if (settings.testingMode !== undefined) { this.testingMode = settings.testingMode; - } - if (settings.selectionMode !== undefined) { this.selectionMode = settings.selectionMode; + this.taskDetailsRedirection = settings.taskDetailsRedirection; } } @@ -64,6 +62,11 @@ export class CloudSettingsComponent implements OnInit { this.setSetting(); } + toggleTaskDetailsRedirection() { + this.taskDetailsRedirection = !this.taskDetailsRedirection; + this.setSetting(); + } + onSelectionModeChange() { this.setSetting(); } @@ -72,7 +75,8 @@ export class CloudSettingsComponent implements OnInit { this.cloudLayoutService.setCurrentSettings({ multiselect: this.multiselect, testingMode: this.testingMode, - selectionMode: this.selectionMode + selectionMode: this.selectionMode, + taskDetailsRedirection: this.taskDetailsRedirection }); } } diff --git a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts index dd7e78f06a..0f51bdcdda 100644 --- a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.ts @@ -85,13 +85,9 @@ export class ProcessesCloudDemoComponent implements OnInit { } setCurrentSettings(settings) { - if (settings.multiselect !== undefined) { + if (settings) { this.multiselect = settings.multiselect; - } - if (settings.testingMode !== undefined) { this.testingMode = settings.testingMode; - } - if (settings.selectionMode !== undefined) { this.selectionMode = settings.selectionMode; } } diff --git a/demo-shell/src/app/components/app-layout/cloud/services/cloud-layout.service.ts b/demo-shell/src/app/components/app-layout/cloud/services/cloud-layout.service.ts index 05b45f2539..6d8bb5ebcf 100644 --- a/demo-shell/src/app/components/app-layout/cloud/services/cloud-layout.service.ts +++ b/demo-shell/src/app/components/app-layout/cloud/services/cloud-layout.service.ts @@ -26,6 +26,7 @@ export class CloudLayoutService { private settings = { multiselect: false, testingMode: false, + taskDetailsRedirection: true, selectionMode: 'single' }; diff --git a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts index 0f381e4a43..f503f885fc 100644 --- a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts @@ -48,6 +48,7 @@ export class TasksCloudDemoComponent implements OnInit { selectedRows: string[] = []; testingMode: boolean; selectionMode: string; + taskDetailsRedirection: boolean; constructor( private cloudLayoutService: CloudLayoutService, @@ -79,14 +80,11 @@ export class TasksCloudDemoComponent implements OnInit { } setCurrentSettings(settings) { - if (settings.multiselect !== undefined) { + if (settings) { this.multiselect = settings.multiselect; - } - if (settings.testingMode !== undefined) { this.testingMode = settings.testingMode; - } - if (settings.selectionMode !== undefined) { this.selectionMode = settings.selectionMode; + this.selectionMode = settings.taskDetailsRedirection; } } @@ -99,7 +97,7 @@ export class TasksCloudDemoComponent implements OnInit { } onRowClick(taskId) { - if (!this.multiselect && this.selectionMode !== 'multiple') { + if (!this.multiselect && this.selectionMode !== 'multiple' && this.taskDetailsRedirection) { this.router.navigate([`/cloud/${this.appName}/task-details/${taskId}`]); } } From 71dca957492db42fddd0b22802d2bb9357db7c1f Mon Sep 17 00:00:00 2001 From: Suzana Dirla Date: Mon, 25 Feb 2019 21:49:36 +0200 Subject: [PATCH 037/223] [ADF-4083] search facetIntervals - empty spaced labels support (#4327) * [ADF-4083] fix space-label issue for intervals * [ADF-4083] remove mention to [SEARCH-1487] issue - for future reference, also check https://issues.alfresco.com/jira/browse/ADF-4144 * [ADF-4083] add missing types * [ADF-4083] refactor method * [ADF-4083] fix test * [ADF-4083] small refactoring * [ADF-4083] better param naming * [ADF-4083] changes after rebase - keep better param naming * [ADF-4083] fix space-label issue for the sets of intervals * [ADF-4083] update test --- demo-shell/src/app.config.json | 2 +- e2e/pages/adf/searchFiltersPage.ts | 2 +- .../search-filter/search-filter.component.ts | 30 ++++++++-- .../search/facet-field.interface.ts | 1 + .../search/search-configuration.interface.ts | 18 +++--- .../search-query-builder.service.spec.ts | 58 ++++++++++++++++++- .../search/search-query-builder.service.ts | 10 +++- 7 files changed, 101 insertions(+), 20 deletions(-) diff --git a/demo-shell/src/app.config.json b/demo-shell/src/app.config.json index 600f4e6ebc..e2d2501839 100644 --- a/demo-shell/src/app.config.json +++ b/demo-shell/src/app.config.json @@ -136,7 +136,7 @@ "expanded": true, "intervals":[ { - "label":"TheCreated", + "label":"The Created", "field":"cm:created", "sets":[ { "label":"lastYear", "start":"2018", "end":"2019", "endInclusive":false }, diff --git a/e2e/pages/adf/searchFiltersPage.ts b/e2e/pages/adf/searchFiltersPage.ts index bb4ac547d7..ea816d45fe 100644 --- a/e2e/pages/adf/searchFiltersPage.ts +++ b/e2e/pages/adf/searchFiltersPage.ts @@ -37,7 +37,7 @@ export class SearchFiltersPage { 'mat-expansion-panel[data-automation-id="expansion-panel-My facet queries"]')); facetQueriesTypeGroup = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Type facet queries"]')); facetQueriesSizeGroup = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Size facet queries"]')); - facetIntervalsByCreated = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-TheCreated"]')); + facetIntervalsByCreated = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-The Created"]')); facetIntervalsByModified = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-TheModified"]')); checkSearchFiltersIsDisplayed() { diff --git a/lib/content-services/search/components/search-filter/search-filter.component.ts b/lib/content-services/search/components/search-filter/search-filter.component.ts index 57010bb9a2..58ea6042db 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.ts @@ -189,7 +189,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { } } - private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] { + private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string): FacetField[] { return configFacetFields.map((field) => { const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {}; const responseBuckets = this.getResponseBuckets(responseField, field) @@ -315,9 +315,31 @@ export class SearchFilterComponent implements OnInit, OnDestroy { } private getCorrespondingFilterQuery (configFacetItem: FacetField, bucketLabel: string): string { - if (!configFacetItem.field || !bucketLabel) { - return null; + let filterQuery = null; + + if (configFacetItem.field && bucketLabel) { + + if (configFacetItem.sets) { + const configSet = configFacetItem.sets.find((set) => bucketLabel === set.label); + + if (configSet) { + filterQuery = this.buildIntervalQuery(configFacetItem.field, configSet); + } + + } else { + filterQuery = `${configFacetItem.field}:"${bucketLabel}"`; + } } - return `${configFacetItem.field}:"${bucketLabel}"`; + + return filterQuery; + } + + private buildIntervalQuery(fieldName: string, interval: any): string { + const start = interval.start; + const end = interval.end; + const startLimit = (interval.startInclusive === undefined || interval.startInclusive === true) ? '[' : '<'; + const endLimit = (interval.endInclusive === undefined || interval.endInclusive === true) ? ']' : '>'; + + return `${fieldName}:${startLimit}"${start}" TO "${end}"${endLimit}`; } } diff --git a/lib/content-services/search/facet-field.interface.ts b/lib/content-services/search/facet-field.interface.ts index 05a3096ead..22d6f78742 100644 --- a/lib/content-services/search/facet-field.interface.ts +++ b/lib/content-services/search/facet-field.interface.ts @@ -31,4 +31,5 @@ export interface FacetField { currentPageSize?: number; checked?: boolean; type?: string; + [propName: string]: any; } diff --git a/lib/content-services/search/search-configuration.interface.ts b/lib/content-services/search/search-configuration.interface.ts index 598883db78..bd2e249276 100644 --- a/lib/content-services/search/search-configuration.interface.ts +++ b/lib/content-services/search/search-configuration.interface.ts @@ -22,28 +22,28 @@ import { SearchCategory } from './search-category.interface'; import { SearchSortingDefinition } from './search-sorting-definition.interface'; export interface SearchConfiguration { - include?: Array; - fields?: Array; - categories: Array; - filterQueries?: Array; + include?: string[]; + fields?: string[]; + categories: SearchCategory[]; + filterQueries?: FilterQuery[]; filterWithContains?: boolean; facetQueries?: { label?: string; pageSize?: number; expanded?: boolean; mincount?: number; - queries: Array; + queries: FacetQuery[]; }; facetFields?: { expanded?: boolean; - fields: Array; + fields: FacetField[]; }; facetIntervals?: { expanded?: boolean; - intervals: Array; + intervals: FacetField[]; }; sorting?: { - options: Array; - defaults: Array; + options: SearchSortingDefinition[]; + defaults: SearchSortingDefinition[]; }; } diff --git a/lib/content-services/search/search-query-builder.service.spec.ts b/lib/content-services/search/search-query-builder.service.spec.ts index 96c97f916a..a223da55ca 100644 --- a/lib/content-services/search/search-query-builder.service.spec.ts +++ b/lib/content-services/search/search-query-builder.service.spec.ts @@ -425,13 +425,59 @@ describe('SearchQueryBuilder', () => { label: 'test_intervals1', field: 'f1', sets: [ - { label: 'interval1', start: 's1', end: 'e1' }, - { label: 'interval2', start: 's2', end: 'e2' } + { label: 'interval1', start: 's1', end: 'e1', startInclusive: true, endInclusive: true }, + { label: 'interval2', start: 's2', end: 'e2', startInclusive: false, endInclusive: true } ] }, { label: 'test_intervals2', field: 'f2', + sets: [ + { label: 'interval3', start: 's3', end: 'e3', startInclusive: true, endInclusive: false }, + { label: 'interval4', start: 's4', end: 'e4', startInclusive: false, endInclusive: false } + ] + } + ] + } + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + builder.queryFragments['cat1'] = 'cm:name:test'; + + const compiled = builder.buildQuery(); + expect(compiled.facetIntervals).toEqual(jasmine.objectContaining(config.facetIntervals)); + }); + + it('should build query with custom facet intervals automatically getting their request compatible labels', () => { + const spacesLabel = { + configValue: 'label with spaces', + requestCompatibleValue: '"label with spaces"' + }; + const noSpacesLabel = { + configValue: 'label', + requestCompatibleValue: 'label' + }; + const spacesLabelForSet = { + configValue: 'label for set', + requestCompatibleValue: '"label for set"' + }; + + const config: SearchConfiguration = { + categories: [ + { id: 'cat1', enabled: true } + ], + facetIntervals: { + intervals: [ + { + label: spacesLabel.configValue, + field: 'f1', + sets: [ + { label: spacesLabelForSet.configValue, start: 's1', end: 'e1' }, + { label: 'interval2', start: 's2', end: 'e2' } + ] + }, + { + label: noSpacesLabel.configValue, + field: 'f2', sets: [ { label: 'interval3', start: 's3', end: 'e3' }, { label: 'interval4', start: 's4', end: 'e4' } @@ -444,7 +490,13 @@ describe('SearchQueryBuilder', () => { builder.queryFragments['cat1'] = 'cm:name:test'; const compiled = builder.buildQuery(); - expect(compiled.facetIntervals).toEqual(jasmine.objectContaining(config.facetIntervals)); + expect(compiled.facetIntervals.intervals[0].label).toEqual(spacesLabel.requestCompatibleValue); + expect(compiled.facetIntervals.intervals[0].label).not.toEqual(spacesLabel.configValue); + expect(compiled.facetIntervals.intervals[1].label).toEqual(noSpacesLabel.requestCompatibleValue); + expect(compiled.facetIntervals.intervals[1].label).toEqual(noSpacesLabel.configValue); + + expect(compiled.facetIntervals.intervals[0].sets[0].label).toEqual(spacesLabelForSet.requestCompatibleValue); + }); it('should build query with sorting', () => { diff --git a/lib/content-services/search/search-query-builder.service.ts b/lib/content-services/search/search-query-builder.service.ts index 3385632d4a..65850d4cba 100644 --- a/lib/content-services/search/search-query-builder.service.ts +++ b/lib/content-services/search/search-query-builder.service.ts @@ -323,9 +323,15 @@ export class SearchQueryBuilderService { return { intervals: configIntervals.intervals.map((interval) => { - label: interval.label, + label: this.getSupportedLabel(interval.label), field: interval.field, - sets: interval.sets + sets: interval.sets.map((set) => { + label: this.getSupportedLabel(set.label), + start: set.start, + end: set.end, + startInclusive: set.startInclusive, + endInclusive: set.endInclusive + }) }) }; } From 0aaa6bea163ec703430f4c80a2274fb94a0e29ac Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Mon, 25 Feb 2019 21:53:20 +0200 Subject: [PATCH 038/223] =?UTF-8?q?[ADF-4068]=20StartTaskComponent=20-=20f?= =?UTF-8?q?ix=20name=20and=20description=20empty=20space=20=E2=80=A6=20(#4?= =?UTF-8?q?351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ADF-4068] StartTaskComponent - fix name and description empty space validation * [ADF-4068] StartTakComponent - add unit test --- lib/process-services/i18n/en.json | 3 ++- .../components/start-task.component.html | 19 +++++++++++-------- .../components/start-task.component.spec.ts | 11 +++++++++++ .../components/start-task.component.ts | 12 ++++++++++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/process-services/i18n/en.json b/lib/process-services/i18n/en.json index f87ee4a9b8..6d2c1283ac 100644 --- a/lib/process-services/i18n/en.json +++ b/lib/process-services/i18n/en.json @@ -134,7 +134,8 @@ "ERROR": { "REQUIRED": "Field required", "DATE": "Date format DD/MM/YYYY", - "MAXIMUM_LENGTH": "Length exceeded, {{characters}} characters max." + "MAXIMUM_LENGTH": "Length exceeded, {{characters}} characters max.", + "MESSAGE": "Enter a different value" } } }, diff --git a/lib/process-services/task-list/components/start-task.component.html b/lib/process-services/task-list/components/start-task.component.html index 107da887ea..822dc415f5 100644 --- a/lib/process-services/task-list/components/start-task.component.html +++ b/lib/process-services/task-list/components/start-task.component.html @@ -7,11 +7,11 @@
{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NAME' | translate}} - - + {{ 'ADF_TASK_LIST.START_TASK.FORM.ERROR.REQUIRED' | translate }} @@ -28,21 +28,24 @@ id="description_id" formControlName="description"> + + {{ 'ADF_TASK_LIST.START_TASK.FORM.ERROR.MESSAGE' | translate }} +
- - - @@ -54,9 +57,9 @@
-
diff --git a/lib/process-services/task-list/components/start-task.component.spec.ts b/lib/process-services/task-list/components/start-task.component.spec.ts index 921732e924..53db72deb0 100644 --- a/lib/process-services/task-list/components/start-task.component.spec.ts +++ b/lib/process-services/task-list/components/start-task.component.spec.ts @@ -404,4 +404,15 @@ describe('StartTaskComponent', () => { fixture.detectChanges(); expect(logSpy).toHaveBeenCalled(); }); + + it('should emit error when description have only white spaces', () => { + fixture.detectChanges(); + let description = component.taskForm.controls['description']; + description.setValue(' '); + fixture.detectChanges(); + expect(description.valid).toBeFalsy(); + description.setValue(''); + fixture.detectChanges(); + expect(description.valid).toBeTruthy(); + }); }); diff --git a/lib/process-services/task-list/components/start-task.component.ts b/lib/process-services/task-list/components/start-task.component.ts index 4d171c9147..1ffbfbdbf9 100644 --- a/lib/process-services/task-list/components/start-task.component.ts +++ b/lib/process-services/task-list/components/start-task.component.ts @@ -102,14 +102,22 @@ export class StartTaskComponent implements OnInit { buildForm() { this.taskForm = this.formBuilder.group({ - name: new FormControl(this.taskDetailsModel.name, [Validators.required, Validators.maxLength(this.maxTaskNameLength)]), - description: new FormControl(''), + name: new FormControl(this.taskDetailsModel.name, [Validators.required, Validators.maxLength(this.maxTaskNameLength), this.whitespaceValidator]), + description: new FormControl('', [this.whitespaceValidator]), formKey: new FormControl('') }); this.taskForm.valueChanges.subscribe((taskFormValues) => this.setTaskDetails(taskFormValues)); } + public whitespaceValidator(control: FormControl) { + if (control.value) { + const isWhitespace = (control.value || '').trim().length === 0; + const isValid = control.value.length === 0 || !isWhitespace; + return isValid ? null : { 'whitespace': true }; + } + } + setTaskDetails(form) { this.taskDetailsModel.name = form.name; this.taskDetailsModel.description = form.description; From 8901261ec9eedf946e600f4c4361cfc6bd3acb9c Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Tue, 26 Feb 2019 09:18:32 +0000 Subject: [PATCH 039/223] Update login.component.md --- docs/core/login.component.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/core/login.component.md b/docs/core/login.component.md index a8c0870fc3..a7c632d3ae 100644 --- a/docs/core/login.component.md +++ b/docs/core/login.component.md @@ -256,8 +256,7 @@ export class MyCustomLogin { // check if the username is in the blacklist if (values.controls['username'].value === 'invalidUsername') { - this.alfrescoLogin.addCustomFormError('username', 'the - username is in blacklist'); + this.alfrescoLogin.addCustomFormError('username', 'the username is in blacklist'); event.preventDefault(); } } From 898d62477e8243e0b08f2577b41f65b9e575a18e Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Tue, 26 Feb 2019 11:41:42 +0200 Subject: [PATCH 040/223] [ADF-4038] People/GroupCloudDemo - add appName filter (#4283) * [ADF-4038] - People/Group Cloud Component add app name filter * [ADF-4038] add unit test, modify ngOnChange appName condition * [ADF-4038] People/GroupCloudDemo - add radio button to filter application name or roles --- demo-shell/resources/i18n/en.json | 5 +- .../people-groups-cloud-demo.component.html | 34 +++++++++++--- .../people-groups-cloud-demo.component.scss | 6 ++- .../people-groups-cloud-demo.component.ts | 47 +++++++++++++++++++ .../components/group-cloud.component.spec.ts | 22 +++++++++ .../group/components/group-cloud.component.ts | 17 ++++--- .../people-cloud.component.spec.ts | 23 ++++++++- .../people-cloud/people-cloud.component.ts | 17 +++++-- 8 files changed, 150 insertions(+), 21 deletions(-) diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index 2148b7f76c..f6aa850fcd 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -293,7 +293,10 @@ "SINGLE": "Single Selection", "MULTI": "Multiple Selection", "PRESELECTED_VALUE": "Preselect: ", - "ROLE": "Roles: " + "ROLE": "Roles: ", + "APP_NAME": "Aplication Name", + "APP_FILTER_MODE": "Filter by application name", + "ROLE_FILTER_MODE": "Filter by role" }, "ABOUT": { "TITLE": "Plugins", diff --git a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.html index c7bcf39771..d7344500b0 100644 --- a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.html @@ -12,17 +12,27 @@ 'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}
- + + {{ + 'PEOPLE_GROUPS_CLOUD.APP_FILTER_MODE' | translate }} + {{ + 'PEOPLE_GROUPS_CLOUD.ROLE_FILTER_MODE' | translate }} + + {{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ['ACTIVITI_ADMIN', "ACTIVITI_USER"] - + + {{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }} + + + {{ 'PEOPLE_GROUPS_CLOUD.PRESELECTED_VALUE' | translate }}: {{ DEFAULT_PEOPLE_PLACEHOLDER }}
- +
@@ -50,18 +60,28 @@ 'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}
- + + {{ + 'PEOPLE_GROUPS_CLOUD.APP_FILTER_MODE' | translate }} + {{ + 'PEOPLE_GROUPS_CLOUD.ROLE_FILTER_MODE' | translate }} + + {{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ['ACTIVITI_ADMIN', "ACTIVITI_USER"] - + + {{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }} + + + Preselect: {{ DEFAULT_GROUP_PLACEHOLDER }}
- +
diff --git a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.scss b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.scss index 539d1c9b00..3603ae7b91 100644 --- a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.scss +++ b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.scss @@ -18,9 +18,13 @@ margin-right: 15px; min-width: 330px; - &-full { + &-big { width:60%; } + + &-full { + width:100%; + } } } } diff --git a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.ts index 5de5f57564..be5e208751 100644 --- a/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/people-groups-cloud-demo.component.ts @@ -27,17 +27,22 @@ import { MatRadioChange } from '@angular/material'; }) export class PeopleGroupCloudDemoComponent { + DEFAULT_FILTER_MODE: string = 'appName'; DEFAULT_GROUP_PLACEHOLDER: string = `[{"id": "1", "name":"activitiUserGroup"}]`; DEFAULT_PEOPLE_PLACEHOLDER: string = `[{"email": "example@alfresco.com", "firstName":"Administrator", "lastName": "ADF"}]`; peopleMode: string = PeopleCloudComponent.MODE_SINGLE; preSelectUsers: string[] = []; peopleRoles: string[] = []; + peopleAppName: string; + peopleFilterMode: string = this.DEFAULT_FILTER_MODE; groupMode: string = GroupCloudComponent.MODE_SINGLE; preSelectGroup: GroupModel[] = []; selectedGroupList: GroupModel[] = []; groupRoles: string[]; + groupAppName: string; + groupFilterMode: string = this.DEFAULT_FILTER_MODE; setPeoplePreselectValue(event: any) { this.preSelectUsers = this.getArrayFromString(event.target.value); @@ -55,6 +60,14 @@ export class PeopleGroupCloudDemoComponent { this.groupRoles = this.getArrayFromString(event.target.value); } + setPeopleAppName(event: any) { + this.peopleAppName = event.target.value; + } + + setGroupAppName(event: any) { + this.groupAppName = event.target.value; + } + onChangePeopleMode(event: MatRadioChange) { this.peopleMode = event.value; this.preSelectUsers = [...this.preSelectUsers]; @@ -65,6 +78,40 @@ export class PeopleGroupCloudDemoComponent { this.preSelectGroup = [...this.preSelectGroup]; } + onChangePeopleFilterMode(event: MatRadioChange) { + this.peopleFilterMode = event.value; + this.resetPeopleFilter(); + } + + onChangeGroupsFilterMode(event: MatRadioChange) { + this.groupFilterMode = event.value; + this.restGroupFilter(); + } + + isPeopleAppNameSelected() { + return this.peopleFilterMode === 'appName'; + } + + isGroupAppNameSelected() { + return this.groupFilterMode === 'appName'; + } + + resetPeopleFilter() { + if (this.isPeopleAppNameSelected()) { + this.peopleRoles = []; + } else { + this.peopleAppName = ''; + } + } + + restGroupFilter() { + if (this.isGroupAppNameSelected()) { + this.groupRoles = []; + } else { + this.groupAppName = ''; + } + } + isStringArray(str: string) { try { const result = JSON.parse(str); diff --git a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts index 338a0f8f13..d6a2cf18a0 100644 --- a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts @@ -26,6 +26,7 @@ import { GroupCloudService } from '../services/group-cloud.service'; import { setupTestBed, AlfrescoApiServiceMock } from '@alfresco/adf-core'; import { mockGroups } from '../mock/group-cloud.mock'; import { GroupModel } from '../models/group.model'; +import { SimpleChange } from '@angular/core'; describe('GroupCloudComponent', () => { let component: GroupCloudComponent; @@ -59,6 +60,9 @@ describe('GroupCloudComponent', () => { }); it('should be able to fetch client id', async(() => { + component.ngOnChanges( { + appName: new SimpleChange(null, component.appName, true) + }); fixture.detectChanges(); fixture.whenStable().then(() => { expect(getClientIdByApplicationNameSpy).toHaveBeenCalled(); @@ -291,4 +295,22 @@ describe('GroupCloudComponent', () => { expect(checkGroupHasAccessSpy).not.toHaveBeenCalled(); }); })); + + it('should load the clients if appName change', async( () => { + component.appName = 'ADF'; + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(getClientIdByApplicationNameSpy).toHaveBeenCalled(); + }); + })); + + it('should filter users if appName change', async(() => { + component.appName = 'ADF'; + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(checkGroupHasAccessSpy).toHaveBeenCalled(); + }); + })); }); diff --git a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts index 7f1d804ea8..4866eae98e 100644 --- a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts @@ -107,19 +107,24 @@ export class GroupCloudComponent implements OnInit, OnChanges { } ngOnInit() { - this.initSearch(); - - if (this.appName) { - this.disableSearch(); - this.loadClientId(); - } } ngOnChanges(changes: SimpleChanges) { if (changes.preSelectGroups && this.hasPreSelectGroups()) { this.loadPreSelectGroups(); } + + if (changes.appName && this.isAppNameChanged(changes.appName)) { + this.disableSearch(); + this.loadClientId(); + } else { + this.enableSearch(); + } + } + + private isAppNameChanged(change) { + return change.previousValue !== change.currentValue && this.appName && this.appName.length > 0; } private async loadClientId() { diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.spec.ts index 1e10a34e1c..0487ffbb35 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.spec.ts @@ -31,6 +31,7 @@ describe('PeopleCloudComponent', () => { let identityService: IdentityUserService; let findUsersSpy: jasmine.Spy; let checkUserHasAccessSpy: jasmine.Spy; + let loadClientsByApplicationNameSpy: jasmine.Spy; setupTestBed({ imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule], @@ -44,7 +45,7 @@ describe('PeopleCloudComponent', () => { identityService = TestBed.get(IdentityUserService); findUsersSpy = spyOn(identityService, 'findUsersByName').and.returnValue(of(mockUsers)); checkUserHasAccessSpy = spyOn(identityService, 'checkUserHasClientApp').and.returnValue(of(true)); - spyOn(identityService, 'getClientIdByApplicationName').and.returnValue(of('mock-client-id')); + loadClientsByApplicationNameSpy = spyOn(identityService, 'getClientIdByApplicationName').and.returnValue(of('mock-client-id')); }); it('should create PeopleCloudComponent', () => { @@ -294,4 +295,24 @@ describe('PeopleCloudComponent', () => { expect(checkUserHasRoleSpy).not.toHaveBeenCalled(); }); })); + + it('should load the clients if appName change', async( () => { + component.appName = 'ADF'; + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(loadClientsByApplicationNameSpy).toHaveBeenCalled(); + }); + })); + + it('should filter users if appName change', async(() => { + component.appName = ''; + fixture.detectChanges(); + component.appName = 'ADF'; + fixture.detectChanges(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(checkUserHasAccessSpy).toHaveBeenCalled(); + }); + })); }); diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.ts b/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.ts index 3ba152a1f9..4fef317370 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.ts @@ -102,17 +102,23 @@ export class PeopleCloudComponent implements OnInit, OnChanges { this.selectedUsersSubject = new BehaviorSubject(this.preSelectUsers); this.selectedUsers$ = this.selectedUsersSubject.asObservable(); this.initSearch(); - - if (this.appName) { - this.disableSearch(); - this.loadClientId(); - } } ngOnChanges(changes: SimpleChanges) { if (changes.preSelectUsers && this.hasPreSelectUsers()) { this.loadPreSelectUsers(); } + + if (changes.appName && this.isAppNameChanged(changes.appName)) { + this.disableSearch(); + this.loadClientId(); + } else { + this.enableSearch(); + } + } + + private isAppNameChanged(change) { + return change.previousValue !== change.currentValue && this.appName && this.appName.length > 0; } private initSearch() { @@ -202,6 +208,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges { } private async loadClientId() { + this.clientId = await this.identityUserService.getClientIdByApplicationName(this.appName).toPromise(); if (this.clientId) { From 69b3591e95b9c03c99e953b378e76595a6531949 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Tue, 26 Feb 2019 10:56:20 +0000 Subject: [PATCH 041/223] uodated roadmap. (#4359) --- docs/roadmap.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 739a8d6254..80e7c520fb 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -6,20 +6,21 @@ Title: ADF roadmap Below is a list of the high level feature themes planned for the forthcoming versions of ADF. -Last updated 15 Jan 2019: +Last updated 26 Feb 2019: -- First Governance components about file plans. +- Continue with the Activiti features. +- Accessibility (508 compliance and automatic tests). +- Search highlighting. +- Better metadata viewer (whitelisting by default). +- Arabic language support. +- Continue with Governance features into ADF. - Better experience in plugin/extensions. -- Accessibility. -- Analytics components to show tables and charts. +- Application dashboards. +- Permission driven menus. - Experimental features: - Artificial Intelligence plugins/components. - Web components for ADF to be framework agnostic. - Rapid Application Development. - - **Disclaimer**: The list above may change in the future and it does not -represent an official promise about features to be implemented. The purpose -of this public roadmap is to share infomation openly with our ecosystem -of customers, partners and enthusiasts. +represent an official promise about features to be implemented. The purpose of this public roadmap is to share infomation openly with our ecosystem of customers, partners and enthusiasts. From 3d771aef3eba45714d1cdc03ef72b6c0abc42da1 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Mon, 25 Feb 2019 22:12:47 +0000 Subject: [PATCH 042/223] name update pkg stages --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4f735e37ee..36f1067834 100644 --- a/.travis.yml +++ b/.travis.yml @@ -111,18 +111,23 @@ jobs: (./scripts/start.sh -dev -t -ss -vjsapi alpha || exit 1;); fi - stage: Update children projects dependency #Update generator-ng2-alfresco-app + name: Update Generator if: tag =~ .*beta.* script: ./scripts/update-project.sh -gnu -t $GITHUB_TOKEN -n generator-ng2-alfresco-app - stage: Update children projects dependency # Test Update alfresco-content-app + name: Update ACA if: tag =~ .*beta.* script: ./scripts/update-project.sh -gnu -t $GITHUB_TOKEN -n alfresco-content-app - stage: Update children projects dependency # Test Update adf-app-manager-ui + name: Update adf-app-manager-ui if: tag =~ .*beta.* script: ./scripts/update-project.sh -gnu -t $GITHUB_TOKEN -n adf-app-manager-ui - - stage: Update children projects dependency # Test Update aalfresco-ng2-components + - stage: Update children projects dependency # Test Update alfresco-ng2-components + name: Update alfresco-ng2-components if: tag =~ .*beta.* script: ./scripts/update-project.sh -gnu -t $GITHUB_TOKEN -n alfresco-ng2-components - stage: Update children projects dependency # Test Update alfresco-modeler-app + name: Update alfresco modeler app if: tag =~ .*beta.* script: ./scripts/update-project.sh -gnu -t $GITHUB_TOKEN -n alfresco-modeler-app - stage: e2e Test # Test core From 30c5965dc4e0fe8c17b3088912bab236f31ff79e Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Tue, 26 Feb 2019 12:14:40 +0000 Subject: [PATCH 043/223] whitelist greenkeeper --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 36f1067834..551135414e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ branches: - master - development - /.*beta.*/ + - /.*greenkeeper.*/ # TRAVIS_PULL_REQUEST == false means is running on dev branch and is not a PR stages: From 9530cc3dcbc97b793bca062b404b96ad31736cf0 Mon Sep 17 00:00:00 2001 From: Suzana Dirla Date: Wed, 27 Feb 2019 01:53:25 +0200 Subject: [PATCH 044/223] [ADF-4154] add FileTypePipe to module's providers (#4362) --- lib/core/pipes/pipe.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/pipes/pipe.module.ts b/lib/core/pipes/pipe.module.ts index 2323414275..2bd70806eb 100644 --- a/lib/core/pipes/pipe.module.ts +++ b/lib/core/pipes/pipe.module.ts @@ -50,7 +50,8 @@ import { FileTypePipe } from './file-type.pipe'; MimeTypeIconPipe, InitialUsernamePipe, NodeNameTooltipPipe, - FormatSpacePipe + FormatSpacePipe, + FileTypePipe ], exports: [ FileSizePipe, From 49edeb7468da95817c3cee9d067070c21384853c Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Wed, 27 Feb 2019 01:54:42 +0200 Subject: [PATCH 045/223] [ADF-4153] TaskListCloud - fix details page redirect issue (#4360) --- .../components/app-layout/cloud/tasks-cloud-demo.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts index f503f885fc..1908f1ae52 100644 --- a/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts +++ b/demo-shell/src/app/components/app-layout/cloud/tasks-cloud-demo.component.ts @@ -84,7 +84,7 @@ export class TasksCloudDemoComponent implements OnInit { this.multiselect = settings.multiselect; this.testingMode = settings.testingMode; this.selectionMode = settings.selectionMode; - this.selectionMode = settings.taskDetailsRedirection; + this.taskDetailsRedirection = settings.taskDetailsRedirection; } } From bfe046637ad2b3cf0ec6c319099a87613fcbd008 Mon Sep 17 00:00:00 2001 From: cristinaj Date: Wed, 27 Feb 2019 11:11:14 +0200 Subject: [PATCH 046/223] [ADF-3995]Implemented test cases for preserved value for process filter (#4353) * Implemented test cases for preserved value for process filter * Update process-custom-filters.e2e.ts --- e2e/pages/adf/configEditorPage.ts | 7 ++ .../adf/dialog/editProcessFilterDialog.ts | 82 +++++++++++++++++++ .../editProcessFilterCloudComponent.ts | 40 ++++++++- .../process-custom-filters.e2e.ts | 29 +++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 e2e/pages/adf/dialog/editProcessFilterDialog.ts diff --git a/e2e/pages/adf/configEditorPage.ts b/e2e/pages/adf/configEditorPage.ts index c31bc7b068..ce6b349b3b 100644 --- a/e2e/pages/adf/configEditorPage.ts +++ b/e2e/pages/adf/configEditorPage.ts @@ -69,4 +69,11 @@ export class ConfigEditorPage { Util.waitUntilElementIsClickable(button); return button.click(); } + + clickEditProcessCloudConfiguration() { + let button = element(by.id('adf-edit-process-filter-conf')); + Util.waitUntilElementIsVisible(button); + Util.waitUntilElementIsClickable(button); + return button.click(); + } } diff --git a/e2e/pages/adf/dialog/editProcessFilterDialog.ts b/e2e/pages/adf/dialog/editProcessFilterDialog.ts new file mode 100644 index 0000000000..325b168cc7 --- /dev/null +++ b/e2e/pages/adf/dialog/editProcessFilterDialog.ts @@ -0,0 +1,82 @@ +/*! + * @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 { by, element } from 'protractor'; +import { Util } from '../../../util/util'; + +export class EditProcessFilterDialog { + + componentElement = element(by.css('adf-cloud-process-filter-dialog-cloud')); + title = element(by.id('adf-process-filter-dialog-title')); + filterNameInput = element(by.id('adf-filter-name-id')); + saveButtonLocator = by.id('adf-save-button-id'); + cancelButtonLocator = by.id('adf-cancel-button-id'); + + clickOnSaveButton() { + let saveButton = this.componentElement.element(this.saveButtonLocator); + Util.waitUntilElementIsVisible(saveButton); + saveButton.click(); + Util.waitUntilElementIsNotVisible(this.componentElement); + return this; + } + + checkSaveButtonIsEnabled() { + Util.waitUntilElementIsVisible(this.componentElement.element(this.saveButtonLocator)); + return this.componentElement.element(this.saveButtonLocator).isEnabled(); + } + + clickOnCancelButton() { + let cancelButton = this.componentElement.element(this.cancelButtonLocator); + Util.waitUntilElementIsVisible(cancelButton); + cancelButton.click(); + Util.waitUntilElementIsNotVisible(this.componentElement); + return this; + } + + checkCancelButtonIsEnabled() { + Util.waitUntilElementIsVisible(this.componentElement.element(this.cancelButtonLocator)); + return this.componentElement.element(this.cancelButtonLocator).isEnabled(); + } + + getFilterName() { + Util.waitUntilElementIsVisible(this.filterNameInput); + return this.filterNameInput.getAttribute('value'); + } + + setFilterName(filterName) { + this.clearFilterName(); + this.filterNameInput.sendKeys(filterName); + return this; + } + + clearFilterName() { + Util.waitUntilElementIsVisible(this.filterNameInput); + this.filterNameInput.click(); + this.filterNameInput.getAttribute('value').then((value) => { + for (let i = value.length; i >= 0; i--) { + this.filterNameInput.sendKeys(protractor.Key.BACK_SPACE); + } + }); + return this; + } + + getTitle() { + Util.waitUntilElementIsVisible(this.title); + return this.title.getText(); + } + +} diff --git a/e2e/pages/adf/process-cloud/editProcessFilterCloudComponent.ts b/e2e/pages/adf/process-cloud/editProcessFilterCloudComponent.ts index 5e8acdb3b0..2a1f122ab6 100644 --- a/e2e/pages/adf/process-cloud/editProcessFilterCloudComponent.ts +++ b/e2e/pages/adf/process-cloud/editProcessFilterCloudComponent.ts @@ -16,11 +16,19 @@ */ import { Util } from '../../../util/util'; import { by, element } from 'protractor'; +import { EditProcessFilterDialog } from '../dialog/editProcessFilterDialog'; export class EditProcessFilterCloudComponent { customiseFilter = element(by.id('adf-edit-process-filter-title-id')); selectedOption = element(by.css('mat-option[class*="mat-selected"]')); + saveAsButton = element(by.css('button[id="adf-save-as-id"]')); + + editProcessFilter = new EditProcessFilterDialog(); + + editProcessFilterDialog() { + return this.editProcessFilter; + } clickCustomiseFilterHeader() { Util.waitUntilElementIsVisible(this.customiseFilter); @@ -59,9 +67,39 @@ export class EditProcessFilterCloudComponent { } clickOnDropDownArrow(option) { - let dropDownArrow = element(by.css("mat-form-field[data-automation-id='" + option + "'] div[class*='arrow']")); + let dropDownArrow = element.all(by.css("mat-form-field[data-automation-id='" + option + "'] div[class*='arrow']")).first(); Util.waitUntilElementIsVisible(dropDownArrow); dropDownArrow.click(); Util.waitUntilElementIsVisible(this.selectedOption); } + + setProcessInstanceId(option) { + return this.setProperty('processInstanceId', option); + } + + getProcessInstanceId() { + return this.getProperty('processInstanceId'); + } + + getProperty(property) { + let locator = element.all(by.css('input[data-automation-id="adf-cloud-edit-process-property-' + property + '"]')).first(); + Util.waitUntilElementIsVisible(locator); + return locator.getAttribute('value'); + } + + setProperty(property, option) { + let locator = element.all(by.css('input[data-automation-id="adf-cloud-edit-process-property-' + property + '"]')).first(); + Util.waitUntilElementIsVisible(locator); + locator.clear(); + locator.sendKeys(option); + locator.sendKeys(protractor.Key.ENTER); + return this; + } + + clickSaveAsButton() { + Util.waitUntilElementIsClickable(this.saveAsButton); + Util.waitUntilElementIsVisible(this.saveAsButton); + this.saveAsButton.click(); + return this.editProcessFilter; + } } diff --git a/e2e/process-services-cloud/process-custom-filters.e2e.ts b/e2e/process-services-cloud/process-custom-filters.e2e.ts index 863aa5c431..e14f645746 100644 --- a/e2e/process-services-cloud/process-custom-filters.e2e.ts +++ b/e2e/process-services-cloud/process-custom-filters.e2e.ts @@ -23,6 +23,7 @@ import { NavigationBarPage } from '../pages/adf/navigationBarPage'; import { ProcessCloudDemoPage } from '../pages/adf/demo-shell/process-services/processCloudDemoPage'; import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage'; import { AppListCloudComponent } from '../pages/adf/process-cloud/appListCloudComponent'; +import { ConfigEditorPage } from '../pages/adf/configEditorPage'; import { ProcessDefinitions } from '../actions/APS-cloud/process-definitions'; import { ProcessInstances } from '../actions/APS-cloud/process-instances'; @@ -32,6 +33,7 @@ import { Query } from '../actions/APS-cloud/query'; describe('Process list cloud', () => { describe('Process List', () => { + const configEditorPage = new ConfigEditorPage(); const settingsPage = new SettingsPage(); const loginSSOPage = new LoginSSOPage(); const navigationBarPage = new NavigationBarPage(); @@ -55,6 +57,16 @@ describe('Process list cloud', () => { loginSSOPage.clickOnSSOButton(); loginSSOPage.loginAPS(user, password); + navigationBarPage.clickConfigEditorButton(); + configEditorPage.clickEditProcessCloudConfiguration(); + configEditorPage.clickClearButton(); + configEditorPage.enterConfiguration('{' + + '"properties": [' + + '"state",' + '"processInstanceId",' + '"sort",' + '"order"' + + ']' + + '}'); + configEditorPage.clickSaveButton(); + await processDefinitionService.init(user, password); let processDefinition = await processDefinitionService.getProcessDefinitions(simpleApp); await processInstancesService.init(user, password); @@ -119,6 +131,23 @@ describe('Process list cloud', () => { }); }); + it('[C297697] The value of the filter should be preserved when saving it', async() => { + processCloudDemoPage.editProcessFilterCloudComponent().clickCustomiseFilterHeader() + .setProcessInstanceId(completedProcess.entry.id); + + processCloudDemoPage.processListCloudComponent().getDataTable().checkSpinnerIsDisplayed().checkSpinnerIsNotDisplayed(); + + expect(tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllDisplayedRows()).toBe(1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(completedProcess.entry.id); + + processCloudDemoPage.editProcessFilterCloudComponent().clickSaveAsButton(); + processCloudDemoPage.editProcessFilterCloudComponent().editProcessFilterDialog().setFilterName('New').clickOnSaveButton(); + expect(processCloudDemoPage.getActiveFilterName()).toBe('New'); + + processCloudDemoPage.editProcessFilterCloudComponent().clickCustomiseFilterHeader(); + expect(processCloudDemoPage.editProcessFilterCloudComponent().getProcessInstanceId()).toEqual(completedProcess.entry.id); + }); + }); }); From cd5870790d4e9e39cc4d8e4d4100a9156cb13254 Mon Sep 17 00:00:00 2001 From: Marouan Bentaleb <38426175+marouanbentaleb@users.noreply.github.com> Date: Wed, 27 Feb 2019 09:12:47 +0000 Subject: [PATCH 047/223] [ADF-4151] Automation test for code viewer (#4355) --- e2e/core/viewer/viewer-component.e2e.ts | 44 +++++++++++++++++++++++++ e2e/pages/adf/demo-shell/aboutPage.ts | 28 ++++++++++++++++ e2e/pages/adf/navigationBarPage.ts | 10 ++++++ e2e/pages/adf/viewerPage.ts | 6 ++++ e2e/resources/adf/a_js_file.js | 1 + e2e/util/resources.js | 4 +++ 6 files changed, 93 insertions(+) create mode 100644 e2e/pages/adf/demo-shell/aboutPage.ts create mode 100644 e2e/resources/adf/a_js_file.js diff --git a/e2e/core/viewer/viewer-component.e2e.ts b/e2e/core/viewer/viewer-component.e2e.ts index d40764da71..88ce3a2207 100644 --- a/e2e/core/viewer/viewer-component.e2e.ts +++ b/e2e/core/viewer/viewer-component.e2e.ts @@ -23,6 +23,7 @@ import { NavigationBarPage } from '../../pages/adf/navigationBarPage'; import { ContentServicesPage } from '../../pages/adf/contentServicesPage'; import { ContentListPage } from '../../pages/adf/dialog/contentListPage'; import { ShareDialog } from '../../pages/adf/dialog/shareDialog'; +import { AboutPage } from '../../pages/adf/demo-shell/aboutPage'; import CONSTANTS = require('../../util/constants'); import resources = require('../../util/resources'); @@ -35,6 +36,7 @@ import { AcsUserModel } from '../../models/ACS/acsUserModel'; import AlfrescoApi = require('alfresco-js-api-node'); import { UploadActions } from '../../actions/ACS/upload.actions'; import { browser } from 'protractor'; +import { viewWrappedDebugError } from '@angular/core/src/view/errors'; xdescribe('Viewer', () => { @@ -48,6 +50,7 @@ xdescribe('Viewer', () => { let pngFileUploaded; const contentList = new ContentListPage(); const shareDialog = new ShareDialog(); + const about = new AboutPage(); let pngFileInfo = new FileModel({ 'name': resources.Files.ADF_DOCUMENTS.PNG.file_name, @@ -452,4 +455,45 @@ xdescribe('Viewer', () => { }); }); }); + + describe('Viewer - Code editor extension', () => { + + let jsFileInfo = new FileModel({ + 'name': resources.Files.ADF_DOCUMENTS.JS.file_name, + 'location': resources.Files.ADF_DOCUMENTS.JS.file_location + }); + + let jsFileUploaded; + + beforeAll(async (done) => { + await this.alfrescoJsApi.login(acsUser.id, acsUser.password); + + jsFileUploaded = await uploadActions.uploadFile(this.alfrescoJsApi, jsFileInfo.location, jsFileInfo.name, '-my-'); + + loginPage.loginToContentServicesUsingUserModel(acsUser); + + done(); + }); + + afterAll(async (done) => { + await this.alfrescoJsApi.login(acsUser.id, acsUser.password); + await uploadActions.deleteFilesOrFolder(this.alfrescoJsApi, jsFileUploaded.entry.id); + done(); + }); + + it('[C297698] Should be able to add an extension for code editor viewer', () => { + navigationBarPage.checkAboutButtonIsDisplayed(); + navigationBarPage.clickAboutButton(); + + about.checkMonacoPluginIsDisplayed(); + + navigationBarPage.clickContentServicesButton(); + + contentServicesPage.waitForTableBody(); + contentServicesPage.checkContentIsDisplayed(jsFileInfo.name); + contentServicesPage.doubleClickRow(jsFileInfo.name); + + viewerPage.checkCodeViewerIsDisplayed(); + }); + }); }); diff --git a/e2e/pages/adf/demo-shell/aboutPage.ts b/e2e/pages/adf/demo-shell/aboutPage.ts new file mode 100644 index 0000000000..3b506fed58 --- /dev/null +++ b/e2e/pages/adf/demo-shell/aboutPage.ts @@ -0,0 +1,28 @@ +/*! + * @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 { by, element } from 'protractor'; +import { Util } from '../../../util/util'; + +export class AboutPage { + + monacoPlugin = element(by.cssContainingText('mat-row > mat-cell', 'monaco plugin')); + + checkMonacoPluginIsDisplayed() { + return Util.waitUntilElementIsVisible(this.monacoPlugin); + } +} diff --git a/e2e/pages/adf/navigationBarPage.ts b/e2e/pages/adf/navigationBarPage.ts index c12d103a49..442f338635 100644 --- a/e2e/pages/adf/navigationBarPage.ts +++ b/e2e/pages/adf/navigationBarPage.ts @@ -46,6 +46,7 @@ export class NavigationBarPage { iconsButton = element(by.css('a[data-automation-id="Icons"]')); customSourcesButton = element(by.css('a[data-automation-id="Custom Sources"]')); settingsButton = element(by.css('a[data-automation-id="Settings"]')); + aboutButton = element(by.css('a[data-automation-id="About"]')); navigateToDatatable() { Util.waitUntilElementIsVisible(this.dataTableButton); @@ -151,6 +152,15 @@ export class NavigationBarPage { return this.headerDataButton.click(); } + clickAboutButton() { + Util.waitUntilElementIsClickable(this.aboutButton); + return this.aboutButton.click(); + } + + checkAboutButtonIsDisplayed() { + return Util.waitUntilElementIsVisible(this.aboutButton); + } + checkMenuButtonIsDisplayed() { return Util.waitUntilElementIsVisible(this.menuButton); } diff --git a/e2e/pages/adf/viewerPage.ts b/e2e/pages/adf/viewerPage.ts index a131f5661f..08b12ff915 100644 --- a/e2e/pages/adf/viewerPage.ts +++ b/e2e/pages/adf/viewerPage.ts @@ -95,6 +95,12 @@ export class ViewerPage { timeButton = element(by.id('adf-viewer-time')); bugButton = element(by.id('adf-viewer-bug')); + codeViewer = element(by.id('adf-monaco-file-editor')); + + checkCodeViewerIsDisplayed() { + return Util.waitUntilElementIsVisible(this.codeViewer); + } + viewFile(fileName) { let fileView = element.all(by.css(`#document-list-container div[filename="${fileName}"]`)).first(); Util.waitUntilElementIsVisible(fileView); diff --git a/e2e/resources/adf/a_js_file.js b/e2e/resources/adf/a_js_file.js new file mode 100644 index 0000000000..09d1b2f0bc --- /dev/null +++ b/e2e/resources/adf/a_js_file.js @@ -0,0 +1 @@ +tiw tiw \ No newline at end of file diff --git a/e2e/util/resources.js b/e2e/util/resources.js index 963165d2b3..c1ec69a40d 100644 --- a/e2e/util/resources.js +++ b/e2e/util/resources.js @@ -323,6 +323,10 @@ exports.Files = { }, ADF_DOCUMENTS: { + JS: { + file_location: "/resources/adf/a_js_file.js", + file_name: "a_js_file.js" + }, PDF: { file_location: "/resources/adf/allFileTypes/a_file_supported.pdf", file_name: "a_file_supported.pdf", From fb12da4ae0851415828fc45887a4826d87e4b4cf Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 27 Feb 2019 11:56:22 +0000 Subject: [PATCH 048/223] [ADF-NO-ISSUE] remove unused pacakges (#4352) * remove unused pacakge * Update package.json tsickle reintroduce --- package.json | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index e9e77ab604..c42cd3bd2e 100644 --- a/package.json +++ b/package.json @@ -78,8 +78,6 @@ "@mat-datetimepicker/core": "^2.0.1", "@mat-datetimepicker/moment": "^2.0.1", "@ngx-translate/core": "^11.0.0", - "@nrwl/nx": "7.1.1", - "@nrwl/schematics": "7.1.1", "adf-monaco-extension": "0.0.8", "adf-tslint-rules": "0.0.6", "alfresco-js-api-node": "2.7.0-beta5", @@ -94,7 +92,6 @@ "ng2-charts": "1.6.0", "ngx-monaco-editor": "^6.0.0", "pdfjs-dist": "^2.0.489", - "protractor-retry": "^1.2.0", "raphael": "2.2.7", "reflect-metadata": "0.1.13", "rxjs": "^6.2.2", @@ -111,16 +108,14 @@ "@types/jasmine": "~2.8.3", "@types/jasminewd2": "~2.0.2", "@types/node": "6.0.90", + "@nrwl/nx": "7.1.1", + "@nrwl/schematics": "7.1.1", "ajv-cli": "^3.0.0", - "angular2-template-loader": "0.6.2", - "astrolabe": "^0.3.6", - "autoprefixer": "6.5.4", "bundlesize": "^0.15.3", "chalk": "^2.3.2", "codelyzer": "^4.5.0", "commander": "^2.15.1", "concurrently": "^3.5.1", - "connect-history-api-fallback": "^1.5.0", "cspell": "^3.1.3", "ejs": "^2.6.1", "extract-text-webpack-plugin": "^4.0.0-beta.0", @@ -146,34 +141,29 @@ "karma-systemjs": "0.16.0", "license-checker": "^25.0.1", "lite-server": "^2.4.0", - "markdown-toc": "^1.2.0", "markdownlint-cli": "^0.12.0", "mdast-util-compact": "^1.0.1", "mdast-util-heading-range": "^2.1.0", "mdast-util-toc": "^2.0.1", "mdast-zone": "^3.0.1", - "merge-stream": "1.0.1", "nconf": "^0.10.0", "ncp": "^2.0.0", "ng-packagr": "4.7.1", "node-sass": "4.9.2", "protractor": "^5.4.0", "protractor-html-reporter-2": "^1.0.4", + "protractor-retry": "^1.2.0", "raw-loader": "0.5.1", - "remap-istanbul": "^0.11.1", "remark": "^9.0.0", "remark-frontmatter": "^1.2.0", "rimraf": "^2.6.2", - "run-sequence": "1.2.2", "rxjs-tslint-rules": "^4.10.0", "sass-loader": "7.1.0", "scss-bundle": "2.3.2", "spdx-license-list": "^5.0.0", "stylelint": "^9.7.1", - "systemjs-builder": "0.15.34", - "traceur": "0.0.111", - "ts-node": "~4.1.0", "tsickle": "^0.34.0", + "ts-node": "~4.1.0", "tslib": "^1.9.0", "tslint": "5.9.1", "typedoc": "^0.11.1", From 596e801283241cb2d92178cb6cac50fb5b3f2530 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 27 Feb 2019 22:25:25 +0000 Subject: [PATCH 049/223] [ADF-3444] Site list displays only a certain number of sites. (#4368) * add infinite scrolling to sites loading * add infinite scrolling to sites loading * add infinite scrolling to sites loading --- lib/content-services/i18n/en.json | 3 + .../sites-dropdown.component.html | 7 +- .../sites-dropdown.component.spec.ts | 102 +++++++++++++++++- .../site-dropdown/sites-dropdown.component.ts | 88 +++++++++++---- 4 files changed, 179 insertions(+), 21 deletions(-) diff --git a/lib/content-services/i18n/en.json b/lib/content-services/i18n/en.json index 71cd50f25d..25624894c6 100644 --- a/lib/content-services/i18n/en.json +++ b/lib/content-services/i18n/en.json @@ -21,6 +21,9 @@ "NO_LABEL": "No" } }, + "ADF_DROPDOWN": { + "LOADING": "Loading..." + }, "ADF_CONFIRM_DIALOG": { "TITLE": "Confirm", "ACTION": "Do you want to proceed?", diff --git a/lib/content-services/site-dropdown/sites-dropdown.component.html b/lib/content-services/site-dropdown/sites-dropdown.component.html index 716a1ccfa5..43ed4f552a 100644 --- a/lib/content-services/site-dropdown/sites-dropdown.component.html +++ b/lib/content-services/site-dropdown/sites-dropdown.component.html @@ -1,17 +1,20 @@ diff --git a/lib/content-services/site-dropdown/sites-dropdown.component.spec.ts b/lib/content-services/site-dropdown/sites-dropdown.component.spec.ts index 4ff38cb72e..5103eb976f 100644 --- a/lib/content-services/site-dropdown/sites-dropdown.component.spec.ts +++ b/lib/content-services/site-dropdown/sites-dropdown.component.spec.ts @@ -46,6 +46,106 @@ describe('DropdownSitesComponent', () => { describe('Rendering tests', () => { + describe('Infinite Loading', () => { + + beforeEach(async(() => { + siteService = TestBed.get(SitesService); + fixture = TestBed.createComponent(DropdownSitesComponent); + debug = fixture.debugElement; + element = fixture.nativeElement; + component = fixture.componentInstance; + })); + + it('Should show loading item if there are more itemes', async(() => { + spyOn(siteService, 'getSites').and.returnValue(of({ + 'list': { + 'pagination': { + 'count': 2, + 'hasMoreItems': true, + 'totalItems': 2, + 'skipCount': 0, + 'maxItems': 100 + }, + 'entries': [ + { + 'entry': { + 'role': 'SiteManager', + 'visibility': 'PUBLIC', + 'guid': 'fake-1', + 'description': 'fake-test-site', + 'id': 'fake-test-site', + 'preset': 'site-dashboard', + 'title': 'fake-test-site' + } + }, + { + 'entry': { + 'role': 'SiteManager', + 'visibility': 'PUBLIC', + 'guid': 'fake-2', + 'description': 'This is a Sample Alfresco Team site.', + 'id': 'swsdp', + 'preset': 'site-dashboard', + 'title': 'fake-test-2' + } + } + ] + } + })); + + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('[data-automation-id="lsite-loading"]')).toBeDefined(); + }); + })); + + it('Should not show loading item if there are more itemes', async(() => { + spyOn(siteService, 'getSites').and.returnValue(of({ + 'list': { + 'pagination': { + 'count': 2, + 'hasMoreItems': false, + 'totalItems': 2, + 'skipCount': 0, + 'maxItems': 100 + }, + 'entries': [ + { + 'entry': { + 'role': 'SiteManager', + 'visibility': 'PUBLIC', + 'guid': 'fake-1', + 'description': 'fake-test-site', + 'id': 'fake-test-site', + 'preset': 'site-dashboard', + 'title': 'fake-test-site' + } + }, + { + 'entry': { + 'role': 'SiteManager', + 'visibility': 'PUBLIC', + 'guid': 'fake-2', + 'description': 'This is a Sample Alfresco Team site.', + 'id': 'swsdp', + 'preset': 'site-dashboard', + 'title': 'fake-test-2' + } + } + ] + } + })); + + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('[data-automation-id="lsite-loading"]')).toBeNull(); + }); + })); + + }); + describe('Sites', () => { beforeEach(async(() => { @@ -93,7 +193,7 @@ describe('DropdownSitesComponent', () => { })); function openSelectBox() { - const selectBox = debug.query(By.css(('[data-automation-id="site-my-files-select"] .mat-select-trigger'))); + const selectBox = debug.query(By.css(('[data-automation-id="site-my-files-option"] .mat-select-trigger'))); selectBox.triggerEventHandler('click', null); } diff --git a/lib/content-services/site-dropdown/sites-dropdown.component.ts b/lib/content-services/site-dropdown/sites-dropdown.component.ts index 31e445517b..d77d29dea4 100644 --- a/lib/content-services/site-dropdown/sites-dropdown.component.ts +++ b/lib/content-services/site-dropdown/sites-dropdown.component.ts @@ -15,9 +15,10 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { SitesService, LogService } from '@alfresco/adf-core'; import { SitePaging, SiteEntry } from '@alfresco/js-api'; +import { MatSelect } from '@angular/material'; export enum Relations { Members = 'members', @@ -68,6 +69,15 @@ export class DropdownSitesComponent implements OnInit { @Output() change: EventEmitter = new EventEmitter(); + @ViewChild('siteSelect') + siteSelect: MatSelect; + + private loading = true; + private skipCount = 0; + private readonly MAX_ITEMS = 50; + private readonly ITEM_HEIGHT = 45; + private readonly ITEM_HEIGHT_TO_WAIT_BEFORE_LOAD_NEXT = (this.ITEM_HEIGHT * (this.MAX_ITEMS / 2)); + selected: SiteEntry = null; public MY_FILES_VALUE = '-my-'; @@ -77,47 +87,89 @@ export class DropdownSitesComponent implements OnInit { } ngOnInit() { + this.siteSelect.openedChange.subscribe(() => { + if (this.siteSelect.panelOpen) { + this.siteSelect.panel.nativeElement.addEventListener('scroll', (event) => this.loadAllOnScroll(event)); + } + }); + if (!this.siteList) { - this.setDefaultSiteList(); + this.loadSiteList(); } } + loadAllOnScroll(event) { + if (this.isInfiniteScrollingEnabled() && this.isScrollInNextFetchArea(event)) { + this.loading = true; + this.loadSiteList(); + } + } + + isScrollInNextFetchArea(event) { + return event.target.scrollTop >= (event.target.scrollHeight - event.target.offsetHeight - this.ITEM_HEIGHT_TO_WAIT_BEFORE_LOAD_NEXT); + } + selectedSite(event: any) { this.change.emit(event.value); } - private setDefaultSiteList() { - let extendedOptions = null; + private loadSiteList() { + let extendedOptions: any = { + skipCount: this.skipCount, + maxItems: this.MAX_ITEMS + }; + + this.skipCount += this.MAX_ITEMS; + if (this.relations) { - extendedOptions = { relations: [this.relations] }; + extendedOptions.relations = [this.relations]; } + this.sitesService.getSites(extendedOptions).subscribe((sitePaging: SitePaging) => { - this.siteList = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging; + if (!this.siteList) { + this.siteList = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging; - if (!this.hideMyFiles) { - let siteEntry = new SiteEntry({ - entry: { - id: '-my-', - guid: '-my-', - title: 'DROPDOWN.MY_FILES_OPTION' + if (!this.hideMyFiles) { + let siteEntry = new SiteEntry({ + entry: { + id: '-my-', + guid: '-my-', + title: 'DROPDOWN.MY_FILES_OPTION' + } + }); + + this.siteList.list.entries.unshift(siteEntry); + + if (!this.value) { + this.value = '-my-'; } - }); - - this.siteList.list.entries.unshift(siteEntry); - - if (!this.value) { - this.value = '-my-'; } + + } else { + let siteList: SitePaging = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging; + + this.siteList.list.entries = this.siteList.list.entries.concat(siteList.list.entries); + this.siteList.list.pagination = sitePaging.list.pagination; } this.selected = this.siteList.list.entries.find((site: SiteEntry) => site.entry.id === this.value); + + this.loading = false; }, (error) => { this.logService.error(error); }); } + showLoading(): boolean { + return this.loading && (this.siteList && this.siteList.list.pagination && this.siteList.list.pagination.hasMoreItems); + } + + isInfiniteScrollingEnabled(): boolean { + return !this.loading && (this.siteList && this.siteList.list.pagination && this.siteList.list.pagination.hasMoreItems); + } + private filteredResultsByMember(sites: SitePaging): SitePaging { const loggedUserName = this.sitesService.getEcmCurrentLoggedUserName(); sites.list.entries = sites.list.entries.filter((site) => this.isCurrentUserMember(site, loggedUserName)); From 00ba61cdeabae7e46d89e778906896a6cb6d4429 Mon Sep 17 00:00:00 2001 From: gmandakini <45559635+gmandakini@users.noreply.github.com> Date: Thu, 28 Feb 2019 09:38:09 +0000 Subject: [PATCH 050/223] [ADF-4105] Role Site Dropdown is Displayed (#4356) * in progress * C277002 - Role Site Dropdown automated. * C277002 - Role Site Dropdown automated. * async updates * async updates * in progress * fixed errors * Fixing tests * fixed errors --- .../permissions-component.e2e.ts | 57 +++++++++++++++---- e2e/pages/adf/contentServicesPage.ts | 5 ++ e2e/pages/adf/dialog/contentListPage.ts | 19 +++++++ e2e/pages/adf/permissionsPage.ts | 5 ++ 4 files changed, 75 insertions(+), 11 deletions(-) diff --git a/e2e/content-services/permissions-component.e2e.ts b/e2e/content-services/permissions-component.e2e.ts index 22953889ba..d173523048 100644 --- a/e2e/content-services/permissions-component.e2e.ts +++ b/e2e/content-services/permissions-component.e2e.ts @@ -36,6 +36,7 @@ describe('Permissions Component', function () { let uploadActions = new UploadActions(); let contentList = new ContentListPage(); let fileOwnerUser, filePermissionUser, file; + let publicSite, folderName; let fileModel = new FileModel({ 'name': resources.Files.ADF_DOCUMENTS.TXT_0B.file_name, @@ -61,23 +62,23 @@ describe('Permissions Component', function () { beforeAll(async (done) => { await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - await alfrescoJsApi.core.peopleApi.addPerson(fileOwnerUser); - await alfrescoJsApi.core.peopleApi.addPerson(filePermissionUser); - let group = await alfrescoJsApi.core.groupsApi.createGroup(groupBody); - groupId = group.entry.id; - browser.driver.sleep(15000); // wait search get the groups - + await alfrescoJsApi.login(fileOwnerUser.id, fileOwnerUser.password); + let siteName = `PUBLIC_TEST_SITE_${Util.generateRandomString(5)}`; + folderName = `MEESEEKS_${Util.generateRandomString(5)}`; + let publicSiteBody = { visibility: 'PUBLIC', title: siteName }; + publicSite = await alfrescoJsApi.core.sitesApi.createSite(publicSiteBody); + await uploadActions.createFolder(alfrescoJsApi, folderName, publicSite.entry.guid); done(); }); afterAll(async (done) => { await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - + await alfrescoJsApi.core.sitesApi.deleteSite(publicSite.entry.id); await alfrescoJsApi.core.groupsApi.deleteGroup(groupId); done(); }); @@ -91,8 +92,8 @@ describe('Permissions Component', function () { loginPage.loginToContentServicesUsingUserModel(fileOwnerUser); contentServicesPage.goToDocumentList(); - contentList.checkContentIsDisplayed(fileModel.name); + contentServicesPage.checkSelectedSiteIsDisplayed('My files'); contentList.rightClickOnRowNamed(fileModel.name); contentList.pressContextMenuActionNamed('Permission'); @@ -101,7 +102,6 @@ describe('Permissions Component', function () { afterEach(async (done) => { await uploadActions.deleteFilesOrFolder(alfrescoJsApi, file.entry.id); - done(); }); @@ -125,6 +125,7 @@ describe('Permissions Component', function () { }); it('[C268974] Inherit Permission', () => { + permissionsPage.checkPermissionContainerIsDisplayed(); permissionsPage.checkPermissionInheritedButtonIsDisplayed(); expect(permissionsPage.getPermissionInheritedButtonText()).toBe('Permission Inherited'); permissionsPage.checkPermissionsDatatableIsDisplayed(); @@ -148,8 +149,9 @@ describe('Permissions Component', function () { contentServicesPage.goToDocumentList(); contentList.checkContentIsDisplayed(fileModel.name); - contentList.rightClickOnRowNamed(fileModel.name); - contentList.pressContextMenuActionNamed('Permission'); + contentServicesPage.checkSelectedSiteIsDisplayed('My files'); + contentList.clickRowMenuActionsButton(fileModel.name); + contentList.clickMenuActionNamed('PERMISSION'); permissionsPage.checkAddPermissionButtonIsDisplayed(); permissionsPage.clickAddPermissionButton(); permissionsPage.checkAddPermissionDialogIsDisplayed(); @@ -210,4 +212,37 @@ describe('Permissions Component', function () { }); + describe('Role Site Dropdown', function () { + + beforeEach(async (done) => { + loginPage.loginToContentServicesUsingUserModel(fileOwnerUser); + browser.get(TestConfig.adf.url + '/files/' + publicSite.entry.guid); + contentList.checkContentIsDisplayed(folderName); + contentServicesPage.checkSelectedSiteIsDisplayed('My files'); + contentList.clickRowMenuActionsButton(folderName); + contentList.clickMenuActionNamed('PERMISSION'); + permissionsPage.checkPermissionInheritedButtonIsDisplayed(); + permissionsPage.checkAddPermissionButtonIsDisplayed(); + permissionsPage.clickAddPermissionButton(); + permissionsPage.checkAddPermissionDialogIsDisplayed(); + permissionsPage.checkSearchUserInputIsDisplayed(); + permissionsPage.searchUserOrGroup(filePermissionUser.getId()); + permissionsPage.clickUserOrGroup(filePermissionUser.getFirstName()); + permissionsPage.checkUserOrGroupIsAdded(filePermissionUser.getId()); + done(); + }); + + it('[C277002] Should display the Role Site dropdown', () => { + + expect(permissionsPage.getRoleCellValue(filePermissionUser.getId())).toEqual('SiteCollaborator'); + permissionsPage.clickRoleDropdown(); + expect(permissionsPage.getRoleDropdownOptions().count()).toBe(4); + expect(permissionsPage.getRoleDropdownOptions().get(0).getText()).toBe('SiteCollaborator'); + expect(permissionsPage.getRoleDropdownOptions().get(1).getText()).toBe('SiteConsumer'); + expect(permissionsPage.getRoleDropdownOptions().get(2).getText()).toBe('SiteContributor'); + expect(permissionsPage.getRoleDropdownOptions().get(3).getText()).toBe('SiteManager'); + + }); + + }); }); diff --git a/e2e/pages/adf/contentServicesPage.ts b/e2e/pages/adf/contentServicesPage.ts index c2a68d8431..60f8b86664 100644 --- a/e2e/pages/adf/contentServicesPage.ts +++ b/e2e/pages/adf/contentServicesPage.ts @@ -66,6 +66,7 @@ export class ContentServicesPage { copyButton = element(by.css('button[data-automation-id="content-node-selector-actions-choose"]')); searchInputElement = element(by.css('input[data-automation-id="content-node-selector-search-input"]')); shareNodeButton = element(by.cssContainingText('mat-icon', ' share ')); + siteListDropdown = element(by.css(`mat-select[data-automation-id='site-my-files-select']`)); getUploadAreaDocumentList() { return new ContentListPage(element(by.css('adf-upload-drag-area'))); @@ -691,4 +692,8 @@ export class ContentServicesPage { Util.waitUntilElementIsClickable(this.shareNodeButton); this.shareNodeButton.click(); } + + checkSelectedSiteIsDisplayed(siteName) { + Util.waitUntilElementIsVisible(this.siteListDropdown.element(by.cssContainingText('.mat-select-value-text span', siteName))); + } } diff --git a/e2e/pages/adf/dialog/contentListPage.ts b/e2e/pages/adf/dialog/contentListPage.ts index de4cd256ea..91af6e2e1f 100644 --- a/e2e/pages/adf/dialog/contentListPage.ts +++ b/e2e/pages/adf/dialog/contentListPage.ts @@ -443,4 +443,23 @@ export class ContentListPage { browser.actions().click(row, protractor.Button.RIGHT).perform(); Util.waitUntilElementIsVisible(element(by.id('adf-context-menu-content'))); } + + clickMenuActionNamed(actionName) { + let actionButton = this.checkMenuActionIsVisible(actionName); + actionButton.click(); + } + + checkMenuActionIsVisible(actionName) { + let actionButton = element(by.css(`button[data-automation-id='DOCUMENT_LIST.ACTIONS.${actionName}']`)); + Util.waitUntilElementIsVisible(actionButton); + Util.waitUntilElementIsClickable(actionButton); + return actionButton; + } + + clickRowMenuActionsButton(rowName) { + let row = this.getRowByRowName(rowName); + Util.waitUntilElementIsVisible(row.element(by.css('.adf-datatable__actions-cell button'))); + row.element(by.css('.adf-datatable__actions-cell button')).click(); + } + } diff --git a/e2e/pages/adf/permissionsPage.ts b/e2e/pages/adf/permissionsPage.ts index 44acd67f97..bc7f8a97d0 100644 --- a/e2e/pages/adf/permissionsPage.ts +++ b/e2e/pages/adf/permissionsPage.ts @@ -38,6 +38,7 @@ export class PermissionsPage { roleDropdownOptions = element.all(by.css('.mat-option-text')); assignPermissionError = element(by.css('simple-snack-bar')); deletePermissionButton = element(by.css(`button[data-automation-id='adf-delete-permission-button']`)); + permissionDisplayContainer = element(by.css(`div[id='adf-permission-display-container']`)); checkAddPermissionButtonIsDisplayed() { Util.waitUntilElementIsVisible(this.addPermissionButton); @@ -138,4 +139,8 @@ export class PermissionsPage { Util.waitUntilElementIsVisible(this.assignPermissionError); return this.assignPermissionError.getText(); } + + checkPermissionContainerIsDisplayed() { + Util.waitUntilElementIsVisible(this.permissionDisplayContainer); + } } From e08bb1ebed2e8c71d7762ddab431385faf49232c Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Thu, 28 Feb 2019 14:39:34 +0000 Subject: [PATCH 051/223] [ADF-4023] Demo-shell - Fix pagination in tasks list (#4372) --- .../cloud/processes-cloud-demo.component.html | 29 ++++++++++--------- .../cloud/tasks-cloud-demo.component.html | 2 ++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html index 02aa4fca19..a9e7b786e1 100644 --- a/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/processes-cloud-demo.component.html @@ -7,20 +7,21 @@ (action)="onProcessFilterAction($event)">
- +
Date: Fri, 1 Mar 2019 11:53:50 +0200 Subject: [PATCH 052/223] [ADF-4015] Implementing edit task filter automated tests (#4337) * Added a few test cases to cover task list and edit task filters cloud components properties. * Add tests for task name property. * Add processInstanceId tests * Added lastModifiedTo and lastModifiedFrom tests. * Added tests for all the properties * Add selectionMode tests. * Add selectionMode tests * Update task-list-selection.e2e.ts * [ADF-4015] Fix lint issues * Using clearField * Modified selection tests * Fixing selection tests * no message * Fix spelling * Removed a sleep from the test * Fix siteListDropDown locator --- .../cloud/cloud-settings.component.html | 2 +- e2e/actions/APS-cloud/tasks.ts | 6 +- e2e/pages/adf/configEditorPage.ts | 14 + e2e/pages/adf/contentServicesPage.ts | 2 +- e2e/pages/adf/dataTablePage.ts | 57 +++- .../process-services/tasksCloudDemoPage.ts | 58 +++- .../editTaskFilterCloudComponent.ts | 105 +++++- .../task-list-properties.e2e.ts | 315 ++++++++++++++++++ .../task-list-selection.e2e.ts | 128 +++++++ .../taskListCloud.config.ts | 84 +++++ .../tasks-custom-filters.e2e.ts | 36 +- 11 files changed, 783 insertions(+), 24 deletions(-) create mode 100644 e2e/process-services-cloud/task-list-properties.e2e.ts create mode 100644 e2e/process-services-cloud/task-list-selection.e2e.ts create mode 100644 e2e/process-services-cloud/taskListCloud.config.ts diff --git a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html index a52dc0782f..3621839066 100644 --- a/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html +++ b/demo-shell/src/app/components/app-layout/cloud/cloud-settings.component.html @@ -8,7 +8,7 @@ {{ 'SETTINGS_CLOUD.TASK_DETAILS_REDIRECTION' | translate }} - + {{ 'SETTINGS_CLOUD.SELECTION_MODE' | translate }} diff --git a/e2e/actions/APS-cloud/tasks.ts b/e2e/actions/APS-cloud/tasks.ts index d92052c255..b22bf3e6e2 100644 --- a/e2e/actions/APS-cloud/tasks.ts +++ b/e2e/actions/APS-cloud/tasks.ts @@ -89,11 +89,11 @@ export class Tasks { return data; } - async createStandaloneSubtask(taskId, appName, name) { - const path = '/' + appName + '-rb/v1/tasks/' + taskId + '/subtask'; + async createStandaloneSubtask(parentTaskId, appName, name) { + const path = '/' + appName + '-rb/v1/tasks'; const method = 'POST'; - const queryParams = {}, postBody = {'name': name, 'payloadType': 'CreateTaskPayload'}; + const queryParams = {}, postBody = {'name': name, 'parentTaskId': parentTaskId, 'payloadType': 'CreateTaskPayload'}; const data = await this.api.performBpmOperation(path, method, queryParams, postBody); return data; diff --git a/e2e/pages/adf/configEditorPage.ts b/e2e/pages/adf/configEditorPage.ts index ce6b349b3b..932d93711f 100644 --- a/e2e/pages/adf/configEditorPage.ts +++ b/e2e/pages/adf/configEditorPage.ts @@ -76,4 +76,18 @@ export class ConfigEditorPage { Util.waitUntilElementIsClickable(button); return button.click(); } + + clickEditTaskConfiguration() { + let button = element(by.id('adf-edit-task-filter-conf')); + Util.waitUntilElementIsVisible(button); + Util.waitUntilElementIsClickable(button); + return button.click(); + } + + clickTaskListCloudConfiguration() { + let button = element(by.id('adf-task-list-cloud-conf')); + Util.waitUntilElementIsVisible(button); + Util.waitUntilElementIsClickable(button); + return button.click(); + } } diff --git a/e2e/pages/adf/contentServicesPage.ts b/e2e/pages/adf/contentServicesPage.ts index 60f8b86664..367745aa4d 100644 --- a/e2e/pages/adf/contentServicesPage.ts +++ b/e2e/pages/adf/contentServicesPage.ts @@ -66,7 +66,7 @@ export class ContentServicesPage { copyButton = element(by.css('button[data-automation-id="content-node-selector-actions-choose"]')); searchInputElement = element(by.css('input[data-automation-id="content-node-selector-search-input"]')); shareNodeButton = element(by.cssContainingText('mat-icon', ' share ')); - siteListDropdown = element(by.css(`mat-select[data-automation-id='site-my-files-select']`)); + siteListDropdown = element(by.css(`mat-select[data-automation-id='site-my-files-option']`)); getUploadAreaDocumentList() { return new ContentListPage(element(by.css('adf-upload-drag-area'))); diff --git a/e2e/pages/adf/dataTablePage.ts b/e2e/pages/adf/dataTablePage.ts index 9057415c00..3fbe9ae800 100644 --- a/e2e/pages/adf/dataTablePage.ts +++ b/e2e/pages/adf/dataTablePage.ts @@ -29,7 +29,7 @@ export class DataTablePage { selectionDropDown = element(by.css(`div[class*='ng-trigger-transformPanel']`)); allSelectedRows = element.all(by.css(`div[class*='is-selected']`)); selectedRowNumber = element(by.css(`div[class*='is-selected'] div[data-automation-id*='text_']`)); - selectAll = element(by.css(`div[class*='header'] label`)); + selectAll = element(by.css(`div[class*='header'] mat-checkbox`)); addRowElement = element(by.xpath(`//span[contains(text(),'Add row')]/..`)); replaceRowsElement = element(by.xpath(`//span[contains(text(),'Replace rows')]/..`)); reset = element(by.xpath(`//span[contains(text(),'Reset to default')]/..`)); @@ -89,6 +89,24 @@ export class DataTablePage { checkbox.click(); } + clickCheckboxByName(rowName) { + let checkbox = this.getRowsName(rowName).element(by.xpath(`ancestor::div[contains(@class, 'adf-datatable-row')]//mat-checkbox/label`)); + Util.waitUntilElementIsVisible(checkbox); + checkbox.click(); + } + + getRowCheckboxByName(rowName) { + return this.getRowsName(rowName).element(by.xpath(`ancestor::div/div/mat-checkbox[contains(@class, 'mat-checkbox-checked')]`)); + } + + checkRowIsNotCheckedByName(rowName) { + Util.waitUntilElementIsNotOnPage(this.getRowCheckboxByName(rowName)); + } + + checkRowIsCheckedByName(rowName) { + Util.waitUntilElementIsVisible(this.getRowCheckboxByName(rowName)); + } + selectRow(rowNumber) { return this.getRowByRowNumber(rowNumber).click(); } @@ -105,6 +123,30 @@ export class DataTablePage { selectMode.click(); } + selectRowByRowName(rowName) { + let row = element(by.cssContainingText(`[data-automation-id*="${rowName}"]`, rowName)); + Util.waitUntilElementIsVisible(row); + Util.waitUntilElementIsClickable(row); + return row.click(); + } + + checkRowIsSelectedByName(rowName) { + let row = element(by.cssContainingText(`[data-automation-id*="${rowName}"]`, rowName)); + let isRowSelected = row.element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`)); + Util.waitUntilElementIsVisible(isRowSelected); + } + + checkRowIsNotSelectedByName(rowName) { + let row = element(by.cssContainingText(`[data-automation-id*="${rowName}"]`, rowName)); + let isRowSelected = row.element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`)); + Util.waitUntilElementIsNotOnPage(isRowSelected); + } + + selectRowByNameWithKeyboard(rowName) { + let row = element(by.cssContainingText(`[data-automation-id*="${rowName}"]`, rowName)); + browser.actions().sendKeys(protractor.Key.COMMAND).click(row).perform(); + } + checkRowIsSelected(rowNumber) { let isRowSelected = this.getRowByRowNumber(rowNumber).element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`)); Util.waitUntilElementIsVisible(isRowSelected); @@ -120,8 +162,15 @@ export class DataTablePage { Util.waitUntilElementIsNotOnPage(this.selectedRowNumber); } + checkAllRowsButtonIsDisplayed() { + Util.waitUntilElementIsVisible(this.selectAll); + Util.waitUntilElementIsVisible(this.selectAll); + return this; + } + checkAllRows() { Util.waitUntilElementIsVisible(this.selectAll); + Util.waitUntilElementIsClickable(this.selectAll); this.selectAll.click(); } @@ -265,6 +314,12 @@ export class DataTablePage { return element.all(by.css(`div[title='Name'][filename="${filename}"]`)).count(); } + getNumberOfRowsDisplayedByName(filename) { + let rowLocator = by.cssContainingText(`[data-automation-id*="${filename}"]`, filename); + Util.waitUntilElementIsVisible(element(rowLocator)); + return element.all(by.css(`div[title='Name'] div[data-automation-id*="${filename}"]`)).count(); + } + checkColumnIsDisplayed(column) { Util.waitUntilElementIsVisible(element(by.css(`div[data-automation-id="auto_id_entry.${column}"]`))); return this; diff --git a/e2e/pages/adf/demo-shell/process-services/tasksCloudDemoPage.ts b/e2e/pages/adf/demo-shell/process-services/tasksCloudDemoPage.ts index 7c41ec05ba..31c213cfdf 100644 --- a/e2e/pages/adf/demo-shell/process-services/tasksCloudDemoPage.ts +++ b/e2e/pages/adf/demo-shell/process-services/tasksCloudDemoPage.ts @@ -20,6 +20,8 @@ import { Util } from '../../../../util/util'; import { TaskFiltersCloudComponent } from '../../process-cloud/taskFiltersCloudComponent'; import { TaskListCloudComponent } from '../../process-cloud/taskListCloudComponent'; import { EditTaskFilterCloudComponent } from '../../process-cloud/editTaskFilterCloudComponent'; +import { FormControllersPage } from '../../material/formControllersPage'; + import { element, by } from 'protractor'; export class TasksCloudDemoPage { @@ -30,10 +32,29 @@ export class TasksCloudDemoPage { taskFilters = element(by.css("mat-expansion-panel[data-automation-id='Task Filters']")); defaultActiveFilter = element.all(by.css('.adf-filters__entry')).first(); - editTaskFilterCloud = new EditTaskFilterCloudComponent(); createButton = element(by.css('button[data-automation-id="create-button"')); newTaskButton = element(by.css('button[data-automation-id="btn-start-task"]')); + settingsButton = element.all(by.cssContainingText('div[class*="mat-tab-label"] .mat-tab-labels div', 'Settings')).first(); + appButton = element.all(by.cssContainingText('div[class*="mat-tab-label"] .mat-tab-labels div', 'App')).first(); + modeDropDownArrow = element(by.css('mat-form-field[data-automation-id="selectionMode"] div[class*="arrow-wrapper"]')); + modeSelector = element(by.css("div[class*='mat-select-panel']")); + displayTaskDetailsToggle = element(by.css('mat-slide-toggle[data-automation-id="taskDetailsRedirection"]')); + multiSelectionToggle = element(by.css('mat-slide-toggle[data-automation-id="multiSelection"]')); + + formControllersPage = new FormControllersPage(); + + editTaskFilterCloud = new EditTaskFilterCloudComponent(); + + disableDisplayTaskDetails() { + this.formControllersPage.disableToggle(this.displayTaskDetailsToggle); + return this; + } + + enableMultiSelection() { + this.formControllersPage.enableToggle(this.multiSelectionToggle); + return this; + } taskFiltersCloudComponent(filter) { return new TaskFiltersCloudComponent(filter); @@ -68,6 +89,10 @@ export class TasksCloudDemoPage { return new TaskListCloudComponent().getAllRowsByColumn('Id'); } + getAllRowsByProcessDefIdColumn() { + return new TaskListCloudComponent().getAllRowsByColumn('Process Definition Id'); + } + clickOnTaskFilters() { Util.waitUntilElementIsVisible(this.taskFilters); return this.taskFilters.click(); @@ -101,4 +126,35 @@ export class TasksCloudDemoPage { return this.defaultActiveFilter.getAttribute('class').then((value) => value.includes('adf-active')); } + clickSettingsButton() { + this.settingsButton.click(); + browser.driver.sleep(400); + Util.waitUntilElementIsVisible(this.multiSelectionToggle); + Util.waitUntilElementIsVisible(this.modeDropDownArrow); + Util.waitUntilElementIsClickable(this.modeDropDownArrow); + return this; + } + + clickAppButton() { + this.appButton.click(); + this.createButtonIsDisplayed(); + return this; + } + + selectSelectionMode(mode) { + this.clickOnSelectionModeDropDownArrow(); + + let modeElement = element.all(by.cssContainingText('mat-option span', mode)).first(); + Util.waitUntilElementIsClickable(modeElement); + Util.waitUntilElementIsVisible(modeElement); + modeElement.click(); + return this; + } + + clickOnSelectionModeDropDownArrow() { + Util.waitUntilElementIsVisible(this.modeDropDownArrow); + Util.waitUntilElementIsClickable(this.modeDropDownArrow); + this.modeDropDownArrow.click(); + Util.waitUntilElementIsVisible(this.modeSelector); + } } diff --git a/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts b/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts index 11de3de411..75326cec82 100644 --- a/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts +++ b/e2e/pages/adf/process-cloud/editTaskFilterCloudComponent.ts @@ -24,6 +24,14 @@ export class EditTaskFilterCloudComponent { customiseFilter = element(by.id('adf-edit-task-filter-title-id')); selectedOption = element(by.css('mat-option[class*="mat-selected"]')); assignment = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-assignment"]')); + priority = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-priority"]')); + taskName = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-taskName"]')); + processDefinitionId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-processDefinitionId"]')); + processInstanceId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-processInstanceId"]')); + lastModifiedFrom = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-lastModifiedFrom"]')); + lastModifiedTo = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-lastModifiedTo"]')); + parentTaskId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-parentTaskId"]')); + owner = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-owner"]')); saveButton = element(by.css('button[id="adf-save-id"]')); saveAsButton = element(by.css('button[id="adf-save-as-id"]')); deleteButton = element(by.css('button[id="adf-delete-id"]')); @@ -92,17 +100,55 @@ export class EditTaskFilterCloudComponent { } setAssignment(option) { - Util.waitUntilElementIsVisible(this.assignment); - this.assignment.clear(); - this.assignment.sendKeys(option); - this.assignment.sendKeys(protractor.Key.ENTER); - return this; + return this.setProperty('assignment', option); } getAssignment() { return this.assignment.getText(); } + setPriority(option) { + return this.setProperty('priority', option); + } + + getPriority() { + return this.priority.getText(); + } + + setParentTaskId(option) { + return this.setProperty('parentTaskId', option); + } + + getParentTaskId() { + return this.parentTaskId.getText(); + } + + setOwner(option) { + return this.setProperty('owner', option); + } + + getOwner() { + return this.owner.getText(); + } + + setLastModifiedFrom(option) { + this.clearField(this.lastModifiedFrom); + return this.setProperty('lastModifiedFrom', option); + } + + getLastModifiedFrom() { + return this.lastModifiedFrom.getText(); + } + + setLastModifiedTo(option) { + this.clearField(this.lastModifiedTo); + return this.setProperty('lastModifiedTo', option); + } + + getLastModifiedTo() { + return this.lastModifiedTo.getText(); + } + checkSaveButtonIsDisplayed() { Util.waitUntilElementIsVisible(this.saveButton); return this; @@ -166,4 +212,53 @@ export class EditTaskFilterCloudComponent { }); } + setAppNameDropDown(option) { + this.clickOnDropDownArrow('appName'); + + let appNameElement = element.all(by.cssContainingText('mat-option span', option)).first(); + Util.waitUntilElementIsClickable(appNameElement); + Util.waitUntilElementIsVisible(appNameElement); + appNameElement.click(); + return this; + } + + getAppNameDropDownValue() { + let locator = element(by.css("mat-select[data-automation-id='adf-cloud-edit-task-property-appName'] span")); + Util.waitUntilElementIsVisible(locator); + return locator.getText(); + } + + setTaskName(option) { + return this.setProperty('taskName', option); + } + + getTaskName() { + return this.taskName.getAttribute('value'); + } + + setProcessDefinitionId(option) { + return this.setProperty('processDefinitionId', option); + } + + getProcessDefinitionId() { + return this.processDefinitionId.getAttribute('value'); + } + + setProcessInstanceId(option) { + return this.setProperty('processInstanceId', option); + } + + setProperty(property, option) { + let locator = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-' + property + '"]')); + Util.waitUntilElementIsVisible(locator); + locator.clear(); + locator.sendKeys(option); + locator.sendKeys(protractor.Key.ENTER); + return this; + } + + getProcessInstanceId() { + return this.processInstanceId.getAttribute('value'); + } + } diff --git a/e2e/process-services-cloud/task-list-properties.e2e.ts b/e2e/process-services-cloud/task-list-properties.e2e.ts new file mode 100644 index 0000000000..fdef8b916e --- /dev/null +++ b/e2e/process-services-cloud/task-list-properties.e2e.ts @@ -0,0 +1,315 @@ +/*! + * @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 TestConfig = require('../test.config'); + +import { LoginSSOPage } from '../pages/adf/loginSSOPage'; +import { SettingsPage } from '../pages/adf/settingsPage'; +import { NavigationBarPage } from '../pages/adf/navigationBarPage'; +import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage'; +import { AppListCloudComponent } from '../pages/adf/process-cloud/appListCloudComponent'; +import { ConfigEditorPage } from '../pages/adf/configEditorPage'; +import { TaskListCloudConfiguration } from './taskListCloud.config'; + +import { Util } from '../util/util'; +import moment = require('moment'); +import { DateUtil } from '../util/dateUtil'; + +import { Tasks } from '../actions/APS-cloud/tasks'; +import { ProcessDefinitions } from '../actions/APS-cloud/process-definitions'; +import { ProcessInstances } from '../actions/APS-cloud/process-instances'; +import { NotificationPage } from '../pages/adf/notificationPage'; + +describe('Edit task filters and task list properties', () => { + + describe('Edit task filters and task list properties', () => { + const configEditorPage = new ConfigEditorPage(); + const settingsPage = new SettingsPage(); + const loginSSOPage = new LoginSSOPage(); + const navigationBarPage = new NavigationBarPage(); + let appListCloudComponent = new AppListCloudComponent(); + let tasksCloudDemoPage = new TasksCloudDemoPage(); + + const tasksService: Tasks = new Tasks(); + const processDefinitionService: ProcessDefinitions = new ProcessDefinitions(); + const processInstancesService: ProcessInstances = new ProcessInstances(); + let notificationPage = new NotificationPage(); + + let silentLogin; + const simpleApp = 'simple-app'; + const candidateUserApp = 'candidateuserapp'; + let noTasksFoundMessage = 'No Tasks Found'; + const user = TestConfig.adf.adminEmail, password = TestConfig.adf.adminPassword; + let createdTask, notAssigned, notDisplayedTask, processDefinition, processInstance, priorityTask, subTask; + let priority = 30; + + let beforeDate = moment().add(-1, 'days').format('DD/MM/YYYY'); + let currentDate = DateUtil.formatDate('DD/MM/YYYY'); + let afterDate = moment().add(1, 'days').format('DD/MM/YYYY'); + + beforeAll(async (done) => { + silentLogin = false; + let jsonFile = new TaskListCloudConfiguration().getConfiguration(); + settingsPage.setProviderBpmSso(TestConfig.adf.hostBPM, TestConfig.adf.hostSso, TestConfig.adf.hostIdentity, silentLogin); + loginSSOPage.clickOnSSOButton(); + loginSSOPage.loginAPS(user, password); + + navigationBarPage.clickConfigEditorButton(); + + configEditorPage.clickTaskListCloudConfiguration(); + configEditorPage.clickClearButton(); + configEditorPage.enterBigConfigurationText(JSON.stringify(jsonFile)).clickSaveButton(); + notificationPage.checkNotificationSnackBarIsDisplayedWithMessage('Save'); + notificationPage.checkNotificationSnackBarIsNotDisplayed(); + + configEditorPage.clickEditTaskConfiguration(); + configEditorPage.clickClearButton(); + browser.driver.sleep(5000); + configEditorPage.enterConfiguration('{' + + '"properties": [' + + '"appName",' + '"state",' + '"assignment",' + + '"taskName",' + '"parentTaskId",' + '"priority",' + + '"standAlone",' + '"owner",' + '"processDefinitionId",' + '"processInstanceId",' + + '"lastModifiedFrom",' + '"lastModifiedTo",' + '"sort",' + '"order"' + + ']' + + '}'); + configEditorPage.clickSaveButton(); + + await tasksService.init(user, password); + createdTask = await tasksService.createStandaloneTask(Util.generateRandomString(), simpleApp); + await tasksService.claimTask(createdTask.entry.id, simpleApp); + notAssigned = await tasksService.createStandaloneTask(Util.generateRandomString(), simpleApp); + priorityTask = await tasksService.createStandaloneTask(Util.generateRandomString(), simpleApp, {priority: priority}); + await tasksService.claimTask(priorityTask.entry.id, simpleApp); + notDisplayedTask = await tasksService.createStandaloneTask(Util.generateRandomString(), candidateUserApp); + await tasksService.claimTask(notDisplayedTask.entry.id, candidateUserApp); + + await processDefinitionService.init(user, password); + processDefinition = await processDefinitionService.getProcessDefinitions(simpleApp); + await processInstancesService.init(user, password); + processInstance = await processInstancesService.createProcessInstance(processDefinition.list.entries[0].entry.key, simpleApp); + + subTask = await tasksService.createStandaloneSubtask(createdTask.entry.id, simpleApp, Util.generateRandomString()); + await tasksService.claimTask(subTask.entry.id, simpleApp); + + done(); + }); + + beforeEach((done) => { + navigationBarPage.navigateToProcessServicesCloudPage(); + appListCloudComponent.checkApsContainer(); + appListCloudComponent.goToApp(simpleApp); + tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader(); + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + done(); + }); + + it('[C292004] Filter by appName', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getAppNameDropDownValue()).toEqual(simpleApp); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(notDisplayedTask.entry.name); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setAppNameDropDown(candidateUserApp); + expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getAppNameDropDownValue()).toEqual(candidateUserApp); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(notDisplayedTask.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297476] Filter by taskName', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setTaskName(createdTask.entry.name); + expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getTaskName()).toEqual(createdTask.entry.name); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + expect(tasksCloudDemoPage.taskListCloudComponent().getDataTable().getNumberOfRowsDisplayedByName(createdTask.entry.name)).toEqual(1); + }); + + it('[C297613] Should be able to see No tasks found when typing a task name that does not exist', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setTaskName('invalidName'); + expect(tasksCloudDemoPage.editTaskFilterCloudComponent().getTaskName()).toEqual('invalidName'); + + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297480] Should be able to see only tasks that are part of a specific process when processInstanceId is set', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setProcessInstanceId(processInstance.entry.id) + .setStateFilterDropDown('ALL').clearAssignment(); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); + + expect(tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllDisplayedRows()).toBe(1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(processInstance.entry.id); + }); + + it('[C297684] Should be able to see No tasks found when typing an invalid processInstanceId', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setProcessInstanceId('invalidTaskId'); + + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297478] Should be able to see only tasks that are assigned to a specific user when assignee is set', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setAssignment('admin.adf'); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(notAssigned.entry.name); + }); + + it('[C297686] Should be able to see No tasks found when typing an invalid user to assignee field', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setAssignment('invalid'); + + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297482] Should be able to see only tasks with specific priority when priority is set', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setPriority(priority); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(priorityTask.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297687] Should be able to see No tasks found when typing unused value for priority field', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setPriority('70'); + + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297481] Should be able to see only tasks with specific parentTaskId when parentTaskId is set', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setParentTaskId(subTask.entry.parentTaskId); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(subTask.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297486] Filter by Owner', () => { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setStateFilterDropDown('ALL').clearAssignment().setOwner('admin.adf'); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(notAssigned.entry.name); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setOwner('invalid'); + + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297484] Task is displayed when typing into lastModifiedFrom field a date before the task created date', function () { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(beforeDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(afterDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297689] Task is not displayed when typing into lastModifiedFrom field the same date as tasks created date', function () { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(currentDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297485] Task is displayed when typing into lastModifiedTo field a date after the task created date', function () { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(afterDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(beforeDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297690] Task is not displayed when typing into lastModifiedTo field the same date as tasks created date', function () { + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(currentDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTask.entry.name); + }); + + it('[C297691] Task is not displayed when typing into lastModifiedFrom field a date before the task due date ' + + 'and into lastModifiedTo a date before task due date', function () { + + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(beforeDate); + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(beforeDate); + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + it('[C297692] Task is displayed when typing into lastModifiedFrom field a date before the tasks due date ' + + 'and into lastModifiedTo a date after', function () { + + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(beforeDate); + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(afterDate); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTask.entry.name); + }); + + it('[C297693] Task is not displayed when typing into lastModifiedFrom field a date after the tasks due date ' + + 'and into lastModifiedTo a date after', function () { + + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + expect(tasksCloudDemoPage.getActiveFilterName()).toBe('My Tasks'); + + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedFrom(afterDate); + tasksCloudDemoPage.editTaskFilterCloudComponent().setLastModifiedTo(afterDate); + expect(tasksCloudDemoPage.taskListCloudComponent().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); + }); + + }); + +}); diff --git a/e2e/process-services-cloud/task-list-selection.e2e.ts b/e2e/process-services-cloud/task-list-selection.e2e.ts new file mode 100644 index 0000000000..7a0c62b317 --- /dev/null +++ b/e2e/process-services-cloud/task-list-selection.e2e.ts @@ -0,0 +1,128 @@ +/*! + * @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 TestConfig = require('../test.config'); + +import { LoginSSOPage } from '../pages/adf/loginSSOPage'; +import { SettingsPage } from '../pages/adf/settingsPage'; +import { NavigationBarPage } from '../pages/adf/navigationBarPage'; +import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage'; +import { AppListCloudComponent } from '../pages/adf/process-cloud/appListCloudComponent'; +import { TaskListCloudConfiguration } from './taskListCloud.config'; + +import { Util } from '../util/util'; +import moment = require('moment'); + +import { Tasks } from '../actions/APS-cloud/tasks'; +describe('Task list cloud - selection', () => { + + describe('Task list cloud - selection', () => { + const settingsPage = new SettingsPage(); + const loginSSOPage = new LoginSSOPage(); + const navigationBarPage = new NavigationBarPage(); + let appListCloudComponent = new AppListCloudComponent(); + let tasksCloudDemoPage = new TasksCloudDemoPage(); + + const tasksService: Tasks = new Tasks(); + + let silentLogin; + const simpleApp = 'simple-app'; + const user = TestConfig.adf.adminEmail, password = TestConfig.adf.adminPassword; + let noOfTasks = 3, response; + let tasks = []; + + beforeAll(async (done) => { + silentLogin = false; + settingsPage.setProviderBpmSso(TestConfig.adf.hostBPM, TestConfig.adf.hostSso, TestConfig.adf.hostIdentity, silentLogin); + loginSSOPage.clickOnSSOButton(); + loginSSOPage.loginAPS(user, password); + + await tasksService.init(user, password); + for (let i = 0; i < noOfTasks; i++) { + response = await tasksService.createStandaloneTask(Util.generateRandomString(), simpleApp); + await tasksService.claimTask(response.entry.id, simpleApp); + tasks.push(response.entry.name); + } + + navigationBarPage.navigateToProcessServicesCloudPage(); + appListCloudComponent.checkApsContainer(); + appListCloudComponent.goToApp(simpleApp); + tasksCloudDemoPage.myTasksFilter().checkTaskFilterIsDisplayed(); + tasksCloudDemoPage.clickSettingsButton().disableDisplayTaskDetails(); + tasksCloudDemoPage.clickAppButton(); + done(); + }); + + it('[C291914] Should not be able to select any row when selection mode is set to None', () => { + tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('None'); + tasksCloudDemoPage.clickAppButton(); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByRowName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkNoRowIsSelected(); + }); + + it('[C291918] Should be able to select only one row when selection mode is set to Single', () => { + tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('Single'); + tasksCloudDemoPage.clickAppButton(); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByRowName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsSelectedByName(tasks[0]); + expect(tasksCloudDemoPage.taskListCloudComponent().getDataTable().getNumberOfSelectedRows()).toEqual(1); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByRowName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsSelectedByName(tasks[1]); + expect(tasksCloudDemoPage.taskListCloudComponent().getDataTable().getNumberOfSelectedRows()).toEqual(1); + }); + + it('[C291919] Should be able to select only one row when selection mode is set to Multiple', () => { + tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('Multiple'); + tasksCloudDemoPage.clickAppButton(); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByRowName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsSelectedByName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().selectRowByNameWithKeyboard(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsSelectedByName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsSelectedByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsNotSelectedByName(tasks[2]); + }); + + it('[C291916] Should be able to select multiple row when multiselect is true', () => { + tasksCloudDemoPage.clickSettingsButton().enableMultiSelection(); + tasksCloudDemoPage.clickAppButton(); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().clickCheckboxByName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsCheckedByName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().clickCheckboxByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsCheckedByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsNotCheckedByName(tasks[2]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().clickCheckboxByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsNotCheckedByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsCheckedByName(tasks[0]); + }); + + it('[C291915] Should be possible select all the rows when multiselect is true', () => { + tasksCloudDemoPage.clickSettingsButton().enableMultiSelection(); + tasksCloudDemoPage.clickAppButton(); + + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkAllRowsButtonIsDisplayed().checkAllRows(); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsCheckedByName(tasks[0]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().clickCheckboxByName(tasks[1]); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkRowIsCheckedByName(tasks[2]); + }); + + }); + +}); diff --git a/e2e/process-services-cloud/taskListCloud.config.ts b/e2e/process-services-cloud/taskListCloud.config.ts new file mode 100644 index 0000000000..3df314de53 --- /dev/null +++ b/e2e/process-services-cloud/taskListCloud.config.ts @@ -0,0 +1,84 @@ +/*! + * @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. + */ + +export class TaskListCloudConfiguration { + + constructor() { + } + + getConfiguration() { + return { + 'presets': { + 'default': [ + { + 'key': 'entry.id', + 'type': 'text', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.ID', + 'sortable': true + }, + { + 'key': 'entry.name', + 'type': 'text', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.NAME', + 'sortable': true, + 'cssClass': 'full-width name-column ellipsis-cell' + }, + { + 'key': 'entry.processDefinitionId', + 'type': 'text', + 'title': 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_DEF_ID', + 'sortable': true, + 'cssClass': 'full-width name-column ellipsis-cell' + }, + { + 'key': 'entry.processInstanceId', + 'type': 'text', + 'title': 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_INSTANCE_ID', + 'sortable': true, + 'cssClass': 'full-width name-column ellipsis-cell' + }, + { + 'key': 'entry.status', + 'type': 'text', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.STATUS', + 'sortable': true + }, + { + 'key': 'entry.priority', + 'type': 'text', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY', + 'sortable': true + }, + { + 'key': 'entry.createdDate', + 'type': 'date', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED_DATE', + 'sortable': true, + 'format': 'timeAgo' + }, + { + 'key': 'entry.lastModified', + 'type': 'date', + 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.LAST_MODIFIED', + 'sortable': true, + 'format': 'timeAgo' + } + ] + } + }; + } +} diff --git a/e2e/process-services-cloud/tasks-custom-filters.e2e.ts b/e2e/process-services-cloud/tasks-custom-filters.e2e.ts index 4cc97626b0..acd543d49e 100644 --- a/e2e/process-services-cloud/tasks-custom-filters.e2e.ts +++ b/e2e/process-services-cloud/tasks-custom-filters.e2e.ts @@ -16,16 +16,13 @@ */ import TestConfig = require('../test.config'); -import resources = require('../util/resources'); import { LoginSSOPage } from '../pages/adf/loginSSOPage'; import { SettingsPage } from '../pages/adf/settingsPage'; import { NavigationBarPage } from '../pages/adf/navigationBarPage'; -import { TasksListPage } from '../pages/adf/process_services/tasksListPage'; import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage'; import { AppListCloudComponent } from '../pages/adf/process-cloud/appListCloudComponent'; -import AlfrescoApi = require('alfresco-js-api-node'); import { Tasks } from '../actions/APS-cloud/tasks'; import { ProcessDefinitions } from '../actions/APS-cloud/process-definitions'; import { ProcessInstances } from '../actions/APS-cloud/process-instances'; @@ -69,8 +66,8 @@ describe('Task filters cloud', () => { completedTask = await tasksService.createAndCompleteTask(completedTaskName, simpleApp); deletedTask = await tasksService.createStandaloneTask(deletedTaskName, simpleApp); await tasksService.deleteTask(deletedTask.entry.id, simpleApp); - for ( let i = 0; i < nrOfTasks; i++ ) { - await tasksService.createStandaloneTask(orderByNameAndPriority[i], simpleApp, {priority: priority}); + for (let i = 0; i < nrOfTasks; i++) { + await tasksService.createStandaloneTask(orderByNameAndPriority[i], simpleApp, { priority: priority }); priority = priority + 20; } @@ -115,19 +112,36 @@ describe('Task filters cloud', () => { it('[C290139] Should display only tasks with all states when All is selected from state dropdown', () => { tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().clearAssignment() .setStateFilterDropDown('ALL'); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(deletedTaskName); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(assignedTaskName); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTaskName); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(completedTaskName); }); + it('[C290154] Should display only tasks with suspended states when Suspended is selected from state dropdown', () => { + tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().clearAssignment() + .setStateFilterDropDown('SUSPENDED'); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(suspendedTasks.list.entries[0].entry.id); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(deletedTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(createdTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(completedTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(assignedTaskName); + }); + + it('[C290060] Should display only tasks with Created state when Created is selected from state dropdown', () => { + tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().clearAssignment().setStateFilterDropDown('CREATED'); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsDisplayed(createdTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(assignedTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(completedTaskName); + tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkContentIsNotDisplayed(deletedTaskName); + }); + it('[C290069] Should display tasks ordered by name when Name is selected from sort dropdown', () => { tasksCloudDemoPage.editTaskFilterCloudComponent().clickCustomiseFilterHeader().setStateFilterDropDown('ASSIGNED') .setSortFilterDropDown('NAME').setOrderFilterDropDown('ASC'); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllRowsNameColumn().then( (list) => { + tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllRowsNameColumn().then((list) => { let initialList = list.slice(0); list.sort(function (firstStr, secondStr) { return firstStr.localeCompare(secondStr); @@ -138,7 +152,7 @@ describe('Task filters cloud', () => { tasksCloudDemoPage.editTaskFilterCloudComponent().setOrderFilterDropDown('DESC'); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); - tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllRowsNameColumn().then( (list) => { + tasksCloudDemoPage.taskListCloudComponent().getDataTable().getAllRowsNameColumn().then((list) => { let initialList = list.slice(0); list.sort(function (firstStr, secondStr) { return firstStr.localeCompare(secondStr); @@ -154,7 +168,7 @@ describe('Task filters cloud', () => { tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); - tasksCloudDemoPage.getAllRowsByIdColumn().then( (list) => { + tasksCloudDemoPage.getAllRowsByIdColumn().then((list) => { let initialList = list.slice(0); list.sort(function (firstStr, secondStr) { return firstStr.localeCompare(secondStr); @@ -165,7 +179,7 @@ describe('Task filters cloud', () => { tasksCloudDemoPage.editTaskFilterCloudComponent().setOrderFilterDropDown('DESC'); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsDisplayed(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().checkSpinnerIsNotDisplayed(); - tasksCloudDemoPage.getAllRowsByIdColumn().then( (list) => { + tasksCloudDemoPage.getAllRowsByIdColumn().then((list) => { let initialList = list.slice(0); list.sort(function (firstStr, secondStr) { return firstStr.localeCompare(secondStr); @@ -174,7 +188,5 @@ describe('Task filters cloud', () => { expect(JSON.stringify(initialList) === JSON.stringify(list)).toEqual(true); }); }); - }); - }); From 249d68882037089d0f2b183decec14da0e6fef05 Mon Sep 17 00:00:00 2001 From: Maurizio Vitale Date: Fri, 1 Mar 2019 16:18:53 +0000 Subject: [PATCH 053/223] adf-testing - Cloud - Move user info e2e to adf-testing (#4369) * Move user info e2e to adf-testing * Create testing wrapper for the api sevice --- .../actions/public-api.ts | 6 ++ .../actions/testing-alfresco-api.service.ts | 52 ++++++++++++++++ .../actions/testing-app-config.service.ts | 42 +++++++++++++ .../pages/app-list-cloud.page.ts | 45 ++++++++++++++ .../pages/login-sso.page.ts | 59 +++++++++++++++++++ .../pages/navigation-bar.page.ts | 36 +++++++++++ .../pages/public-api.ts | 8 +++ .../pages/user-info-dialog.page.ts | 47 +++++++++++++++ .../lib/process-services-cloud/public-api.ts | 6 ++ lib/testing/src/public-api.ts | 1 + 10 files changed, 302 insertions(+) create mode 100644 lib/testing/src/lib/process-services-cloud/actions/public-api.ts create mode 100644 lib/testing/src/lib/process-services-cloud/actions/testing-alfresco-api.service.ts create mode 100644 lib/testing/src/lib/process-services-cloud/actions/testing-app-config.service.ts create mode 100644 lib/testing/src/lib/process-services-cloud/pages/app-list-cloud.page.ts create mode 100644 lib/testing/src/lib/process-services-cloud/pages/login-sso.page.ts create mode 100644 lib/testing/src/lib/process-services-cloud/pages/navigation-bar.page.ts create mode 100644 lib/testing/src/lib/process-services-cloud/pages/public-api.ts create mode 100644 lib/testing/src/lib/process-services-cloud/pages/user-info-dialog.page.ts create mode 100644 lib/testing/src/lib/process-services-cloud/public-api.ts diff --git a/lib/testing/src/lib/process-services-cloud/actions/public-api.ts b/lib/testing/src/lib/process-services-cloud/actions/public-api.ts new file mode 100644 index 0000000000..b55db50921 --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/actions/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of testing + */ + +export * from './testing-alfresco-api.service'; +export * from './testing-app-config.service'; diff --git a/lib/testing/src/lib/process-services-cloud/actions/testing-alfresco-api.service.ts b/lib/testing/src/lib/process-services-cloud/actions/testing-alfresco-api.service.ts new file mode 100644 index 0000000000..62bf797a4d --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/actions/testing-alfresco-api.service.ts @@ -0,0 +1,52 @@ +/*! + * @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 { AlfrescoApiCompatibility, AlfrescoApiConfig } from '@alfresco/js-api'; +import { AlfrescoApiService, AppConfigValues, AppConfigService } from '@alfresco/adf-core'; + +export class TestingAlfrescoApiService extends AlfrescoApiService { + + protected alfrescoApi: AlfrescoApiCompatibility; + + config = { + }; + + constructor(public appConfig: AppConfigService) { + super(null, null); + let oauth = Object.assign({}, this.appConfig.get(AppConfigValues.OAUTHCONFIG, null)); + this.config = new AlfrescoApiConfig({ + provider: this.appConfig.get(AppConfigValues.PROVIDERS), + hostEcm: this.appConfig.get(AppConfigValues.ECMHOST), + hostBpm: this.appConfig.get(AppConfigValues.BPMHOST), + authType: this.appConfig.get(AppConfigValues.AUTHTYPE, 'BASIC'), + contextRootBpm: this.appConfig.get(AppConfigValues.CONTEXTROOTBPM), + contextRoot: this.appConfig.get(AppConfigValues.CONTEXTROOTECM), + disableCsrf: this.appConfig.get(AppConfigValues.DISABLECSRF), + withCredentials: this.appConfig.get(AppConfigValues.AUTH_WITH_CREDENTIALS, false), + oauth2: oauth + }); + } + + getInstance(): AlfrescoApiCompatibility { + if (this.alfrescoApi) { + this.alfrescoApi.configureJsApi(this.config); + } else { + this.alfrescoApi = new AlfrescoApiCompatibility(this.config); + } + return this.alfrescoApi; + } +} diff --git a/lib/testing/src/lib/process-services-cloud/actions/testing-app-config.service.ts b/lib/testing/src/lib/process-services-cloud/actions/testing-app-config.service.ts new file mode 100644 index 0000000000..436af00168 --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/actions/testing-app-config.service.ts @@ -0,0 +1,42 @@ +/*! + * @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 { AppConfigService } from '@alfresco/adf-core'; + +export class TestingAppConfigService extends AppConfigService { + + config = { + }; + + constructor(config?) { + super(null); + this.config = config; + } + + getLocationHostname(): string { + return ''; + } + + getLocationPort(prefix: string = ''): string { + return ''; + } + + getLocationProtocol(): string { + return ''; + } + +} diff --git a/lib/testing/src/lib/process-services-cloud/pages/app-list-cloud.page.ts b/lib/testing/src/lib/process-services-cloud/pages/app-list-cloud.page.ts new file mode 100644 index 0000000000..41efdbbd92 --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/pages/app-list-cloud.page.ts @@ -0,0 +1,45 @@ +/*! + * @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 { element, by } from 'protractor'; +import { BrowserVisibility } from '../../core/browser-visibility'; + +export class AppListCloudPage { + + apsAppsContainer = element(by.css('adf-cloud-app-list')); + + checkApsContainer() { + BrowserVisibility.waitUntilElementIsVisible(this.apsAppsContainer); + } + + goToApp(applicationName) { + let app = element(by.css('mat-card[title="' + applicationName + '"]')); + BrowserVisibility.waitUntilElementIsVisible(app); + app.click(); + } + + checkAppIsNotDisplayed(applicationName) { + let app = element(by.css('mat-card[title="' + applicationName + '"]')); + return BrowserVisibility.waitUntilElementIsNotOnPage(app); + } + + checkAppIsDisplayed(applicationName) { + let app = element(by.css('mat-card[title="' + applicationName + '"]')); + return BrowserVisibility.waitUntilElementIsVisible(app); + } + +} diff --git a/lib/testing/src/lib/process-services-cloud/pages/login-sso.page.ts b/lib/testing/src/lib/process-services-cloud/pages/login-sso.page.ts new file mode 100644 index 0000000000..f638b81d8f --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/pages/login-sso.page.ts @@ -0,0 +1,59 @@ +/*! + * @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 { element, by, browser, protractor } from 'protractor'; +import { BrowserVisibility } from '../../core/browser-visibility'; +export class LoginSSOPage { + + ssoButton = element(by.css(`[data-automation-id="login-button-sso"]`)); + usernameField = element(by.id('username')); + passwordField = element(by.id('password')); + loginButton = element(by.css('input[class="submit"]')); + header = element(by.id('adf-header')); + + loginAPS(username, password) { + BrowserVisibility.waitUntilElementIsVisible(this.usernameField); + this.enterUsername(username); + this.enterPassword(password); + this.clickLoginButton(); + browser.actions().sendKeys(protractor.Key.ENTER).perform(); + return BrowserVisibility.waitUntilElementIsVisible(this.header); + } + + clickOnSSOButton() { + BrowserVisibility.waitUntilElementIsVisible(this.ssoButton); + this.ssoButton.click(); + } + + enterUsername(username) { + BrowserVisibility.waitUntilElementIsVisible(this.usernameField); + this.usernameField.clear(); + this.usernameField.sendKeys(username); + } + + enterPassword(password) { + BrowserVisibility.waitUntilElementIsVisible(this.passwordField); + this.passwordField.clear(); + this.passwordField.sendKeys(password); + } + + clickLoginButton() { + BrowserVisibility.waitUntilElementIsVisible(this.loginButton); + return this.loginButton.click(); + } + +} diff --git a/lib/testing/src/lib/process-services-cloud/pages/navigation-bar.page.ts b/lib/testing/src/lib/process-services-cloud/pages/navigation-bar.page.ts new file mode 100644 index 0000000000..145209c20d --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/pages/navigation-bar.page.ts @@ -0,0 +1,36 @@ +/*! + * @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 { by, element } from 'protractor'; +import { BrowserVisibility } from '../../core/browser-visibility'; +import { AppListCloudPage } from './app-list-cloud.page'; + +export class NavigationBarPage { + processServicesCloudButton = element(by.css('a[data-automation-id="Process Cloud"]')); + userProfileButton = element(by.css('button[data-automation-id="adf-user-profile"]')); + + navigateToProcessServicesCloudPage() { + BrowserVisibility.waitUntilElementIsVisible(this.processServicesCloudButton); + this.processServicesCloudButton.click(); + return new AppListCloudPage(); + } + + clickUserProfile() { + BrowserVisibility.waitUntilElementIsVisible(this.userProfileButton); + this.userProfileButton.click(); + } +} diff --git a/lib/testing/src/lib/process-services-cloud/pages/public-api.ts b/lib/testing/src/lib/process-services-cloud/pages/public-api.ts new file mode 100644 index 0000000000..ef0c0640d4 --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/pages/public-api.ts @@ -0,0 +1,8 @@ +/* + * Public API Surface of testing + */ + +export * from './app-list-cloud.page'; +export * from './navigation-bar.page'; +export * from './login-sso.page'; +export * from './user-info-dialog.page'; diff --git a/lib/testing/src/lib/process-services-cloud/pages/user-info-dialog.page.ts b/lib/testing/src/lib/process-services-cloud/pages/user-info-dialog.page.ts new file mode 100644 index 0000000000..7819a7d2e7 --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/pages/user-info-dialog.page.ts @@ -0,0 +1,47 @@ +/*! + * @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 { element, by, browser, protractor } from 'protractor'; +import { BrowserVisibility } from '../../core/browser-visibility'; + +export class UserInfoDialogPage { + + dialog = element.all(by.css('mat-card[class*="adf-userinfo-card"]')).first(); + userInfoSsoHeaderTitle = this.dialog.element(by.css('div[id="identity-username"]')); + userInfoSsoTitle = element(by.css('.adf-userinfo__detail-title')); + ssoEmail = element(by.id('identity-email')); + + getSsoHeaderTitle () { + BrowserVisibility.waitUntilElementIsVisible(this.userInfoSsoHeaderTitle); + return this.userInfoSsoHeaderTitle.getText(); + } + + getSsoTitle() { + BrowserVisibility.waitUntilElementIsVisible(this.userInfoSsoTitle); + return this.userInfoSsoTitle.getText(); + } + + getSsoEmail() { + BrowserVisibility.waitUntilElementIsVisible(this.ssoEmail); + return this.ssoEmail.getText(); + } + + closeUserProfile() { + BrowserVisibility.waitUntilElementIsVisible(this.dialog); + browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + } +} diff --git a/lib/testing/src/lib/process-services-cloud/public-api.ts b/lib/testing/src/lib/process-services-cloud/public-api.ts new file mode 100644 index 0000000000..dcc70fb91a --- /dev/null +++ b/lib/testing/src/lib/process-services-cloud/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of testing + */ + +export * from './pages/public-api'; +export * from './actions/public-api'; diff --git a/lib/testing/src/public-api.ts b/lib/testing/src/public-api.ts index f064dd3d68..b3cc1140a0 100644 --- a/lib/testing/src/public-api.ts +++ b/lib/testing/src/public-api.ts @@ -5,4 +5,5 @@ export * from './lib/core/public-api'; export * from './lib/content-services/public-api'; export * from './lib/process-services/public-api'; +export * from './lib/process-services-cloud/public-api'; export * from './lib/testing.module'; From a7d058fd2e2252bc15788e6d3f69e7c07c3ed608 Mon Sep 17 00:00:00 2001 From: Maurizio Vitale Date: Fri, 1 Mar 2019 16:19:41 +0000 Subject: [PATCH 054/223] Process Service Cloud - Add the identity user service (#4374) * Add the identity service to cloud * Expose the service --- .../src/lib/services/identity-user.service.ts | 60 +++++++++++++++++++ .../src/lib/services/public-api.ts | 18 ++++++ lib/process-services-cloud/src/public-api.ts | 1 + 3 files changed, 79 insertions(+) create mode 100644 lib/process-services-cloud/src/lib/services/identity-user.service.ts create mode 100644 lib/process-services-cloud/src/lib/services/public-api.ts diff --git a/lib/process-services-cloud/src/lib/services/identity-user.service.ts b/lib/process-services-cloud/src/lib/services/identity-user.service.ts new file mode 100644 index 0000000000..2fad48b9e9 --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/identity-user.service.ts @@ -0,0 +1,60 @@ +/*! + * @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 { Injectable } from '@angular/core'; +import { throwError as observableThrowError, Observable, from } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { AppConfigService, LogService, AlfrescoApiService } from '@alfresco/adf-core'; + +@Injectable() +export class IdentityUserService { + + constructor( + private apiService: AlfrescoApiService, + private appConfigService: AppConfigService, + private logService: LogService + ) { } + + /** + * Finds groups filtered by username. + * @param username Object containing the name filter string + * @returns List of users information + */ + findUsersByUsername(username: string): Observable { + const url = this.getUserApi(); + const httpMethod = 'GET', pathParams = {}, queryParams = { search: username }, bodyParam = {}, headerParams = {}, + formParams = {}, contentTypes = ['application/json'], accepts = ['application/json']; + + return (from(this.apiService.getInstance().oauth2Auth.callCustomApi( + url, httpMethod, pathParams, queryParams, + headerParams, formParams, bodyParam, + contentTypes, accepts, Object, null, null) + )).pipe( + catchError((err) => this.handleError(err)) + ); + } + + private getUserApi() { + return `${this.appConfigService.get('identityHost')}/users`; + } + + private handleError(error: any) { + this.logService.error(error); + return observableThrowError(error || 'Server error'); + } +} diff --git a/lib/process-services-cloud/src/lib/services/public-api.ts b/lib/process-services-cloud/src/lib/services/public-api.ts new file mode 100644 index 0000000000..a1ee695bf0 --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/public-api.ts @@ -0,0 +1,18 @@ +/*! + * @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. + */ + +export * from './identity-user.service'; diff --git a/lib/process-services-cloud/src/public-api.ts b/lib/process-services-cloud/src/public-api.ts index 187f0cbcae..37fd3dd798 100644 --- a/lib/process-services-cloud/src/public-api.ts +++ b/lib/process-services-cloud/src/public-api.ts @@ -21,3 +21,4 @@ export * from './lib/app/public-api'; export * from './lib/process/public-api'; export * from './lib/task/public-api'; export * from './lib/group/public-api'; +export * from './lib/services/public-api'; From 7da9bd89cb9c0a4bc225be863be3554feefa14b5 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Fri, 1 Mar 2019 17:23:31 +0100 Subject: [PATCH 055/223] [ADF-4122] Add sticky header feature to datatable and refactor styles (#4370) * [ADF-4122] Add sticky header config to datatable and refactor styles * [ADF-4122] Fix core unit tests * [ADF-4122] Commit requested changes * [ADF-4122] Fix e2e tests * Update docs/core/datatable.component.md Co-Authored-By: davidcanonieto --- demo-shell/src/app/app.component.scss | 6 +- .../datatable/datatable.component.html | 55 +-- .../datatable/datatable.component.ts | 17 +- .../app/components/files/files.component.html | 5 +- .../app/components/files/files.component.scss | 18 +- .../process-service.component.scss | 10 +- .../trashcan/trashcan.component.html | 4 +- docs/core/data-column.component.md | 2 +- docs/core/datatable.component.md | 76 ++++ docs/docassets/images/datatable-expand-5.png | Bin 0 -> 103154 bytes .../images/datatable-sticky-header.png | Bin 0 -> 56846 bytes .../images/datatable-truncated-text.png | Bin 0 -> 62353 bytes .../images/datatable-wrapped-text.png | Bin 0 -> 81099 bytes e2e/pages/adf/contentServicesPage.ts | 20 +- e2e/pages/adf/dialog/contentListPage.ts | 2 +- e2e/pages/adf/process-services/tasksPage.ts | 2 +- ...content-node-selector-panel.component.html | 2 +- ...content-node-selector-panel.component.scss | 47 +-- .../components/document-list.component.scss | 13 +- .../document-list.component.spec.ts | 6 +- .../datatable/datatable.component.html | 43 +- .../datatable/datatable.component.scss | 381 +++++++++--------- .../datatable/datatable.component.spec.ts | 16 +- .../datatable/datatable.component.ts | 4 + .../empty-content.component.scss | 6 +- .../people-list/people-list.component.scss | 4 +- .../components/people/people.component.scss | 2 +- 27 files changed, 418 insertions(+), 323 deletions(-) create mode 100644 docs/docassets/images/datatable-expand-5.png create mode 100644 docs/docassets/images/datatable-sticky-header.png create mode 100644 docs/docassets/images/datatable-truncated-text.png create mode 100644 docs/docassets/images/datatable-wrapped-text.png diff --git a/demo-shell/src/app/app.component.scss b/demo-shell/src/app/app.component.scss index f15d10be2c..88ac91260b 100644 --- a/demo-shell/src/app/app.component.scss +++ b/demo-shell/src/app/app.component.scss @@ -10,12 +10,12 @@ router-outlet[name='overlay'] + * { @media (max-width: 425px) { adf-content-node-selector { .adf-content-node-selector-content-list { - .adf-data-table-cell { + .adf-datatable-cell { display: none; } - .adf-data-table-cell:first-child, - .adf-data-table-cell:nth-child(2) { + .adf-datatable-cell:first-child, + .adf-datatable-cell:nth-child(2) { display: table-cell; } } diff --git a/demo-shell/src/app/components/datatable/datatable.component.html b/demo-shell/src/app/components/datatable/datatable.component.html index 5d95002843..6c0cbd8ce7 100644 --- a/demo-shell/src/app/components/datatable/datatable.component.html +++ b/demo-shell/src/app/components/datatable/datatable.component.html @@ -2,28 +2,37 @@ - - - - + + Sticky header + + +
+ + + + +
Selected items: {{ dataTable.selection?.length }} @@ -47,4 +56,4 @@ -
+
\ No newline at end of file diff --git a/demo-shell/src/app/components/datatable/datatable.component.ts b/demo-shell/src/app/components/datatable/datatable.component.ts index bfe9185149..16e6a48c5f 100644 --- a/demo-shell/src/app/components/datatable/datatable.component.ts +++ b/demo-shell/src/app/components/datatable/datatable.component.ts @@ -54,6 +54,7 @@ export class DataTableComponent { multiselect = false; data: FilteredDataAdapter; + stickyHeader = false; @Input() selectionMode = 'single'; @@ -100,18 +101,18 @@ export class DataTableComponent { }, { id: 4, - name: 'Image 1', + name: 'Image 8', createdOn: new Date(2016, 6, 2, 15, 8, 4), createdBy: this._createdBy, - icon: this._imageUrl + icon: 'material-icons://alarm' } ], [ { type: 'image', key: 'icon', title: '', srTitle: 'Thumbnail' }, - { type: 'text', key: 'id', title: 'Id', sortable: true , cssClass: 'adf-desktop-only'}, - { type: 'text', key: 'createdOn', title: 'Created On', sortable: true }, - { type: 'text', key: 'name', title: 'Name', cssClass: 'adf-full-width adf-name-column', sortable: true }, - { type: 'text', key: 'createdBy.name', title: 'Created By', sortable: true, cssClass: 'adf-desktop-only'} + { type: 'text', key: 'id', title: 'Id', sortable: true , cssClass: 'adf-ellipsis-cell' }, + { type: 'text', key: 'createdOn', title: 'Created On', sortable: true, cssClass: 'adf-ellipsis-cell adf-expand-cell-5' }, + { type: 'text', key: 'name', title: 'Name', cssClass: '', sortable: true }, + { type: 'text', key: 'createdBy.name', title: 'Created By', sortable: true, cssClass: ''} ] ); @@ -199,4 +200,8 @@ export class DataTableComponent { onRowDblClick(event) { this.logService.log(event); } + + toggleStickyHeader() { + this.stickyHeader = !this.stickyHeader; + } } diff --git a/demo-shell/src/app/components/files/files.component.html b/demo-shell/src/app/components/files/files.component.html index 4b47c3e023..09ffaca6f5 100644 --- a/demo-shell/src/app/components/files/files.component.html +++ b/demo-shell/src/app/components/files/files.component.html @@ -202,6 +202,7 @@ + class="adf-ellipsis-cell adf-expand-cell-5"> @@ -254,7 +255,7 @@ key="name" title="{{'DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}" [formatTooltip]="getNodeNameTooltip" - class="adf-full-width adf-ellipsis-cell"> + class="adf-ellipsis-cell adf-expand-cell-5"> -
+
Actions
-
+
-
{{ col.title | translate}}
-
+
Actions
@@ -57,7 +58,7 @@ [ngClass]="getRowStyle(row)" (keyup)="onRowKeyUp(row, $event)"> -
+
-
+
-
+
{{ asIconValue(row, col) }} @@ -156,7 +157,7 @@
-
+
@@ -167,7 +168,7 @@
+ class="adf-datatable-cell adf-datatable__actions-cell">
- {{ bucket.display || bucket.label | translate }} - ( - {{ bucket.count }} - ) + {{ bucket.display || bucket.label | translate }} {{ getBucketCountDisplay(bucket) }}
diff --git a/lib/content-services/search/components/search-filter/search-filter.component.spec.ts b/lib/content-services/search/components/search-filter/search-filter.component.spec.ts index e33fc432df..265b7adf3c 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.spec.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.spec.ts @@ -193,7 +193,7 @@ describe('SearchFilterComponent', () => { component.onDataLoaded(data); - expect(component.responseFacets.length).toBe(0); + expect(component.responseFacets).toBeNull(); }); it('should fetch facet fields from response payload', () => { @@ -397,10 +397,10 @@ describe('SearchFilterComponent', () => { }; component.responseFacets = [ - { label: 'f1', field: 'f1', buckets: {items: [ + { type: 'field', label: 'f1', field: 'f1', buckets: {items: [ { label: 'b1', count: 10, filterQuery: 'filter', checked: true }, { label: 'b2', count: 1, filterQuery: 'filter2' }] }}, - { label: 'f2', field: 'f2', buckets: {items: [] }} + { type: 'field', label: 'f2', field: 'f2', buckets: {items: [] }} ]; component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]); @@ -437,10 +437,10 @@ describe('SearchFilterComponent', () => { }; component.responseFacets = [ - { label: 'f1', field: 'f1', buckets: {items: [ + { type: 'field', label: 'f1', field: 'f1', buckets: {items: [ { label: 'b1', count: 10, filterQuery: 'filter', checked: true }, { label: 'b2', count: 1, filterQuery: 'filter2' }] }}, - { label: 'f2', field: 'f2', buckets: {items: [] }} + { type: 'field', label: 'f2', field: 'f2', buckets: {items: [] }} ]; component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]); @@ -477,10 +477,10 @@ describe('SearchFilterComponent', () => { }; component.responseFacets = [ - { label: 'f1', field: 'f1', buckets: {items: [ + { type: 'field', label: 'f1', field: 'f1', buckets: {items: [ { label: 'b1', count: 10, filterQuery: 'filter', checked: true }, { label: 'b2', count: 1, filterQuery: 'filter2' }] }}, - { label: 'f2', field: 'f2', buckets: {items: [] }} + { type: 'field', label: 'f2', field: 'f2', buckets: {items: [] }} ]; component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]); const data = { diff --git a/lib/content-services/search/components/search-filter/search-filter.component.ts b/lib/content-services/search/components/search-filter/search-filter.component.ts index 58ea6042db..178323e9aa 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.ts @@ -37,6 +37,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy { private DEFAULT_PAGE_SIZE = 5; isAlive = true; + + /** All facet field items to be displayed in the component. These are updated according to the response. + * When a new search is performed, the already existing items are updated with the new bucket count values and + * the newly received items are added to the responseFacets. + */ responseFacets: FacetField[] = null; private facetQueriesPageSize = this.DEFAULT_PAGE_SIZE; @@ -44,7 +49,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { facetExpanded = { 'default': false }; - + displayResetButton: boolean; selectedBuckets: Array<{ field: FacetField, bucket: FacetFieldBucket }> = []; constructor(public queryBuilder: SearchQueryBuilderService, @@ -61,6 +66,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { if (queryBuilder.config && queryBuilder.config.facetIntervals) { this.facetExpanded['interval'] = queryBuilder.config.facetIntervals.expanded; } + this.displayResetButton = this.queryBuilder.config && !!this.queryBuilder.config.resetButton; this.queryBuilder.updated.pipe( takeWhile(() => this.isAlive) @@ -149,6 +155,24 @@ export class SearchFilterComponent implements OnInit, OnDestroy { } } + resetAllSelectedBuckets() { + this.responseFacets.forEach((field) => { + if (field && field.buckets) { + for (let bucket of field.buckets.items) { + bucket.checked = false; + this.queryBuilder.removeUserFacetBucket(field, bucket); + } + this.updateSelectedBuckets(); + } + }); + this.queryBuilder.update(); + } + + resetAll() { + this.resetAllSelectedBuckets(); + this.responseFacets = null; + } + shouldExpand(field: FacetField): boolean { return this.facetExpanded[field.type] || this.facetExpanded['default']; } @@ -164,69 +188,53 @@ export class SearchFilterComponent implements OnInit, OnDestroy { } private parseFacets(context: ResultSetContext) { - if (!this.responseFacets) { - const responseFacetFields = this.parseFacetFields(context); - const responseFacetIntervals = this.parseFacetIntervals(context); - const responseGroupedFacetQueries = this.parseFacetQueries(context); - this.responseFacets = responseFacetFields.concat(...responseGroupedFacetQueries, ...responseFacetIntervals); - - } else { - this.responseFacets = this.responseFacets - .map((field) => { - - let responseField = (context.facets || []).find((response) => response.label === field.label && response.type === field.type); - - (field && field.buckets && field.buckets.items || []) - .map((bucket) => { - const responseBucket = ((responseField && responseField.buckets) || []).find((respBucket) => respBucket.label === bucket.label); - - bucket.count = responseBucket ? this.getCountValue(responseBucket) : 0; - return bucket; - }); - - return field; - }); - } + this.parseFacetFields(context); + this.parseFacetIntervals(context); + this.parseFacetQueries(context); } - private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string): FacetField[] { - return configFacetFields.map((field) => { - const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {}; + private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string) { + configFacetFields.forEach((field) => { + const responseField = this.findFacet(context, itemType, field.label); const responseBuckets = this.getResponseBuckets(responseField, field) .filter(this.getFilterByMinCount(field.mincount)); + const alreadyExistingField = this.findResponseFacet(itemType, field.label); - const bucketList = new SearchFilterList(responseBuckets, field.pageSize); - bucketList.filter = (bucket: FacetFieldBucket): boolean => { - if (bucket && bucketList.filterText) { - const pattern = (bucketList.filterText || '').toLowerCase(); - const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase(); - return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern); + if (alreadyExistingField) { + const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || []; + + this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets); + } else if (responseField) { + + const bucketList = new SearchFilterList(responseBuckets, field.pageSize); + bucketList.filter = this.getBucketFilterFunction(bucketList); + + if (!this.responseFacets) { + this.responseFacets = []; } - return true; - }; - - return { - ...field, - type: responseField.type, - label: field.label, - pageSize: field.pageSize | this.DEFAULT_PAGE_SIZE, - currentPageSize: field.pageSize | this.DEFAULT_PAGE_SIZE, - buckets: bucketList - }; + this.responseFacets.push( { + ...field, + type: responseField.type || itemType, + label: field.label, + pageSize: field.pageSize | this.DEFAULT_PAGE_SIZE, + currentPageSize: field.pageSize | this.DEFAULT_PAGE_SIZE, + buckets: bucketList + }); + } }); } - private parseFacetFields(context: ResultSetContext): FacetField[] { + private parseFacetFields(context: ResultSetContext) { const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || []; - return this.parseFacetItems(context, configFacetFields, 'field'); + this.parseFacetItems(context, configFacetFields, 'field'); } - private parseFacetIntervals(context: ResultSetContext): FacetField[] { + private parseFacetIntervals(context: ResultSetContext) { const configFacetIntervals = this.queryBuilder.config.facetIntervals && this.queryBuilder.config.facetIntervals.intervals || []; - return this.parseFacetItems(context, configFacetIntervals, 'interval'); + this.parseFacetItems(context, configFacetIntervals, 'interval'); } - private parseFacetQueries(context: ResultSetContext): FacetField[] { + private parseFacetQueries(context: ResultSetContext) { const configFacetQueries = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.queries || []; const configGroups = configFacetQueries.reduce((acc, query) => { const group = this.queryBuilder.getQueryGroup(query); @@ -238,36 +246,38 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return acc; }, []); - const result = []; const mincount = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.mincount; const mincountFilter = this.getFilterByMinCount(mincount); Object.keys(configGroups).forEach((group) => { - const responseField = (context.facets || []).find((response) => response.type === 'query' && response.label === group) || {}; + const responseField = this.findFacet(context, 'query', group); const responseBuckets = this.getResponseQueryBuckets(responseField, configGroups[group]) .filter(mincountFilter); + const alreadyExistingField = this.findResponseFacet('query', group); - const bucketList = new SearchFilterList(responseBuckets, this.facetQueriesPageSize); - bucketList.filter = (bucket: FacetFieldBucket): boolean => { - if (bucket && bucketList.filterText) { - const pattern = (bucketList.filterText || '').toLowerCase(); - const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase(); - return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern); + if (alreadyExistingField) { + const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || []; + + this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets); + } else if (responseField) { + + const bucketList = new SearchFilterList(responseBuckets, this.facetQueriesPageSize); + bucketList.filter = this.getBucketFilterFunction(bucketList); + + if (!this.responseFacets) { + this.responseFacets = []; } - return true; - }; - - result.push( { - field: group, - type: responseField.type, - label: group, - pageSize: this.DEFAULT_PAGE_SIZE, - currentPageSize: this.DEFAULT_PAGE_SIZE, - buckets: bucketList - }); + this.responseFacets.push( { + field: group, + type: responseField.type || 'query', + label: group, + pageSize: this.DEFAULT_PAGE_SIZE, + currentPageSize: this.DEFAULT_PAGE_SIZE, + buckets: bucketList + }); + } }); - return result; } private getResponseBuckets(responseField: GenericFacetResponse, configField: FacetField): FacetFieldBucket[] { @@ -304,6 +314,10 @@ export class SearchFilterComponent implements OnInit, OnDestroy { || 0; } + getBucketCountDisplay(bucket: FacetFieldBucket): string { + return bucket.count === null ? '' : `(${bucket.count})`; + } + private getFilterByMinCount(mincountInput: number) { return (bucket) => { let mincount = mincountInput; @@ -342,4 +356,55 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return `${fieldName}:${startLimit}"${start}" TO "${end}"${endLimit}`; } + + private findFacet(context: ResultSetContext, itemType: string, fieldLabel: string): GenericFacetResponse { + return (context.facets || []).find((response) => response.type === itemType && response.label === fieldLabel) || {}; + } + + private findResponseFacet(itemType: string, fieldLabel: string): FacetField { + return (this.responseFacets || []).find((response) => response.type === itemType && response.label === fieldLabel); + } + + private updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets) { + const bucketsToDelete = []; + + alreadyExistingBuckets + .map((bucket) => { + const responseBucket = ((responseField && responseField.buckets) || []).find((respBucket) => respBucket.label === bucket.label); + + if (!responseBucket) { + bucketsToDelete.push(bucket); + } + bucket.count = this.getCountValue(responseBucket); + return bucket; + }); + + const hasSelection = this.selectedBuckets + .find((selBuckets) => alreadyExistingField.label === selBuckets.field.label && alreadyExistingField.type === selBuckets.field.type); + + if (!hasSelection && bucketsToDelete.length) { + bucketsToDelete.forEach((bucket) => { + alreadyExistingField.buckets.deleteItem(bucket); + }); + } + + responseBuckets.forEach((respBucket) => { + const existingBucket = alreadyExistingBuckets.find((oldBucket) => oldBucket.label === respBucket.label); + + if (!existingBucket) { + alreadyExistingField.buckets.addItem(respBucket); + } + }); + } + + private getBucketFilterFunction (bucketList) { + return (bucket: FacetFieldBucket): boolean => { + if (bucket && bucketList.filterText) { + const pattern = (bucketList.filterText || '').toLowerCase(); + const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase(); + return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern); + } + return true; + }; + } } diff --git a/lib/content-services/search/components/search-radio/search-radio.component.ts b/lib/content-services/search/components/search-radio/search-radio.component.ts index 2842e8d88e..18146ae9fb 100644 --- a/lib/content-services/search/components/search-radio/search-radio.component.ts +++ b/lib/content-services/search/components/search-radio/search-radio.component.ts @@ -62,9 +62,10 @@ export class SearchRadioComponent implements SearchWidget, OnInit { } } - this.setValue( - this.getSelectedValue() - ); + const initialValue = this.getSelectedValue(); + if (initialValue !== null) { + this.setValue(initialValue); + } } private getSelectedValue(): string { diff --git a/lib/content-services/search/search-configuration.interface.ts b/lib/content-services/search/search-configuration.interface.ts index bd2e249276..7dff04b770 100644 --- a/lib/content-services/search/search-configuration.interface.ts +++ b/lib/content-services/search/search-configuration.interface.ts @@ -27,6 +27,7 @@ export interface SearchConfiguration { categories: SearchCategory[]; filterQueries?: FilterQuery[]; filterWithContains?: boolean; + resetButton?: boolean; facetQueries?: { label?: string; pageSize?: number; diff --git a/lib/core/app-config/schema.json b/lib/core/app-config/schema.json index c29f531bb1..33eddb0584 100644 --- a/lib/core/app-config/schema.json +++ b/lib/core/app-config/schema.json @@ -926,6 +926,9 @@ "filterWithContains": { "type": "boolean" }, + "resetButton": { + "type": "boolean" + }, "facetFields": { "type": "object", "required": [ From a0007d82b2dcae3f4c1832e8f89c0f0fb10ed979 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Tue, 5 Mar 2019 15:51:48 +0100 Subject: [PATCH 076/223] [ADF-4156] Regression - Return ISO format for date properties on Task List Component (#4393) --- .../task-list-demo/task-list-demo.component.html | 12 ++++++++++-- .../task-list/components/task-list.component.spec.ts | 6 +++--- .../task-list/components/task-list.component.ts | 10 ---------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/demo-shell/src/app/components/task-list-demo/task-list-demo.component.html b/demo-shell/src/app/components/task-list-demo/task-list-demo.component.html index 446b769672..dd27c62036 100644 --- a/demo-shell/src/app/components/task-list-demo/task-list-demo.component.html +++ b/demo-shell/src/app/components/task-list-demo/task-list-demo.component.html @@ -169,8 +169,16 @@ - - + + +
{{entry.row.obj.created | date:'MMM d, yyyy' }}
+
+
+ + +
{{entry.row.obj.dueDate | date:'MMM d, yyyy' }}
+
+
diff --git a/lib/process-services/task-list/components/task-list.component.spec.ts b/lib/process-services/task-list/components/task-list.component.spec.ts index c81c344491..3a42c62abd 100644 --- a/lib/process-services/task-list/components/task-list.component.spec.ts +++ b/lib/process-services/task-list/components/task-list.component.spec.ts @@ -123,9 +123,9 @@ describe('TaskListComponent', () => { expect(component.rows[0]['assignee'].firstName).toEqual('firstNameFake1'); expect(component.rows[0]['assignee'].lastName).toEqual('lastNameFake1'); expect(component.rows[0][('assignee')].email).toEqual('emailFake1'); - expect(component.rows[0]['created']).toEqual('Mar 1, 2017'); - expect(component.rows[0]['dueDate']).toEqual('Apr 2, 2017'); - expect(component.rows[0]['endDate']).toEqual('May 3, 2017'); + expect(component.rows[0]['created'].toISOString()).toEqual('2017-03-01T12:25:17.189Z'); + expect(component.rows[0]['dueDate'].toISOString()).toEqual('2017-04-02T12:25:17.189Z'); + expect(component.rows[0]['endDate'].toISOString()).toEqual('2017-05-03T12:25:31.129Z'); expect(component.rows[0]['duration']).toEqual(13940); expect(component.rows[0]['priority']).toEqual(50); expect(component.rows[0]['parentTaskId']).toEqual(1); diff --git a/lib/process-services/task-list/components/task-list.component.ts b/lib/process-services/task-list/components/task-list.component.ts index 2320430eaa..79258ceded 100644 --- a/lib/process-services/task-list/components/task-list.component.ts +++ b/lib/process-services/task-list/components/task-list.component.ts @@ -38,7 +38,6 @@ import moment from 'moment-es6'; export class TaskListComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent { static PRESET_KEY = 'adf-task-list.presets'; - public FORMAT_DATE: string = 'll'; @ContentChild(CustomEmptyContentTemplateDirective) customEmptyContent: CustomEmptyContentTemplateDirective; @@ -347,15 +346,6 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft if (!task.name) { task.name = 'No name'; } - if (task.created) { - task.created = moment(task.created).format(this.FORMAT_DATE); - } - if (task.dueDate) { - task.dueDate = moment(task.dueDate).format(this.FORMAT_DATE); - } - if (task.endDate) { - task.endDate = moment(task.endDate).format(this.FORMAT_DATE); - } return task; }); return instances; From 0802ff7e7dc582f4ec24489d44f61872c0295528 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Tue, 5 Mar 2019 17:33:29 +0000 Subject: [PATCH 077/223] arabic support (#4397) --- demo-shell/resources/i18n/ar.json | 314 ++++++++++++++ lib/content-services/i18n/ar.json | 349 ++++++++++++++++ lib/core/i18n/ar.json | 388 ++++++++++++++++++ lib/insights/i18n/ar.json | 51 +++ .../src/lib/i18n/ar.json | 195 +++++++++ lib/process-services/i18n/ar.json | 332 +++++++++++++++ 6 files changed, 1629 insertions(+) create mode 100644 demo-shell/resources/i18n/ar.json create mode 100644 lib/content-services/i18n/ar.json create mode 100644 lib/core/i18n/ar.json create mode 100644 lib/insights/i18n/ar.json create mode 100644 lib/process-services-cloud/src/lib/i18n/ar.json create mode 100644 lib/process-services/i18n/ar.json diff --git a/demo-shell/resources/i18n/ar.json b/demo-shell/resources/i18n/ar.json new file mode 100644 index 0000000000..fbc762d755 --- /dev/null +++ b/demo-shell/resources/i18n/ar.json @@ -0,0 +1,314 @@ +{ + "APP": { + "INFO_DRAWER": { + "TITLE": "التفاصيل", + "COMMENTS": "تعليقات", + "PROPERTIES": "الخصائص", + "VERSIONS": "الإصدارات" + }, + "HOME": { + "TITLE": "المكونات الزاوّية لـ Alfresco", + "DOCUMENTATION": "الوثائق" + }, + "LOGOUT": { + "TITLE": "صفحة الخروج", + "SUB_TITLE": "تم خروجك الآن", + "LOGIN": "دخول", + "HOME": "رئيسية" + }, + "ADF_VERSION_MANAGER": { + "ALLOW_DELETE": "السماح بالحذف", + "SHOW_COMMENTS": "إظهار التعليقات على الإصدارات", + "ALLOW_DOWNLOAD": "تمكين تنزيل الإصدار", + "READ_ONLY": "للقراءة فقط", + "COMMENTS": "إظهار التعليقات" + }, + "PERSONAL-FILES": "الملفات الشخصية", + "WARN-MULTIPLE-UPLOADS": "عرض تحذير للتحميلات المتعددة.", + "CUSTOM-PERMISSION-MESSAGE": "تمكين رسالة إذن مخصصة", + "MEDIUM-TIME-FORMAT": "تمكين تنسيق وقت متوسط لقائمة المستندات", + "SEARCH": { + "RADIO": { + "NONE": "بلا", + "ALL": "الكل", + "FOLDER": "مجلد", + "DOCUMENT": "مستند" + } + } + }, + "title": "مرحبًا", + "VERSION": { + "NO_PERMISSION": "ليس لديك إذن لإدارة إصدارات هذا المحتوى", + "NO_PERMISSION_EVENT": "ليس لديك إذن ${event.permission} ل${event.action} ${event.type}", + "CHOOSE_FILE": "تحديد ملف لرؤية إصداراته", + "DIALOG": { + "CLOSE": "إغلاق", + "TITLE": "إدارة الإصدارات" + } + }, + "METADATA": { + "DIALOG": { + "CLOSE": "إغلاق", + "TITLE": "بيانات تعريف" + } + }, + "APP_LAYOUT": { + "APP_NAME": "تطبيق ADF التوضيحي", + "HOME": "رئيسية", + "NODE-SELECTOR": "محدد العقدة", + "CONTENT_SERVICES": "Content Services", + "BREADCRUMB": "مسار التنقل", + "NOTIFICATIONS": "إعلامات", + "TASK_LIST": "قائمة المهام", + "PROCESS_LIST": "مهام عملية", + "PROCESS_CLOUD": "سحابة العملية", + "CARD_VIEW": "CardView", + "PROCESS_SERVICES": "Process Services", + "LOGIN": "دخول", + "CUSTOM_SOURCES": "مصادر مخصصة", + "DATATABLE": "جدول بيانات", + "DATATABLE_LAZY": "جدول بيانات (بطيء)", + "DOCUMENT_LIST": "قائمة المستندات", + "TEMPLATE": "قالب", + "FORM": "نموذج", + "FORM_LIST": "قائمة النماذج", + "FORM_LOADING": "تحميل النموذج", + "UPLOADER": "مُحمِّل", + "WEBSCRIPT": "برنامج الويب النصي", + "TAG": "علامة", + "TRASHCAN": "سلة المحذوفات", + "SOCIAL": "اجتماعي", + "SETTINGS": "إعدادات", + "CONFIG-EDITOR": "محرر التكوين", + "OVERLAY_VIEWER": "عارض مركّب", + "ABOUT": "حول", + "SEARCH": "بحث ممتد", + "EXTENDED_SEARCH_QUERY_BODY": "بحث ممتد بنص الاستعلام", + "WORD_TO_SEARCH": "بحث عن كلمة", + "SEARCH_CREATED_BY": "تم الإنشاء بواسطة", + "SEARCH_SERVICE_APPROACH": "حدد هذا لتعطيل خاصية الإدخال والتكوين باستخدام الخدمة", + "HEADER_DATA": "بيانات العنوان", + "TREE_VIEW": "طريقة عرض الشجرة", + "ICONS": "أيقونات", + "PEOPLE_GROUPS_CLOUD": "سحابة الأشخاص/المجموعة", + "PEOPLE_CLOUD": "مكون سحابة الأشخاص", + "GROUPS_CLOUD": "مكون سحابة المجموعة" + }, + "TRASHCAN": { + "ACTIONS": { + "DELETE_PERMANENT": "حذف بشكل دائم", + "RESTORE": "استعادة" + }, + "EMPTY_STATE": { + "TITLE": "المهملات فارغة", + "FIRST_TEXT": "تم نقل العناصر المحذوفة إلى المهملات.", + "SECOND_TEXT": "أفرغ المهملات لحذف العناصر بشكل دائم." + } + }, + "DOCUMENT_LIST": { + "MULTISELECT_CHECKBOXES": "تحديد متعدد (مع خانات اختيارات)", + "THUMBNAILS": "تمكين الصور المصغرة", + "ALLOW_DROP_FILES": "تمكين إسقاط ملفات في مجلد", + "MULTIPLE_FILE_UPLOAD": "تحميل ملفات متعددة", + "FOLDER_UPLOAD": "تحميل مجلد", + "CUSTOM_FILTER": "عامل تصفية امتدادات مخصص", + "MAX_SIZE": "عامل تصفية الحد الأقصى للحجم", + "ENABLE_VERSIONING": "تمكين تعيين الإصدار", + "DESCRIPTION_UPLOAD": "تمكين التحميل", + "ENABLE_INFINITE_SCROLL": "تمكين التمرير اللانهائي", + "MULTISELECT_DESCRIPTION": "استخدام Cmd (Mac) أو Ctrl (Windows) لتبديل تحديد العناصر المتعددة", + "RECENT": { + "EMPTY_STATE": { + "TITLE": "قائمة أحدث الملفات فارغة" + }, + "TITLE": "أحدث الملفات" + }, + "COLUMNS": { + "DISPLAY_NAME": "اسم العرض", + "IS_LOCKED": "قفل", + "TAG": "علامة", + "NODE_ID": "معرف العقدة", + "CREATED_BY": "تم الإنشاء بواسطة", + "CREATED_ON": "تم الإنشاء في", + "CREATED": "تم الإنشاء", + "SIZE": "الحجم", + "DELETED_ON": "تم الحذف", + "DELETED_BY": "تم الحذف بواسطة" + }, + "TOOLBAR": { + "CARDVIEW": "وضع عرض البطاقة", + "SHARE_EDIT": "إعدادات التحرير", + "NEW_FOLDER": "مجلد جديد", + "EDIT_FOLDER": "تحرير مجلد", + "DOWNLOAD": "تنزيل", + "DELETE": "حذف", + "FAVORITES": "إضافة إلى المفضلات", + "SHARE": "مشاركة", + "THEME": "تحديد نسق", + "SHOW_VERSION": "إظهار الإصدار", + "HIDE_VERSION": "إخفاء الإصدار", + "LISTVIEW": "وضع عرض القائمة", + "CREATE_LIBRARY": "إنشاء مكتبة" + }, + "ACTIONS": { + "VERSIONS": "إدارة الإصدارات", + "LOCK": "قفل", + "METADATA": "معلومات", + "DOWNLOAD": "تنزيل", + "PERMISSION": "إذن", + "FOLDER": { + "COPY": "نسخ", + "MOVE": "نقل", + "DELETE": "حذف" + }, + "DOCUMENT": { + "COPY": "نسخ", + "MOVE": "نقل", + "DELETE": "حذف", + "PROCESS_ACTION": "بدء العملية" + } + } + }, + "DATATABLE": { + "RESET_DEFAULT": "إعادة تعيين إلى افتراضي", + "ADD_ROW": "إضافة صف", + "REPLACE_ROWS": "استبدال صفوف", + "REPLACE_COLUMNS": "استبدال أعمدة", + "LOAD_NODE": "تحميل عقدة", + "MULTISELECT": "تحديد متعدد", + "MULTISELECT_DESCRIPTION": "استخدام Cmd (Mac) أو Ctrl (Windows) لتبديل تحديد العناصر المتعددة" + }, + "ANALYTICS_REPORT": { + "NO_REPORT_MESSAGE": "لم يتم تحديد تقرير. اختر تقريرًا من القائمة" + }, + "PS-TAB": { + "TASKS-TAB": "المهام", + "PROCESSES-TAB": "العملية", + "REPORTS-TAB": "تقارير", + "SETTINGS-TAB": "إعدادات", + "START-TASK": "بدء مهمة", + "START-PROCESS": "بدء العملية", + "PROCESS-AUDIT-LOG": "سجل تدقيق العملية", + "TASK-AUDIT-LOG": "سجل تدقيق المهمة", + "TASK-SHOW-HEADER": "إظهار تفاصيل العنوان" + }, + "PS_CLOUD_TAB": { + "APPS_TAB": "تطبيق", + "SETTINGS_TAB": "إعدادات" + }, + "FORM-LIST": { + "STORE": "متجر", + "RESTORE": "استعادة" + }, + "FORM-LOADING": { + "FORM_DATA": "بيانات النموذج", + "FORM_DATA_MESSAGE": "إدخال قيم لملء النموذج", + "TYPEAHEAD_PLACEHOLDER": "التنبؤ بالحروف", + "RADIO_PLACEHOLDER": "زر الراديو", + "SELECT_PLACEHOLDER": "القائمة المنسدلة" + }, + "LOGIN": { + "CONTENT_SERVICES": "Content Services", + "PROCESS_SERVICES": "Process Services", + "LOGIN_FOOTER": "تذييل الدخول", + "SHOW_REMEMBERME": "إظهار تذكرني", + "SHOW_SUCCESS_ROUTE": "إظهار طريق النجاح", + "CUSTOM_LOGO": "شعار مخصص" + }, + "SEARCH": { + "RESULTS": "نتائج البحث", + "NO_RESULT": "لم يتم العثور على نتائج", + "FACET_FIELDS": { + "TYPE": "1:النوع", + "SIZE": "2:الحجم", + "CREATOR": "3:المُنشئ", + "MODIFIER": "4:المُعدل", + "CREATED": "5:تم الإنشاء" + }, + "FACET_QUERIES": { + "MY_FACET_QUERIES": "استعلامات واجهتي", + "CREATED_THIS_YEAR": "1.تم الإنشاء هذا العام", + "MIMETYPE": "2.النوع: HTML", + "XTRASMALL": "3.الحجم: فائق الصغر", + "SMALL": "4.الحجم: صغير", + "MEDIUM": "5.الحجم: متوسط", + "LARGE": "6.الحجم: كبير", + "XTRALARGE": "7.الحجم: فائق الكبر", + "XXTRALARGE": "8.الحجم: كبير XX" + } + }, + "SOCIAL": { + "LIKE": "مكون الإعجاب", + "RATING": "مكون التقييم" + }, + "TAG": { + "LIST": "خدمات محتوى علامات القائمة", + "INSERT": "إدراج معرف العقدة", + "NODE_LIST": "قائمة العلامات حسب معرف العقدة" + }, + "DEMO_PERMISSION": { + "INHERIT_PERMISSION_BUTTON": "إذن التوريث", + "INHERITED_PERMISSIONS_BUTTON": "تم توريث الإذن" + }, + "TASK_LIST_DEMO": { + "ERROR_MESSAGE": { + "APP_ID_REQUIRED_ERROR": "إدراج معرف التطبيق", + "APP_ID_TYPE_ERROR": "يجب أن يكون معرف التطبيق رقمًا", + "NUMBER_TYPE_ERROR": "يجب أن تكون القيمة رقمًا", + "NUMBER_GREATER_THAN": "يجب أن تكون القيمة أكبر من {{ value }} أو تساويها" + }, + "TOOLTIP_MESSAGE": { + "START_INPUT": "صفحة البدء" + } + }, + "PROCESS_LIST_DEMO": { + "ERROR_MESSAGE": { + "APP_ID_REQUIRED_ERROR": "إدراج معرف التطبيق", + "APP_ID_TYPE_ERROR": "يجب أن يكون معرف التطبيق رقمًا", + "NUMBER_GREATER_THAN": "يجب أن تكون القيمة أكبر من {{ value }} أو تساويها" + } + }, + "GROUP-TITLE1-TRANSLATION-KEY": "الترجمة الأولى للعنوان المخصص", + "GROUP-TITLE2-TRANSLATION-KEY": "الترجمة الثانية للعنوان المخصص", + "ERROR_CONTENT": { + "507": { + "TITLE": "قرص ACS ممتلئ", + "DESCRIPTION": "يتجاوز المحتوى حد سعة التخزين المكون للشبكة أو النظام", + "SECONDARY_BUTTON": { + "TEXT": "" + }, + "RETURN_BUTTON": { + "TEXT": "عودة إلى الرئيسية" + } + } + }, + "PROCESS_LIST_CLOUD_DEMO": { + "TITLE": "العرض التوضيحي لسحابة قائمة العملية", + "CUSTOMIZE_FILTERS": "تخصيص عامل التصفية" + }, + "TASK_LIST_CLOUD_DEMO": { + "CUSTOMIZE_FILTERS": "تخصيص عامل التصفية" + }, + "PEOPLE_GROUPS_CLOUD": { + "SINGLE": "فردي", + "MULTI": "متعدد", + "PRESELECTED_VALUE": "قيمة محددة مسبقًا" + }, + "ABOUT": { + "TITLE": "مكونات إضافية", + "TABLE_HEADERS": { + "ID": "المعرف", + "NAME": "الاسم", + "VERSION": "الإصدار", + "VENDOR": "المورد", + "LICENSE": "الترخيص", + "RUNTIME": "وقت التشغيل", + "DESCRIPTION": "الوصف" + } + }, + "SETTINGS_CLOUD": { + "MULTISELECTION": "تحديد متعدد", + "TESTING_MODE": "وضع الاختبار", + "SELECTION_MODE": "وضع التحديد" + } +} \ No newline at end of file diff --git a/lib/content-services/i18n/ar.json b/lib/content-services/i18n/ar.json new file mode 100644 index 0000000000..2839cf48ff --- /dev/null +++ b/lib/content-services/i18n/ar.json @@ -0,0 +1,349 @@ +{ + "ADF_VERSION_LIST": { + "ACTIONS": { + "RESTORE": "استعادة", + "DELETE": "حذف", + "DOWNLOAD": "تنزيل", + "UPLOAD": { + "TITLE": "تحميل إصدار جديد", + "TOOLTIP": "القيد: يجب عليك تحميل ملف بنفس الاسم لإنشاء إصدار جديد منه", + "MAJOR": "تغييرات كبيرة (2.0)", + "MINOR": "تغييرات صغيرة (1.1)", + "COMMENT": "ترك تعليق", + "ADD": "إضافة إصدار جديد", + "CANCEL": "إلغاء" + } + }, + "CONFIRM_DELETE": { + "TITLE": "حذف إصدار", + "MESSAGE": "لم يمكن استعادة إصدارات الملفات المحذوفة. أتريد المتابعة؟", + "YES_LABEL": "نعم", + "NO_LABEL": "لا" + } + }, + "ADF_CONFIRM_DIALOG": { + "TITLE": "تأكيد", + "ACTION": "هل تريد المتابعة؟", + "YES_LABEL": "نعم", + "NO_LABEL": "لا" + }, + "ADF-DOCUMENT-LIST": { + "EMPTY": { + "HEADER": "هذا المجلد فارغ" + }, + "NO_PERMISSION": "ليس لديك إذن لعرض هذا الملف أو المجلد.", + "LAYOUT": { + "CREATED": "تم الإنشاء", + "THUMBNAIL": "صورة مصغرة", + "NAME": "الاسم", + "LOCATION": "مكان", + "SIZE": "الحجم", + "DELETED_ON": "تم الحذف", + "DELETED_BY": "تم الحذف بواسطة", + "STATUS": "الحالة", + "MODIFIED_ON": "تم التعديل", + "MODIFIED_BY": "تم التعديل بواسطة", + "SHARED_BY": "تمت المشاركة بواسطة", + "LOAD_MORE": "تحميل المزيد" + }, + "MENU_ACTIONS": { + "VIEW": "عرض", + "REMOVE": "إزالة", + "DOWNLOAD": "تنزيل" + } + }, + "ALFRESCO_DOCUMENT_LIST": { + "BUTTON": { + "ACTION_CREATE": "إنشاء...", + "ACTION_NEW_FOLDER": "مجلد جديد", + "CREATE": "إنشاء", + "CANCEL": "إلغاء" + } + }, + "DROPDOWN": { + "PLACEHOLDER_LABEL": "قائمة المواقع", + "MY_FILES_OPTION": "ملفاتي" + }, + "NODE_SELECTOR": { + "CANCEL": "إلغاء", + "CHOOSE": "اختيار", + "COPY": "نسخ", + "COPY_ITEM": "نسخ '{{ name }}' إلى...", + "MOVE": "نقل", + "MOVE_ITEM": "نقل '{{ name }}' إلى...", + "NO_RESULTS": "لم يتم العثور على نتائج", + "SEARCH": "بحث", + "SEARCH_RESULTS": "نتائج البحث", + "SELECT_LOCATION": "تحديد المكان" + }, + "OPERATION": { + "SUCCESS": { + "CONTENT": { + "COPY": "نسخ ناجح", + "MOVE": "نقل ناجح" + }, + "FOLDER": { + "COPY": "نسخ ناجح", + "MOVE": "نقل ناجح" + } + }, + "ERROR": { + "CONFLICT": "هذا الاسم قيد الاستخدام بالفعل، جرّب اسمًا مختلفًا.", + "UNKNOWN": "كان الإجراء غير ناجح. حاول مرة أخرى أو اتصل بفريق تكنولوجيا المعلومات.", + "PERMISSION": "ليس لديك حق الوصول لفعل هذا." + } + }, + "TAG": { + "LABEL": { + "NEWTAG": "علامة جديدة" + }, + "MESSAGES": { + "EXIST": "العلامة موجودة بالفعل" + }, + "BUTTON": { + "ADD": "إضافة علامة" + } + }, + "ADF_FILE_UPLOAD": { + "BUTTON": { + "MINIMIZE": "تصغير", + "MAXIMIZE": "تكبير", + "CLOSE": "إغلاق", + "CANCEL_ALL": "إلغاء التحميلات", + "CANCEL_FILE": "إلغاء التحميل", + "REMOVE_FILE": "إزالة الملف المحمل" + }, + "STATUS": { + "FILE_CANCELED_STATUS": "تم الإلغاء" + }, + "CONFIRMATION": { + "BUTTON": { + "CANCEL": "نعم", + "CONTINUE": "لا" + }, + "MESSAGE": { + "TITLE": "إلغاء التحميل", + "TEXT": "قم بإيقاف التحميل وإزالة الملفات التي تم تحميلها بالفعل." + } + } + }, + "FILE_UPLOAD": { + "BUTTON": { + "UPLOAD_FILE": "تحميل ملف", + "UPLOAD_FOLDER": "تحميل مجلد" + }, + "VERSION": { + "MESSAGES": { + "NO_ACCEPTED_FILE_TYPES": "يرجى ملاحظة أن إعداد \"acceptedFilesType\" ليس له تأثير على تحميلات الإصدارات الجديدة. سيكون نوع الملف هو نفس نوع الملف الأصلي.", + "INCOMPATIBLE_VERSION": "يمكن تحميل ملف من نفس النوع فقط كإصدار جديد." + } + }, + "MESSAGES": { + "UPLOAD_CANCELED": "تم إلغاء التحميل", + "UPLOAD_PROGRESS": "تم تحميل {{ completed }} / {{ total }}", + "UPLOAD_ERROR": "{{ total }} تحميل غير ناجح", + "UPLOAD_ERRORS": "{{ total }} تحميلات غير ناجحة", + "PROGRESS": "التحميل قيد التقدم...", + "FOLDER_ALREADY_EXIST": "المجلد {0} موجود بالفعل", + "FOLDER_NOT_SUPPORTED": "تحميل المجلد غير مدعوم بواسطة المستعرض، جرّب مستعرضًا آخر", + "REMOVE_FILE_ERROR": "تعذرت إزالة {{ fileName }}، حاول مرة أخرى أو تحقق من الأمر مع فريق تكنولوجيا المعلومات لديك.", + "REMOVE_FILES_ERROR": "تعذرت إزالة الملفات {{ total }}، حاول مرة أخرى أو تحقق من الأمر مع فريق تكنولوجيا المعلومات لديك.", + "EXCEED_MAX_FILE_SIZE": "الملف {{ fileName }} أكبر من حجم الملف المسموح" + }, + "ACTION": { + "UNDO": "تراجع" + }, + "ERRORS": { + "GENERIC": "التحميل غير ناجح. اتصل بقسم تكنولوجيا المعلومات في حالة استمرار هذه المشكلة", + "500": "خطأ في خادم الإنترنت، حاول مرة أخرى أو اتصل بدعم تكنولوجيا المعلومات [500]", + "504": "انتهلت مهلة الخادم، حاول مرة أخرى أو اتصل بدعم تكنولوجيا المعلومات [504]", + "403": "أذونات غير كافية للتحميل في هذا المكان [403]", + "404": "مكان التحميل لم يعد موجودًا [404]" + }, + "ARIA-LABEL": { + "ERROR": "خطأ في التحميل" + } + }, + "WEBSCRIPT": { + "ERROR": "تعذر إتمام الإجراء. شارك هذه الرسالة مع فريق تكنولوجيا المعلومات لديك: حدث خطأ أثناء إلغاء تسلسل{{data}} كـ {{contentType}}" + }, + "SEARCH": { + "CONTROL": {}, + "BUTTON": { + "TOOLTIP": "بحث", + "ARIA-LABEL": "زر البحث" + }, + "INPUT": { + "ARIA-LABEL": "إدخال البحث" + }, + "RESULTS": { + "SUMMARY": "تم العثور على نتيجة {{numResults}} ل{{searchTerm}}", + "NONE": "لم يتم العثور على نتائج ل{{searchTerm}}", + "ERROR": "نواجه مشكلة أثناء البحث - حاول مرة أخرى.", + "COLUMNS": { + "NAME": "اسم العرض", + "MODIFIED_BY": "تم التعديل بواسطة", + "MODIFIED_AT": "تم التعديل في" + } + }, + "FILTER": { + "ACTIONS": { + "CLEAR": "مسح", + "APPLY": "تطبيق", + "CLEAR-ALL": "مسح الكل", + "SHOW-MORE": "إظهار المزيد", + "SHOW-LESS": "إظهار أقل", + "FILTER-CATEGORY": "فئة عامل التصفية" + }, + "RANGE": { + "FROM": "من", + "TO": "إلى", + "FROM-DATE": "من", + "TO-DATE": "إلى" + }, + "VALIDATION": { + "REQUIRED-VALUE": "القيمة المطلوبة", + "NO-DAYS": "لم يتم تحديد أيام.", + "INVALID-FORMAT": "تنسيق غير صالح", + "INVALID-DATE": "تاريخ غير صالح. يجب أن يكون التاريخ بالتنسيق '{{ requiredFormat }}'", + "BEYOND-MAX-DATE": "يتجاوز التاريخ الحد الأقصى للتاريخ." + } + }, + "ICONS": { + "ft_ic_raster_image": "ملف صورة", + "ft_ic_pdf": "مستند PDF", + "ft_ic_ms_excel": "ملف Microsoft Excel", + "ft_ic_ms_word": "مستند Microsoft Word", + "ft_ic_ms_powerpoint": "ملف Microsoft PowerPoint", + "ft_ic_video": "ملف فيديو", + "ft_ic_document": "ملف مستند", + "ft_ic_website": "مورد ويب", + "ft_ic_archive": "ملف أرشيف", + "ft_ic_presentation": "ملف عرض تقديمي", + "ft_ic_spreadsheet": "ملف جدول بيانات" + }, + "DOCUMENT_LIST": { + "COLUMNS": { + "DISPLAY_NAME": "اسم العرض", + "CREATED_BY": "تم الإنشاء بواسطة", + "CREATED_ON": "تم الإنشاء" + }, + "ACTIONS": { + "FOLDER": { + "DELETE": "حذف", + "MOVE": "نقل", + "COPY": "نسخ" + }, + "DOCUMENT": { + "DOWNLOAD": "تنزيل", + "DELETE": "حذف", + "MOVE": "نقل", + "COPY": "نسخ", + "PROCESS_ACTION": "بدء العملية" + } + } + } + }, + "PERMISSION": { + "LACKOF": "ليس لديك الإذن {{permission}} ل{{action}} من نوع {{type}}" + }, + "METADATA": { + "BASIC": { + "HEADER": "الخصائص", + "NAME": "الاسم", + "TITLE": "العنوان", + "DESCRIPTION": "الوصف", + "AUTHOR": "المؤلف", + "MIMETYPE": "Mimetype", + "SIZE": "الحجم", + "CREATOR": "المنشئ", + "CREATED_DATE": "تاريخ الإنشاء", + "MODIFIER": "المعدل", + "MODIFIED_DATE": "تاريخ التعديل" + } + }, + "SHARE": { + "PUBLIC-LINK": "رابط عام", + "DIALOG-TITLE": "مشاركة", + "DESCRIPTION": "انقر فوق الرابط أدناه لنسخه إلى الحافظة.", + "TITLE": "رابط للمشاركة", + "EXPIRES": "تنتهي الصلاحية في", + "CLIPBOARD-MESSAGE": "تم نسخ الرابط إلى الحافظة", + "CLOSE": "إغلاق", + "CONFIRMATION": { + "DIALOG-TITLE": "إزالة هذا الرابط المشترك", + "MESSAGE": "سيتم حذف هذا الرابط وسيتم إنشاء رابط جديد في المرة التالية التي يتم فيها مشاركة هذا الملف", + "CANCEL": "إلغاء", + "REMOVE": "إزالة" + } + }, + "PERMISSION_MANAGER": { + "PERMISSION_DISPLAY": { + "INHERITED": "تم توريث", + "AUTHORITY_ID": "معرف المرجع", + "ROLE": "الدور", + "LOCALLY_SET": "تعيين محلي", + "NO_PERMISSIONS": "لا توجد أذونات" + }, + "ADD-PERMISSION": { + "SEARCH": "بحث", + "TYPE-MESSAGE": "اكتب شيئًا لبدء البحث في المجموعات أو الأشخاص", + "NO-RESULT": "لم يتم العثور على نتيجة لهذا البحث", + "ADD-ACTION": "إضافة", + "CLOSE-ACTION": "إغلاق", + "BASE-DIALOG-TITLE": "بحث عن مجموعة أو أشخاص لإضافتهم...", + "EVERYONE": "كل شخص" + }, + "ERROR": { + "DUPLICATE-PERMISSION": "أحد الأذونات أو أكثر من إذن قمت بتعيينه موجود بالفعل : {{list}}", + "NOT-ALLOWED": "غير مسموح لك بتغيير الأذونات" + } + }, + "ADF-TREE-VIEW": { + "MISSING-ID": "معرف العقدة غير متوفر!" + }, + "LIBRARY": { + "DIALOG": { + "CREATE_TITLE": "إنشاء مكتبة", + "CREATE": "إنشاء", + "UPDATE": "تحديث", + "EDIT": "تحرير", + "CANCEL": "إلغاء", + "FORM": { + "DESCRIPTION": "الوصف", + "SITE_ID": "معرف المكتبة", + "NAME": "الاسم", + "VISIBILITY": "رؤية" + } + }, + "ROLE": { + "MANAGER": "مدير", + "COLLABORATOR": "معاون", + "CONTRIBUTOR": "مساهم", + "CONSUMER": "مستهلك" + }, + "VISIBILITY": { + "PRIVATE": "خاص", + "PUBLIC": "عام", + "MODERATED": "معتدل" + }, + "HINTS": { + "SITE_TITLE_EXISTS": "اسم المكتبة قيد الاستخدام بالفعل" + }, + "ERRORS": { + "GENERIC": "نواجه مشكلة", + "EXISTENT_SITE": "معرف المكتبة هذا غير متاح. جرّب معرف مكتبة مختلفًا.", + "CONFLICT": "معرف المكتبة هذا قيد الاستخدام بالفعل. تحقق من سلة المحذوفات.", + "ID_TOO_LONG": "استخدم 72 حرفًا أو أقل لاسم URL", + "DESCRIPTION_TOO_LONG": "استخدم 512 حرفًا أو أقل للوصف", + "TITLE_TOO_LONG": "استخدم 256 أو أقل للعنوان", + "TITLE_TOO_SHORT": "يجب أن يكون طول العنوان حرفين على الأقل", + "ILLEGAL_CHARACTERS": "استخدم الأرقام والحروف فقط", + "ONLY_SPACES": "لا يمكن أن يحتوي اسم المكتبة على أية مسافات", + "LIBRARY_UPDATE_ERROR": "حدث خطأ أثناء تحديث خصائص المكتبة" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "تم تحديث خصائص المكتبة" + } + } +} \ No newline at end of file diff --git a/lib/core/i18n/ar.json b/lib/core/i18n/ar.json new file mode 100644 index 0000000000..69161e6754 --- /dev/null +++ b/lib/core/i18n/ar.json @@ -0,0 +1,388 @@ +{ + "SAVE": "حفظ", + "COMPLETE": "تم", + "START PROCESS": "بدء العملية", + "FORM": { + "START_FORM": { + "TITLE": "نموذج البدء" + }, + "PREVIEW": { + "IMAGE_NOT_AVAILABLE": "المعاينة غير متاحة" + }, + "FIELD": { + "LOCALSTORAGE": "التخزين المحلي", + "SOURCE": "تحديد المصدر من ", + "SHOW_FILE": "إظهار", + "DOWNLOAD_FILE": "تنزيل", + "REMOVE_FILE": "إزالة", + "UPLOAD": "تحميل", + "REQUIRED": "*مطلوب", + "VALIDATOR": { + "INVALID_NUMBER": "استخدم تنسيق رقم مختلفًا", + "INVALID_DATE": "استخدم تنسيق تاريخ مختلفًا", + "INVALID_VALUE": "أدخل قيمة مختلفة", + "NOT_GREATER_THAN": "لا يمكن أن يكون أكبر من {{ maxValue }}", + "NOT_LESS_THAN": "لا يمكن أن يكون أقل من {{ minValue }}", + "AT_LEAST_LONG": "أدخل {{ minLength }} حرف (حرفًا) على الأقل", + "NO_LONGER_THAN": "أدخل {{ maxLength }} حرف (حرفًا) كحد أقصى" + } + } + }, + "CORE": { + "HEADER": { + "LOGO_ARIA": "شعار الشركة" + }, + "FILE_SIZE": { + "BYTES": "بايت", + "KB": "كيلوبايت", + "MB": "ميجابايت", + "GB": "جيجابايت", + "TB": "تيرابايت", + "PB": "بيتابايت", + "EB": "إكسابايت", + "ZB": "زيتابايت", + "YB": "يوتابايت" + }, + "PAGINATION": { + "ARIA": { + "ITEMS_PER_PAGE": "محدد حجم الصفحة", + "CURRENT_PAGE": "محدد الصفحة الحالية", + "PREVIOUS_PAGE": "زر الصفحة السابقة", + "NEXT_PAGE": "زر الصفحة التالية" + }, + "ITEMS_RANGE": "إظهار {{ range }} من {{ total }}", + "ITEMS_PER_PAGE": "عناصر لكل صفحة", + "CURRENT_PAGE": "الصفحة {{ number }}", + "TOTAL_PAGES": "من {{ total }}" + }, + "DIALOG": { + "DOWNLOAD_ZIP": { + "ACTIONS": { + "CANCEL": "إلغاء" + }, + "TITLE": "إضافة الملفات إلى الضغط، قد يستغرق هذا بضع دقائق" + } + }, + "FILE_DIALOG": { + "FILE_LOCK": "تأمين ملف", + "ALLOW_OTHERS_CHECKBOX": "السماح للمالك بتعديل هذا الملف", + "FILE_LOCK_CHECKBOX": "تأمين ملف", + "TIME_LOCK_CHECKBOX": "تأمين الوقت", + "SAVE_BUTTON": { + "LABEL": "حفظ" + }, + "CANCEL_BUTTON": { + "LABEL": "إلغاء" + } + }, + "FOLDER_DIALOG": { + "CREATE_FOLDER_TITLE": "إنشاء مجلد جديد", + "EDIT_FOLDER_TITLE": "تحرير مجلد", + "FOLDER_NAME": { + "LABEL": "الاسم", + "ERRORS": { + "REQUIRED": "اسم المجلد مطلوب", + "SPECIAL_CHARACTERS": "لا يمكن أن يحتوي اسم المجلد على هذه الحروف * \" < > \\ / ? : |", + "ENDING_DOT": "لا يمكن أن ينتهي اسم المجلد بنقطة .", + "ONLY_SPACES": "لايمكن أن يحتوي اسم المجلد على مسافات فقط" + } + }, + "FOLDER_DESCRIPTION": { + "LABEL": "الوصف" + }, + "CREATE_BUTTON": { + "LABEL": "إنشاء" + }, + "UPDATE_BUTTON": { + "LABEL": "تحديث" + }, + "CANCEL_BUTTON": { + "LABEL": "إلغاء" + } + }, + "MESSAGES": { + "ERRORS": { + "GENERIC": "كان الإجراء غير ناجح. حاول مرة أخرى أو اتصل بفريق تكنولوجيا المعلومات.", + "EXISTENT_FOLDER": "يوجد مجلد بهذا الاسم بالفعل. جرّب اسمًا مختلفًا." + } + }, + "RESTORE_NODE": { + "VIEW": "عرض", + "PARTIAL_PLURAL": "لم تتم استعادة {{ number }} من العناصر بسبب مشكلات في مكان الاستعادة", + "NODE_EXISTS": "تتعذر الاستعادة، عنصر {{ name }} موجود بالفعل", + "LOCATION_MISSING": "تتعذر استعادة العنصر {{ name }}، المكان الأصلي لم يعد موجودًا", + "GENERIC": "حدثت مشكلة في استعادة العنصر {{ name }}", + "PLURAL": "نجحت الاستعادة", + "SINGULAR": "تمت استعادة العنصر {{ name }}" + }, + "DELETE_NODE": { + "SINGULAR": "تم حذف {{ name }}", + "PLURAL": "تم حذف {{ number }} عناصر", + "PARTIAL_SINGULAR": "تم حذف {{ success }} عنصر، تعذر حذف {{ failed }}", + "PARTIAL_PLURAL": "تم حذف {{ success }} عناصر، تعذر حذف {{ failed }}", + "ERROR_SINGULAR": "تعذر حذف {{ name }}", + "ERROR_PLURAL": "تعذر حذف {{ number }} من العناصر" + }, + "HOST_SETTINGS": { + "TYPE-AUTH": "نوع المصادقة", + "BASIC": "مصادقة أساسية", + "SSO": "SSO", + "IMPLICIT-FLOW": "تدفق ضمني", + "PROVIDER": "موفر", + "REQUIRED": "هذا الحقل مطلوب", + "CS_URL_ERROR": "لا يتطابق عنوان Content Services مع تنسيق URL", + "PS_URL_ERROR": "لا يتطابق عنوان Process Services مع تنسيق URL", + "TITLE": "إعدادات", + "CS-HOST": "عنوان URL لـ Content Services", + "BP-HOST": "عنوان URL لـ Process Services", + "BACK": "عودة", + "APPLY": "تطبيق", + "NOT_VALID": "لم يتم التعرف على http(s)://host|ip:port(/path)، جرّب عنوان URL مختلفًا.", + "REDIRECT": "إعادة توجيه URI", + "REDIRECT_LOGOUT": "إعادة توجيه خروج URI", + "SILENT": "دخول صامت", + "SCOPE": "نطاق", + "CLIENT": "معرف العميل" + }, + "CARDVIEW": { + "KEYVALUEPAIRS": { + "ADD": "إضافة جديد", + "NAME": "الاسم", + "VALUE": "القيمة" + }, + "VALIDATORS": { + "FLOAT_VALIDATION_ERROR": "استخدام تنسيق رقم", + "INT_VALIDATION_ERROR": "استخدام تنسيق عدد صحيح" + } + }, + "METADATA": { + "BASIC": { + "HEADER": "الخصائص", + "NAME": "الاسم", + "TITLE": "العنوان", + "DESCRIPTION": "الوصف", + "AUTHOR": "المؤلف", + "MIMETYPE": "Mimetype", + "SIZE": "الحجم", + "CREATOR": "المنشئ", + "CREATED_DATE": "تاريخ الإنشاء", + "MODIFIER": "المعدل", + "MODIFIED_DATE": "تاريخ التعديل" + }, + "ACTIONS": { + "EDIT": "تحرير", + "SAVE": "حفظ", + "CANCEL": "إلغاء", + "TOGGLE": "تبديل القيمة" + } + } + }, + "COMMENTS": { + "NONE": "لا تعليقات", + "ADD": "إضافة", + "HEADER": "التعليقات ({{ count }})", + "CREATED_BY_HEADER": "تم الإنشاء بواسطة", + "MESSAGE_HEADER": "رسالة", + "DIALOG": { + "TITLE": "تعليق جديد", + "LABELS": { + "MESSAGE": "رسالة" + }, + "BUTTON": { + "ADD": "إضافة تعليق", + "CANCEL": "إلغاء" + } + } + }, + "LOGIN": { + "LOGO": "Alfresco", + "LABEL": { + "LOGIN": "تسجيل دخول", + "USERNAME": "اسم مستخدم", + "PASSWORD": "كلمة سر", + "REMEMBER": "تذكرني" + }, + "MESSAGES": { + "USERNAME-REQUIRED": "مطلوب", + "USERNAME-MIN": "يلزم أن يكون اسم المستخدم {{ minLength }} من الحروف على الأقل.", + "PASSWORD-REQUIRED": "أدخل كلمة السر لتسجيل الدخول", + "LOGIN-ERROR-CREDENTIALS": "لقد أدخلت اسم مستخدم أو كلمة سر غير معروفة", + "LOGIN-ERROR-PROVIDERS": "لا يمكن أن يكون الموفرون غير محددين", + "LOGIN-ERROR-CORS": "استثناء CORS، تحقق من تكوين الخادم لديك", + "LOGIN-ERROR-CSRF": "استثناء CSRF، قم بتعيين [disableCsrf]=\"true\" في login.component", + "LOGIN-ECM-LICENSE": "مستودع Alfresco Content Services في وضع للقراءة فقط" + }, + "BUTTON": { + "LOGIN": "تسجيل دخول", + "CHECKING": "فحص", + "WELCOME": "مرحبًا", + "SSO": "تسجيل دخول SSO" + }, + "ACTION": { + "HELP": "بحاجة لمساعدة؟", + "REGISTER": "تسجيل" + }, + "DIALOG": { + "CANCEL": "إلغاء", + "CHOOSE": "اختيار" + } + }, + "ADF-DATATABLE": { + "EMPTY": { + "HEADER": "هذه القائمة فارغة", + "DRAG-AND-DROP": { + "TITLE": "سحب وإفلات", + "SUBTITLE": "لتحميل الملفات" + } + }, + "CONTENT-ACTIONS": { + "TOOLTIP": "إجراءات المحتوى" + } + }, + "USER_PROFILE": { + "LABELS": { + "ECM": { + "JOB_TITLE": "عنوان المهمة" + }, + "BPM": { + "TENANT": "مستأجر" + } + }, + "TAB": { + "CS": "Content Services", + "PS": "Process Services" + } + }, + "ADF_VIEWER": { + "ACTIONS": { + "BACK": "عودة", + "OPEN_WITH": "فتح باستخدام", + "DOWNLOAD": "تنزيل", + "PRINT": "طباعة", + "SHARE": "مشاركة", + "MORE_ACTIONS": "إجراءات أكثر", + "INFO": "معلومات", + "FULLSCREEN": "تنشيط وضع ملء الشاشة", + "CLOSE": "إغلاق", + "NEXT_FILE": "الملف التالي", + "PREV_FILE": "الملف السابق" + }, + "ARIA": { + "PREVIOUS_PAGE": "الصفحة السابقة", + "NEXT_PAGE": "الصفحة التالية", + "ZOOM_IN": "تكبير", + "ZOOM_OUT": "تصغير", + "FIT_PAGE": "ملاءمة الصفحة", + "ROTATE_LEFT": "تدوير لليسار", + "ROTATE_RIGHT": "تدوير لليمين", + "RESET": "إعادة تعيين" + }, + "PAGE_LABEL": { + "SHOWING": "إظهار", + "OF": "من" + }, + "LOADING": "تحميل", + "UNKNOWN_FORMAT": "تعذر تحميل المعاينة", + "SIDEBAR": { + "THUMBNAILS": { + "PAGE": "الصفحة{{ pageNum }}" + }, + "METADATA": { + "MORE_INFORMATION": "معلومات أكثر", + "LESS_INFORMATION": "معلومات أقل" + } + }, + "PDF_DIALOG": { + "SUBMIT": "إرسال", + "CLOSE": "إغلاق", + "PLACEHOLDER": "كلمة سر", + "ERROR": "كلمة السر خطأ" + } + }, + "ERROR_CONTENT": { + "UNKNOWN": { + "TITLE": "نواجه مشكلة.", + "DESCRIPTION": "يبدو أن شيئًا ما خطأ قد حدث.", + "SECONDARY_BUTTON": { + "TEXT": "" + }, + "RETURN_BUTTON": { + "TEXT": "عودة إلى الرئيسية" + } + }, + "403": { + "TITLE": "ليس لديك إذن للوصول إلى هذا الخادم.", + "DESCRIPTION": "غير مسموح لك بالوصول إلى هذا المورد على الخادم.", + "SECONDARY_BUTTON": { + "TEXT": "إبلاغ عن مشكلة" + }, + "RETURN_BUTTON": { + "TEXT": "عودة إلى الرئيسية" + } + }, + "404": { + "TITLE": "حدث خطأ.", + "DESCRIPTION": "تعذر العثور على الصفحة التي تبحث عنها.", + "SECONDARY_BUTTON": { + "TEXT": "" + }, + "RETURN_BUTTON": { + "TEXT": "عودة إلى الرئيسية" + } + } + }, + "ABOUT": { + "SERVER_SETTINGS": { + "TITLE": "إعدادات الخادم", + "DESCRIPTION": "تم أخذ القيم أدناه من AppConfigService", + "CONTENT_SERVICE_HOST": "Alfresco Content Services URL: {{ value }}", + "PROCESS_SERVICE_HOST": "Alfresco Process Services URL: {{ value }}" + }, + "VERSIONS": { + "TITLE": "إصدارات المنتج", + "CONTENT_SERVICE": "ECM", + "PROCESS_SERVICE": "BPM", + "LABELS": { + "EDITION": "الإصدار", + "VERSION": "الإصدار", + "LICENSE": "الترخيص", + "STATUS": "الحالة", + "MODULES": "وحدات" + } + }, + "SOURCE_CODE": { + "TITLE": "رمز المصدر", + "DESCRIPTION": "أنت تقوم بتشغيل المشروع استنادًا إلى الالتزام التالي:" + }, + "PACKAGES": { + "TITLE": "حزم", + "DESCRIPTION": "يستخدم المشروع الحالي مكتبات ADF التالية:" + }, + "TABLE_HEADERS": { + "MODULES": { + "ID": "المعرف", + "TITLE": "العنوان", + "DESCRIPTION": "الوصف", + "INSTALL_DATE": "تاريخ التثبيت", + "INSTALL_STATE": "حالة التثبيت", + "VERSION_MIN": "أقل إصدار", + "VERSION_MAX": "أقصى إصدار" + }, + "STATUS": { + "READ_ONLY": "للقراءة فقط", + "AUDIT_ENABLED": "تم تمكين التدقيق", + "QUICK_SHARE_ENABLED": "تم تمكين المشاركة السريعة", + "THUMBNAIL_ENABLED": "إنشاء صورة مصغرة" + }, + "LICENSE": { + "ISSUES_AT": "تم الإصدار في", + "EXPIRES_AT": "تنتهي الصلاحية في", + "REMAINING_DAYS": "الأيام المتبقية", + "HOLDER": "الحامل", + "MODE": "الوضع", + "CLUSTER_ENABLED": "تم تمكين الكتلة", + "CRYPTODOC_ENABLED": "تم تمكين مستند تشفير" + } + } + } +} \ No newline at end of file diff --git a/lib/insights/i18n/ar.json b/lib/insights/i18n/ar.json new file mode 100644 index 0000000000..62d00c322e --- /dev/null +++ b/lib/insights/i18n/ar.json @@ -0,0 +1,51 @@ +{ + "ANALYTICS": { + "TITLE": "تحليلات", + "MESSAGES": { + "UNKNOWN-WIDGET-TYPE": "نوع عنصر واجهة مستخدم غير معروف", + "FILL-PARAMETER": "اختر من خيارات التقرير", + "NO-DATA-FOUND": "لم يتم العثور على بيانات", + "ZERO-DATA-FOUND": "توجد القيم الصفرية فقط", + "ICON-SETTING": "إعدادات", + "ICON-SAVE": "حفظ", + "ICON-DELETE": "حذف", + "ICON-EXPORT-CSV": "تصدير" + } + }, + "__KEY_REPORTING": { + "DEFAULT-REPORTS": { + "PROCESS-DEFINITION-OVERVIEW": { + "GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS": "العدد الإجمالي لتعريفات العملية", + "GENERAL-TABLE-TOTAL-PROCESS-INSTANCES": "العدد الإجمالي لمثيلات العملية", + "GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES": "العدد الإجمالي لمثيلات العملية النشطة", + "GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES": "العدد الإجمالي لمثيلات العملية المكتملة" + } + } + }, + "REPORTING": { + "DEFAULT-REPORTS": { + "PROCESS-HEAT-MAP": { + "TYPE-FILTERING": "تضمين المرور عبر الخطوات مثل أحداث البدء والبوابات" + }, + "PROCESS-INSTANCES-OVERVIEW": { + "PROCESS-DEFINITION": "تعريف العملية", + "DATE-RANGE": "نطاق التاريخ", + "SLOW-PROC-INST-NUMBER": "كم عدد مثيلات العملية الأبطأ التي ينبغي عرضها؟" + }, + "TASK-OVERVIEW": { + "PROCESS-DEFINITION": "تعريف العملية", + "DATE-RANGE": "نطاق التاريخ", + "DATE-RANGE-INTERVAL": "تجميع التواريخ حسب" + }, + "TASK-SLA": { + "TASK": "مهمة", + "PROCESS-DEFINITION": "تعريف العملية", + "DATE-RANGE": "نطاق التاريخ", + "SLA-DURATION": "وقت إكمال SLA المطلوب" + } + }, + "PROCESS-STATUS": "حالة العملية", + "TASK-STATUS": "حالة المهمة" + }, + "DIAGRAMS": "مخططات" +} \ No newline at end of file diff --git a/lib/process-services-cloud/src/lib/i18n/ar.json b/lib/process-services-cloud/src/lib/i18n/ar.json new file mode 100644 index 0000000000..8c95553854 --- /dev/null +++ b/lib/process-services-cloud/src/lib/i18n/ar.json @@ -0,0 +1,195 @@ +{ + "ADF_CLOUD_PROCESS_LIST": { + "MESSAGES": { + "TITLE": "لم يتم العثور على عمليات", + "SUBTITLE": "إنشاء عملية جديدة تريد العثور عليها بسهولة لاحقًا", + "NONE": "لم يتم تحديد عامل تصفية مثيل العملية." + }, + "PROPERTIES": { + "NAME": "الاسم", + "CREATED": "تم الإنشاء", + "STATUS": "الحالة", + "START_DATE": "تاريخ البدء", + "ID": "المعرف" + }, + "ADF_CLOUD_START_PROCESS": { + "BUTTON": "بدء العملية", + "NO_PROCESS_DEFINITIONS": "لا يمكنك بدء عملية حيث لا توجد تعريفات عملية متاحة", + "FORM": { + "TITLE": "بدء العملية", + "LABEL": { + "TYPE": "تحديد عملية", + "NAME": "اسم العملية" + }, + "TYPE_PLACEHOLDER": "اختر واحدة...", + "ACTION": { + "START": "بدء العملية", + "CANCEL": "إلغاء" + } + }, + "ERROR": { + "LOAD_PROCESS_DEFS": "تعذر تحميل تعريفات العملية، تأكد أن لديك حق الوصول.", + "START": "تعذر بدء مثيل عملية جديد، تأكد أن لديك حق الوصول.", + "PROCESS_NAME_REQUIRED": "اسم العملية مطلوب", + "PROCESS_DEFINITION_REQUIRED": "تعريف العملية مطلوب", + "MAXIMUM_LENGTH": "تم تجاوز الطول، {{characters}} من الحد الأقصى للحروف." + } + } + }, + "ADF_CLOUD_TASK_LIST": { + "APPS": { + "TITLE": "لم يتم العثور على تطبيقات", + "SUBTITLE": "أنشئ تطبيقًا جديدًا تريد العثور عليه بسهولة لاحقًا" + }, + "START_TASK": { + "FORM": { + "TITLE": "بدء مهمة", + "LABEL": { + "NAME": "الاسم", + "DESCRIPTION": "الوصف", + "ATTACHFORM": "إرفاق نموذج", + "ASSIGNEE": "معين له", + "CANDIDATE_GROUP": "المجموعة المرشحة", + "FORM": "نموذج", + "DATE": "اختيار تاريخ" + }, + "ACTION": { + "START": "بدء", + "CANCEL": "إلغاء" + } + } + }, + "PROPERTIES": { + "NAME": "الاسم", + "ASSIGNEE": "معين له", + "ID": "المعرف", + "STATUS": "الحالة", + "PRIORITY": "أولوية", + "CREATED_DATE": "تاريخ الإنشاء", + "LAST_MODIFIED": "آخر تعديل", + "CREATED": "تم الإنشاء" + }, + "LIST": { + "MESSAGES": { + "TITLE": "لم يتم العثور على مهام", + "SUBTITLE": "أنشئ مهمة جديدة تريد العثور عليها بسهولة لاحقًا", + "NONE": "لم يتم العثور على قوائم مهام" + } + } + }, + "ADF_CLOUD_TASK_FILTERS": { + "MY_TASKS": "مهامي", + "COMPLETED_TASKS": "مهام مكتملة" + }, + "ADF_CLOUD_PROCESS_FILTERS": { + "ALL_PROCESSES": "جميع العمليات", + "RUNNING_PROCESSES": "عمليات قيد التشغيل", + "COMPLETED_PROCESSES": "عمليات مكتملة" + }, + "ADF_CLOUD_START_TASK": { + "ERROR": { + "MESSAGE": "أدخل قيمة مختلفة", + "REQUIRED": "حقل مطلوب", + "DATE": "تنسيق التاريخ ي‎ي/ش‎ش/س‎س‎س‎س", + "MAXIMUM_LENGTH": "تم تجاوز الطول، {{characters}} من الحد الأقصى للحروف." + } + }, + "ADF_CLOUD_EDIT_TASK_FILTER": { + "TITLE": "تخصيص عامل التصفية", + "TOOL_TIP": { + "SAVE": "حفظ عامل التصفية", + "SAVE_AS": "حفظ عامل التصفية باسم", + "DELETE": "حذف عامل التصفية" + }, + "LABEL": { + "APP_NAME": "ApplicationName", + "PROCESS_DEF_ID": "ProcessDefinitionId", + "STATUS": "الحالة", + "ASSIGNMENT": "مهمة", + "DIRECTION": "اتجاه", + "PROCESS_INSTANCE_ID": "ProcessInstanceId", + "TASK_NAME": "TaskName", + "PARENT_TASK_ID": "ParentTaskId", + "PRIORITY": "أولوية", + "STAND_ALONE": "StandAlone", + "LAST_MODIFIED_FROM": "LastModifiedFrom", + "LAST_MODIFIED_TO": "LastModifiedTo", + "OWNER": "مالك", + "SORT": "فرز" + }, + "DIALOG": { + "TITLE": "حفظ عامل التصفية باسم", + "SAVE": "حفظ", + "CANCEL": "إلغاء" + } + }, + "ADF_CLOUD_EDIT_PROCESS_FILTER": { + "TITLE": "تخصيص عامل التصفية", + "LABEL": { + "APP_NAME": "ApplicationName", + "STATUS": "الحالة", + "INITIATOR": "بادئ", + "ASSIGNMENT": "مهمة", + "SORT": "فرز", + "DIRECTION": "اتجاه", + "PROCESS_DEF_ID": "ProcessDefinitionId", + "PROCESS_DEF_KEY": "ProcessDefinitionKey", + "PROCESS_INS_ID": "ProcessInstanceId", + "START_DATE": "StartDate", + "LAST_MODIFIED": "LastModified", + "LAST_MODIFIED_DATE_FORM": "LastModifiedFrom", + "LAST_MODIFIED_TO": "LastModifiedTo", + "PROCESS_NAME": "ProcessName" + }, + "ERROR": { + "DATE": "تنسيق التاريخ ي‎ي/ش‎ش/س‎س‎س‎س" + }, + "TOOL_TIP": { + "SAVE": "حفظ عامل التصفية", + "SAVE_AS": "حفظ عامل التصفية باسم", + "DELETE": "حذف عامل التصفية" + }, + "DIALOG": { + "TITLE": "حفظ عامل التصفية باسم", + "SAVE": "حفظ", + "CANCEL": "إلغاء" + } + }, + "ADF_CLOUD_GROUPS": { + "SEARCH-GROUP": "مجموعات", + "ERROR": { + "NOT_FOUND": "لم يتم العثور على مجموعة بالاسم {{groupName}}" + } + }, + "ADF_CLOUD_TASK_HEADER": { + "BUTTON": { + "CLAIM": "مطالبة", + "UNCLAIM": "إعادة وضع في قائمة الانتظار" + }, + "PROPERTIES": { + "TASK_NAME": "مهمة", + "THUMBNAIL": "صورة مصغرة", + "DURATION": "المدة", + "PARENT_TASK_ID": "معرف المهمة الأصلية", + "NAME": "الاسم", + "ASSIGNEE": "معين له", + "ASSIGNEE_DEFAULT": "لا يوجد معين له", + "PRIORITY": "أولوية", + "DUE_DATE": "تاريخ الاستحقاق", + "DUE_DATE_DEFAULT": "لا يوجد تاريخ", + "STATUS": "الحالة", + "CATEGORY": "الفئة", + "CATEGORY_DEFAULT": "لا توجد فئة", + "PARENT_NAME": "اسم العنصر الأصلي", + "PARENT_NAME_DEFAULT": "لا يوجد عنصر أصلي", + "CREATED_BY": "تم الإنشاء بواسطة", + "CREATED": "تم الإنشاء", + "END_DATE": "تاريخ الانتهاء", + "ID": "المعرف", + "DESCRIPTION": "الوصف", + "DESCRIPTION_DEFAULT": "لا يوجد وصف", + "FORM_NAME": "اسم النموذج", + "FORM_NAME_DEFAULT": "لا يوجد نموذج" + } + } +} \ No newline at end of file diff --git a/lib/process-services/i18n/ar.json b/lib/process-services/i18n/ar.json new file mode 100644 index 0000000000..c089498c45 --- /dev/null +++ b/lib/process-services/i18n/ar.json @@ -0,0 +1,332 @@ +{ + "DIALOG": { + "SAVE_MESSAGE": "انقر فوق حفظ لإضافة تقرير بالإعدادات الحالية إلى قائمة تقاريرك.", + "EXPORT_MESSAGE": "" + }, + "DATE-WIDGET": { + "START-DATE": "تاريخ البدء", + "END-DATE": "تاريخ الانتهاء", + "MESSAGES": { + "START-DATE-REQUIRED": "تاريخ البدء مطلوب", + "START-LESS-THAN-END-DATE": "يجب أن يكون تاريخ البدء قبل تاريخ الانتهاء" + } + }, + "ADF_TASK_LIST": { + "APPS": { + "TITLE": "لم يتم العثور على تطبيقات", + "SUBTITLE": "أنشئ تطبيقًا جديدًا تريد العثور عليه بسهولة لاحقًا", + "TASK_APP_NAME": "تطبيق المهمة" + }, + "LIST": { + "MESSAGES": { + "TITLE": "لم يتم العثور على مهام", + "SUBTITLE": "أنشئ مهمة جديدة تريد العثور عليها بسهولة لاحقًا", + "NONE": "لم يتم العثور على قوائم مهام" + } + }, + "PROPERTIES": { + "TASK_NAME": "مهمة", + "THUMBNAIL": "صورة مصغرة", + "DURATION": "المدة", + "PARENT_TASK_ID": "معرف المهمة الأصلية", + "NAME": "الاسم", + "ASSIGNEE": "معين له", + "ASSIGNEE_DEFAULT": "لا يوجد معين له", + "PRIORITY": "أولوية", + "DUE_DATE": "تاريخ الاستحقاق", + "DUE_DATE_DEFAULT": "لا يوجد تاريخ", + "STATUS": "الحالة", + "CATEGORY": "الفئة", + "CATEGORY_DEFAULT": "لا توجد فئة", + "PARENT_NAME": "اسم العنصر الأصلي", + "PARENT_NAME_DEFAULT": "لا يوجد عنصر أصلي", + "CREATED_BY": "تم الإنشاء بواسطة", + "CREATED": "تم الإنشاء", + "END_DATE": "تاريخ الانتهاء", + "ID": "المعرف", + "DESCRIPTION": "الوصف", + "DESCRIPTION_DEFAULT": "لا يوجد وصف", + "FORM_NAME": "اسم النموذج", + "FORM_NAME_DEFAULT": "لا يوجد نموذج" + }, + "MENU_ACTIONS": { + "VIEW_CONTENT": "عرض", + "REMOVE_CONTENT": "إزالة", + "DOWNLOAD_CONTENT": "تنزيل", + "DOWNLOAD_AUDIT": "تنزيل التدقيق" + }, + "DETAILS": { + "LABELS": { + "INFO_DRAWER_TITLE": "أنشطة", + "INFO_DRAWER_TAB_ACTIVITY_TITLE": "نشاط", + "INFO_DRAWER_TAB_DETAILS_TITLE": "التفاصيل", + "ASSIGNEE": "معين له", + "DUE": "مستحق", + "FORM": "نموذج", + "PEOPLE": "الأشخاص هذه المهمة مشتركة مع", + "COMMENTS": "تعليقات", + "CHECKLIST": "قائمة الاختيار", + "INVOLVED_PEOPLE": "الأشخاص المشتركون", + "ADD_PEOPLE": "إضافة أشخاص ومجموعات", + "ADD_ASSIGNEE": "إضافة معين له جديد" + }, + "BUTTON": { + "COMPLETE": "تم", + "CLAIM": "مطالبة", + "UNCLAIM": "إعادة وضع في قائمة الانتظار", + "DRAG-ATTACHMENT": "إفلات الملفات لتحميلها", + "UPLOAD-ATTACHMENT": "تحميل المرفق" + }, + "MESSAGES": { + "NONE": "لم يتم العثور على تفاصيل المهمة", + "CLAIM": "انقر فوق مطالبة للعمل على هذه المهمة" + }, + "FORM": { + "NONE": "لا يوجد نموذج" + }, + "DUE": { + "NONE": "لا يوجد تاريخ استحقاق" + }, + "ASSIGNEE": { + "NONE": "لا يوجد معين له" + }, + "PEOPLE": { + "NONE": "لا يوجد شخص مشترك" + }, + "CHECKLIST": { + "NONE": "لا توجد قائمة اختيار", + "DIALOG": { + "TITLE": "اختيار جديد", + "CANCEL-BUTTON": "إلغاء", + "ADD-BUTTON": "إضافة قائمة اختيار", + "PLACEHOLDER": "الاسم" + } + }, + "ERROR": { + "TITLE": "تعذر إتمام الإجراء", + "DESCRIPTION": "حاول مرة أخرى أو تحقق من أن لديك حق الوصول.", + "CLOSE": "إغلاق" + } + }, + "FILTERS": { + "MESSAGES": { + "NONE": "لم يتم تحديد عامل تصفية للمهمة" + } + }, + "START_TASK": { + "DEFAULT_NAME": "مهمتي الافتراضية", + "BUTTON": "إنشاء مهمة", + "FORM": { + "TITLE": "بدء مهمة", + "LABEL": { + "NONE": "بلا", + "NAME": "الاسم", + "DESCRIPTION": "الوصف", + "ATTACHFORM": "إرفاق نموذج", + "ASSIGNEE": "معين له", + "FORM": "نموذج", + "DATE": "اختيار تاريخ" + }, + "ACTION": { + "START": "بدء", + "CANCEL": "إلغاء" + }, + "ERROR": { + "REQUIRED": "حقل مطلوب", + "DATE": "تنسيق التاريخ ي‎ي/ش‎ش/س‎س‎س‎س", + "MAXIMUM_LENGTH": "تم تجاوز الطول، {{characters}} من الحد الأقصى للحروف." + } + } + }, + "PEOPLE": { + "ASSIGNEE": "معين له", + "DIALOG_CLOSE": "إغلاق", + "ADD_USER": "إضافة", + "ADD_ASSIGNEE": "تعيين", + "SEARCH_USER": "بحث عن مستخدم", + "SEARCH": { + "NO_USERS": "لم يتم العثور على شخص للاشتراك" + } + }, + "ATTACHMENT": { + "EMPTY": { + "HEADER": "هذه القائمة فارغة", + "DRAG-AND-DROP": { + "TITLE": "سحب وإفلات", + "SUBTITLE": "لتحميل الملفات" + } + }, + "EMPTY-LIST": { + "HEADER": "لا توجد ملفات متاحة" + } + }, + "STANDALONE_TASK": { + "NO_FORM_MESSAGE": "لا توجد نماذج مرفقة", + "COMPLETE_TASK_MESSAGE": "اكتملت المهمة {{taskName}}", + "COMPLETE_TASK_SUB_MESSAGE": "لا توجد نماذج لإضافتها" + }, + "ATTACH_FORM": { + "SELECT_FORM": "حدد نموذجًا لإرفاقه", + "REMOVE_FORM": "إزالة النموذج", + "SELECT_OPTION": "حدد خيارًا" + } + }, + "ADF_PROCESS_LIST": { + "LIST": { + "TITLE": "لم يتم العثور على عمليات", + "SUBTITLE": "إنشاء عملية جديدة تريد العثور عليها بسهولة لاحقًا", + "SUMMARY": "تم العثور على {{total}} من مثيلات العملية", + "ERROR": "تعذر تحميل مثيلات العمليات. حاول مرة أخرى أو شارك الرسالة التالية مع فريق تكنولوجيا المعلومات لديك: {{errorMessage}}" + }, + "FILTERS": { + "MESSAGES": { + "NONE": "لم يتم تحديد عامل تصفية مثيل العملية." + } + }, + "PROPERTIES": { + "PROCESS_NAME": "العملية", + "NAME": "الاسم", + "THUMBNAIL": "صورة مصغرة", + "STATUS": "الحالة", + "END_DATE": "تاريخ الانتهاء", + "END_DATE_DEFAULT": "لا يوجد تاريخ", + "CATEGORY": "الفئة", + "CATEGORY_DEFAULT": "لا توجد فئة", + "CREATED_BY": "تم الإنشاء بواسطة", + "CREATED_BY_DEFAULT": "لا يوجد معين له", + "CREATED": "تم الإنشاء", + "BUSINESS_KEY": "مفتاح أعمال", + "BUSINESS_KEY_DEFAULT": "بلا", + "DESCRIPTION": "الوصف", + "DESCRIPTION_DEFAULT": "لا يوجد وصف", + "ID": "المعرف" + }, + "MENU_ACTIONS": { + "VIEW_CONTENT": "عرض", + "REMOVE_CONTENT": "إزالة", + "DOWNLOAD_CONTENT": "تنزيل", + "DOWNLOAD_AUDIT": "تنزيل التدقيق", + "VIEW_TASK": "عرض المهمة" + }, + "DETAILS": { + "LABELS": { + "STARTED_BY": "بدأ حسب", + "STARTED": "بدأ", + "ENDED": "انتهى", + "COMMENTS": "تعليقات", + "START_FORM": "نموذج البدء", + "TASKS_ACTIVE": "مهام نشطة", + "TASKS_COMPLETED": "مهام مكتملة", + "TASK_SUBTITLE": "تم التعيين إلى {{user}}، تم إنشاء {{created}}" + }, + "BUTTON": { + "CANCEL": "إلغاء العملية", + "CLOSE": "إغلاق", + "SHOW_DIAGRAM": "إظهار المخطط", + "DRAG-ATTACHMENT": "إفلات الملفات لتحميلها", + "UPLOAD-ATTACHMENT": "تحميل المرفق" + }, + "MESSAGES": { + "NONE": "لم يتم العثور على تفاصيل العملية" + }, + "TASKS": { + "NO_ACTIVE": "لا توجد مهام نشطة حاليًا", + "NO_COMPLETED": "لم تكتمل أي مهام بعد", + "TASK_DETAILS": "تفاصيل المهمة", + "TASK_CLOSE": "إغلاق" + }, + "COMMENTS": { + "ADD": "إضافة تعليق", + "HEADER": "التعليقات ({{ count }})", + "NONE": "لا تعليقات", + "BUTTON": { + "ADD": "إضافة تعليق" + }, + "ADD_DIALOG": { + "TITLE": "تعليق جديد", + "LABEL": { + "MESSAGE": "رسالة" + }, + "BUTTON": { + "ADD": "إضافة تعليق", + "CANCEL": "إلغاء" + } + } + }, + "ADD_DIALOG": { + "TITLE": "تعيين متغير العملية", + "LABEL": { + "NAME": "الاسم", + "VALUE": "القيمة", + "SCOPE": "نطاق" + } + }, + "EDIT_DIALOG": { + "TITLE": "تحرير متغير العملية" + }, + "ERROR_DIALOG": { + "TITLE": "تعذر إتمام الإجراء", + "DESCRIPTION": "قد لا يكون لديك مستوى الوصول المطلوب، تحقق الأمر مع فريق تكنولوجيا المعلومات لديك." + } + }, + "START_PROCESS": { + "BUTTON": "بدء العملية", + "NO_PROCESS_DEFINITIONS": "لا يمكنك بدء عملية حيث لا توجد تعريفات عملية متاحة", + "FORM": { + "TITLE": "بدء العملية", + "LABEL": { + "TYPE": "تحديد عملية", + "NAME": "اسم العملية" + }, + "TYPE_PLACEHOLDER": "اختر واحدة...", + "ACTION": { + "START": "بدء العملية", + "CANCEL": "إلغاء" + } + }, + "ERROR": { + "LOAD_PROCESS_DEFS": "تعذر تحميل تعريفات العملية، تأكد أن لديك حق الوصول.", + "START": "تعذر بدء مثيل عملية جديد، تأكد أن لديك حق الوصول.", + "MAXIMUM_LENGTH": "تم تجاوز الطول، {{characters}} من الحد الأقصى للحروف." + } + }, + "PROCESS-ATTACHMENT": { + "EMPTY": { + "HEADER": "هذه القائمة فارغة", + "DRAG-AND-DROP": { + "TITLE": "سحب وإفلات", + "SUBTITLE": "لتحميل الملفات" + } + }, + "EMPTY-LIST": { + "HEADER": "لا توجد ملفات متاحة" + }, + "COLUMNS": { + "NAME": "الاسم", + "CREATED-ON": "تم الإنشاء في" + } + } + }, + "ADF_SIDEBAR_ACTION_MENU": { + "BUTTON": { + "CREATE": "إنشاء", + "NEW_TASK": "مهمة جديدة", + "NEW_PROCESS": "عملية جديدة" + } + }, + "APP": { + "DIALOG": { + "START": "متابعة", + "TITLE": "تحديد تطبيق عملية", + "LIST": "قائمة تطبيقات العمليات", + "ERROR": "توجد مشكلة في الاتصال Process Services" + } + }, + "ATTACH-FILE": { + "ACTIONS": { + "LOGIN": "دخول", + "CANCEL": "إلغاء", + "CHOOSE": "اختيار" + } + } +} \ No newline at end of file From aba5674e80a757d034e8159f62d2f9ad8bf9cf43 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Wed, 6 Mar 2019 09:53:43 +0000 Subject: [PATCH 078/223] [ADF-3735] SSO Role guard and Login error improvement (#4377) * fix lint and doc * Update auth-guard-sso-role.service.md * Update auth-guard-sso-role.service.md * fix json en * restore en.json file --- demo-shell/resources/i18n/en.json | 8 +- demo-shell/src/app/app.routes.ts | 8 +- docs/core/README.md | 3 +- docs/core/auth-guard-sso-role.service.md | 57 +++++++++ lib/core/i18n/en.json | 3 +- .../login/components/login.component.html | 17 +-- .../login/components/login.component.spec.ts | 117 ++++++++++------- lib/core/login/components/login.component.ts | 34 +++-- .../auth-guard-sso-role.service.spec.ts | 119 ++++++++++++++++++ .../services/auth-guard-sso-role.service.ts | 82 ++++++++++++ lib/core/services/authentication.service.ts | 7 ++ lib/core/services/public-api.ts | 1 + 12 files changed, 387 insertions(+), 69 deletions(-) create mode 100644 docs/core/auth-guard-sso-role.service.md create mode 100644 lib/core/services/auth-guard-sso-role.service.spec.ts create mode 100644 lib/core/services/auth-guard-sso-role.service.ts diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index f6aa850fcd..dca6df9a7e 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -311,9 +311,9 @@ } }, "SETTINGS_CLOUD": { - "MULTISELECTION": "Multiselection", - "TESTING_MODE": "Testing Mode", - "SELECTION_MODE": "Selection Mode", - "TASK_DETAILS_REDIRECTION": "Display task details on task click" + "MULTISELECTION": "Multiselection", + "TESTING_MODE": "Testing Mode", + "SELECTION_MODE": "Selection Mode", + "TASK_DETAILS_REDIRECTION": "Display task details on task click" } } diff --git a/demo-shell/src/app/app.routes.ts b/demo-shell/src/app/app.routes.ts index 95f89b1ffa..99e0abbf53 100644 --- a/demo-shell/src/app/app.routes.ts +++ b/demo-shell/src/app/app.routes.ts @@ -17,7 +17,7 @@ import { ModuleWithProviders } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AuthGuard, AuthGuardEcm, ErrorContentComponent, AuthGuardBpm } from '@alfresco/adf-core'; +import { AuthGuard, AuthGuardEcm, ErrorContentComponent, AuthGuardBpm, AuthGuardSsoRoleService } from '@alfresco/adf-core'; import { AppLayoutComponent } from './components/app-layout/app-layout.component'; import { LoginComponent } from './components/login/login.component'; import { HomeComponent } from './components/home/home.component'; @@ -143,6 +143,8 @@ export const appRoutes: Routes = [ }, { path: 'cloud', + canActivate: [AuthGuardSsoRoleService], + data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403'}, children: [ { path: '', @@ -359,6 +361,10 @@ export const appRoutes: Routes = [ path: 'error/:id', component: ErrorContentComponent }, + { + path: 'error/no-authorization', + component: ErrorContentComponent + }, { path: '**', redirectTo: 'error/404' diff --git a/docs/core/README.md b/docs/core/README.md index 251f8282b0..25862ad747 100644 --- a/docs/core/README.md +++ b/docs/core/README.md @@ -103,7 +103,8 @@ for more information about installing and using the source code. | [Apps process service](apps-process.service.md) | Gets details of the Process Services apps that are deployed for the user. | [Source](../../lib/core/services/apps-process.service.ts) | | [Auth guard bpm service](auth-guard-bpm.service.md) | Adds authentication with Process Services to a route within the app. | [Source](../../lib/core/services/auth-guard-bpm.service.ts) | | [Auth guard ecm service](auth-guard-ecm.service.md) | Adds authentication with Content Services to a route within the app. | [Source](../../lib/core/services/auth-guard-ecm.service.ts) | -| [Auth guard service](auth-guard.service.md) | Adds authentication to a route within the app. | [Source](../../lib/core/services/auth-guard.service.ts) | +| [Auth guard service](auth-guard.service.md) | Adds authentication to a route within the app. | [Source](../../lib/core/services/auth-guard.service.ts) +| [Auth guard SSO Role service](auth-guard-sso-role.service.md) | check the roles on a user | [Source](../../lib/core/services/auth-guard-sso-role.service.ts) | | [Authentication service](authentication.service.md) | Provides authentication to ACS and APS. | [Source](../../lib/core/services/authentication.service.ts) | | [Comment content service](comment-content.service.md) | Adds and retrieves comments for nodes in Content Services. | [Source](../../lib/core/services/comment-content.service.ts) | | [Comment process service](comment-process.service.md) | Adds and retrieves comments for task and process instances in Process Services. | [Source](../../lib/core/services/comment-process.service.ts) | diff --git a/docs/core/auth-guard-sso-role.service.md b/docs/core/auth-guard-sso-role.service.md new file mode 100644 index 0000000000..7669f975c7 --- /dev/null +++ b/docs/core/auth-guard-sso-role.service.md @@ -0,0 +1,57 @@ +--- +Title: Auth Guard SSO Role service +Added: v3.1.0 +Status: Active +--- + +# [Auth Guard SSO role service](../../lib/core/services/auth-guard-sso-role.service.ts "Defined in auth-guard-sso-role.service.ts") + +Allow to check the user roles of a user + +## Details + +The Auth Guard SSO role service implements an Angular +[route guard](https://angular.io/guide/router#milestone-5-route-guards) +to check the user has the right role permission. This is typically used with the +`canActivate` guard check in the route definition. The roles that user needs to have in order to access the route has to be specified in the roles array as in the example below: + + +```ts +const appRoutes: Routes = [ + ... + { + path: 'examplepath', + component: ExampleComponent, + canActivate: [ AuthGuardSsoRoleService ], + data: { roles: ['USER_ROLE1', 'USER_ROLE2']} + }, + ... +] +``` + +If the user now clicks on a link or button that follows this route, they will be not able to access to this content if the user does not have the roles. + +## Redirect over forbidden + +If the you want to redirect the user to a different page over a forbidden error you can use the **redirectUrl** as the example below: + +```ts +const appRoutes: Routes = [ + ... + { + path: 'examplepath', + component: ExampleComponent, + canActivate: [ AuthGuardSsoRoleService ], + data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403'} + }, + ... +] +``` + +Note: you can use this Guard in and with the other ADF auth guard. + +## See also + +- [Auth guard ecm service](auth-guard-ecm.service.md) +- [Auth guard bpm service](auth-guard-bpm.service.md) +- [Auth guard service](auth-guard.service.md) diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index 7597a6c54a..243c197156 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -210,7 +210,8 @@ "LOGIN-ERROR-PROVIDERS": "Providers cannot be undefined", "LOGIN-ERROR-CORS": "CORS exception, check your server configuration", "LOGIN-ERROR-CSRF": "CSRF exception, set [disableCsrf]=\"true\" in login.component", - "LOGIN-ECM-LICENSE": "Alfresco Content Services repository is in read-only mode" + "LOGIN-ECM-LICENSE": "Alfresco Content Services repository is in read-only mode", + "SSO-WRONG-CONFIGURATION": "SSO Authentication server unreachable" }, "BUTTON": { "LOGIN": "SIGN IN", diff --git a/lib/core/login/components/login.component.html b/lib/core/login/components/login.component.html index 71bb7023b8..4e9ee9bb99 100644 --- a/lib/core/login/components/login.component.html +++ b/lib/core/login/components/login.component.html @@ -21,15 +21,16 @@