mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ADF-2232] Content metadata layout oriented configuration (#2918)
* Small refactoring I. * Small refactoring II. * On the way of implementing the soultion * Refactoring aspect to groups and supporting different type of configs * Fixed linter errors * Fix debug project runner * Fix linting errors * Fix and align tests * Config factory tests * Layout oriented config parser * Adding layout oriented config to the config factory * Update config schema * Layout oriented config * Aspect oriented and indifferent configs alignment to the new propertyGroups structure * Remove dead codes * Fixinfinite loading error and custom type properties * Add documentation * Fix tests
This commit is contained in:
parent
a863631f0d
commit
c109b9f6f3
@ -32,13 +32,13 @@ The different aspects and their properties to be shown can be configured as appl
|
|||||||
|
|
||||||
## Application config presets
|
## Application config presets
|
||||||
|
|
||||||
In the application config file you can define different presets for the metadata component or override the default preset. The **default** preset is "*" if not set, meaning the component will display every aspects and properties of the nodes without filtering. One can think about presets as **whitelist filters** for the content metadata component.
|
In the application config file you can define different presets for the metadata component or override the default preset. The **default** preset is "*" if not set, meaning the component will display every aspects and properties of the nodes without filtering.
|
||||||
|
|
||||||
Beside the default preset you can define as many presets as you want, if you'd like to use different metadata components with different presets.
|
Beside the default preset you can define as many presets as you want, if you'd like to use different metadata components with different presets.
|
||||||
|
|
||||||
To understand presets better, you can have a look at on the following different example configurations.
|
To understand presets better, you can have a look at on the following different example configurations.
|
||||||
|
|
||||||
### Mimicking the default "default" preset
|
### Indifferent config
|
||||||
|
|
||||||
If you don't have any preset configured manually in you application config, this would be equivalent as if you had the application config as defined below:
|
If you don't have any preset configured manually in you application config, this would be equivalent as if you had the application config as defined below:
|
||||||
|
|
||||||
@ -52,7 +52,12 @@ If you don't have any preset configured manually in you application config, this
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
### Whitelisting only a few aspects in the default preset
|
|
||||||
|
### Aspect oriented config
|
||||||
|
|
||||||
|
With this type of configuration you are able to "whitelist" aspects and properties for a preset, but everything will be grouped by aspects and there is no further way to group properties. If you want to group different properties in groups you define, scroll down a bit and have a look at on the layout oriented configruration.
|
||||||
|
|
||||||
|
#### Whitelisting only a few aspects in the default preset
|
||||||
|
|
||||||
If you want to restrict to only a few aspects (e.g.: exif, your-custom-aspect), you have to use the name of that particular aspect to be able to whitelist it. In case of exif aspect this is "exif:exif".
|
If you want to restrict to only a few aspects (e.g.: exif, your-custom-aspect), you have to use the name of that particular aspect to be able to whitelist it. In case of exif aspect this is "exif:exif".
|
||||||
|
|
||||||
@ -69,7 +74,7 @@ If you want to restrict to only a few aspects (e.g.: exif, your-custom-aspect),
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
### Whitelisting only a few properties of a few aspects in the default preset
|
#### Whitelisting only a few properties of a few aspects in the default preset
|
||||||
|
|
||||||
If you want to filter more, you can do this on property level also. For this, you have to list the names of whitelisted aspect properties in an array of strings. Again, for identifying a property, you have to use its name.
|
If you want to filter more, you can do this on property level also. For this, you have to list the names of whitelisted aspect properties in an array of strings. Again, for identifying a property, you have to use its name.
|
||||||
|
|
||||||
@ -79,14 +84,14 @@ If you want to filter more, you can do this on property level also. For this, yo
|
|||||||
"presets": {
|
"presets": {
|
||||||
"default": {
|
"default": {
|
||||||
"custom:aspect": "*",
|
"custom:aspect": "*",
|
||||||
"exif:exif": [ "exif:width", "exif:height"]
|
"exif:exif": [ "exif:pixelXDimension", "exif:pixelYDimension"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
### Whitelisting only a few properties of a few aspects in a custom preset
|
#### Whitelisting only a few properties of a few aspects in a custom preset
|
||||||
|
|
||||||
And finally, you can create any custom aspect following the same rules.
|
And finally, you can create any custom aspect following the same rules.
|
||||||
|
|
||||||
@ -97,13 +102,108 @@ And finally, you can create any custom aspect following the same rules.
|
|||||||
"default": "*",
|
"default": "*",
|
||||||
"kitten-images": {
|
"kitten-images": {
|
||||||
"custom:aspect": "*",
|
"custom:aspect": "*",
|
||||||
"exif:exif": [ "exif:width", "exif:height"]
|
"exif:exif": [ "exif:pixelXDimension", "exif:pixelYDimension"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Layout oriented config
|
||||||
|
|
||||||
|
Beside the aspect oriented configuration, it is possible to configure the groups and properties in a more detailed way. With this type of configuration any property of any aspect / type can be "cherry picked"-ed and grouped into and accordion drawer, with defining a translatable title in the preset configuration.
|
||||||
|
|
||||||
|
|
||||||
|
#### Basic elements
|
||||||
|
|
||||||
|
The following config will result in one accordion group named "TRANSLATABLE_TITLE_FOR_GROUP_1", with all the properties from the custom:aspect followed by the two properties (exif:pixelXDimension, exif:pixelYDimension) from the exif:exif aspect followed by one property (custom:myPropertyName) from custom:type.
|
||||||
|
|
||||||
|
```json
|
||||||
|
...
|
||||||
|
"content-metadata": {
|
||||||
|
"presets": {
|
||||||
|
"kitten-images": [{
|
||||||
|
"title": "TRANSLATABLE_TITLE_FOR_GROUP_1",
|
||||||
|
"items": [
|
||||||
|
{ "aspect": "custom:aspect", "properties": "*" },
|
||||||
|
{ "aspect": "exif:exif", "properties": [ "exif:pixelXDimension", "exif:pixelYDimension"] },
|
||||||
|
{ "type": "custom:type", "properties": [ "custom:myPropertyName" ] },
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### More complex example
|
||||||
|
|
||||||
|
As a more complex config, you can study the one below:
|
||||||
|
|
||||||
|
```json
|
||||||
|
...
|
||||||
|
"content-metadata": {
|
||||||
|
"presets": {
|
||||||
|
"kittens": [
|
||||||
|
{
|
||||||
|
"title": "GROUP-TITLE1-TRANSLATION-KEY",
|
||||||
|
"items": [
|
||||||
|
// We would like to show every property from the exif:exif aspect
|
||||||
|
{
|
||||||
|
"aspect": "exif:exif",
|
||||||
|
"properties": "*"
|
||||||
|
},
|
||||||
|
// We would like to show the two properties (kitten:custom1, kitten:custom3) from the kitten:vet- records aspect
|
||||||
|
{
|
||||||
|
"aspect": "kitten:vet-records",
|
||||||
|
"properties": [ "kitten:custom1", "kitten:custom3" ]
|
||||||
|
},
|
||||||
|
// We would like to show the owner:name property from the owner:parameters aspect
|
||||||
|
{
|
||||||
|
"aspect": "owner:parameters",
|
||||||
|
"properties": [ "owner:name" ]
|
||||||
|
},
|
||||||
|
// We would like to show all the properties from the type kitten:kitten
|
||||||
|
{
|
||||||
|
"type": "kitten:kitten",
|
||||||
|
"properties": [ "kitten:name", "kitten:color" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "GROUP-TITLE2-TRANSLATION-KEY",
|
||||||
|
"items": [
|
||||||
|
// We would like to show the two properties (kitten:favourite-food, kitten:recommended-food) from the kitten:food aspect
|
||||||
|
{
|
||||||
|
"aspect": "kitten:food",
|
||||||
|
"properties": [ "kitten:favourite-food", "kitten:recommended-food" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
The end result of this config would be two accordion groups with the properties like this:
|
||||||
|
|
||||||
|
|
||||||
|
|GROUP-TITLE1-TRANSLATION-KEY|
|
||||||
|
|---|
|
||||||
|
|exif:param1|
|
||||||
|
|exif:param2|
|
||||||
|
|...|
|
||||||
|
|exif:paramN|
|
||||||
|
|kitten:custom1|
|
||||||
|
|kitten:custom3|
|
||||||
|
|owner:name|
|
||||||
|
|kitten:name|
|
||||||
|
|kitten:color|
|
||||||
|
|
||||||
|
|
||||||
|
|GROUP-TITLE2-TRANSLATION-KEY|
|
||||||
|
|---|
|
||||||
|
|kitten:favourite-food|
|
||||||
|
|kitten:recommended-food|
|
||||||
|
|
||||||
## What happens when there is a whitelisted aspect in the config but the given node doesn't relate to that aspect
|
## What happens when there is a whitelisted aspect in the config but the given node doesn't relate to that aspect
|
||||||
|
|
||||||
Nothing, this aspect (as it is not related to the node) will be simply ignored and not be displayed. The aspects to be displayed are calculated as an intersection of the preset's aspects and the aspects related to the node.
|
Nothing, this aspect (as it is not related to the node) will be simply ignored and not be displayed. The aspects to be displayed are calculated as an intersection of the preset's aspects and the aspects related to the node.
|
||||||
|
@ -25,10 +25,10 @@ import { ContentMetadataComponent } from '../content-metadata/content-metadata.c
|
|||||||
import { MatExpansionModule, MatCardModule, MatButtonModule, MatIconModule } from '@angular/material';
|
import { MatExpansionModule, MatCardModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||||
import { PropertyDescriptorLoaderService } from '../../services/properties-loader.service';
|
import { PropertyGroupTranslatorService } from '../../services/property-groups-translator.service';
|
||||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||||
import { AspectWhiteListService } from '../../services/aspect-whitelist.service';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
|
import { ContentMetadataConfigFactory } from '../../services/config/content-metadata-config.factory';
|
||||||
|
|
||||||
describe('ContentMetadataCardComponent', () => {
|
describe('ContentMetadataCardComponent', () => {
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
ContentMetadataService,
|
ContentMetadataService,
|
||||||
BasicPropertiesService,
|
BasicPropertiesService,
|
||||||
PropertyDescriptorLoaderService,
|
PropertyGroupTranslatorService,
|
||||||
|
ContentMetadataConfigFactory,
|
||||||
PropertyDescriptorsService,
|
PropertyDescriptorsService,
|
||||||
AspectWhiteListService,
|
|
||||||
AlfrescoApiService
|
AlfrescoApiService
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@ -65,6 +65,7 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
node = <MinimalNodeEntryEntity> {
|
node = <MinimalNodeEntryEntity> {
|
||||||
aspectNames: [],
|
aspectNames: [],
|
||||||
|
nodeType: '',
|
||||||
content: {},
|
content: {},
|
||||||
properties: {},
|
properties: {},
|
||||||
createdByUser: {},
|
createdByUser: {},
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<adf-card-view
|
<adf-card-view
|
||||||
class="adf-metadata-properties-basic"
|
|
||||||
data-automation-id="adf-metadata-properties-basic"
|
|
||||||
[properties]="basicProperties$ | async"
|
[properties]="basicProperties$ | async"
|
||||||
[editable]="editable"
|
[editable]="editable"
|
||||||
[displayEmpty]="displayEmpty">
|
[displayEmpty]="displayEmpty">
|
||||||
@ -17,18 +15,17 @@
|
|||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<ng-container *ngIf="expanded">
|
<ng-container *ngIf="expanded">
|
||||||
<ng-container *ngIf="aspects$ | async; else loading; let aspects">
|
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
|
||||||
<div *ngFor="let aspect of aspects" class="adf-metadata-properties-aspect">
|
<div *ngFor="let group of groupedProperties" class="adf-metadata-grouped-properties-container">
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
{{aspect.title}}
|
{{group.title}}
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<adf-card-view
|
<adf-card-view
|
||||||
class="adf-node-aspect-properties"
|
[properties]="group.properties"
|
||||||
[properties]="aspect.properties"
|
|
||||||
[editable]="editable"
|
[editable]="editable"
|
||||||
[displayEmpty]="displayEmpty">
|
[displayEmpty]="displayEmpty">
|
||||||
</adf-card-view>
|
</adf-card-view>
|
||||||
|
@ -25,13 +25,13 @@ import { ContentMetadataComponent } from './content-metadata.component';
|
|||||||
import { MatExpansionModule, MatButtonModule, MatIconModule } from '@angular/material';
|
import { MatExpansionModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||||
import { PropertyDescriptorLoaderService } from '../../services/properties-loader.service';
|
import { PropertyGroupTranslatorService } from '../../services/property-groups-translator.service';
|
||||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||||
import { AspectWhiteListService } from '../../services/aspect-whitelist.service';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { CardViewBaseItemModel, CardViewComponent, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
import { CardViewBaseItemModel, CardViewComponent, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
||||||
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
|
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { ContentMetadataConfigFactory } from '../../services/config/content-metadata-config.factory';
|
||||||
|
|
||||||
describe('ContentMetadataComponent', () => {
|
describe('ContentMetadataComponent', () => {
|
||||||
|
|
||||||
@ -53,9 +53,9 @@ describe('ContentMetadataComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
ContentMetadataService,
|
ContentMetadataService,
|
||||||
BasicPropertiesService,
|
BasicPropertiesService,
|
||||||
PropertyDescriptorLoaderService,
|
PropertyGroupTranslatorService,
|
||||||
PropertyDescriptorsService,
|
PropertyDescriptorsService,
|
||||||
AspectWhiteListService,
|
ContentMetadataConfigFactory,
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
NodesApiService,
|
NodesApiService,
|
||||||
{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }
|
{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }
|
||||||
@ -69,6 +69,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
node = <MinimalNodeEntryEntity> {
|
node = <MinimalNodeEntryEntity> {
|
||||||
id: 'node-id',
|
id: 'node-id',
|
||||||
aspectNames: [],
|
aspectNames: [],
|
||||||
|
nodeType: '',
|
||||||
content: {},
|
content: {},
|
||||||
properties: {},
|
properties: {},
|
||||||
createdByUser: {},
|
createdByUser: {},
|
||||||
@ -198,19 +199,19 @@ describe('ContentMetadataComponent', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should load the aspect properties on node change', () => {
|
it('should load the group properties on node change', () => {
|
||||||
spyOn(contentMetadataService, 'getAspectProperties');
|
spyOn(contentMetadataService, 'getGroupedProperties');
|
||||||
|
|
||||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||||
|
|
||||||
expect(contentMetadataService.getAspectProperties).toHaveBeenCalledWith(expectedNode, 'custom-preset');
|
expect(contentMetadataService.getGroupedProperties).toHaveBeenCalledWith(expectedNode, 'custom-preset');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass through the loaded aspect properties to the card view', async(() => {
|
it('should pass through the loaded group properties to the card view', async(() => {
|
||||||
const expectedProperties = [];
|
const expectedProperties = [];
|
||||||
component.expanded = true;
|
component.expanded = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
spyOn(contentMetadataService, 'getAspectProperties').and.callFake(() => {
|
spyOn(contentMetadataService, 'getGroupedProperties').and.callFake(() => {
|
||||||
return Observable.of([{ properties: expectedProperties }]);
|
return Observable.of([{ properties: expectedProperties }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -218,22 +219,22 @@ describe('ContentMetadataComponent', () => {
|
|||||||
|
|
||||||
component.basicProperties$.subscribe(() => {
|
component.basicProperties$.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const firstAspectPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-properties-aspect adf-card-view')).componentInstance;
|
const firstGroupedPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-grouped-properties-container adf-card-view')).componentInstance;
|
||||||
expect(firstAspectPropertiesComponent.properties).toBe(expectedProperties);
|
expect(firstGroupedPropertiesComponent.properties).toBe(expectedProperties);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should pass through the displayEmpty to the card view of aspect properties', async(() => {
|
it('should pass through the displayEmpty to the card view of grouped properties', async(() => {
|
||||||
component.expanded = true;
|
component.expanded = true;
|
||||||
component.displayEmpty = false;
|
component.displayEmpty = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
spyOn(contentMetadataService, 'getAspectProperties').and.returnValue(Observable.of([{ properties: [] }]));
|
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(Observable.of([{ properties: [] }]));
|
||||||
|
|
||||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||||
|
|
||||||
component.basicProperties$.subscribe(() => {
|
component.basicProperties$.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const basicPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-properties-aspect adf-card-view')).componentInstance;
|
const basicPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-grouped-properties-container adf-card-view')).componentInstance;
|
||||||
expect(basicPropertiesComponent.displayEmpty).toBe(false);
|
expect(basicPropertiesComponent.displayEmpty).toBe(false);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -20,7 +20,7 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { CardViewItem, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
import { CardViewItem, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
||||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
import { CardViewAspect } from '../../interfaces/content-metadata.interfaces';
|
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-content-metadata',
|
selector: 'adf-content-metadata',
|
||||||
@ -49,7 +49,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
nodeHasBeenUpdated: boolean = false;
|
nodeHasBeenUpdated: boolean = false;
|
||||||
basicProperties$: Observable<CardViewItem[]>;
|
basicProperties$: Observable<CardViewItem[]>;
|
||||||
aspects$: Observable<CardViewAspect[]>;
|
groupedProperties$: Observable<CardViewGroup[]>;
|
||||||
|
|
||||||
constructor(private contentMetadataService: ContentMetadataService,
|
constructor(private contentMetadataService: ContentMetadataService,
|
||||||
private cardViewUpdateService: CardViewUpdateService,
|
private cardViewUpdateService: CardViewUpdateService,
|
||||||
@ -75,7 +75,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
this.nodeHasBeenUpdated = false;
|
this.nodeHasBeenUpdated = false;
|
||||||
|
|
||||||
this.basicProperties$ = this.contentMetadataService.getBasicProperties(node);
|
this.basicProperties$ = this.contentMetadataService.getBasicProperties(node);
|
||||||
this.aspects$ = this.contentMetadataService.getAspectProperties(node, this.preset);
|
this.groupedProperties$ = this.contentMetadataService.getGroupedProperties(node, this.preset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@ import { CardViewModule , FileSizePipe } from '@alfresco/adf-core';
|
|||||||
import { ContentMetadataComponent } from './components/content-metadata/content-metadata.component';
|
import { ContentMetadataComponent } from './components/content-metadata/content-metadata.component';
|
||||||
import { ContentMetadataCardComponent } from './components/content-metadata-card/content-metadata-card.component';
|
import { ContentMetadataCardComponent } from './components/content-metadata-card/content-metadata-card.component';
|
||||||
import { PropertyDescriptorsService } from './services/property-descriptors.service';
|
import { PropertyDescriptorsService } from './services/property-descriptors.service';
|
||||||
import { PropertyDescriptorLoaderService } from './services/properties-loader.service';
|
import { ContentMetadataConfigFactory } from './services/config/content-metadata-config.factory';
|
||||||
import { AspectWhiteListService } from './services/aspect-whitelist.service';
|
|
||||||
import { BasicPropertiesService } from './services/basic-properties.service';
|
import { BasicPropertiesService } from './services/basic-properties.service';
|
||||||
import { ContentMetadataService } from './services/content-metadata.service';
|
import { ContentMetadataService } from './services/content-metadata.service';
|
||||||
|
import { PropertyGroupTranslatorService } from './services/property-groups-translator.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -48,9 +48,9 @@ import { ContentMetadataService } from './services/content-metadata.service';
|
|||||||
providers: [
|
providers: [
|
||||||
ContentMetadataService,
|
ContentMetadataService,
|
||||||
PropertyDescriptorsService,
|
PropertyDescriptorsService,
|
||||||
PropertyDescriptorLoaderService,
|
ContentMetadataConfigFactory,
|
||||||
AspectWhiteListService,
|
|
||||||
BasicPropertiesService,
|
BasicPropertiesService,
|
||||||
|
PropertyGroupTranslatorService,
|
||||||
FileSizePipe
|
FileSizePipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CardViewItem} from '@alfresco/adf-core';
|
export declare interface AspectOrientedConfig {
|
||||||
|
[key: string]: string | string[];
|
||||||
export interface CardViewAspect {
|
|
||||||
name: string;
|
|
||||||
properties: CardViewItem[]
|
|
||||||
}
|
}
|
@ -15,11 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AspectProperty } from "./aspect-property.interface";
|
import { CardViewItem } from '@alfresco/adf-core';
|
||||||
|
|
||||||
export interface Aspect {
|
export interface CardViewGroup {
|
||||||
name: string;
|
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
properties: CardViewItem[];
|
||||||
properties: AspectProperty[]
|
|
||||||
}
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PropertyGroupContainer } from './property-group.interface';
|
||||||
|
import { OrganisedPropertyGroup } from './organised-property-group.interface';
|
||||||
|
|
||||||
|
export interface ContentMetadataConfig {
|
||||||
|
isGroupAllowed(groupname: string): boolean;
|
||||||
|
reorganiseByConfig(propertyGroups: PropertyGroupContainer): OrganisedPropertyGroup[];
|
||||||
|
}
|
@ -15,6 +15,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './aspect-property.interface';
|
export * from './aspect-oriented-config.interface';
|
||||||
export * from './aspect.interface';
|
export * from './property.interface';
|
||||||
export * from './card-view-aspect.interface';
|
export * from './property-group.interface';
|
||||||
|
export * from './organised-property-group.interface';
|
||||||
|
export * from './card-view-group.interface';
|
||||||
|
export * from './content-metadata-config.interface';
|
||||||
|
export * from './indifferent-config.interface';
|
||||||
|
export * from './layout-oriented-config.interface';
|
||||||
|
export * from './preset-config.interface';
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export declare type InDifferentConfig = '*';
|
@ -0,0 +1,29 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface LayoutOrientedConfigItem {
|
||||||
|
aspect?: string;
|
||||||
|
type?: string;
|
||||||
|
properties: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutOrientedConfigLayoutBlock {
|
||||||
|
title: string;
|
||||||
|
items: LayoutOrientedConfigItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutOrientedConfig extends Array<LayoutOrientedConfigLayoutBlock> {}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Property } from './property.interface';
|
||||||
|
|
||||||
|
export interface OrganisedPropertyGroup {
|
||||||
|
title: string;
|
||||||
|
properties: Property[];
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { InDifferentConfig } from './indifferent-config.interface';
|
||||||
|
import { AspectOrientedConfig } from './aspect-oriented-config.interface';
|
||||||
|
import { LayoutOrientedConfig } from './layout-oriented-config.interface';
|
||||||
|
|
||||||
|
export declare type PresetConfig = InDifferentConfig | AspectOrientedConfig | LayoutOrientedConfig;
|
@ -0,0 +1,31 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Property } from './property.interface';
|
||||||
|
|
||||||
|
export interface PropertyGroup {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
properties: {
|
||||||
|
[key: string]: Property
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PropertyGroupContainer {
|
||||||
|
[key: string]: PropertyGroup;
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface AspectProperty {
|
export interface Property {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
@ -1,75 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AspectWhiteListService {
|
|
||||||
|
|
||||||
static readonly DEFAULT_PRESET = '*';
|
|
||||||
static readonly DEFAULT_PRESET_NAME = 'default';
|
|
||||||
|
|
||||||
preset: object | string = AspectWhiteListService.DEFAULT_PRESET;
|
|
||||||
|
|
||||||
constructor(private appConfigService: AppConfigService,
|
|
||||||
private logService: LogService) {}
|
|
||||||
|
|
||||||
public choosePreset(presetName: string) {
|
|
||||||
try {
|
|
||||||
const preset = this.appConfigService.config['content-metadata'].presets[presetName];
|
|
||||||
|
|
||||||
if (preset) {
|
|
||||||
this.preset = preset;
|
|
||||||
} else if (presetName !== AspectWhiteListService.DEFAULT_PRESET_NAME) {
|
|
||||||
this.logService.error(`No content-metadata preset for: ${presetName}`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.preset = AspectWhiteListService.DEFAULT_PRESET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public isAspectAllowed(aspectName) {
|
|
||||||
if (this.isEveryAspectAllowed) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const aspectNames = Object.keys(this.preset);
|
|
||||||
return aspectNames.indexOf(aspectName) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isPropertyAllowed(aspectName, propertyName) {
|
|
||||||
if (this.isEveryAspectAllowed || this.isEveryPropertyAllowedFor(aspectName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.preset[aspectName]) {
|
|
||||||
return this.preset[aspectName].indexOf(propertyName) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isEveryAspectAllowed(): boolean {
|
|
||||||
return typeof this.preset === 'string' && this.preset === AspectWhiteListService.DEFAULT_PRESET;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isEveryPropertyAllowedFor(aspectName): boolean {
|
|
||||||
const whitedListedProperties = this.preset[aspectName];
|
|
||||||
return typeof whitedListedProperties === 'string' && whitedListedProperties === AspectWhiteListService.DEFAULT_PRESET;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ export class BasicPropertiesService {
|
|||||||
|
|
||||||
constructor(private fileSizePipe: FileSizePipe) {}
|
constructor(private fileSizePipe: FileSizePipe) {}
|
||||||
|
|
||||||
getBasicProperties(node: MinimalNodeEntryEntity) {
|
getProperties(node: MinimalNodeEntryEntity) {
|
||||||
return [
|
return [
|
||||||
new CardViewTextItemModel({
|
new CardViewTextItemModel({
|
||||||
label: 'CORE.METADATA.BASIC.NAME',
|
label: 'CORE.METADATA.BASIC.NAME',
|
||||||
|
@ -0,0 +1,178 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AspectOrientedConfigService } from './aspect-oriented-config.service';
|
||||||
|
import { AspectOrientedConfig, Property, OrganisedPropertyGroup, PropertyGroupContainer } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
describe('AspectOrientedConfigService', () => {
|
||||||
|
|
||||||
|
let configService: AspectOrientedConfigService;
|
||||||
|
|
||||||
|
function createConfigService(configObj: AspectOrientedConfig) {
|
||||||
|
return new AspectOrientedConfigService(configObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('reorganiseByConfig', () => {
|
||||||
|
|
||||||
|
interface TestCase {
|
||||||
|
name: string;
|
||||||
|
config: AspectOrientedConfig;
|
||||||
|
expectations: OrganisedPropertyGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const property1 = <Property> { name: 'property1' },
|
||||||
|
property2 = <Property> { name: 'property2' },
|
||||||
|
property3 = <Property> { name: 'property3' },
|
||||||
|
property4 = <Property> { name: 'property4' };
|
||||||
|
|
||||||
|
const propertyGroups: PropertyGroupContainer = {
|
||||||
|
berseria: { title: 'Berseria', description: '', name: 'berseria', properties: { property1, property2 } },
|
||||||
|
zestiria: { title: 'Zestiria', description: '', name: 'zestiria', properties: { property3, property4 } }
|
||||||
|
};
|
||||||
|
|
||||||
|
const testCases: TestCase[] = [
|
||||||
|
{
|
||||||
|
name: 'Empty config',
|
||||||
|
config: {},
|
||||||
|
expectations: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'One property from One group',
|
||||||
|
config: {
|
||||||
|
'berseria': [ 'property1' ]
|
||||||
|
},
|
||||||
|
expectations: [{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property1 ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'More properties from One group',
|
||||||
|
config: {
|
||||||
|
'berseria': [ 'property1', 'property2' ]
|
||||||
|
},
|
||||||
|
expectations: [{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property1, property2 ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'One-one properties from More group',
|
||||||
|
config: {
|
||||||
|
'berseria': [ 'property1' ],
|
||||||
|
'zestiria': [ 'property3' ]
|
||||||
|
},
|
||||||
|
expectations: [
|
||||||
|
{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property1 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Zestiria',
|
||||||
|
properties: [ property3 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'More properties from More groups',
|
||||||
|
config: {
|
||||||
|
'zestiria': [ 'property4', 'property3' ],
|
||||||
|
'berseria': [ 'property2', 'property1' ]
|
||||||
|
},
|
||||||
|
expectations: [
|
||||||
|
{
|
||||||
|
title: 'Zestiria',
|
||||||
|
properties: [ property4, property3 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property2, property1 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Wildcard',
|
||||||
|
config: {
|
||||||
|
'berseria': '*',
|
||||||
|
'zestiria': [ 'property4' ]
|
||||||
|
},
|
||||||
|
expectations: [
|
||||||
|
{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property1, property2 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Zestiria',
|
||||||
|
properties: [ property4 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not existing group',
|
||||||
|
config: {
|
||||||
|
'berseria': '*',
|
||||||
|
'not-existing-group': '*',
|
||||||
|
'zestiria': [ 'property4' ]
|
||||||
|
},
|
||||||
|
expectations: [
|
||||||
|
{
|
||||||
|
title: 'Berseria',
|
||||||
|
properties: [ property1, property2 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Zestiria',
|
||||||
|
properties: [ property4 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not existing property',
|
||||||
|
config: {
|
||||||
|
'berseria': [ 'not-existing-property' ],
|
||||||
|
'zestiria': [ 'property4' ]
|
||||||
|
},
|
||||||
|
expectations: [
|
||||||
|
{
|
||||||
|
title: 'Zestiria',
|
||||||
|
properties: [ property4 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach((testCase) => {
|
||||||
|
it(`should pass for: ${testCase.name}`, () => {
|
||||||
|
configService = createConfigService(testCase.config);
|
||||||
|
|
||||||
|
const organisedPropertyGroups = configService.reorganiseByConfig(propertyGroups);
|
||||||
|
|
||||||
|
expect(organisedPropertyGroups.length).toBe(testCase.expectations.length, 'Group count should match');
|
||||||
|
testCase.expectations.forEach((expectation, i) => {
|
||||||
|
expect(organisedPropertyGroups[i].title).toBe(expectation.title, 'Group\'s title should match' );
|
||||||
|
expect(organisedPropertyGroups[i].properties.length).toBe(
|
||||||
|
expectation.properties.length,
|
||||||
|
`Property count for "${organisedPropertyGroups[i].title}" group should match.`
|
||||||
|
);
|
||||||
|
|
||||||
|
expectation.properties.forEach((property, j) => {
|
||||||
|
expect(organisedPropertyGroups[i].properties[j]).toBe(property, `Property should match ${property.name}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,65 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ContentMetadataConfig, AspectOrientedConfig, OrganisedPropertyGroup, PropertyGroupContainer } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
import { getGroup, getProperty } from './property-group-reader';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AspectOrientedConfigService implements ContentMetadataConfig {
|
||||||
|
|
||||||
|
constructor(private config: AspectOrientedConfig) {}
|
||||||
|
|
||||||
|
public isGroupAllowed(groupName: string): boolean {
|
||||||
|
const groupNames = Object.keys(this.config);
|
||||||
|
return groupNames.indexOf(groupName) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reorganiseByConfig(propertyGroups: PropertyGroupContainer): OrganisedPropertyGroup[] {
|
||||||
|
const aspects = this.config,
|
||||||
|
aspectNames = Object.keys(aspects);
|
||||||
|
|
||||||
|
return aspectNames
|
||||||
|
.reduce((groupAccumulator, aspectName) => {
|
||||||
|
const newGroup = this.getOrganisedPropertyGroup(propertyGroups, aspectName);
|
||||||
|
return groupAccumulator.concat(newGroup);
|
||||||
|
}, [])
|
||||||
|
.filter(organisedPropertyGroup => organisedPropertyGroup.properties.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOrganisedPropertyGroup(propertyGroups, aspectName) {
|
||||||
|
const group = getGroup(propertyGroups, aspectName);
|
||||||
|
let newGroup = [];
|
||||||
|
|
||||||
|
if (group) {
|
||||||
|
const aspectProperties = this.config[aspectName];
|
||||||
|
let properties;
|
||||||
|
|
||||||
|
if (aspectProperties === '*') {
|
||||||
|
properties = getProperty(propertyGroups, aspectName, aspectProperties);
|
||||||
|
} else {
|
||||||
|
properties = (<string[]> aspectProperties)
|
||||||
|
.map((propertyName) => getProperty(propertyGroups, aspectName, propertyName))
|
||||||
|
.filter(props => props !== undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
newGroup = [ { title: group.title, properties } ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGroup;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||||
|
import { IndifferentConfigService } from './indifferent-config.service';
|
||||||
|
import { AspectOrientedConfigService } from './aspect-oriented-config.service';
|
||||||
|
import { LayoutOrientedConfigService } from './layout-oriented-config.service';
|
||||||
|
import { ContentMetadataConfigFactory } from './content-metadata-config.factory';
|
||||||
|
import { ContentMetadataConfig } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
describe('ContentMetadataConfigFactory', () => {
|
||||||
|
|
||||||
|
let factory: ContentMetadataConfigFactory,
|
||||||
|
appConfig: AppConfigService,
|
||||||
|
config: ContentMetadataConfig;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
ContentMetadataConfigFactory,
|
||||||
|
AppConfigService,
|
||||||
|
{ provide: LogService, useValue: { error: () => {} }}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
factory = TestBed.get(ContentMetadataConfigFactory);
|
||||||
|
appConfig = TestBed.get(AppConfigService);
|
||||||
|
});
|
||||||
|
|
||||||
|
function setConfig(presetName, presetConfig) {
|
||||||
|
appConfig.config['content-metadata'] = {
|
||||||
|
presets: {
|
||||||
|
[presetName]: presetConfig
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get', () => {
|
||||||
|
|
||||||
|
let logService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
logService = TestBed.get(LogService);
|
||||||
|
spyOn(logService, 'error').and.stub();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back to default preset if no preset is provided as parameter', () => {
|
||||||
|
config = factory.get();
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back to default preset if no preset is set', () => {
|
||||||
|
config = factory.get('default');
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||||
|
expect(logService.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back to the default preset if the requested preset does not exist', () => {
|
||||||
|
config = factory.get('not-existing-preset');
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log an error message if the requested preset does not exist', () => {
|
||||||
|
config = factory.get('not-existing-preset');
|
||||||
|
|
||||||
|
expect(logService.error).toHaveBeenCalledWith('No content-metadata preset for: not-existing-preset');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back the IndifferentConfigService preset if the preset config is indifferent', () => {
|
||||||
|
setConfig('default', '*');
|
||||||
|
|
||||||
|
config = factory.get('default');
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back the AspectOrientedConfigService preset if the preset config is aspect oriented', () => {
|
||||||
|
setConfig('default', { 'exif:exif' : '*'});
|
||||||
|
|
||||||
|
config = factory.get('default');
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(AspectOrientedConfigService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get back the LayoutOrientedConfigService preset if the preset config is layout oriented', () => {
|
||||||
|
setConfig('default', []);
|
||||||
|
|
||||||
|
config = factory.get('default');
|
||||||
|
|
||||||
|
expect(config).toEqual(jasmine.any(LayoutOrientedConfigService));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,79 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||||
|
import { AspectOrientedConfigService } from './aspect-oriented-config.service';
|
||||||
|
import { IndifferentConfigService } from './indifferent-config.service';
|
||||||
|
import { LayoutOrientedConfigService } from './layout-oriented-config.service';
|
||||||
|
import {
|
||||||
|
PresetConfig,
|
||||||
|
ContentMetadataConfig,
|
||||||
|
AspectOrientedConfig,
|
||||||
|
InDifferentConfig,
|
||||||
|
LayoutOrientedConfig
|
||||||
|
} from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContentMetadataConfigFactory {
|
||||||
|
|
||||||
|
static readonly INDIFFERENT_PRESET = '*';
|
||||||
|
static readonly DEFAULT_PRESET_NAME = 'default';
|
||||||
|
|
||||||
|
constructor(private appConfigService: AppConfigService, private logService: LogService) {}
|
||||||
|
|
||||||
|
public get(presetName: string = 'default'): ContentMetadataConfig {
|
||||||
|
let presetConfig;
|
||||||
|
try {
|
||||||
|
presetConfig = this.appConfigService.config['content-metadata'].presets[presetName];
|
||||||
|
} catch {
|
||||||
|
if (presetName !== ContentMetadataConfigFactory.DEFAULT_PRESET_NAME) {
|
||||||
|
this.logService.error(`No content-metadata preset for: ${presetName}`);
|
||||||
|
}
|
||||||
|
presetConfig = ContentMetadataConfigFactory.INDIFFERENT_PRESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.createConfig(presetConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createConfig(presetConfig: PresetConfig): ContentMetadataConfig {
|
||||||
|
let config: ContentMetadataConfig;
|
||||||
|
|
||||||
|
if (this.isLayoutOrientedPreset(presetConfig)) {
|
||||||
|
config = new LayoutOrientedConfigService(<LayoutOrientedConfig> presetConfig);
|
||||||
|
} else if (this.isAspectOrientedPreset(presetConfig)) {
|
||||||
|
config = new AspectOrientedConfigService(<AspectOrientedConfig> presetConfig);
|
||||||
|
} else {
|
||||||
|
config = new IndifferentConfigService(<InDifferentConfig> presetConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.freeze(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAspectOrientedPreset(presetConfig: PresetConfig): boolean {
|
||||||
|
return this.isObject(presetConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLayoutOrientedPreset(presetConfig: PresetConfig): boolean {
|
||||||
|
return Array.isArray(presetConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isObject(x) {
|
||||||
|
return x != null && typeof x === 'object';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ContentMetadataConfig,
|
||||||
|
InDifferentConfig,
|
||||||
|
OrganisedPropertyGroup,
|
||||||
|
PropertyGroupContainer
|
||||||
|
} from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class IndifferentConfigService implements ContentMetadataConfig {
|
||||||
|
|
||||||
|
constructor(config: InDifferentConfig) {}
|
||||||
|
|
||||||
|
public isGroupAllowed(groupName: string): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reorganiseByConfig(propertyGroups: PropertyGroupContainer): OrganisedPropertyGroup[] {
|
||||||
|
return Object.keys(propertyGroups)
|
||||||
|
.map((groupName) => {
|
||||||
|
const propertyGroup = propertyGroups[groupName],
|
||||||
|
properties = propertyGroup.properties;
|
||||||
|
|
||||||
|
return Object.assign({}, propertyGroup, {
|
||||||
|
properties: Object.keys(properties).map(propertyName => properties[propertyName])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,247 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { LayoutOrientedConfigService } from './layout-oriented-config.service';
|
||||||
|
import { LayoutOrientedConfig, Property, OrganisedPropertyGroup, PropertyGroupContainer } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
describe('LayoutOrientedConfigService', () => {
|
||||||
|
|
||||||
|
let configService: LayoutOrientedConfigService;
|
||||||
|
|
||||||
|
function createConfigService(configObj: LayoutOrientedConfig) {
|
||||||
|
return new LayoutOrientedConfigService(configObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('isGroupAllowed', () => {
|
||||||
|
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
config: [],
|
||||||
|
expectation: false,
|
||||||
|
groupNameToQuery: 'berseria'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: [{ title: 'Deamons', items: [{ aspect: 'berseria', properties: '*' }] }],
|
||||||
|
expectation: true,
|
||||||
|
groupNameToQuery: 'berseria'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: [{ title: 'Deamons', items: [{ type: 'berseria', properties: '*' }] }],
|
||||||
|
expectation: true,
|
||||||
|
groupNameToQuery: 'berseria'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: [{ title: 'Deamons', items: [
|
||||||
|
{ aspect: 'zestiria', properties: '*' }, { aspect: 'berseria', properties: '*' }
|
||||||
|
]}],
|
||||||
|
expectation: true,
|
||||||
|
groupNameToQuery: 'berseria'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: [
|
||||||
|
{ title: 'Deamons', items: [{ aspect: 'zestiria', properties: '*' }] },
|
||||||
|
{ title: 'Malakhims', items: [{ aspect: 'berseria', properties: '*' }] }
|
||||||
|
],
|
||||||
|
expectation: true,
|
||||||
|
groupNameToQuery: 'berseria'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: [
|
||||||
|
{ title: 'Deamons', items: [{ aspect: 'zestiria', properties: '*' }] },
|
||||||
|
{ title: 'Malakhims', items: [{ type: 'berseria', properties: '*' }] }
|
||||||
|
],
|
||||||
|
expectation: false,
|
||||||
|
groupNameToQuery: 'phantasia'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach((testCase, index) => {
|
||||||
|
it(`should return ${testCase.expectation.toString()} for test case index #${index}`, () => {
|
||||||
|
configService = createConfigService(testCase.config);
|
||||||
|
|
||||||
|
const isAllowed = configService.isGroupAllowed(testCase.groupNameToQuery);
|
||||||
|
|
||||||
|
expect(isAllowed).toBe(testCase.expectation);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reorganiseByConfig', () => {
|
||||||
|
|
||||||
|
interface TestCase {
|
||||||
|
name: string;
|
||||||
|
config: LayoutOrientedConfig;
|
||||||
|
expectations: OrganisedPropertyGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const property1 = <Property> { name: 'property1' },
|
||||||
|
property2 = <Property> { name: 'property2' },
|
||||||
|
property3 = <Property> { name: 'property3' },
|
||||||
|
property4 = <Property> { name: 'property4' };
|
||||||
|
|
||||||
|
const propertyGroups: PropertyGroupContainer = {
|
||||||
|
berseria: { title: 'Berseria', description: '', name: 'berseria', properties: { property1, property2 } },
|
||||||
|
zestiria: { title: 'Zestiria', description: '', name: 'zestiria', properties: { property3, property4 } }
|
||||||
|
};
|
||||||
|
|
||||||
|
const testCases: TestCase[] = [
|
||||||
|
{
|
||||||
|
name: 'Empty config',
|
||||||
|
config: [],
|
||||||
|
expectations: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'First property of a group in one item',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'berseria', properties: [ 'property1' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property1 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Second property of a group in one item',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'berseria', properties: [ 'property2' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property2 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'More properties from one group in one item',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'berseria', properties: [ 'property2', 'property1' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property2, property1 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'First property of the second group in one item',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: [ 'property4' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property4 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'One-one properties from multiple groups in one item',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: [ 'property4' ] },
|
||||||
|
{ aspect: 'berseria', properties: [ 'property1' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property4, property1 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Multiple properties mixed from multiple groups in multiple items',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: [ 'property4' ] },
|
||||||
|
{ type: 'berseria', properties: [ 'property1' ] }
|
||||||
|
]},
|
||||||
|
{ title: 'Second group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: [ 'property3' ] },
|
||||||
|
{ type: 'berseria', properties: [ 'property2', 'property1' ] },
|
||||||
|
{ aspect: 'zestiria', properties: [ 'property4' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property4, property1 ] },
|
||||||
|
{ title: 'Second group', properties: [ property3, property2, property1, property4 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Multiple properties mixed from multiple groups in multiple items with "*"',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: '*' },
|
||||||
|
{ type: 'berseria', properties: [ 'property1' ] }
|
||||||
|
]},
|
||||||
|
{ title: 'Second group', items: [
|
||||||
|
{ type: 'berseria', properties: [ 'property2', 'property1' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property3, property4, property1 ] },
|
||||||
|
{ title: 'Second group', properties: [ property2, property1 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not existing property',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: '*' },
|
||||||
|
{ type: 'berseria', properties: [ 'not-existing-property' ] },
|
||||||
|
{ type: 'berseria', properties: [ 'property2' ] }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property3, property4, property2 ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not existing group',
|
||||||
|
config: [
|
||||||
|
{ title: 'First group', items: [
|
||||||
|
{ aspect: 'zestiria', properties: '*' },
|
||||||
|
{ type: 'not-existing-group', properties: '*' },
|
||||||
|
{ type: 'berseria', properties: [ 'property2' ] },
|
||||||
|
{ type: 'not-existing-group', properties: 'not-existing-property' }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
expectations: [
|
||||||
|
{ title: 'First group', properties: [ property3, property4, property2 ] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach((testCase) => {
|
||||||
|
it(`should pass for: ${testCase.name}`, () => {
|
||||||
|
configService = createConfigService(testCase.config);
|
||||||
|
|
||||||
|
const organisedPropertyGroups = configService.reorganiseByConfig(propertyGroups);
|
||||||
|
|
||||||
|
expect(organisedPropertyGroups.length).toBe(testCase.expectations.length, 'Group count should match');
|
||||||
|
testCase.expectations.forEach((expectation, i) => {
|
||||||
|
expect(organisedPropertyGroups[i].title).toBe(expectation.title, 'Group\'s title should match' );
|
||||||
|
expect(organisedPropertyGroups[i].properties.length).toBe(
|
||||||
|
expectation.properties.length,
|
||||||
|
`Property count for "${organisedPropertyGroups[i].title}" group should match.`
|
||||||
|
);
|
||||||
|
|
||||||
|
expectation.properties.forEach((property, j) => {
|
||||||
|
expect(organisedPropertyGroups[i].properties[j]).toBe(property, `Property should match ${property.name}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,74 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ContentMetadataConfig,
|
||||||
|
LayoutOrientedConfig,
|
||||||
|
LayoutOrientedConfigItem,
|
||||||
|
OrganisedPropertyGroup,
|
||||||
|
PropertyGroupContainer
|
||||||
|
} from '../../interfaces/content-metadata.interfaces';
|
||||||
|
import { getProperty } from './property-group-reader';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LayoutOrientedConfigService implements ContentMetadataConfig {
|
||||||
|
|
||||||
|
constructor(private config: LayoutOrientedConfig) {}
|
||||||
|
|
||||||
|
public isGroupAllowed(groupName: string): boolean {
|
||||||
|
return this.getMatchingGroups(groupName).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reorganiseByConfig(propertyGroups: PropertyGroupContainer): OrganisedPropertyGroup[] {
|
||||||
|
const layoutBlocks = this.config;
|
||||||
|
|
||||||
|
return layoutBlocks.map((layoutBlock) => {
|
||||||
|
const flattenedItems = this.flattenItems(layoutBlock.items),
|
||||||
|
properties = flattenedItems.reduce((props, explodedItem) => {
|
||||||
|
const property = getProperty(propertyGroups, explodedItem.groupName, explodedItem.propertyName) || [];
|
||||||
|
return props.concat(property);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: layoutBlock.title,
|
||||||
|
properties
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private flattenItems(items) {
|
||||||
|
return items.reduce((accumulator, item) => {
|
||||||
|
const properties = Array.isArray(item.properties) ? item.properties : [item.properties];
|
||||||
|
const flattenedProperties = properties.map(propertyName => {
|
||||||
|
return {
|
||||||
|
groupName: item.aspect || item.type,
|
||||||
|
propertyName
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return accumulator.concat(flattenedProperties);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMatchingGroups(groupName: string): LayoutOrientedConfigItem[] {
|
||||||
|
return this.config
|
||||||
|
.map(layoutBlock => layoutBlock.items)
|
||||||
|
.reduce((accumulator, items) => accumulator.concat(items), [])
|
||||||
|
.filter((item) => item.aspect === groupName || item.type === groupName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PropertyGroup, Property, PropertyGroupContainer } from '../../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
const emptyGroup = {
|
||||||
|
properties: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertObjectToArray(object: any): Property[] {
|
||||||
|
return Object.keys(object).map(key => object[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGroup(propertyGroups: PropertyGroupContainer, groupName: string): PropertyGroup | undefined {
|
||||||
|
return propertyGroups[groupName];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProperty(propertyGroups: PropertyGroupContainer, groupName: string, propertyName: string): Property | Property[] | undefined {
|
||||||
|
const groupDefinition = getGroup(propertyGroups, groupName) || emptyGroup;
|
||||||
|
let propertyDefinitions;
|
||||||
|
|
||||||
|
if (propertyName === '*') {
|
||||||
|
propertyDefinitions = convertObjectToArray(groupDefinition.properties);
|
||||||
|
} else {
|
||||||
|
propertyDefinitions = groupDefinition.properties[propertyName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyDefinitions;
|
||||||
|
}
|
@ -1,325 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
|
||||||
import { ContentMetadataService } from './content-metadata.service';
|
|
||||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
|
||||||
import { BasicPropertiesService } from './basic-properties.service';
|
|
||||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
|
||||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { Aspect, AspectProperty } from '../interfaces/content-metadata.interfaces';
|
|
||||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
|
||||||
import {
|
|
||||||
CardViewTextItemModel,
|
|
||||||
CardViewDateItemModel,
|
|
||||||
CardViewIntItemModel,
|
|
||||||
CardViewFloatItemModel,
|
|
||||||
LogService,
|
|
||||||
CardViewBoolItemModel,
|
|
||||||
CardViewDatetimeItemModel
|
|
||||||
} from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
describe('ContentMetadataService', () => {
|
|
||||||
|
|
||||||
let service: ContentMetadataService,
|
|
||||||
descriptorsService: PropertyDescriptorsService,
|
|
||||||
aspects: Aspect[],
|
|
||||||
aspect: Aspect,
|
|
||||||
aspectProperty: AspectProperty,
|
|
||||||
node: MinimalNodeEntryEntity;
|
|
||||||
|
|
||||||
const dummyPreset = 'default';
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
ContentMetadataService,
|
|
||||||
BasicPropertiesService,
|
|
||||||
AspectWhiteListService,
|
|
||||||
PropertyDescriptorLoaderService,
|
|
||||||
AlfrescoApiService,
|
|
||||||
{ provide: LogService, useValue: { error: () => {} }},
|
|
||||||
PropertyDescriptorsService
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = TestBed.get(ContentMetadataService);
|
|
||||||
descriptorsService = TestBed.get(PropertyDescriptorsService);
|
|
||||||
|
|
||||||
node = <MinimalNodeEntryEntity> { properties: {} };
|
|
||||||
aspectProperty = {
|
|
||||||
name: 'FAS:PLAGUE',
|
|
||||||
title: 'The Faro Plague',
|
|
||||||
dataType: '',
|
|
||||||
defaultValue: '',
|
|
||||||
mandatory: false,
|
|
||||||
multiValued: false
|
|
||||||
};
|
|
||||||
aspect = {
|
|
||||||
name: 'FAS:FAS',
|
|
||||||
title: 'Faro Automated Solutions',
|
|
||||||
description: 'Faro Automated Solutions is an old Earth corporation that manufactured robots in the mid-21st century.',
|
|
||||||
properties: [aspectProperty]
|
|
||||||
};
|
|
||||||
aspects = [];
|
|
||||||
spyOn(descriptorsService, 'getAspects').and.returnValue(Observable.of(aspects));
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
TestBed.resetTestingModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('General transformation', () => {
|
|
||||||
|
|
||||||
it('should translate more properties in an aspect properly', () => {
|
|
||||||
aspect.properties = [{
|
|
||||||
name: 'FAS:PLAGUE',
|
|
||||||
title: 'title',
|
|
||||||
dataType: 'd:text',
|
|
||||||
defaultValue: 'defaultValue',
|
|
||||||
mandatory: false,
|
|
||||||
multiValued: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'FAS:ALOY',
|
|
||||||
title: 'title',
|
|
||||||
dataType: 'd:text',
|
|
||||||
defaultValue: 'defaultValue',
|
|
||||||
mandatory: false,
|
|
||||||
multiValued: false
|
|
||||||
}];
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
expect(cardViewAspect[0].properties.length).toBe(2);
|
|
||||||
expect(cardViewAspect[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First property should be instance of CardViewTextItemModel');
|
|
||||||
expect(cardViewAspect[0].properties[1] instanceof CardViewTextItemModel).toBeTruthy('Second property should be instance of CardViewTextItemModel');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate every property in every aspect properly', () => {
|
|
||||||
aspects.push(
|
|
||||||
Object.assign({}, aspect, {
|
|
||||||
properties: [{
|
|
||||||
name: 'FAS:PLAGUE',
|
|
||||||
title: 'title',
|
|
||||||
dataType: 'd:text',
|
|
||||||
defaultValue: 'defaultvalue',
|
|
||||||
mandatory: false,
|
|
||||||
multiValued: false
|
|
||||||
}]
|
|
||||||
}),
|
|
||||||
Object.assign({}, aspect, {
|
|
||||||
properties: [{
|
|
||||||
name: 'FAS:ALOY',
|
|
||||||
title: 'title',
|
|
||||||
dataType: 'd:text',
|
|
||||||
defaultValue: 'defaultvalue',
|
|
||||||
mandatory: false,
|
|
||||||
multiValued: false
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
expect(cardViewAspect.length).toBe(2);
|
|
||||||
expect(cardViewAspect[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First aspect\'s property should be instance of CardViewTextItemModel');
|
|
||||||
expect(cardViewAspect[1].properties[0] instanceof CardViewTextItemModel).toBeTruthy('Second aspect\'s property should be instance of CardViewTextItemModel');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log an error if unrecognised type is found', () => {
|
|
||||||
const logService = TestBed.get(LogService);
|
|
||||||
spyOn(logService, 'error').and.stub();
|
|
||||||
|
|
||||||
aspectProperty.name = 'FAS:PLAGUE';
|
|
||||||
aspectProperty.title = 'The Faro Plague';
|
|
||||||
aspectProperty.dataType = 'daemonic:scorcher';
|
|
||||||
aspectProperty.defaultValue = 'Daemonic beast';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
expect(logService.error).toHaveBeenCalledWith('Unknown type for mapping: daemonic:scorcher');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fall back to singleline property type if unrecognised type is found', () => {
|
|
||||||
aspectProperty.name = 'FAS:PLAGUE';
|
|
||||||
aspectProperty.title = 'The Faro Plague';
|
|
||||||
aspectProperty.dataType = 'daemonic:scorcher';
|
|
||||||
aspectProperty.defaultValue = 'Daemonic beast';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Different types\'s attributes', () => {
|
|
||||||
|
|
||||||
ContentMetadataService.RECOGNISED_ECM_TYPES.forEach((dataType) => {
|
|
||||||
it(`should translate properly the basic attributes of a property for ${dataType}`, () => {
|
|
||||||
aspectProperty.name = 'prefix:name';
|
|
||||||
aspectProperty.title = 'title';
|
|
||||||
aspectProperty.defaultValue = 'default value';
|
|
||||||
aspectProperty.dataType = dataType;
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'prefix:name': null };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property = cardViewAspect[0].properties[0];
|
|
||||||
expect(property.label).toBe(aspectProperty.title);
|
|
||||||
expect(property.key).toBe('properties.prefix:name');
|
|
||||||
expect(property.default).toBe(aspectProperty.defaultValue);
|
|
||||||
expect(property.editable).toBeTruthy('Property should be editable');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the multiline and value attributes for d:text', () => {
|
|
||||||
aspectProperty.dataType = 'd:text';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
|
||||||
expect(property.value).toBe('The Chariot Line');
|
|
||||||
expect(property.multiline).toBeFalsy('Property should be singleline');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the multiline and value attributes for d:mltext', () => {
|
|
||||||
aspectProperty.dataType = 'd:mltext';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
|
||||||
expect(property.value).toBe('The Chariot Line');
|
|
||||||
expect(property.multiline).toBeTruthy('Property should be multiline');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:date', () => {
|
|
||||||
const expectedValue = new Date().toISOString();
|
|
||||||
aspectProperty.dataType = 'd:date';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': expectedValue };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewDateItemModel = <CardViewDateItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewDateItemModel).toBeTruthy('Property should be instance of CardViewDateItemModel');
|
|
||||||
expect(property.value).toBe(expectedValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:date', () => {
|
|
||||||
const expectedValue = new Date().toISOString();
|
|
||||||
aspectProperty.dataType = 'd:datetime';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': expectedValue };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewDatetimeItemModel = <CardViewDatetimeItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewDatetimeItemModel).toBeTruthy('Property should be instance of CardViewDatetimeItemModel');
|
|
||||||
expect(property.value).toBe(expectedValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:int', () => {
|
|
||||||
aspectProperty.dataType = 'd:int';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': '1024' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewIntItemModel = <CardViewIntItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
|
||||||
expect(property.value).toBe(1024);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:long', () => {
|
|
||||||
aspectProperty.dataType = 'd:long';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': '1024' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewIntItemModel = <CardViewIntItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
|
||||||
expect(property.value).toBe(1024);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:float', () => {
|
|
||||||
aspectProperty.dataType = 'd:float';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': '1024.24' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
|
||||||
expect(property.value).toBe(1024.24);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:double', () => {
|
|
||||||
aspectProperty.dataType = 'd:double';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': '1024.24' };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
|
||||||
expect(property.value).toBe(1024.24);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should translate properly the value attribute for d:boolean', () => {
|
|
||||||
aspectProperty.dataType = 'd:boolean';
|
|
||||||
aspects.push(aspect);
|
|
||||||
|
|
||||||
node.properties = { 'FAS:PLAGUE': true };
|
|
||||||
|
|
||||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
|
||||||
const property: CardViewBoolItemModel = <CardViewBoolItemModel> cardViewAspect[0].properties[0];
|
|
||||||
expect(property instanceof CardViewBoolItemModel).toBeTruthy('Property should be instance of CardViewBoolItemModel');
|
|
||||||
expect(property.value).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -17,123 +17,44 @@
|
|||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
|
||||||
import { BasicPropertiesService } from './basic-properties.service';
|
import { BasicPropertiesService } from './basic-properties.service';
|
||||||
import {
|
|
||||||
CardViewItemProperties,
|
|
||||||
CardViewItem,
|
|
||||||
CardViewTextItemModel,
|
|
||||||
CardViewBoolItemModel,
|
|
||||||
CardViewDateItemModel,
|
|
||||||
CardViewDatetimeItemModel,
|
|
||||||
CardViewIntItemModel,
|
|
||||||
CardViewFloatItemModel,
|
|
||||||
LogService
|
|
||||||
} from '@alfresco/adf-core';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { AspectProperty, CardViewAspect, Aspect } from '../interfaces/content-metadata.interfaces';
|
import { PropertyGroupTranslatorService } from './property-groups-translator.service';
|
||||||
|
import { CardViewItem } from '@alfresco/adf-core';
|
||||||
const D_TEXT = 'd:text';
|
import { CardViewGroup } from '../interfaces/content-metadata.interfaces';
|
||||||
const D_MLTEXT = 'd:mltext';
|
import { ContentMetadataConfigFactory } from './config/content-metadata-config.factory';
|
||||||
const D_DATE = 'd:date';
|
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||||
const D_DATETIME = 'd:datetime';
|
|
||||||
const D_INT = 'd:int';
|
|
||||||
const D_LONG = 'd:long';
|
|
||||||
const D_FLOAT = 'd:float';
|
|
||||||
const D_DOUBLE = 'd:double';
|
|
||||||
const D_BOOLEAN = 'd:boolean';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ContentMetadataService {
|
export class ContentMetadataService {
|
||||||
|
|
||||||
static readonly RECOGNISED_ECM_TYPES = [ D_TEXT, D_MLTEXT, D_DATE, D_DATETIME, D_INT, D_LONG , D_FLOAT, D_DOUBLE, D_BOOLEAN ];
|
constructor(
|
||||||
|
private basicPropertiesService: BasicPropertiesService,
|
||||||
constructor(private basicPropertiesService: BasicPropertiesService,
|
private contentMetadataConfigFactory: ContentMetadataConfigFactory,
|
||||||
private propertyDescriptorsService: PropertyDescriptorsService,
|
private propertyGroupTranslatorService: PropertyGroupTranslatorService,
|
||||||
private logService: LogService) {}
|
private propertyDescriptorsService: PropertyDescriptorsService
|
||||||
|
) {}
|
||||||
|
|
||||||
getBasicProperties(node: MinimalNodeEntryEntity): Observable<CardViewItem[]> {
|
getBasicProperties(node: MinimalNodeEntryEntity): Observable<CardViewItem[]> {
|
||||||
return Observable.of(this.basicPropertiesService.getBasicProperties(node));
|
return Observable.of(this.basicPropertiesService.getProperties(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAspectProperties(node: MinimalNodeEntryEntity, preset: string): Observable<CardViewAspect[]> {
|
getGroupedProperties(node: MinimalNodeEntryEntity, presetName: string = 'default'): Observable<CardViewGroup[]> {
|
||||||
return this.propertyDescriptorsService.getAspects(node, preset)
|
const config = this.contentMetadataConfigFactory.get(presetName),
|
||||||
.map(aspects => this.translateAspects(aspects, node.properties));
|
groupNames = node.aspectNames
|
||||||
}
|
.concat(node.nodeType)
|
||||||
|
.filter(groupName => config.isGroupAllowed(groupName));
|
||||||
|
|
||||||
private translateAspects(aspects: Aspect[], nodeProperties): CardViewAspect[] {
|
let groupedProperties;
|
||||||
return aspects.map(aspect => {
|
|
||||||
const translatedAspect: any = Object.assign({}, aspect);
|
|
||||||
translatedAspect.properties = this.translateProperties(aspect.properties, nodeProperties);
|
|
||||||
return translatedAspect;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private translateProperties(aspectProperties: AspectProperty[], nodeProperties: any): CardViewItem[] {
|
if (groupNames.length > 0) {
|
||||||
return aspectProperties.map(aspectProperty => {
|
groupedProperties = this.propertyDescriptorsService.load(groupNames)
|
||||||
return this.translateProperty(aspectProperty, nodeProperties[aspectProperty.name]);
|
.map(groups => config.reorganiseByConfig(groups))
|
||||||
});
|
.map(groups => this.propertyGroupTranslatorService.translateToCardViewGroups(groups, node.properties));
|
||||||
}
|
} else {
|
||||||
|
groupedProperties = Observable.of([]);
|
||||||
private translateProperty(aspectProperty: AspectProperty, nodeProperty: any): CardViewItem {
|
|
||||||
this.checkECMTypeValidity(aspectProperty.dataType);
|
|
||||||
|
|
||||||
let propertyDefinition: CardViewItemProperties = {
|
|
||||||
label: aspectProperty.title,
|
|
||||||
value: nodeProperty,
|
|
||||||
key: this.getAspectPropertyKey(aspectProperty.name),
|
|
||||||
default: aspectProperty.defaultValue,
|
|
||||||
editable: true
|
|
||||||
};
|
|
||||||
let property;
|
|
||||||
|
|
||||||
switch (aspectProperty.dataType) {
|
|
||||||
|
|
||||||
case D_MLTEXT:
|
|
||||||
property = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
|
||||||
multiline: true
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_INT:
|
|
||||||
case D_LONG:
|
|
||||||
property = new CardViewIntItemModel(propertyDefinition);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_FLOAT:
|
|
||||||
case D_DOUBLE:
|
|
||||||
property = new CardViewFloatItemModel(propertyDefinition);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_DATE:
|
|
||||||
property = new CardViewDateItemModel(propertyDefinition);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_DATETIME:
|
|
||||||
property = new CardViewDatetimeItemModel(propertyDefinition);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_BOOLEAN:
|
|
||||||
property = new CardViewBoolItemModel(propertyDefinition);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case D_TEXT:
|
|
||||||
default:
|
|
||||||
property = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
|
||||||
multiline: false
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return property;
|
return groupedProperties;
|
||||||
}
|
|
||||||
|
|
||||||
private checkECMTypeValidity(ecmPropertyType) {
|
|
||||||
if (ContentMetadataService.RECOGNISED_ECM_TYPES.indexOf(ecmPropertyType) === -1) {
|
|
||||||
this.logService.error(`Unknown type for mapping: ${ecmPropertyType}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getAspectPropertyKey(propertyName) {
|
|
||||||
return `properties.${propertyName}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
|
||||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { ClassesApi } from 'alfresco-js-api';
|
|
||||||
|
|
||||||
describe('PropertyDescriptorLoaderService', () => {
|
|
||||||
|
|
||||||
let aspectProperties: PropertyDescriptorLoaderService,
|
|
||||||
classesApi: ClassesApi;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
PropertyDescriptorLoaderService,
|
|
||||||
AlfrescoApiService
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
aspectProperties = TestBed.get(PropertyDescriptorLoaderService);
|
|
||||||
const alfrescoApiService = TestBed.get(AlfrescoApiService);
|
|
||||||
classesApi = alfrescoApiService.classesApi;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
TestBed.resetTestingModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load the aspects passed by paramter', () => {
|
|
||||||
spyOn(classesApi, 'getClass');
|
|
||||||
|
|
||||||
aspectProperties.load(['exif:exif', 'cm:content', 'custom:custom'])
|
|
||||||
.subscribe(() => {});
|
|
||||||
|
|
||||||
expect(classesApi.getClass).toHaveBeenCalledTimes(3);
|
|
||||||
expect(classesApi.getClass).toHaveBeenCalledWith('exif_exif');
|
|
||||||
expect(classesApi.getClass).toHaveBeenCalledWith('cm_content');
|
|
||||||
expect(classesApi.getClass).toHaveBeenCalledWith('custom_custom');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should merge the forked values', (done) => {
|
|
||||||
|
|
||||||
const exifResponse = {
|
|
||||||
name: 'exif:exif',
|
|
||||||
id: 1,
|
|
||||||
properties: {
|
|
||||||
'exif:1': { id: 'exif:1:id', name: 'exif:1' },
|
|
||||||
'exif:2': { id: 'exif:2:id', name: 'exif:2' }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const contentResponse = {
|
|
||||||
name: 'cm:content',
|
|
||||||
id: 2,
|
|
||||||
properties: {
|
|
||||||
'cm:content': { id: 'cm:content:id', name: 'cm:content' }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const apiResponses = [ exifResponse, contentResponse ];
|
|
||||||
let counter = 0;
|
|
||||||
|
|
||||||
spyOn(classesApi, 'getClass').and.callFake(() => {
|
|
||||||
return Observable.of(apiResponses[counter++]);
|
|
||||||
});
|
|
||||||
|
|
||||||
aspectProperties.load(['exif:exif', 'cm:content'])
|
|
||||||
.subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
expect(data[0]).toBe(exifResponse);
|
|
||||||
expect(data[1]).toBe(contentResponse);
|
|
||||||
},
|
|
||||||
complete: done
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,36 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright 2016 Alfresco Software, Ltd.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
|
||||||
import { forkJoin } from 'rxjs/observable/forkJoin';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { defer } from 'rxjs/observable/defer';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PropertyDescriptorLoaderService {
|
|
||||||
|
|
||||||
constructor(private alfrescoApiService: AlfrescoApiService) {}
|
|
||||||
|
|
||||||
load(aspects: string[]): Observable<any> {
|
|
||||||
const aspectFetchStreams = aspects
|
|
||||||
.map(aspectName => aspectName.replace(':', '_'))
|
|
||||||
.map(aspectName => defer( () => this.alfrescoApiService.classesApi.getClass(aspectName)) );
|
|
||||||
|
|
||||||
return forkJoin(aspectFetchStreams);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,193 +17,80 @@
|
|||||||
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
|
||||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
|
||||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { AspectProperty } from '../interfaces/content-metadata.interfaces';
|
import { ClassesApi } from 'alfresco-js-api';
|
||||||
|
import { PropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
describe('PropertyDescriptorsService', () => {
|
describe('PropertyDescriptorLoaderService', () => {
|
||||||
|
|
||||||
let contentMetadataService: PropertyDescriptorsService,
|
let service: PropertyDescriptorsService,
|
||||||
aspectProperties: PropertyDescriptorLoaderService,
|
classesApi: ClassesApi;
|
||||||
appConfigService: AppConfigService,
|
|
||||||
logService: LogService,
|
|
||||||
node: MinimalNodeEntryEntity,
|
|
||||||
testPresets: any;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
PropertyDescriptorsService,
|
PropertyDescriptorsService,
|
||||||
PropertyDescriptorLoaderService,
|
|
||||||
AppConfigService,
|
|
||||||
AspectWhiteListService,
|
|
||||||
{ provide: LogService, useValue: { error: () => {} }},
|
|
||||||
AlfrescoApiService
|
AlfrescoApiService
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
contentMetadataService = TestBed.get(PropertyDescriptorsService);
|
service = TestBed.get(PropertyDescriptorsService);
|
||||||
aspectProperties = TestBed.get(PropertyDescriptorLoaderService);
|
const alfrescoApiService = TestBed.get(AlfrescoApiService);
|
||||||
appConfigService = TestBed.get(AppConfigService);
|
classesApi = alfrescoApiService.classesApi;
|
||||||
logService = TestBed.get(LogService);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
TestBed.resetTestingModule();
|
TestBed.resetTestingModule();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAspects', () => {
|
it('should load the groups passed by paramter', () => {
|
||||||
|
spyOn(classesApi, 'getClass');
|
||||||
|
|
||||||
beforeEach(() => {
|
service.load(['exif:exif', 'cm:content', 'custom:custom'])
|
||||||
node = <MinimalNodeEntryEntity> { aspectNames: [ 'exif:exif', 'cm:content', 'custom:custom' ] };
|
.subscribe(() => {});
|
||||||
|
|
||||||
testPresets = {};
|
expect(classesApi.getClass).toHaveBeenCalledTimes(3);
|
||||||
appConfigService.config['content-metadata'] = {
|
expect(classesApi.getClass).toHaveBeenCalledWith('exif_exif');
|
||||||
presets: testPresets
|
expect(classesApi.getClass).toHaveBeenCalledWith('cm_content');
|
||||||
};
|
expect(classesApi.getClass).toHaveBeenCalledWith('custom_custom');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge the forked values', (done) => {
|
||||||
|
|
||||||
|
const exifResponse: PropertyGroup = {
|
||||||
|
name: 'exif:exif',
|
||||||
|
title: '',
|
||||||
|
properties: {
|
||||||
|
'exif:1': { title: 'exif:1:id', name: 'exif:1', dataType: '', mandatory: false, multiValued: false },
|
||||||
|
'exif:2': { title: 'exif:2:id', name: 'exif:2', dataType: '', mandatory: false, multiValued: false }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentResponse: PropertyGroup = {
|
||||||
|
name: 'cm:content',
|
||||||
|
title: '',
|
||||||
|
properties: {
|
||||||
|
'cm:content': { title: 'cm:content:id', name: 'cm:content', dataType: '', mandatory: false, multiValued: false }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiResponses = [ exifResponse, contentResponse ];
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
spyOn(classesApi, 'getClass').and.callFake(() => {
|
||||||
|
return Observable.of(apiResponses[counter++]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the aspect properties loading for the default aspects related to the given node and defined in the app config', () => {
|
service.load(['exif:exif', 'cm:content'])
|
||||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
.subscribe({
|
||||||
testPresets.default = { 'exif:exif': [], 'custom:custom': [], 'banana:banana': [] };
|
next: (data) => {
|
||||||
|
expect(data['exif:exif']).toBe(exifResponse);
|
||||||
contentMetadataService.getAspects(node);
|
expect(data['cm:content']).toBe(contentResponse);
|
||||||
|
},
|
||||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'custom:custom']);
|
complete: done
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the aspect properties loading for the defined aspects related to the given node and defined in the app config', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
|
||||||
testPresets.pink = { 'cm:content': [], 'custom:custom': [] };
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node, 'pink');
|
|
||||||
|
|
||||||
expect(aspectProperties.load).toHaveBeenCalledWith(['cm:content', 'custom:custom']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the aspect properties loading for all the node aspectNames if the "*" widecard is used for the preset', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
|
||||||
testPresets.default = '*';
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node);
|
|
||||||
|
|
||||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'cm:content', 'custom:custom']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the aspect properties loading for all the node aspectNames if there is no preset data defined in the app config', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
|
||||||
spyOn(logService, 'error').and.stub();
|
|
||||||
appConfigService.config['content-metadata'] = undefined;
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node);
|
|
||||||
|
|
||||||
expect(logService.error).not.toHaveBeenCalled();
|
|
||||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'cm:content', 'custom:custom']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show meaningful error when invalid preset are given', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
|
||||||
spyOn(logService, 'error').and.stub();
|
|
||||||
testPresets.pink = { 'cm:content': {}, 'custom:custom': {} };
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node, 'blue');
|
|
||||||
|
|
||||||
expect(logService.error).toHaveBeenCalledWith('No content-metadata preset for: blue');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter out properties which are not defined in the particular aspect', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
|
||||||
return Observable.of([
|
|
||||||
{
|
|
||||||
name: 'exif:exif',
|
|
||||||
properties: [
|
|
||||||
{ name: 'exif:1' },
|
|
||||||
{ name: 'exif:2' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testPresets.default = { 'exif:exif': ['exif:2'] };
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node).subscribe({
|
|
||||||
next: (aspects) => {
|
|
||||||
expect(aspects[0].name).toBe('exif:exif');
|
|
||||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:2' });
|
|
||||||
expect(aspects[0].properties).not.toContain(<AspectProperty> { name: 'exif:1' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept "*" wildcard for aspect properties', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
|
||||||
return Observable.of([
|
|
||||||
{
|
|
||||||
name: 'exif:exif',
|
|
||||||
properties: [
|
|
||||||
{ name: 'exif:1' },
|
|
||||||
{ name: 'exif:2' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'custom:custom',
|
|
||||||
properties: [
|
|
||||||
{ name: 'custom:1' },
|
|
||||||
{ name: 'custom:2' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
testPresets.default = {
|
|
||||||
'exif:exif': '*',
|
|
||||||
'custom:custom': ['custom:1']
|
|
||||||
};
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node).subscribe({
|
|
||||||
next: (aspects) => {
|
|
||||||
expect(aspects.length).toBe(2);
|
|
||||||
expect(aspects[0].name).toBe('exif:exif');
|
|
||||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:1' });
|
|
||||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:2' });
|
|
||||||
|
|
||||||
expect(aspects[1].name).toBe('custom:custom');
|
|
||||||
expect(aspects[1].properties).toContain(<AspectProperty> { name: 'custom:1' });
|
|
||||||
expect(aspects[1].properties).not.toContain(<AspectProperty> { name: 'custom:2' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter out aspects which are not present in app config preset', () => {
|
|
||||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
|
||||||
return Observable.of([
|
|
||||||
{
|
|
||||||
name: 'exif:exif',
|
|
||||||
properties: [
|
|
||||||
{ name: 'exif:1' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
testPresets.default = {
|
|
||||||
'exif:exif': ['exif:1'],
|
|
||||||
'banana:banana': ['banana:1']
|
|
||||||
};
|
|
||||||
|
|
||||||
contentMetadataService.getAspects(node).subscribe({
|
|
||||||
next: (aspects) => {
|
|
||||||
expect(aspects.length).toBe(1);
|
|
||||||
expect(aspects[0].name).toBe('exif:exif');
|
|
||||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:1' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,45 +16,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
import { forkJoin } from 'rxjs/observable/forkJoin';
|
||||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Aspect, AspectProperty } from '../interfaces/content-metadata.interfaces';
|
import { defer } from 'rxjs/observable/defer';
|
||||||
|
import { PropertyGroup, PropertyGroupContainer } from '../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PropertyDescriptorsService {
|
export class PropertyDescriptorsService {
|
||||||
|
|
||||||
constructor(private aspectWhiteListService: AspectWhiteListService,
|
constructor(private alfrescoApiService: AlfrescoApiService) {}
|
||||||
private aspectPropertiesService: PropertyDescriptorLoaderService) {}
|
|
||||||
|
|
||||||
getAspects(node: MinimalNodeEntryEntity, presetName: string = 'default'): Observable<Aspect[]> {
|
load(groupNames: string[]): Observable<PropertyGroupContainer> {
|
||||||
this.aspectWhiteListService.choosePreset(presetName);
|
const groupFetchStreams = groupNames
|
||||||
|
.map(groupName => groupName.replace(':', '_'))
|
||||||
|
.map(groupName => defer( () => this.alfrescoApiService.classesApi.getClass(groupName)) );
|
||||||
|
|
||||||
return this.loadAspectDescriptors(node.aspectNames)
|
return forkJoin(groupFetchStreams)
|
||||||
.map(this.filterPropertiesByWhitelist.bind(this));
|
.map(this.convertToObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadAspectDescriptors(aspectsAssignedToNode: string[]): Observable<any> {
|
private convertToObject(propertyGroupsArray: PropertyGroup[]): PropertyGroupContainer {
|
||||||
const aspectsToLoad = aspectsAssignedToNode
|
return propertyGroupsArray.reduce((propertyGroups, propertyGroup) => {
|
||||||
.filter(nodeAspectName => this.aspectWhiteListService.isAspectAllowed(nodeAspectName));
|
return Object.assign({}, propertyGroups, {
|
||||||
|
[propertyGroup.name]: propertyGroup
|
||||||
return this.aspectPropertiesService.load(aspectsToLoad);
|
|
||||||
}
|
|
||||||
|
|
||||||
private filterPropertiesByWhitelist(aspectDescriptors): Aspect[] {
|
|
||||||
return aspectDescriptors.map((aspectDescriptor) => {
|
|
||||||
return Object.assign({}, aspectDescriptor, {
|
|
||||||
properties: this.getFilteredPropertiesArray(aspectDescriptor)
|
|
||||||
});
|
});
|
||||||
});
|
}, {});
|
||||||
}
|
|
||||||
|
|
||||||
private getFilteredPropertiesArray(aspectDescriptor): AspectProperty[] {
|
|
||||||
const aspectName = aspectDescriptor.name;
|
|
||||||
|
|
||||||
return Object.keys(aspectDescriptor.properties)
|
|
||||||
.map(propertyName => aspectDescriptor.properties[propertyName])
|
|
||||||
.filter(property => this.aspectWhiteListService.isPropertyAllowed(aspectName, property.name));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,285 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
import { PropertyGroupTranslatorService } from './property-groups-translator.service';
|
||||||
|
import { Property, OrganisedPropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||||
|
import {
|
||||||
|
CardViewTextItemModel,
|
||||||
|
CardViewDateItemModel,
|
||||||
|
CardViewIntItemModel,
|
||||||
|
CardViewFloatItemModel,
|
||||||
|
LogService,
|
||||||
|
CardViewBoolItemModel,
|
||||||
|
CardViewDatetimeItemModel
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
describe('PropertyGroupTranslatorService', () => {
|
||||||
|
|
||||||
|
let service: PropertyGroupTranslatorService,
|
||||||
|
propertyGroups: OrganisedPropertyGroup[],
|
||||||
|
propertyGroup: OrganisedPropertyGroup,
|
||||||
|
property: Property,
|
||||||
|
propertyValues: { [key: string]: any };
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
PropertyGroupTranslatorService,
|
||||||
|
{ provide: LogService, useValue: { error: () => {} }}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = TestBed.get(PropertyGroupTranslatorService);
|
||||||
|
|
||||||
|
property = {
|
||||||
|
name: 'FAS:PLAGUE',
|
||||||
|
title: 'The Faro Plague',
|
||||||
|
dataType: '',
|
||||||
|
defaultValue: '',
|
||||||
|
mandatory: false,
|
||||||
|
multiValued: false
|
||||||
|
};
|
||||||
|
propertyGroup = {
|
||||||
|
title: 'Faro Automated Solutions',
|
||||||
|
properties: [property]
|
||||||
|
};
|
||||||
|
propertyGroups = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('General transformation', () => {
|
||||||
|
|
||||||
|
it('should translate EVERY properties in ONE group properly', () => {
|
||||||
|
propertyGroup.properties = [{
|
||||||
|
name: 'FAS:PLAGUE',
|
||||||
|
title: 'title',
|
||||||
|
dataType: 'd:text',
|
||||||
|
defaultValue: 'defaultValue',
|
||||||
|
mandatory: false,
|
||||||
|
multiValued: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'FAS:ALOY',
|
||||||
|
title: 'title',
|
||||||
|
dataType: 'd:text',
|
||||||
|
defaultValue: 'defaultValue',
|
||||||
|
mandatory: false,
|
||||||
|
multiValued: false
|
||||||
|
}];
|
||||||
|
propertyGroups.push(propertyGroup);
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||||
|
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
expect(cardViewGroup[0].properties.length).toBe(2);
|
||||||
|
expect(cardViewGroup[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First property should be instance of CardViewTextItemModel');
|
||||||
|
expect(cardViewGroup[0].properties[1] instanceof CardViewTextItemModel).toBeTruthy('Second property should be instance of CardViewTextItemModel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate EVERY property in EVERY group properly', () => {
|
||||||
|
propertyGroups.push(
|
||||||
|
Object.assign({}, propertyGroup, {
|
||||||
|
properties: [{
|
||||||
|
name: 'FAS:PLAGUE',
|
||||||
|
title: 'title',
|
||||||
|
dataType: 'd:text',
|
||||||
|
defaultValue: 'defaultvalue',
|
||||||
|
mandatory: false,
|
||||||
|
multiValued: false
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
Object.assign({}, propertyGroup, {
|
||||||
|
properties: [{
|
||||||
|
name: 'FAS:ALOY',
|
||||||
|
title: 'title',
|
||||||
|
dataType: 'd:text',
|
||||||
|
defaultValue: 'defaultvalue',
|
||||||
|
mandatory: false,
|
||||||
|
multiValued: false
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||||
|
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
expect(cardViewGroup.length).toBe(2);
|
||||||
|
expect(cardViewGroup[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First group\'s property should be instance of CardViewTextItemModel');
|
||||||
|
expect(cardViewGroup[1].properties[0] instanceof CardViewTextItemModel).toBeTruthy('Second group\'s property should be instance of CardViewTextItemModel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log an error if unrecognised type is found', () => {
|
||||||
|
const logService = TestBed.get(LogService);
|
||||||
|
spyOn(logService, 'error').and.stub();
|
||||||
|
|
||||||
|
property.name = 'FAS:PLAGUE';
|
||||||
|
property.title = 'The Faro Plague';
|
||||||
|
property.dataType = 'daemonic:scorcher';
|
||||||
|
property.defaultValue = 'Daemonic beast';
|
||||||
|
propertyGroups.push(propertyGroup);
|
||||||
|
|
||||||
|
service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
expect(logService.error).toHaveBeenCalledWith('Unknown type for mapping: daemonic:scorcher');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fall back to singleline property type if unrecognised type is found', () => {
|
||||||
|
property.name = 'FAS:PLAGUE';
|
||||||
|
property.title = 'The Faro Plague';
|
||||||
|
property.dataType = 'daemonic:scorcher';
|
||||||
|
property.defaultValue = 'Daemonic beast';
|
||||||
|
propertyGroups.push(propertyGroup);
|
||||||
|
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
const cardViewProperty: CardViewTextItemModel = <CardViewTextItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Different types\'s attributes', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
propertyGroups.push(propertyGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
PropertyGroupTranslatorService.RECOGNISED_ECM_TYPES.forEach((dataType) => {
|
||||||
|
it(`should translate properly the basic attributes of a property for ${dataType}`, () => {
|
||||||
|
property.name = 'prefix:name';
|
||||||
|
property.title = 'title';
|
||||||
|
property.defaultValue = 'default value';
|
||||||
|
property.dataType = dataType;
|
||||||
|
|
||||||
|
propertyValues = { 'prefix:name': null };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty = cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty.label).toBe(property.title);
|
||||||
|
expect(cardViewProperty.key).toBe('properties.prefix:name');
|
||||||
|
expect(cardViewProperty.default).toBe(property.defaultValue);
|
||||||
|
expect(cardViewProperty.editable).toBeTruthy('Property should be editable');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the multiline and value attributes for d:text', () => {
|
||||||
|
property.dataType = 'd:text';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewTextItemModel = <CardViewTextItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe('The Chariot Line');
|
||||||
|
expect(cardViewProperty.multiline).toBeFalsy('Property should be singleline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the multiline and value attributes for d:mltext', () => {
|
||||||
|
property.dataType = 'd:mltext';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewTextItemModel = <CardViewTextItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe('The Chariot Line');
|
||||||
|
expect(cardViewProperty.multiline).toBeTruthy('Property should be multiline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:date', () => {
|
||||||
|
const expectedValue = new Date().toISOString();
|
||||||
|
property.dataType = 'd:date';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': expectedValue };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewDateItemModel = <CardViewDateItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewDateItemModel).toBeTruthy('Property should be instance of CardViewDateItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:datetime', () => {
|
||||||
|
const expectedValue = new Date().toISOString();
|
||||||
|
property.dataType = 'd:datetime';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': expectedValue };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewDatetimeItemModel = <CardViewDatetimeItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewDatetimeItemModel).toBeTruthy('Property should be instance of CardViewDatetimeItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:int', () => {
|
||||||
|
property.dataType = 'd:int';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': '1024' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewIntItemModel = <CardViewIntItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(1024);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:long', () => {
|
||||||
|
property.dataType = 'd:long';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': '1024' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewIntItemModel = <CardViewIntItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(1024);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:float', () => {
|
||||||
|
property.dataType = 'd:float';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': '1024.24' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(1024.24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:double', () => {
|
||||||
|
property.dataType = 'd:double';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': '1024.24' };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(1024.24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate properly the value attribute for d:boolean', () => {
|
||||||
|
property.dataType = 'd:boolean';
|
||||||
|
|
||||||
|
propertyValues = { 'FAS:PLAGUE': true };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||||
|
|
||||||
|
const cardViewProperty: CardViewBoolItemModel = <CardViewBoolItemModel> cardViewGroup[0].properties[0];
|
||||||
|
expect(cardViewProperty instanceof CardViewBoolItemModel).toBeTruthy('Property should be instance of CardViewBoolItemModel');
|
||||||
|
expect(cardViewProperty.value).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,122 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
CardViewItemProperties,
|
||||||
|
CardViewItem,
|
||||||
|
CardViewTextItemModel,
|
||||||
|
CardViewBoolItemModel,
|
||||||
|
CardViewDateItemModel,
|
||||||
|
CardViewDatetimeItemModel,
|
||||||
|
CardViewIntItemModel,
|
||||||
|
CardViewFloatItemModel,
|
||||||
|
LogService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { Property, CardViewGroup, OrganisedPropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||||
|
|
||||||
|
const D_TEXT = 'd:text';
|
||||||
|
const D_MLTEXT = 'd:mltext';
|
||||||
|
const D_DATE = 'd:date';
|
||||||
|
const D_DATETIME = 'd:datetime';
|
||||||
|
const D_INT = 'd:int';
|
||||||
|
const D_LONG = 'd:long';
|
||||||
|
const D_FLOAT = 'd:float';
|
||||||
|
const D_DOUBLE = 'd:double';
|
||||||
|
const D_BOOLEAN = 'd:boolean';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PropertyGroupTranslatorService {
|
||||||
|
|
||||||
|
static readonly RECOGNISED_ECM_TYPES = [ D_TEXT, D_MLTEXT, D_DATE, D_DATETIME, D_INT, D_LONG , D_FLOAT, D_DOUBLE, D_BOOLEAN ];
|
||||||
|
|
||||||
|
constructor(private logService: LogService) {}
|
||||||
|
|
||||||
|
public translateToCardViewGroups(propertyGroups: OrganisedPropertyGroup[], propertyValues): CardViewGroup[] {
|
||||||
|
return propertyGroups.map(propertyGroup => {
|
||||||
|
const translatedPropertyGroup: any = Object.assign({}, propertyGroup);
|
||||||
|
translatedPropertyGroup.properties = this.translateArray(propertyGroup.properties, propertyValues);
|
||||||
|
return translatedPropertyGroup;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private translateArray(properties: Property[], propertyValues: any): CardViewItem[] {
|
||||||
|
return properties.map(property => {
|
||||||
|
return this.translate(property, propertyValues[property.name]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private translate(property: Property, propertyValue: any): CardViewItem {
|
||||||
|
this.checkECMTypeValidity(property.dataType);
|
||||||
|
|
||||||
|
const prefix = 'properties.';
|
||||||
|
|
||||||
|
let propertyDefinition: CardViewItemProperties = {
|
||||||
|
label: property.title,
|
||||||
|
value: propertyValue,
|
||||||
|
key: `${prefix}${property.name}`,
|
||||||
|
default: property.defaultValue,
|
||||||
|
editable: true
|
||||||
|
};
|
||||||
|
let cardViewItemProperty;
|
||||||
|
|
||||||
|
switch (property.dataType) {
|
||||||
|
|
||||||
|
case D_MLTEXT:
|
||||||
|
cardViewItemProperty = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
||||||
|
multiline: true
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_INT:
|
||||||
|
case D_LONG:
|
||||||
|
cardViewItemProperty = new CardViewIntItemModel(propertyDefinition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_FLOAT:
|
||||||
|
case D_DOUBLE:
|
||||||
|
cardViewItemProperty = new CardViewFloatItemModel(propertyDefinition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_DATE:
|
||||||
|
cardViewItemProperty = new CardViewDateItemModel(propertyDefinition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_DATETIME:
|
||||||
|
cardViewItemProperty = new CardViewDatetimeItemModel(propertyDefinition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_BOOLEAN:
|
||||||
|
cardViewItemProperty = new CardViewBoolItemModel(propertyDefinition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case D_TEXT:
|
||||||
|
default:
|
||||||
|
cardViewItemProperty = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
||||||
|
multiline: false
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardViewItemProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkECMTypeValidity(ecmPropertyType) {
|
||||||
|
if (PropertyGroupTranslatorService.RECOGNISED_ECM_TYPES.indexOf(ecmPropertyType) === -1) {
|
||||||
|
this.logService.error(`Unknown type for mapping: ${ecmPropertyType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -158,7 +158,105 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"content-metadata-layout-group": {
|
||||||
|
"description": "Content metadata's layout groups definition",
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"description": "Content metadata's one layout group definition",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"title",
|
||||||
|
"items"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Content metadata's one layout group definition's title"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Content metadata's one layout group definition's items",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"aspect",
|
||||||
|
"properties"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"aspect": {
|
||||||
|
"description": "Aspect group",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "Wildcard character",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\*$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"aspect",
|
||||||
|
"properties"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"aspect": {
|
||||||
|
"description": "Aspect group",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "list of aspect properties",
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"properties"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"description": "Type group",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "Wildcard character",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\*$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"properties"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"description": "Type group",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "list of type properties",
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -326,7 +424,8 @@
|
|||||||
".*": {
|
".*": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{ "type": "string", "pattern": "^\\*$", "description": "Wildcard for every aspect"},
|
{ "type": "string", "pattern": "^\\*$", "description": "Wildcard for every aspect"},
|
||||||
{ "$ref": "#/definitions/content-metadata-aspect" }
|
{ "$ref": "#/definitions/content-metadata-aspect" },
|
||||||
|
{ "$ref": "#/definitions/content-metadata-layout-group" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ enable_test(){
|
|||||||
enable_testbrowser(){
|
enable_testbrowser(){
|
||||||
if [[ ! -z $1 ]]; then
|
if [[ ! -z $1 ]]; then
|
||||||
if [[ $1 != "-"* ]]; then
|
if [[ $1 != "-"* ]]; then
|
||||||
|
EXEC_SINGLE_TEST=true
|
||||||
SINGLE_TEST=$1
|
SINGLE_TEST=$1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@ -104,7 +105,7 @@ while [[ $1 == -* ]]; do
|
|||||||
shift;
|
shift;
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
-d|--debug) enable_testbrowser $2; shift; shift;;
|
-d|--debug) enable_testbrowser $2; shift; if $EXEC_SINGLE_TEST == true; then shift; fi ;;
|
||||||
-ft|--fasttest) enable_fast_test; shift;;
|
-ft|--fasttest) enable_fast_test; shift;;
|
||||||
-gitjsapi) enable_js_api_git_link $2; shift 2;;
|
-gitjsapi) enable_js_api_git_link $2; shift 2;;
|
||||||
-vjsapi) version_js_api $2; shift 2;;
|
-vjsapi) version_js_api $2; shift 2;;
|
||||||
@ -166,13 +167,16 @@ if $RUN_TEST == true; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if $RUN_TESTBROWSER == true; then
|
if $RUN_TESTBROWSER == true; then
|
||||||
for PACKAGE in ${projects[@]}
|
DESTDIR="$DIR/../lib/"
|
||||||
do
|
cd $DESTDIR
|
||||||
DESTDIR="$DIR/../lib/"
|
if $EXEC_SINGLE_TEST == true; then
|
||||||
cd $DESTDIR
|
cp -n "$DESTDIR/config/karma-test-shim.js" "$DESTDIR/$SINGLE_TEST/"
|
||||||
if [[ $PACKAGE == $SINGLE_TEST ]]; then
|
debug_project $SINGLE_TEST
|
||||||
|
else
|
||||||
|
for PACKAGE in ${projects[@]}
|
||||||
|
do
|
||||||
debug_project $PACKAGE
|
debug_project $PACKAGE
|
||||||
fi
|
done
|
||||||
done
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user