mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-5305] - Added aspect list component (#6549)
* [ADF-5305] - Creation of aspect list and aspect list dialog components * [ADF-5305] - unit test for aspect list * [ADF-5305] - added filtering for aspects * [ADF-5305] - enabling tests * [ADF-5305] - added filtering and unit test * [ADF-5305] - added context action to demo shell * [ADF-5305] - added button on metadata card for opening aspects * [ADF-5305] - fixed unit test for filtering aspects * [ADF-5305] - added documentation * [ADF-5305] - fixed lint * [ADF-5305] - Updated the js-api calls * [ADF-5305] - Removed circle dependency * [ADF-5305] - Simplified code * [ADF-5305] - revert changes on package.json * [ADF-5305] - removed extra cspell word * [ADF-5305] - added filtering on aspect list service * [ADF-5305] - fix unit test for aspect service * [ADF-5305] - reverted changes to package-loc * [ADF-5305] - removed unused changes * [ADF-5305] - attempt to fix PR #§ * [ADF-5305] - attempt to fix PR #2 * [ADF-5305] - attempt to fix PR #3 * [ADF-5305] - attempt to fix PR #4 * [ADF-5305] - attempt to fix PR #5 Co-authored-by: Vito Albano <vitoalbano@vitoalbano-mbp-0120.local>
This commit is contained in:
@@ -94,6 +94,7 @@
|
|||||||
"SEARCH_SERVICE_APPROACH": "Check this to disable the input property and configure using the service",
|
"SEARCH_SERVICE_APPROACH": "Check this to disable the input property and configure using the service",
|
||||||
"HEADER_DATA": "Header Data",
|
"HEADER_DATA": "Header Data",
|
||||||
"TREE_VIEW": "Tree View",
|
"TREE_VIEW": "Tree View",
|
||||||
|
"EXPAND_LIST": "Expandable item list",
|
||||||
"ICONS": "Icons",
|
"ICONS": "Icons",
|
||||||
"PEOPLE_GROUPS_CLOUD": "People/Group Cloud",
|
"PEOPLE_GROUPS_CLOUD": "People/Group Cloud",
|
||||||
"TASK_HEADER_CLOUD": {
|
"TASK_HEADER_CLOUD": {
|
||||||
@@ -166,6 +167,7 @@
|
|||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"VERSIONS": "Manage versions",
|
"VERSIONS": "Manage versions",
|
||||||
|
"ASPECTS": "Update Aspects",
|
||||||
"LOCK": "Lock",
|
"LOCK": "Lock",
|
||||||
"METADATA": "Info",
|
"METADATA": "Info",
|
||||||
"DOWNLOAD": "Download",
|
"DOWNLOAD": "Download",
|
||||||
|
@@ -1197,5 +1197,13 @@
|
|||||||
{
|
{
|
||||||
"name": "subprocessapp"
|
"name": "subprocessapp"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"aspect-visible": {
|
||||||
|
"default" : ["cm:generalclassifiable", "cm:complianceable",
|
||||||
|
"cm:dublincore", "cm:effectivity", "cm:summarizable",
|
||||||
|
"cm:versionable", "cm:templatable","cm:emailed", "emailserver:aliasable",
|
||||||
|
"cm:taggable", "app:inlineeditable", "cm:geographic", "exif:exif",
|
||||||
|
"audio:audio", "cm:indexControl", "dp:restrictable", "smf:customConfigSmartFolder", "smf:systemConfigSmartFolder"],
|
||||||
|
"ai": ["ai:products", "ai:dates", "ai:places", "ai:events", "ai:organizations", "ai:people", "ai:things", "ai:quantities", "ai:creativeWorks", "ai:labels", "ai:textLines"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -93,6 +93,7 @@ import {
|
|||||||
CustomEditorComponent,
|
CustomEditorComponent,
|
||||||
CustomWidgetComponent
|
CustomWidgetComponent
|
||||||
} from './components/cloud/custom-form-components/custom-editor.component';
|
} from './components/cloud/custom-form-components/custom-editor.component';
|
||||||
|
import { AspectListSampleComponent } from './components/aspect-list-sample/aspect-list-sample.component';
|
||||||
|
|
||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from '@angular/common';
|
||||||
import localeFr from '@angular/common/locales/fr';
|
import localeFr from '@angular/common/locales/fr';
|
||||||
@@ -180,6 +181,7 @@ registerLocaleData(localeSv);
|
|||||||
DemoErrorComponent,
|
DemoErrorComponent,
|
||||||
FormLoadingComponent,
|
FormLoadingComponent,
|
||||||
TreeViewSampleComponent,
|
TreeViewSampleComponent,
|
||||||
|
AspectListSampleComponent,
|
||||||
CloudLayoutComponent,
|
CloudLayoutComponent,
|
||||||
AppsCloudDemoComponent,
|
AppsCloudDemoComponent,
|
||||||
TasksCloudDemoComponent,
|
TasksCloudDemoComponent,
|
||||||
|
@@ -54,6 +54,7 @@ import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-clo
|
|||||||
import { FilteredSearchComponent } from './components/files/filtered-search.component';
|
import { FilteredSearchComponent } from './components/files/filtered-search.component';
|
||||||
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.component';
|
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.component';
|
||||||
import { ServiceTaskListCloudDemoComponent } from './components/cloud/service-task-list-cloud-demo.component';
|
import { ServiceTaskListCloudDemoComponent } from './components/cloud/service-task-list-cloud-demo.component';
|
||||||
|
import { AspectListSampleComponent } from './components/aspect-list-sample/aspect-list-sample.component';
|
||||||
|
|
||||||
export const appRoutes: Routes = [
|
export const appRoutes: Routes = [
|
||||||
{ path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.AppLoginModule) },
|
{ path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.AppLoginModule) },
|
||||||
@@ -413,6 +414,11 @@ export const appRoutes: Routes = [
|
|||||||
component: TreeViewSampleComponent,
|
component: TreeViewSampleComponent,
|
||||||
canActivate: [AuthGuardEcm]
|
canActivate: [AuthGuardEcm]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'expandable-list',
|
||||||
|
component: AspectListSampleComponent,
|
||||||
|
canActivate: [AuthGuardEcm]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'about',
|
path: 'about',
|
||||||
loadChildren: () => import('./components/about/about.module').then(m => m.AppAboutModule)
|
loadChildren: () => import('./components/about/about.module').then(m => m.AppAboutModule)
|
||||||
|
@@ -86,6 +86,7 @@ export class AppLayoutComponent implements OnInit, OnDestroy {
|
|||||||
/* cspell:disable-next-line */
|
/* cspell:disable-next-line */
|
||||||
{ href: '/overlay-viewer', icon: 'pageview', title: 'APP_LAYOUT.OVERLAY_VIEWER' },
|
{ href: '/overlay-viewer', icon: 'pageview', title: 'APP_LAYOUT.OVERLAY_VIEWER' },
|
||||||
{ href: '/treeview', icon: 'nature', title: 'APP_LAYOUT.TREE_VIEW' },
|
{ href: '/treeview', icon: 'nature', title: 'APP_LAYOUT.TREE_VIEW' },
|
||||||
|
{ href: '/expandable-list', icon: 'hot_tub', title: 'APP_LAYOUT.EXPAND_LIST' },
|
||||||
{ href: '/icons', icon: 'tag_faces', title: 'APP_LAYOUT.ICONS' },
|
{ href: '/icons', icon: 'tag_faces', title: 'APP_LAYOUT.ICONS' },
|
||||||
{ href: '/about', icon: 'info_outline', title: 'APP_LAYOUT.ABOUT' }
|
{ href: '/about', icon: 'info_outline', title: 'APP_LAYOUT.ABOUT' }
|
||||||
];
|
];
|
||||||
|
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="example-button-container">
|
||||||
|
<p> ASPECT CHOSEN :</p>
|
||||||
|
<p>{{currentResult}}</p>
|
||||||
|
<br>
|
||||||
|
<mat-form-field class="example-almost-full-width">
|
||||||
|
<mat-label>Node Id For Aspects</mat-label>
|
||||||
|
<input matInput placeholder="Node Id" [(ngModel)]="currentNodeId">
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button color="primary" aria-label="Click to show the list" (click)="showAspectForNode()">
|
||||||
|
Show/Hide List
|
||||||
|
</button>
|
||||||
|
<adf-aspect-list [nodeId]="currentNodeId" *ngIf="isShowed" (valueChanged)="onValueChanged($event)"></adf-aspect-list>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button mat-fab color="primary" aria-label="Open dialog" (click)="openAspectDialog()">
|
||||||
|
Dialog
|
||||||
|
</button>
|
||||||
|
</div>
|
@@ -0,0 +1,7 @@
|
|||||||
|
.example-button-container {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-almost-full-width {
|
||||||
|
width: 70%;
|
||||||
|
}
|
@@ -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 { AspectListService } from '@alfresco/adf-content-services';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-expandable-menu',
|
||||||
|
templateUrl: 'aspect-list-sample.component.html',
|
||||||
|
styleUrls: ['aspect-list-sample.component.scss']
|
||||||
|
})
|
||||||
|
export class AspectListSampleComponent {
|
||||||
|
|
||||||
|
currentNodeId: string = '';
|
||||||
|
isShowed: boolean = false;
|
||||||
|
|
||||||
|
currentResult: string[] = [];
|
||||||
|
|
||||||
|
constructor(private aspectListService: AspectListService) { }
|
||||||
|
|
||||||
|
showAspectForNode() {
|
||||||
|
this.isShowed = !this.isShowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
openAspectDialog() {
|
||||||
|
this.aspectListService.openAspectListDialog(this.currentNodeId).subscribe((result) => this.currentResult = Array.from(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChanged(aspects) {
|
||||||
|
this.currentResult = Array.from(aspects);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -437,6 +437,12 @@
|
|||||||
handler="lock"
|
handler="lock"
|
||||||
title="DOCUMENT_LIST.ACTIONS.LOCK">
|
title="DOCUMENT_LIST.ACTIONS.LOCK">
|
||||||
</content-action>
|
</content-action>
|
||||||
|
<content-action
|
||||||
|
icon="beach_access"
|
||||||
|
target="document"
|
||||||
|
title="DOCUMENT_LIST.ACTIONS.ASPECTS"
|
||||||
|
(execute)="onAspectUpdate($event)">
|
||||||
|
</content-action>
|
||||||
</content-actions>
|
</content-actions>
|
||||||
</adf-document-list>
|
</adf-document-list>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -26,7 +26,8 @@ import {
|
|||||||
PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent,
|
PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent,
|
||||||
SharedLinksApiService,
|
SharedLinksApiService,
|
||||||
FormRenderingService,
|
FormRenderingService,
|
||||||
FileUploadEvent
|
FileUploadEvent,
|
||||||
|
NodesApiService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -36,7 +37,8 @@ import {
|
|||||||
ConfirmDialogComponent,
|
ConfirmDialogComponent,
|
||||||
LibraryDialogComponent,
|
LibraryDialogComponent,
|
||||||
ContentMetadataService,
|
ContentMetadataService,
|
||||||
FilterSearch
|
FilterSearch,
|
||||||
|
AspectListService
|
||||||
} from '@alfresco/adf-content-services';
|
} from '@alfresco/adf-content-services';
|
||||||
|
|
||||||
import { SelectAppsDialogComponent, ProcessFormRenderingService } from '@alfresco/adf-process-services';
|
import { SelectAppsDialogComponent, ProcessFormRenderingService } from '@alfresco/adf-process-services';
|
||||||
@@ -228,7 +230,9 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
public authenticationService: AuthenticationService,
|
public authenticationService: AuthenticationService,
|
||||||
public alfrescoApiService: AlfrescoApiService,
|
public alfrescoApiService: AlfrescoApiService,
|
||||||
private contentMetadataService: ContentMetadataService,
|
private contentMetadataService: ContentMetadataService,
|
||||||
private sharedLinksApiService: SharedLinksApiService) {
|
private sharedLinksApiService: SharedLinksApiService,
|
||||||
|
private aspectListService: AspectListService,
|
||||||
|
private nodeService: NodesApiService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
showFile(event) {
|
showFile(event) {
|
||||||
@@ -467,6 +471,14 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAspectUpdate(event: any) {
|
||||||
|
this.aspectListService.openAspectListDialog(event.value.entry.id).subscribe((aspectList) => {
|
||||||
|
this.nodeService.updateNode(event.value.entry.id, {aspectNames : [...aspectList]}).subscribe(() => {
|
||||||
|
this.openSnackMessageInfo('Node Aspects Updated');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onManageMetadata(event: any) {
|
onManageMetadata(event: any) {
|
||||||
const contentEntry = event.value.entry;
|
const contentEntry = event.value.entry;
|
||||||
const displayEmptyMetadata = this.displayEmptyMetadata;
|
const displayEmptyMetadata = this.displayEmptyMetadata;
|
||||||
|
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
Title: Aspect List Dialog component
|
||||||
|
Added: v2.0.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2021-01-20
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Aspect List Dialog component](../../../lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts "Defined in aspect-list-dialog.component.ts")
|
||||||
|
|
||||||
|
Allows a user to choose aspects for a node.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The [Aspect List Dialog component](aspect-list-dialog.component.md) works as a dialog showing the list of aspects available.
|
||||||
|
It is possible to filter the aspect showed via the app.config.json.
|
||||||
|
### Showing the dialog
|
||||||
|
|
||||||
|
Unlike most components, the [Aspect List Dialog component](aspect-list-dialog.component.md) is typically shown in a dialog box
|
||||||
|
rather than the main page and you are responsible for opening the dialog yourself. You can use the
|
||||||
|
[Angular Material Dialog](https://material.angular.io/components/dialog/overview) for this,
|
||||||
|
as shown in the usage example. ADF provides the [`AspectListDialogComponentData`](../../../lib/content-services/src/lib/aspect-list/aspect-list-dialog-data.interface.ts) interface
|
||||||
|
to work with the Dialog's
|
||||||
|
[data option](https://material.angular.io/components/dialog/overview#sharing-data-with-the-dialog-component-):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface AspectListDialogComponentData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
overTableMessage: string;
|
||||||
|
select: Subject<string[]>;
|
||||||
|
nodeId?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The properties are described in the table below:
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| title | `string` | "" | Dialog title |
|
||||||
|
| description | `string` | "" | Text to appear as description under the dialog title |
|
||||||
|
| overTableMessage | `string` | "" | Text that will be showed on the top of the aspect list table |
|
||||||
|
| select | [`Subject<Node>`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md) | | Event emitted with the current node selection when the dialog closes |
|
||||||
|
| nodeId | `string` | "" | Identifier of a node to apply aspects to. |
|
||||||
|
|
||||||
|
If you don't want to manage the dialog yourself then it is easier to use the
|
||||||
|
[Aspect List component](aspect-list.component.md), or the
|
||||||
|
methods of the [Aspect List service](../services/aspect-list.service.md), which create
|
||||||
|
the dialog for you.
|
||||||
|
|
||||||
|
### Usage example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { AspectListDialogComponentData, AspectListDialogComponent} from '@adf/content-services'
|
||||||
|
import { Subject } from 'rxjs/Subject';
|
||||||
|
...
|
||||||
|
|
||||||
|
constructor(dialog: MatDialog ... ) {}
|
||||||
|
|
||||||
|
openSelectorDialog() {
|
||||||
|
data: AspectListDialogComponentData = {
|
||||||
|
title: "Choose an item",
|
||||||
|
description: "Choose",
|
||||||
|
overTableMessage: "Over Table Message",
|
||||||
|
nodeId: currentNodeID,
|
||||||
|
select: new Subject<Node[]>()
|
||||||
|
};
|
||||||
|
|
||||||
|
this.dialog.open(
|
||||||
|
AspectListDialogComponent,
|
||||||
|
{
|
||||||
|
data, panelClass: 'adf-aspect-list-dialog',
|
||||||
|
width: '630px'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
data.select.subscribe((selections: Node[]) => {
|
||||||
|
// Use or store selection...
|
||||||
|
},
|
||||||
|
(error)=>{
|
||||||
|
//your error handling
|
||||||
|
},
|
||||||
|
()=>{
|
||||||
|
//action called when an action or cancel is clicked on the dialog
|
||||||
|
this.dialog.closeAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All the results will be streamed to the select [subject](http://reactivex.io/rxjs/manual/overview.html#subject) present in the [`AspectListDialogComponentData`](../../../lib/content-services/src/lib/aspect-list/aspect-list-dialog-data.interface.ts) object passed to the dialog.
|
||||||
|
When the dialog action is selected by clicking, the `data.select` stream will be completed.
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Aspect list component](aspect-list.component.md)
|
44
docs/content-services/components/aspect-list.component.md
Normal file
44
docs/content-services/components/aspect-list.component.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
Title: Aspect List component
|
||||||
|
Added: v2.0.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2021-01-20
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Aspect List component](../../../lib/content-services/src/lib/aspect-list/aspect-list.component.ts "Defined in aspect-list.component.ts")
|
||||||
|
|
||||||
|
This component will show in an expandable row list with checkboxes all the aspect of a node, if a node id is given, or otherwise a complete list.
|
||||||
|
The aspect are filtered via the app.config.json in this way :
|
||||||
|
|
||||||
|
```json
|
||||||
|
"aspect-visible": {
|
||||||
|
"default" : ["as:aspectThatWillBeShowedIfPresent"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-aspect-list (valueChanged)="onValueChanged($event)" [nodeId]="nodeId">
|
||||||
|
</adf-aspect-list>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| nodeId | `string` | | Identifier of a node to apply likes to. |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| valueChanged | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted every time the user select a new aspect. |
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
* [Aspect List Dialog component](rating.component.md)
|
||||||
|
* [Aspect List service](../services/rating.service.md)
|
||||||
|
* [Node Aspect service](../services/rating.service.md)
|
@@ -0,0 +1,26 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
export interface AspectListDialogComponentData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
overTableMessage: string;
|
||||||
|
select: Subject<string[]>;
|
||||||
|
nodeId?: string;
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
<header mat-dialog-title data-automation-id="aspect-list-dialog-title" class="adf-aspect-list-dialog">
|
||||||
|
<h4 class="adf-aspect-list-dialog-title">{{title | translate}}</h4>
|
||||||
|
<div class="adf-aspect-list-dialog-description">{{description | translate}}</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="adf-aspect-list-dialog-information">
|
||||||
|
<p id="aspect-list-dialog-over-table-message">{{overTableMessage | translate}}</p>
|
||||||
|
<p id="aspect-list-dialog-counter">{{currentAspectSelection ? currentAspectSelection.length : 0}}
|
||||||
|
{{'ADF-ASPECT-LIST.DIALOG.SELECTED' | translate}}</p>
|
||||||
|
</div>
|
||||||
|
<mat-dialog-content class="adf-aspect-dialog-content">
|
||||||
|
<adf-aspect-list #aspectList (valueChanged)="onValueChanged($event)" [nodeId]="currentNodeId">
|
||||||
|
</adf-aspect-list>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<div>
|
||||||
|
<button mat-button (click)="aspectList.reset()" id="aspect-list-dialog-actions-reset"
|
||||||
|
data-automation-id="aspect-list-dialog-actions-reset">{{
|
||||||
|
'ADF-ASPECT-LIST.DIALOG.RESET' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-button (click)="aspectList.clear()" id="aspect-list-dialog-actions-clear"
|
||||||
|
data-automation-id="aspect-list-dialog-actions-clear">{{
|
||||||
|
'ADF-ASPECT-LIST.DIALOG.CLEAR' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button mat-button (click)="onCancel()" id="aspect-list-dialog-actions-cancel"
|
||||||
|
data-automation-id="aspect-list-dialog-actions-cancel">{{
|
||||||
|
'ADF-ASPECT-LIST.DIALOG.CANCEL' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-button (click)="onApply()" id="aspect-list-dialog-actions-apply"
|
||||||
|
data-automation-id="aspect-list-dialog-actions-apply">{{
|
||||||
|
'ADF-ASPECT-LIST.DIALOG.APPLY' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-dialog-actions>
|
@@ -0,0 +1,55 @@
|
|||||||
|
@mixin adf-aspect-list-dialog-theme($theme) {
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
$warn: map-get($theme, warn);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
|
||||||
|
.adf {
|
||||||
|
|
||||||
|
&-aspect-list-dialog-title {
|
||||||
|
font-size: large;
|
||||||
|
font-weight: 200;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aspect-list-dialog-description {
|
||||||
|
font-size: small;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aspect-list-dialog-information {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aspect-list-dialog {
|
||||||
|
.mat-dialog-actions {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aspect-dialog-content {
|
||||||
|
padding-top: 3px;
|
||||||
|
|
||||||
|
.adf-aspect-property-table {
|
||||||
|
|
||||||
|
.mat-cell {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-name {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,272 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { AspectListDialogComponent } from './aspect-list-dialog.component';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of, Subject } from 'rxjs';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface';
|
||||||
|
import { NodesApiService } from 'core';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
import { delay } from 'rxjs/operators';
|
||||||
|
import { AspectEntry } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
const aspectListMock: AspectEntry[] = [{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:aspectZero',
|
||||||
|
id: 'frs:AspectOne',
|
||||||
|
description: 'First Aspect with random description',
|
||||||
|
title: 'FirstAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'channelPassword',
|
||||||
|
title: 'The authenticated channel password',
|
||||||
|
dataType: 'd:encrypted'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'channelUsername',
|
||||||
|
title: 'The authenticated channel username',
|
||||||
|
dataType: 'd:encrypted'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:AspectZer',
|
||||||
|
id: 'frs:SecondAspect',
|
||||||
|
description: 'Second Aspect description',
|
||||||
|
title: 'SecondAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'assetId',
|
||||||
|
title: 'Published Asset Id',
|
||||||
|
dataType: 'd:text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'assetUrl',
|
||||||
|
title: 'Published Asset URL',
|
||||||
|
dataType: 'd:text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
describe('AspectListDialogComponent', () => {
|
||||||
|
let fixture: ComponentFixture<AspectListDialogComponent>;
|
||||||
|
let aspectListService: AspectListService;
|
||||||
|
let nodeService: NodesApiService;
|
||||||
|
let data: AspectListDialogComponentData;
|
||||||
|
|
||||||
|
describe('Without passing node id', () => {
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
data = <AspectListDialogComponentData> {
|
||||||
|
title: 'Title',
|
||||||
|
description: 'Description that can be longer or shorter',
|
||||||
|
overTableMessage: 'Over here',
|
||||||
|
select: new Subject<string[]>()
|
||||||
|
};
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule,
|
||||||
|
MatDialogModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||||
|
{
|
||||||
|
provide: MatDialogRef,
|
||||||
|
useValue: {
|
||||||
|
keydownEvents: () => of(null),
|
||||||
|
backdropClick: () => of(null),
|
||||||
|
close: jasmine.createSpy('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
||||||
|
fixture = TestBed.createComponent(AspectListDialogComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show 4 actions : CLEAR, RESET, CANCEL and APPLY', () => {
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-reset')).not.toBeNull();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-reset')).toBeDefined();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-clear')).not.toBeNull();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-clear')).toBeDefined();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-cancel')).not.toBeNull();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-cancel')).toBeDefined();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-apply')).not.toBeNull();
|
||||||
|
expect(fixture.nativeElement.querySelector('#aspect-list-dialog-actions-apply')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show basic information for the dialog', () => {
|
||||||
|
const dialogTitle = fixture.nativeElement.querySelector('[data-automation-id="aspect-list-dialog-title"] .adf-aspect-list-dialog-title');
|
||||||
|
expect(dialogTitle).not.toBeNull();
|
||||||
|
expect(dialogTitle.innerText).toBe(data.title);
|
||||||
|
|
||||||
|
const dialogDescription = fixture.nativeElement.querySelector('[data-automation-id="aspect-list-dialog-title"] .adf-aspect-list-dialog-description');
|
||||||
|
expect(dialogDescription).not.toBeNull();
|
||||||
|
expect(dialogDescription.innerText).toBe(data.description);
|
||||||
|
|
||||||
|
const overTableMessage = fixture.nativeElement.querySelector('#aspect-list-dialog-over-table-message');
|
||||||
|
expect(overTableMessage).not.toBeNull();
|
||||||
|
expect(overTableMessage.innerText).toBe(data.overTableMessage);
|
||||||
|
|
||||||
|
const selectionCounter = fixture.nativeElement.querySelector('#aspect-list-dialog-counter');
|
||||||
|
expect(selectionCounter).not.toBeNull();
|
||||||
|
expect(selectionCounter.innerText).toBe('0 ADF-ASPECT-LIST.DIALOG.SELECTED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the counter when an option is selcted and unselected', async () => {
|
||||||
|
const firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox).not.toBeNull();
|
||||||
|
let selectionCounter = fixture.nativeElement.querySelector('#aspect-list-dialog-counter');
|
||||||
|
expect(selectionCounter).not.toBeNull();
|
||||||
|
expect(selectionCounter.innerText).toBe('0 ADF-ASPECT-LIST.DIALOG.SELECTED');
|
||||||
|
firstAspectCheckbox.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
selectionCounter = fixture.nativeElement.querySelector('#aspect-list-dialog-counter');
|
||||||
|
expect(selectionCounter).not.toBeNull();
|
||||||
|
expect(selectionCounter.innerText).toBe('1 ADF-ASPECT-LIST.DIALOG.SELECTED');
|
||||||
|
|
||||||
|
firstAspectCheckbox.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
selectionCounter = fixture.nativeElement.querySelector('#aspect-list-dialog-counter');
|
||||||
|
expect(selectionCounter).not.toBeNull();
|
||||||
|
expect(selectionCounter.innerText).toBe('0 ADF-ASPECT-LIST.DIALOG.SELECTED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset to the node values when Reset button is clicked', async () => {
|
||||||
|
let firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox).not.toBeNull();
|
||||||
|
firstAspectCheckbox.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const resetButton: HTMLButtonElement = fixture.nativeElement.querySelector('#aspect-list-dialog-actions-reset');
|
||||||
|
expect(resetButton).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox.checked).toBeTruthy();
|
||||||
|
resetButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
firstAspectCheckbox = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox.checked).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear all the value when Clear button is clicked', async () => {
|
||||||
|
let firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox).not.toBeNull();
|
||||||
|
firstAspectCheckbox.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const clearButton: HTMLButtonElement = fixture.nativeElement.querySelector('#aspect-list-dialog-actions-clear');
|
||||||
|
expect(clearButton).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox.checked).toBeTruthy();
|
||||||
|
clearButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
firstAspectCheckbox = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox.checked).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete the select stream Cancel button is clicked', (done) => {
|
||||||
|
data.select.subscribe(() => { }, () => { }, () => done());
|
||||||
|
const cancelButton: HTMLButtonElement = fixture.nativeElement.querySelector('#aspect-list-dialog-actions-cancel');
|
||||||
|
expect(cancelButton).toBeDefined();
|
||||||
|
cancelButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Passing the node id', () => {
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
data = <AspectListDialogComponentData> {
|
||||||
|
title: 'Title',
|
||||||
|
description: 'Description that can be longer or shorter',
|
||||||
|
overTableMessage: 'Over here',
|
||||||
|
select: new Subject<string[]>(),
|
||||||
|
nodeId: 'fake-node-id'
|
||||||
|
};
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule,
|
||||||
|
MatDialogModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||||
|
{
|
||||||
|
provide: MatDialogRef,
|
||||||
|
useValue: {
|
||||||
|
close: jasmine.createSpy('close'),
|
||||||
|
keydownEvents: () => of(null),
|
||||||
|
backdropClick: () => of(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
await TestBed.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
|
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
||||||
|
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
||||||
|
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne'] }).pipe(delay(0)));
|
||||||
|
fixture = TestBed.createComponent(AspectListDialogComponent);
|
||||||
|
fixture.componentInstance.data.select = new Subject<string[]>();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show checked the current aspects of the node', async () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenRenderingDone();
|
||||||
|
const firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox).not.toBeNull();
|
||||||
|
expect(firstAspectCheckbox.checked).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,66 @@
|
|||||||
|
/*!
|
||||||
|
* @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, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface';
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-aspect-list-dialog',
|
||||||
|
templateUrl: './aspect-list-dialog.component.html',
|
||||||
|
styleUrls: ['./aspect-list-dialog.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class AspectListDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
currentNodeId: string;
|
||||||
|
overTableMessage: string;
|
||||||
|
|
||||||
|
currentAspectSelection: string[] = [];
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialogRef<AspectListDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: AspectListDialogComponentData) {
|
||||||
|
this.title = data.title;
|
||||||
|
this.description = data.description;
|
||||||
|
this.overTableMessage = data.overTableMessage;
|
||||||
|
this.currentNodeId = data.nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dialog.backdropClick().subscribe(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChanged(aspectList: string[]) {
|
||||||
|
this.currentAspectSelection = aspectList;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.data.select.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onApply() {
|
||||||
|
this.data.select.next(this.currentAspectSelection);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
<div id="aspect-list-container" class="adf-aspect-list-container">
|
||||||
|
<mat-accordion class="adf-accordion-aspect-list">
|
||||||
|
<mat-expansion-panel *ngFor="let aspect of (aspects$ | async); let colIndex = index" [id]="'aspect-list-'+aspect?.entry?.title">
|
||||||
|
<mat-expansion-panel-header [id]="'aspect-list-'+aspect?.entry?.title+'header'">
|
||||||
|
<mat-panel-title>
|
||||||
|
<mat-checkbox class="adf-aspect-list-check-button" [id]="'aspect-list-'+colIndex+'-check'"
|
||||||
|
[checked]="nodeAspects?.includes(aspect?.entry?.id)"
|
||||||
|
(click)="onCheckBoxClick($event)"
|
||||||
|
(change)="onChange($event, aspect?.entry?.id)">
|
||||||
|
</mat-checkbox>
|
||||||
|
<p class="adf-aspect-list-element-title">{{aspect?.entry?.title}}</p>
|
||||||
|
</mat-panel-title>
|
||||||
|
<mat-panel-description [id]="'aspect-list-'+colIndex+'-title'"
|
||||||
|
[matTooltip]="aspect?.entry?.title">
|
||||||
|
{{aspect?.entry?.title}}
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<p class="adf-property-paragraph" [id]="'aspect-list-'+colIndex+'-description'"> {{aspect?.entry?.description}}</p>
|
||||||
|
|
||||||
|
<table mat-table [dataSource]="aspect?.entry?.properties" *ngIf="aspect?.entry?.properties?.length > 0" class="adf-aspect-property-table" [id]="'aspect-list-'+colIndex+'-properties-table'">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'ADF-ASPECT-LIST.PROPERTY_NAME' | translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.id}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'ADF-ASPECT-LIST.DESCRIPTION' | translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.title}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="dataType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'ADF-ASPECT-LIST.DATA_TYPE' | translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.dataType}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="propertyColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: propertyColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
</div>
|
@@ -0,0 +1,72 @@
|
|||||||
|
@mixin adf-aspect-list-theme($theme) {
|
||||||
|
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
$warn: map-get($theme, warn);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
.adf {
|
||||||
|
|
||||||
|
&-aspect-list-container {
|
||||||
|
|
||||||
|
padding-top: 3px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.adf-aspect-list-check-button {
|
||||||
|
margin-right: 5px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-aspect-list-element-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-accordion-aspect-list {
|
||||||
|
|
||||||
|
.mat-expansion-panel-spacing {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-expansion-panel-header {
|
||||||
|
font-size: smaller;
|
||||||
|
border-right-style: inset;
|
||||||
|
border-left-style: outset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-expansion-panel-header-title {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-expansion-panel-header-description {
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1 1 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-aspect-property-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.mat-column-name {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-description {
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-type {
|
||||||
|
width: 20%;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,221 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { NodesApiService, setupTestBed } from '@alfresco/adf-core';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AspectListComponent } from './aspect-list.component';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { AspectEntry } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
const aspectListMock: AspectEntry[] = [{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:aspectZero',
|
||||||
|
id: 'frs:AspectOne',
|
||||||
|
description: 'First Aspect with random description',
|
||||||
|
title: 'FirstAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'channelPassword',
|
||||||
|
title: 'The authenticated channel password',
|
||||||
|
dataType: 'd:propA'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'channelUsername',
|
||||||
|
title: 'The authenticated channel username',
|
||||||
|
dataType: 'd:propB'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:AspectZer',
|
||||||
|
id: 'frs:SecondAspect',
|
||||||
|
description: 'Second Aspect description',
|
||||||
|
title: 'SecondAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'assetId',
|
||||||
|
title: 'Published Asset Id',
|
||||||
|
dataType: 'd:text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'assetUrl',
|
||||||
|
title: 'Published Asset URL',
|
||||||
|
dataType: 'd:text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
describe('AspectListComponent', () => {
|
||||||
|
|
||||||
|
let component: AspectListComponent;
|
||||||
|
let fixture: ComponentFixture<AspectListComponent>;
|
||||||
|
let aspectListService: AspectListService;
|
||||||
|
let nodeService: NodesApiService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule
|
||||||
|
],
|
||||||
|
providers: [AspectListService]
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When passing a node id', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AspectListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
||||||
|
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
||||||
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
|
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne'] }));
|
||||||
|
component.nodeId = 'fake-node-id';
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show all the aspects', () => {
|
||||||
|
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
|
||||||
|
const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect');
|
||||||
|
|
||||||
|
expect(firstElement).not.toBeNull();
|
||||||
|
expect(firstElement).toBeDefined();
|
||||||
|
expect(secondElement).not.toBeNull();
|
||||||
|
expect(secondElement).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the details when a row is clicked', () => {
|
||||||
|
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
|
||||||
|
firstElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const firstElementDesc = fixture.nativeElement.querySelector('#aspect-list-0-description');
|
||||||
|
expect(firstElementDesc).not.toBeNull();
|
||||||
|
expect(firstElementDesc).toBeDefined();
|
||||||
|
|
||||||
|
const firstElementPropertyTable = fixture.nativeElement.querySelector('#aspect-list-0-properties-table');
|
||||||
|
expect(firstElementPropertyTable).not.toBeNull();
|
||||||
|
expect(firstElementPropertyTable).toBeDefined();
|
||||||
|
const nameProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-name');
|
||||||
|
expect(nameProperties[0]).not.toBeNull();
|
||||||
|
expect(nameProperties[0]).toBeDefined();
|
||||||
|
expect(nameProperties[0].innerText).toBe('channelPassword');
|
||||||
|
expect(nameProperties[1]).not.toBeNull();
|
||||||
|
expect(nameProperties[1]).toBeDefined();
|
||||||
|
expect(nameProperties[1].innerText).toBe('channelUsername');
|
||||||
|
|
||||||
|
const titleProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-title');
|
||||||
|
expect(titleProperties[0]).not.toBeNull();
|
||||||
|
expect(titleProperties[0]).toBeDefined();
|
||||||
|
expect(titleProperties[0].innerText).toBe('The authenticated channel password');
|
||||||
|
expect(titleProperties[1]).not.toBeNull();
|
||||||
|
expect(titleProperties[1]).toBeDefined();
|
||||||
|
expect(titleProperties[1].innerText).toBe('The authenticated channel username');
|
||||||
|
|
||||||
|
const dataTypeProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-dataType');
|
||||||
|
expect(dataTypeProperties[0]).not.toBeNull();
|
||||||
|
expect(dataTypeProperties[0]).toBeDefined();
|
||||||
|
expect(dataTypeProperties[0].innerText).toBe('d:propA');
|
||||||
|
expect(dataTypeProperties[1]).not.toBeNull();
|
||||||
|
expect(dataTypeProperties[1]).toBeDefined();
|
||||||
|
expect(dataTypeProperties[1].innerText).toBe('d:propB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show as checked the node properties', () => {
|
||||||
|
const firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
|
||||||
|
expect(firstAspectCheckbox).toBeDefined();
|
||||||
|
expect(firstAspectCheckbox).not.toBeNull();
|
||||||
|
expect(firstAspectCheckbox.checked).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove aspects unchecked', (done) => {
|
||||||
|
const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input');
|
||||||
|
expect(secondElement).toBeDefined();
|
||||||
|
expect(secondElement).not.toBeNull();
|
||||||
|
expect(secondElement.checked).toBeFalsy();
|
||||||
|
secondElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.nodeAspects.length).toBe(2);
|
||||||
|
expect(component.nodeAspects[1]).toBe('frs:SecondAspect');
|
||||||
|
component.valueChanged.subscribe((aspects) => {
|
||||||
|
expect(aspects.length).toBe(1);
|
||||||
|
expect(aspects[0]).toBe('frs:AspectOne');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
secondElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset the properties on reset', (done) => {
|
||||||
|
const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input');
|
||||||
|
expect(secondElement).toBeDefined();
|
||||||
|
expect(secondElement).not.toBeNull();
|
||||||
|
expect(secondElement.checked).toBeFalsy();
|
||||||
|
secondElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.nodeAspects.length).toBe(2);
|
||||||
|
component.valueChanged.subscribe((aspects) => {
|
||||||
|
expect(aspects.length).toBe(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
component.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear all the properties on clear', (done) => {
|
||||||
|
expect(component.nodeAspects.length).toBe(1);
|
||||||
|
component.valueChanged.subscribe((aspects) => {
|
||||||
|
expect(aspects.length).toBe(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
component.clear();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When no node id is passed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AspectListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show all the aspects', () => {
|
||||||
|
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
|
||||||
|
const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect');
|
||||||
|
|
||||||
|
expect(firstElement).not.toBeNull();
|
||||||
|
expect(firstElement).toBeDefined();
|
||||||
|
expect(secondElement).not.toBeNull();
|
||||||
|
expect(secondElement).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -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, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { NodesApiService } from '@alfresco/adf-core';
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
import { concatMap, takeUntil, tap } from 'rxjs/operators';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||||
|
import { AspectEntry } from '@alfresco/js-api';
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-aspect-list',
|
||||||
|
templateUrl: './aspect-list.component.html',
|
||||||
|
styleUrls: ['./aspect-list.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
|
||||||
|
export class AspectListComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
/** Node Id of the node that we want to update */
|
||||||
|
@Input()
|
||||||
|
nodeId: string = '';
|
||||||
|
|
||||||
|
/** Emitted every time the user select a new aspect */
|
||||||
|
@Output()
|
||||||
|
valueChanged: EventEmitter<string[]> = new EventEmitter<string[]>();
|
||||||
|
|
||||||
|
propertyColumns: string[] = ['name', 'title', 'dataType'];
|
||||||
|
aspects$: Observable<AspectEntry[]> = null;
|
||||||
|
nodeAspects: string[] = [];
|
||||||
|
nodeAspectStatus: string[] = null;
|
||||||
|
|
||||||
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.nodeId) {
|
||||||
|
this.aspects$ = this.nodeApiService.getNode(this.nodeId).pipe(
|
||||||
|
tap((node) => {
|
||||||
|
this.nodeAspects = node.aspectNames.filter((aspect) => this.aspectListService.getVisibleAspects().includes(aspect));
|
||||||
|
this.nodeAspectStatus = Array.from(node.aspectNames);
|
||||||
|
this.valueChanged.emit(this.nodeAspects);
|
||||||
|
}),
|
||||||
|
concatMap(() => this.aspectListService.getAspects()),
|
||||||
|
takeUntil(this.onDestroy$));
|
||||||
|
} else {
|
||||||
|
this.aspects$ = this.aspectListService.getAspects()
|
||||||
|
.pipe(takeUntil(this.onDestroy$));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckBoxClick(event: Event) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(change: MatCheckboxChange, prefixedName: string) {
|
||||||
|
if (change.checked) {
|
||||||
|
this.nodeAspects.push(prefixedName);
|
||||||
|
} else {
|
||||||
|
this.nodeAspects.splice(this.nodeAspects.indexOf(prefixedName), 1);
|
||||||
|
}
|
||||||
|
this.valueChanged.emit(this.nodeAspects);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
if (this.nodeAspectStatus && this.nodeAspectStatus.length > 0) {
|
||||||
|
this.nodeAspects.splice(0, this.nodeAspects.length, ...this.nodeAspectStatus);
|
||||||
|
this.valueChanged.emit(this.nodeAspects);
|
||||||
|
} else {
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.nodeAspects = [];
|
||||||
|
this.valueChanged.emit(this.nodeAspects);
|
||||||
|
}
|
||||||
|
}
|
@@ -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 { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { AspectListComponent } from './aspect-list.component';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { PipeModule } from '@alfresco/adf-core';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { AspectListDialogComponent } from './aspect-list-dialog.component';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
PipeModule,
|
||||||
|
TranslateModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AspectListComponent,
|
||||||
|
AspectListDialogComponent
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AspectListComponent,
|
||||||
|
AspectListDialogComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AspectListModule { }
|
@@ -0,0 +1,166 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { AspectEntry, AspectPaging } from '@alfresco/js-api';
|
||||||
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AlfrescoApiService, AppConfigService, setupTestBed } from 'core';
|
||||||
|
import { of, Subject } from 'rxjs';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
|
||||||
|
const aspectListMock: AspectEntry[] = [{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:aspectZero',
|
||||||
|
id: 'frs:AspectOne',
|
||||||
|
description: 'First Aspect with random description',
|
||||||
|
title: 'FirstAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'channelPassword',
|
||||||
|
title: 'The authenticated channel password',
|
||||||
|
dataType: 'd:propA'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'channelUsername',
|
||||||
|
title: 'The authenticated channel username',
|
||||||
|
dataType: 'd:propB'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:AspectZer',
|
||||||
|
id: 'frs:SecondAspect',
|
||||||
|
description: 'Second Aspect description',
|
||||||
|
title: 'SecondAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'assetId',
|
||||||
|
title: 'Published Asset Id',
|
||||||
|
dataType: 'd:text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'assetUrl',
|
||||||
|
title: 'Published Asset URL',
|
||||||
|
dataType: 'd:text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
const customAspectListMock: AspectEntry[] = [{
|
||||||
|
entry: {
|
||||||
|
parentId: 'frs:aspectZero',
|
||||||
|
id: 'frs:AspectCustom',
|
||||||
|
description: 'First Aspect with random description',
|
||||||
|
title: 'FirstAspect',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'channelPassword',
|
||||||
|
title: 'The authenticated channel password',
|
||||||
|
dataType: 'd:propA'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'channelUsername',
|
||||||
|
title: 'The authenticated channel username',
|
||||||
|
dataType: 'd:propB'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
const listAspectResp: AspectPaging = {
|
||||||
|
list : {
|
||||||
|
entries: aspectListMock
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const customListAspectResp: AspectPaging = {
|
||||||
|
list : {
|
||||||
|
entries: customAspectListMock
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('AspectListService', () => {
|
||||||
|
|
||||||
|
describe('should open the dialog', () => {
|
||||||
|
let service: AspectListService;
|
||||||
|
let materialDialog: MatDialog;
|
||||||
|
let spyOnDialogOpen: jasmine.Spy;
|
||||||
|
let spyOnDialogClose: jasmine.Spy;
|
||||||
|
const afterOpenObservable: Subject<any> = new Subject();
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule,
|
||||||
|
MatDialogModule
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = TestBed.inject(AspectListService);
|
||||||
|
materialDialog = TestBed.inject(MatDialog);
|
||||||
|
spyOnDialogOpen = spyOn(materialDialog, 'open').and.returnValue({
|
||||||
|
afterOpen: () => afterOpenObservable,
|
||||||
|
afterClosed: () => of({}),
|
||||||
|
componentInstance: {
|
||||||
|
error: new Subject<any>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spyOnDialogClose = spyOn(materialDialog, 'closeAll');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open the aspect list dialog', () => {
|
||||||
|
service.openAspectListDialog();
|
||||||
|
expect(spyOnDialogOpen).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the dialog', () => {
|
||||||
|
service.close();
|
||||||
|
expect(spyOnDialogClose).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should fetch the list of the aspects', () => {
|
||||||
|
|
||||||
|
let service: AspectListService;
|
||||||
|
const appConfigService: AppConfigService = new AppConfigService(null);
|
||||||
|
|
||||||
|
const aspectTypesApi = jasmine.createSpyObj('AspectsApi', ['listAspects']);
|
||||||
|
const apiService: AlfrescoApiService = new AlfrescoApiService(null, null);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(appConfigService, 'get').and.returnValue({ 'default': ['frs:AspectOne'] });
|
||||||
|
spyOnProperty(apiService, 'aspectsApi').and.returnValue(aspectTypesApi);
|
||||||
|
service = new AspectListService(apiService, appConfigService, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the list of only available aspects', async(() => {
|
||||||
|
aspectTypesApi.listAspects.and.returnValues(of(listAspectResp), of(customListAspectResp));
|
||||||
|
service.getAspects().subscribe((list) => {
|
||||||
|
expect(list.length).toBe(2);
|
||||||
|
expect(list[0].entry.id).toBe('frs:AspectOne');
|
||||||
|
expect(list[1].entry.id).toBe('frs:AspectCustom');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
112
lib/content-services/src/lib/aspect-list/aspect-list.service.ts
Normal file
112
lib/content-services/src/lib/aspect-list/aspect-list.service.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
|
||||||
|
import { from, Observable, Subject, zip } from 'rxjs';
|
||||||
|
import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface';
|
||||||
|
import { AspectListDialogComponent } from './aspect-list-dialog.component';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { AspectEntry, AspectPaging } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AspectListService {
|
||||||
|
|
||||||
|
constructor(private alfrescoApiService: AlfrescoApiService,
|
||||||
|
private appConfigService: AppConfigService, private dialog: MatDialog) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getAspects(): Observable<AspectEntry[]> {
|
||||||
|
const visibleAspectList = this.getVisibleAspects();
|
||||||
|
const standardAspects$ = this.getStandardAspects(visibleAspectList);
|
||||||
|
const customAspects$ = this.getCustomAspects();
|
||||||
|
return zip(standardAspects$, customAspects$).pipe(
|
||||||
|
map(([standardAspectList, customAspectList]) => [...standardAspectList, ...customAspectList])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStandardAspects(whiteList: string[]): Observable<AspectEntry[]> {
|
||||||
|
const where = `(modelIds in ('cm:contentmodel', 'emailserver:emailserverModel', 'smf:smartFolder', 'app:applicationmodel' ))`;
|
||||||
|
return from(this.alfrescoApiService.aspectsApi.listAspects(where))
|
||||||
|
.pipe(
|
||||||
|
map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomAspects(): Observable<AspectEntry[]> {
|
||||||
|
const where = `(not namespaceUri matches('http://www.alfresco.*')`;
|
||||||
|
return from(this.alfrescoApiService.aspectsApi.listAspects(where))
|
||||||
|
.pipe(
|
||||||
|
map((result: AspectPaging) => result?.list?.entries)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private filterAspectByConfig(visibleAspectList: string[], aspectEntries: AspectEntry[]): AspectEntry[] {
|
||||||
|
let result = aspectEntries ? aspectEntries : [];
|
||||||
|
if (visibleAspectList?.length > 0 && aspectEntries) {
|
||||||
|
result = aspectEntries.filter((value) => {
|
||||||
|
return visibleAspectList.includes(value?.entry?.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibleAspects(): string[] {
|
||||||
|
let visibleAspectList: string[] = [];
|
||||||
|
const aspectVisibleConfig = this.appConfigService.get('aspect-visible');
|
||||||
|
if (aspectVisibleConfig) {
|
||||||
|
for (const aspectGroup of Object.keys(aspectVisibleConfig)) {
|
||||||
|
visibleAspectList = visibleAspectList.concat(aspectVisibleConfig[aspectGroup]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visibleAspectList;
|
||||||
|
}
|
||||||
|
|
||||||
|
openAspectListDialog(nodeId?: string): Observable<string[]> {
|
||||||
|
const select = new Subject<string[]>();
|
||||||
|
select.subscribe({
|
||||||
|
complete: this.close.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: AspectListDialogComponentData = {
|
||||||
|
title: 'ADF-ASPECT-LIST.DIALOG.TITLE',
|
||||||
|
description: 'ADF-ASPECT-LIST.DIALOG.DESCRIPTION',
|
||||||
|
overTableMessage: 'ADF-ASPECT-LIST.DIALOG.OVER-TABLE-MESSAGE',
|
||||||
|
select,
|
||||||
|
nodeId
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openDialog(data, 'adf-aspect-list-dialog', '750px');
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
private openDialog(data: AspectListDialogComponentData, panelClass: string, width: string) {
|
||||||
|
this.dialog.open(AspectListDialogComponent, {
|
||||||
|
data,
|
||||||
|
panelClass,
|
||||||
|
width,
|
||||||
|
disableClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialog.closeAll();
|
||||||
|
}
|
||||||
|
}
|
18
lib/content-services/src/lib/aspect-list/index.ts
Normal file
18
lib/content-services/src/lib/aspect-list/index.ts
Normal file
@@ -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 './public-api';
|
@@ -0,0 +1,74 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AlfrescoApiService, NodesApiService, setupTestBed } from 'core';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
import { NodeAspectService } from './node-aspect.service';
|
||||||
|
|
||||||
|
describe('NodeAspectService', () => {
|
||||||
|
|
||||||
|
let aspectListService: AspectListService;
|
||||||
|
let nodeAspectService: NodeAspectService;
|
||||||
|
let nodeApiService: NodesApiService;
|
||||||
|
let alfrescoApiService: AlfrescoApiService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
nodeAspectService = TestBed.inject(NodeAspectService);
|
||||||
|
nodeApiService = TestBed.inject(NodesApiService);
|
||||||
|
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open the aspect list dialog', () => {
|
||||||
|
spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of([]));
|
||||||
|
spyOn(nodeApiService, 'updateNode').and.returnValue(of({}));
|
||||||
|
nodeAspectService.updateNodeAspects('fake-node-id');
|
||||||
|
expect(aspectListService.openAspectListDialog).toHaveBeenCalledWith('fake-node-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the node when the aspect dialog apply the changes', () => {
|
||||||
|
const expectedParameters = { aspectNames: ['a', 'b', 'c'] };
|
||||||
|
spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c']));
|
||||||
|
spyOn(nodeApiService, 'updateNode').and.returnValue(of({}));
|
||||||
|
nodeAspectService.updateNodeAspects('fake-node-id');
|
||||||
|
expect(nodeApiService.updateNode).toHaveBeenCalledWith('fake-node-id', expectedParameters);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send and update node event once the node has been updated', (done) => {
|
||||||
|
alfrescoApiService.nodeUpdated.subscribe((nodeUpdated) => {
|
||||||
|
expect(nodeUpdated.id).toBe('fake-node-id');
|
||||||
|
expect(nodeUpdated.aspectNames).toEqual(['a', 'b', 'c']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
const fakeNode = { id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] };
|
||||||
|
spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c']));
|
||||||
|
spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode));
|
||||||
|
nodeAspectService.updateNodeAspects('fake-node-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { AlfrescoApiService, NodesApiService } from '@alfresco/adf-core';
|
||||||
|
import { AspectListService } from './aspect-list.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class NodeAspectService {
|
||||||
|
|
||||||
|
constructor(private alfrescoApiService: AlfrescoApiService,
|
||||||
|
private nodesApiService: NodesApiService,
|
||||||
|
private aspectListService: AspectListService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeAspects(nodeId: string) {
|
||||||
|
this.aspectListService.openAspectListDialog(nodeId).subscribe((aspectList) => {
|
||||||
|
this.nodesApiService.updateNode(nodeId, { aspectNames: [...aspectList] }).subscribe((updatedNode) => {
|
||||||
|
this.alfrescoApiService.nodeUpdated.next(updatedNode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
25
lib/content-services/src/lib/aspect-list/public-api.ts
Normal file
25
lib/content-services/src/lib/aspect-list/public-api.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
* @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 './aspect-list.component';
|
||||||
|
export * from './aspect-list-dialog.component';
|
||||||
|
export * from './aspect-list.service';
|
||||||
|
export * from './node-aspect.service';
|
||||||
|
|
||||||
|
export * from './aspect-list-dialog-data.interface';
|
||||||
|
|
||||||
|
export * from './aspect-list.module';
|
@@ -13,6 +13,14 @@
|
|||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-footer class="adf-content-metadata-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
<mat-card-footer class="adf-content-metadata-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||||
<div>
|
<div>
|
||||||
|
<button *ngIf="!readOnly && hasAllowableOperations()"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="openAspectDialog()"
|
||||||
|
[attr.title]="'CORE.METADATA.ACTIONS.EDIT_ASPECTS' | translate"
|
||||||
|
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT_ASPECTS' | translate"
|
||||||
|
data-automation-id="meta-data-card-edit-aspect">
|
||||||
|
<mat-icon>menu</mat-icon>
|
||||||
|
</button>
|
||||||
<button *ngIf="!readOnly && hasAllowableOperations()"
|
<button *ngIf="!readOnly && hasAllowableOperations()"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
(click)="toggleEdit()"
|
(click)="toggleEdit()"
|
||||||
|
@@ -24,6 +24,7 @@ import { setupTestBed, AllowableOperationsEnum } from '@alfresco/adf-core';
|
|||||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||||
import { SimpleChange } from '@angular/core';
|
import { SimpleChange } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NodeAspectService } from 'content-services/src/lib/aspect-list';
|
||||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
let contentMetadataService: ContentMetadataService;
|
let contentMetadataService: ContentMetadataService;
|
||||||
let node: Node;
|
let node: Node;
|
||||||
const preset = 'custom-preset';
|
const preset = 'custom-preset';
|
||||||
|
let nodeAspectService: NodeAspectService = null;
|
||||||
|
|
||||||
setupTestBed({
|
setupTestBed({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -57,6 +59,7 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
|
|
||||||
component.node = node;
|
component.node = node;
|
||||||
component.preset = preset;
|
component.preset = preset;
|
||||||
|
nodeAspectService = TestBed.inject(NodeAspectService);
|
||||||
spyOn(contentMetadataService, 'getContentTypeProperty').and.returnValue(of([]));
|
spyOn(contentMetadataService, 'getContentTypeProperty').and.returnValue(of([]));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -211,4 +214,18 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
component.ngOnChanges({ displayAspect });
|
component.ngOnChanges({ displayAspect });
|
||||||
expect(component.expanded).toBeTruthy();
|
expect(component.expanded).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call the aspect dialog when edit aspect is clicked', () => {
|
||||||
|
component.editable = true;
|
||||||
|
component.node.id = 'fake-node-id';
|
||||||
|
component.node.allowableOperations = [AllowableOperationsEnum.UPDATE];
|
||||||
|
spyOn(nodeAspectService, 'updateNodeAspects').and.stub();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const button = fixture.debugElement.query(By.css('[data-automation-id="meta-data-card-edit-aspect"]'));
|
||||||
|
button.triggerEventHandler('click', {});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(nodeAspectService.updateNodeAspects).toHaveBeenCalledWith('fake-node-id');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||||
import { Node } from '@alfresco/js-api';
|
import { Node } from '@alfresco/js-api';
|
||||||
import { ContentService, AllowableOperationsEnum } from '@alfresco/adf-core';
|
import { ContentService, AllowableOperationsEnum } from '@alfresco/adf-core';
|
||||||
|
import { NodeAspectService } from '../../../aspect-list/node-aspect.service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-content-metadata-card',
|
selector: 'adf-content-metadata-card',
|
||||||
templateUrl: './content-metadata-card.component.html',
|
templateUrl: './content-metadata-card.component.html',
|
||||||
@@ -27,6 +27,7 @@ import { ContentService, AllowableOperationsEnum } from '@alfresco/adf-core';
|
|||||||
host: { 'class': 'adf-content-metadata-card' }
|
host: { 'class': 'adf-content-metadata-card' }
|
||||||
})
|
})
|
||||||
export class ContentMetadataCardComponent implements OnChanges {
|
export class ContentMetadataCardComponent implements OnChanges {
|
||||||
|
|
||||||
/** (required) The node entity to fetch metadata about */
|
/** (required) The node entity to fetch metadata about */
|
||||||
@Input()
|
@Input()
|
||||||
node: Node;
|
node: Node;
|
||||||
@@ -80,7 +81,7 @@ export class ContentMetadataCardComponent implements OnChanges {
|
|||||||
|
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
|
|
||||||
constructor(private contentService: ContentService) {
|
constructor(private contentService: ContentService, private nodeAspectService: NodeAspectService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
@@ -104,4 +105,8 @@ export class ContentMetadataCardComponent implements OnChanges {
|
|||||||
hasAllowableOperations() {
|
hasAllowableOperations() {
|
||||||
return this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.UPDATE);
|
return this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openAspectDialog() {
|
||||||
|
this.nodeAspectService.updateNodeAspects(this.node.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,7 @@ import { ContentMetadataModule } from './content-metadata/content-metadata.modul
|
|||||||
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
|
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
|
||||||
import { TreeViewModule } from './tree-view/tree-view.module';
|
import { TreeViewModule } from './tree-view/tree-view.module';
|
||||||
import { ContentTypeModule } from './content-type/content-type.module';
|
import { ContentTypeModule } from './content-type/content-type.module';
|
||||||
|
import { AspectListModule } from './aspect-list/aspect-list.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -65,7 +66,8 @@ import { ContentTypeModule } from './content-type/content-type.module';
|
|||||||
PermissionManagerModule,
|
PermissionManagerModule,
|
||||||
VersionManagerModule,
|
VersionManagerModule,
|
||||||
TreeViewModule,
|
TreeViewModule,
|
||||||
ContentTypeModule
|
ContentTypeModule,
|
||||||
|
AspectListModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -95,6 +97,7 @@ import { ContentTypeModule } from './content-type/content-type.module';
|
|||||||
PermissionManagerModule,
|
PermissionManagerModule,
|
||||||
VersionManagerModule,
|
VersionManagerModule,
|
||||||
TreeViewModule,
|
TreeViewModule,
|
||||||
|
AspectListModule,
|
||||||
ContentTypeModule
|
ContentTypeModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@@ -459,5 +459,20 @@
|
|||||||
"ACCESSIBILITY": {
|
"ACCESSIBILITY": {
|
||||||
"ARIA_LABEL": "Open {{ name }}"
|
"ARIA_LABEL": "Open {{ name }}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ADF-ASPECT-LIST" : {
|
||||||
|
"PROPERTY_NAME": "Property Name",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"DATA_TYPE": "Data Type",
|
||||||
|
"DIALOG" : {
|
||||||
|
"TITLE" : "Customize Properties",
|
||||||
|
"DESCRIPTION": "Manage the properties associated with selected file(s). Choose from property aspects listed below, to expose and apply additional metadata and funcitonality",
|
||||||
|
"RESET": "Reset",
|
||||||
|
"CLEAR": "Clear",
|
||||||
|
"CANCEL": "Cancel",
|
||||||
|
"APPLY": "Apply",
|
||||||
|
"OVER-TABLE-MESSAGE" : "Select property aspects",
|
||||||
|
"SELECTED": "Selected"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,8 @@
|
|||||||
@import '../tree-view/components/tree-view.component';
|
@import '../tree-view/components/tree-view.component';
|
||||||
@import '../version-manager/version-comparison.component';
|
@import '../version-manager/version-comparison.component';
|
||||||
@import '../content-type/content-type-dialog.component';
|
@import '../content-type/content-type-dialog.component';
|
||||||
|
@import '../aspect-list/aspect-list.component';
|
||||||
|
@import '../aspect-list//aspect-list-dialog.component';
|
||||||
|
|
||||||
@mixin adf-content-services-theme($theme) {
|
@mixin adf-content-services-theme($theme) {
|
||||||
@include adf-breadcrumb-theme($theme);
|
@include adf-breadcrumb-theme($theme);
|
||||||
@@ -54,4 +56,6 @@
|
|||||||
@include adf-search-chip-list-theme($theme);
|
@include adf-search-chip-list-theme($theme);
|
||||||
@include adf-version-comparison-theme($theme);
|
@include adf-version-comparison-theme($theme);
|
||||||
@include adf-content-type-dialog-theme($theme);
|
@include adf-content-type-dialog-theme($theme);
|
||||||
|
@include adf-aspect-list-theme($theme);
|
||||||
|
@include adf-aspect-list-dialog-theme($theme);
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,7 @@ export * from './lib/permission-manager/index';
|
|||||||
export * from './lib/content-node-share/index';
|
export * from './lib/content-node-share/index';
|
||||||
export * from './lib/tree-view/index';
|
export * from './lib/tree-view/index';
|
||||||
export * from './lib/group/index';
|
export * from './lib/group/index';
|
||||||
|
export * from './lib/aspect-list/index';
|
||||||
export * from './lib/content-type/index';
|
export * from './lib/content-type/index';
|
||||||
|
|
||||||
export * from './lib/content.module';
|
export * from './lib/content.module';
|
||||||
|
@@ -60,7 +60,6 @@ import { DirectionalityConfigService } from './services/directionality-config.se
|
|||||||
import { SearchTextModule } from './search-text/search-text-input.module';
|
import { SearchTextModule } from './search-text/search-text-input.module';
|
||||||
import { versionCompatibilityFactory } from './services/version-compatibility-factory';
|
import { versionCompatibilityFactory } from './services/version-compatibility-factory';
|
||||||
import { VersionCompatibilityService } from './services/version-compatibility.service';
|
import { VersionCompatibilityService } from './services/version-compatibility.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
@@ -223,12 +223,14 @@
|
|||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"CLEAR": "Clear",
|
"CLEAR": "Clear",
|
||||||
"TOGGLE": "Toggle value",
|
"TOGGLE": "Toggle value",
|
||||||
"COPY_TO_CLIPBOARD": "Double click to copy value"
|
"COPY_TO_CLIPBOARD": "Double click to copy value",
|
||||||
|
"EDIT_ASPECTS": "Aspect Edit"
|
||||||
},
|
},
|
||||||
"ACCESSIBILITY": {
|
"ACCESSIBILITY": {
|
||||||
"EDIT": "Edit button",
|
"EDIT": "Edit button",
|
||||||
"DATEPICKER": "Use the arrow keys to navigate between dates. Up and down move to the next or previous week but on the same day. Left and right move to the next or previous day. Press Enter or Return to select a date.",
|
"DATEPICKER": "Use the arrow keys to navigate between dates. Up and down move to the next or previous week but on the same day. Left and right move to the next or previous day. Press Enter or Return to select a date.",
|
||||||
"COPY_TO_CLIPBOARD_MESSAGE": "Value copied to clipboard"
|
"COPY_TO_CLIPBOARD_MESSAGE": "Value copied to clipboard",
|
||||||
|
"EDIT_ASPECTS": "Aspect Edit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
|
@@ -23,7 +23,7 @@ import {
|
|||||||
SearchApi,
|
SearchApi,
|
||||||
Node,
|
Node,
|
||||||
GroupsApi,
|
GroupsApi,
|
||||||
AlfrescoApiCompatibility, AlfrescoApiConfig, TypesApi
|
AlfrescoApiCompatibility, AlfrescoApiConfig, AspectsApi, TypesApi
|
||||||
} from '@alfresco/js-api';
|
} from '@alfresco/js-api';
|
||||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
import { Subject, Observable, BehaviorSubject } from 'rxjs';
|
import { Subject, Observable, BehaviorSubject } from 'rxjs';
|
||||||
@@ -102,6 +102,10 @@ export class AlfrescoApiService {
|
|||||||
return new GroupsApi(this.getInstance());
|
return new GroupsApi(this.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get aspectsApi(): AspectsApi {
|
||||||
|
return new AspectsApi(this.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
get typesApi(): TypesApi {
|
get typesApi(): TypesApi {
|
||||||
return new TypesApi(this.getInstance());
|
return new TypesApi(this.getInstance());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user