[ADF-4673] Add editable property to metadata config (#5557)

* [ADF-4673] Add editable property to metadata config

* Add readonly mode for aspect oriented config

* Fix linting
This commit is contained in:
davidcanonieto
2020-03-20 22:16:35 +00:00
committed by GitHub
parent 94f4b69b76
commit 3fcf965eca
10 changed files with 266 additions and 75 deletions

View File

@@ -22,7 +22,10 @@
"redirectSilentIframeUri": "{protocol}//{hostname}{:port}/assets/silent-refresh.html",
"redirectUri": "/",
"redirectUriLogout": "/logout",
"publicUrls": ["**/preview/s/*", "**/settings"]
"publicUrls": [
"**/preview/s/*",
"**/settings"
]
},
"application": {
"storagePrefix": "ADF",
@@ -753,7 +756,7 @@
},
"adf-cloud-start-process": {
"name": "My Default Cloud Name"
},
},
"adf-process-list": {
"presets": {
"default": [

View File

@@ -18,6 +18,8 @@ Displays and edits metadata related to a node.
- [Properties](#properties)
- [Details](#details)
- [Application config presets](#application-config-presets)
- [Indifferent config](#indifferent-config)
- [Aspect oriented config](#aspect-oriented-config)
- [Layout oriented config](#layout-oriented-config)
- [Displaying all properties](#displaying-all-properties)
- [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-doesnt-relate-to-that-aspect)
@@ -229,6 +231,31 @@ The result of this config would be two accordion groups with the following prope
| kitten:favourite-food |
| kitten:recommended-food |
#### Making properties editable
When using the layout oriented config you can also set whether or not the properties are going to be editable.
```json
...
"content-metadata": {
"presets": {
"kitten-images": [{
"title": "TRANSLATABLE_TITLE_FOR_GROUP_1",
"items": [
{
"aspect": "custom:aspect",
"properties": "*",
"editable": false
}
]
}]
}
}
...
```
As seen above in the example the `custom:aspect` aspect will always be on read-only mode since these properties are not editable. If the editable is enabled, then these properties will be able to be edited by the user.
### Displaying all properties
You can list all the properties by simply adding the `includeAll: boolean` to your config. This config will display all the aspects and properties available for that specific file.
@@ -282,6 +309,24 @@ example below shows this with an aspect-oriented config:
},
```
### Making aspects and properties read only
Whenever you have properties that you want to protect from users editing their values you can add them to your configuration to make them read only. `readOnlyAspects` will make the whole aspect and its properties non editable.
If you want to disable the editing for specific properties you will need to add them to the `readOnlyProperties` property.
```json
"content-metadata": {
"presets": {
"default": {
"includeAll": true,
"readOnlyAspects": ["cm:author"],
"readOnlyProperties": ["cm:fileVersion"]
}
}
},
```
## What happens when there is a whitelisted aspect in the config but the given node doesn't relate to that aspect
Nothing - since this aspect is not related to the node, it will simply be ignored and not

View File

@@ -21,6 +21,7 @@ export interface LayoutOrientedConfigItem {
properties: string | string[];
includeAll?: boolean;
exclude?: string | string[];
editable?: boolean;
}
export interface LayoutOrientedConfigLayoutBlock {

View File

@@ -23,4 +23,5 @@ export interface Property {
defaultValue?: any;
mandatory: boolean;
multiValued: boolean;
editable?: boolean;
}

View File

@@ -222,5 +222,35 @@ describe('AspectOrientedConfigService', () => {
});
});
});
it(`should set as readOnly the properties defined in the config inside readOnlyAspects`, () => {
const testCase = {
name: 'Not existing property',
config: {
includeAll: true,
readOnlyAspects: ['berseria'],
readOnlyProperties: ['property3']
},
expectations: [
{
title: 'Berseria',
properties: [ property1, property2 ]
},
{
title: 'Zestiria',
properties: [ property3, property4 ]
}
]
};
configService = createConfigService(testCase.config);
const organisedPropertyGroups = configService.appendAllPreset(propertyGroups);
expect(organisedPropertyGroups.length).toBe(testCase.expectations.length, 'Group count should match');
expect(organisedPropertyGroups[0].properties[0].editable).toBe(false);
expect(organisedPropertyGroups[0].properties[1].editable).toBe(false);
expect(organisedPropertyGroups[1].properties[0].editable).toBe(false);
expect(organisedPropertyGroups[1].properties[1].editable).toBe(undefined);
});
});
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { ContentMetadataConfig, OrganisedPropertyGroup, PropertyGroupContainer } from '../../interfaces/content-metadata.interfaces';
import { ContentMetadataConfig, OrganisedPropertyGroup, PropertyGroupContainer, Property } from '../../interfaces/content-metadata.interfaces';
import { getGroup, getProperty } from './property-group-reader';
export class AspectOrientedConfigService implements ContentMetadataConfig {
@@ -43,19 +43,53 @@ export class AspectOrientedConfigService implements ContentMetadataConfig {
}
public appendAllPreset(propertyGroups: PropertyGroupContainer): OrganisedPropertyGroup[] {
const groups = Object.keys(propertyGroups)
const groups = Object.keys(propertyGroups)
.map((groupName) => {
const propertyGroup = propertyGroups[groupName],
properties = propertyGroup.properties;
if (this.isAspectReadOnly(groupName)) {
Object.keys(properties).map((propertyName) => this.setReadOnlyProperty(properties[propertyName]));
}
return Object.assign({}, propertyGroup, {
properties: Object.keys(properties).map((propertyName) => properties[propertyName])
properties: Object.keys(properties).map((propertyName) => {
if (this.isPropertyReadOnly(propertyName)) {
this.setReadOnlyProperty(properties[propertyName]);
}
return properties[propertyName];
})
});
});
return groups;
}
private setReadOnlyProperty(property: Property) {
property.editable = false;
}
private isPropertyReadOnly(propertyName: string): boolean {
const readOnlyAspects = this.config.readOnlyProperties;
if (Array.isArray(readOnlyAspects)) {
return readOnlyAspects.includes(propertyName);
} else {
return readOnlyAspects === propertyName;
}
}
private isAspectReadOnly(propertyGroupName: string): boolean {
const readOnlyAspects = this.config.readOnlyAspects;
if (Array.isArray(readOnlyAspects)) {
return readOnlyAspects.includes(propertyGroupName);
} else {
return readOnlyAspects === propertyGroupName;
}
}
public filterExcludedPreset(propertyGroups: OrganisedPropertyGroup[]): OrganisedPropertyGroup[] {
if (this.config.exclude) {
return propertyGroups.filter((preset) => {

View File

@@ -45,9 +45,11 @@ describe('LayoutOrientedConfigService', () => {
groupNameToQuery: 'berseria'
},
{
config: [{ title: 'Deamons', items: [
{ aspect: 'zestiria', properties: '*' }, { aspect: 'berseria', properties: '*' }
]}],
config: [{
title: 'Deamons', items: [
{ aspect: 'zestiria', properties: '*' }, { aspect: 'berseria', properties: '*' }
]
}],
expectation: true,
groupNameToQuery: 'berseria'
},
@@ -98,11 +100,14 @@ describe('LayoutOrientedConfigService', () => {
const property1 = <Property> { name: 'property1' },
property2 = <Property> { name: 'property2' },
property3 = <Property> { name: 'property3' },
property4 = <Property> { name: 'property4' };
property4 = <Property> { name: 'property4' },
property5 = <Property> { name: 'property5' },
property6 = <Property> { name: 'property6' };
const propertyGroups: PropertyGroupContainer = {
berseria: { title: 'Berseria', description: '', name: 'berseria', properties: { property1, property2 } },
zestiria: { title: 'Zestiria', description: '', name: 'zestiria', properties: { property3, property4 } }
zestiria: { title: 'Zestiria', description: '', name: 'zestiria', properties: { property3, property4 } },
otherTales: { title: 'Other tales', description: '', name: 'otherTales', properties: { property5, property6 } }
};
const testCases: TestCase[] = [
@@ -114,118 +119,155 @@ describe('LayoutOrientedConfigService', () => {
{
name: 'First property of a group in one item',
config: [
{ title: 'First group', items: [
{ aspect: 'berseria', properties: [ 'property1' ] }
]}
{
title: 'First group', items: [
{ aspect: 'berseria', properties: ['property1'] }
]
}
],
expectations: [
{ title: 'First group', properties: [ property1 ] }
{ title: 'First group', properties: [property1] }
]
},
{
name: 'Second property of a group in one item',
config: [
{ title: 'First group', items: [
{ aspect: 'berseria', properties: [ 'property2' ] }
]}
{
title: 'First group', items: [
{ aspect: 'berseria', properties: ['property2'] }
]
}
],
expectations: [
{ title: 'First group', properties: [ property2 ] }
{ title: 'First group', properties: [property2] }
]
},
{
name: 'Properties with editable flag',
config: [
{
title: 'Editable property', items: [
{ aspect: 'otherTales', properties: ['property5'], editable: true },
{ aspect: 'otherTales', properties: ['property6'], editable: false }
]
}
],
expectations: [
{ title: 'Editable property', properties: [property5, property6] }
]
},
{
name: 'More properties from one group in one item',
config: [
{ title: 'First group', items: [
{ aspect: 'berseria', properties: [ 'property2', 'property1' ] }
]}
{
title: 'First group', items: [
{ aspect: 'berseria', properties: ['property2', 'property1'] }
]
}
],
expectations: [
{ title: 'First group', properties: [ property2, property1 ] }
{ 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' ] }
]}
{
title: 'First group', items: [
{ aspect: 'zestiria', properties: ['property4'] }
]
}
],
expectations: [
{ title: 'First group', properties: [ property4 ] }
{ 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' ] }
]}
{
title: 'First group', items: [
{ aspect: 'zestiria', properties: ['property4'] },
{ aspect: 'berseria', properties: ['property1'] }
]
}
],
expectations: [
{ title: 'First group', properties: [ property4, property1 ] }
{ 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' ] }
]}
{
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 ] }
{ 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' ] }
]}
{
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 ] }
{ 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' ] }
]}
{
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 ] }
{ 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' }
]}
{
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 ] }
{ title: 'First group', properties: [property3, property4, property2] }
]
}
];
@@ -238,7 +280,7 @@ describe('LayoutOrientedConfigService', () => {
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].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.`

View File

@@ -19,7 +19,8 @@ import {
ContentMetadataConfig,
LayoutOrientedConfigItem,
OrganisedPropertyGroup,
PropertyGroupContainer
PropertyGroupContainer,
Property
} from '../../interfaces/content-metadata.interfaces';
import { getProperty } from './property-group-reader';
@@ -40,7 +41,8 @@ export class LayoutOrientedConfigService implements ContentMetadataConfig {
const organisedPropertyGroup = layoutBlocks.map((layoutBlock) => {
const flattenedItems = this.flattenItems(layoutBlock.items),
properties = flattenedItems.reduce((props, explodedItem) => {
const property = getProperty(propertyGroups, explodedItem.groupName, explodedItem.propertyName) || [];
let property = getProperty(propertyGroups, explodedItem.groupName, explodedItem.propertyName) || [];
property = this.setEditableProperty(property, explodedItem);
return props.concat(property);
}, []);
@@ -89,13 +91,24 @@ export class LayoutOrientedConfigService implements ContentMetadataConfig {
return includeAllProperty !== undefined ? includeAllProperty : false;
}
private setEditableProperty(propertyGroup: Property | Property[], itemConfig): Property | Property[] {
if (Array.isArray(propertyGroup)) {
propertyGroup.map((property) => property.editable = itemConfig.editable !== undefined ? itemConfig.editable : true);
} else {
propertyGroup.editable = itemConfig.editable !== undefined ? itemConfig.editable : true;
}
return propertyGroup;
}
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
propertyName,
editable: item.editable
};
});

View File

@@ -87,7 +87,7 @@ export class PropertyGroupTranslatorService {
value: propertyValue,
key: `${prefix}${property.name}`,
default: property.defaultValue,
editable: true
editable: property.editable !== undefined ? property.editable : true
};
let cardViewItemProperty;

View File

@@ -338,7 +338,9 @@
"type": "object",
"required": [
"includeAll",
"exclude"
"exclude",
"readOnlyAspects",
"readOnlyProperties"
],
"properties": {
"includeAll": {
@@ -350,7 +352,20 @@
"description": "Property name",
"type": "string"
}
},
"readOnlyAspects": {
"type": "array",
"items": {
"description": "Disable editing in these aspects",
"type": "string"
}
},
"readOnlyProperties": {
"type": "array",
"items": {
"description": "Disable editing in these properties",
"type": "string"
}
}
}
}
@@ -397,7 +412,8 @@
"type": "object",
"required": [
"aspect",
"properties"
"properties",
"editing"
],
"properties": {
"aspect": {
@@ -407,6 +423,10 @@
"properties": {
"description": "list of aspect properties",
"type": "array"
},
"editing": {
"description": "Enable/disable editing for this aspect",
"type": "boolean"
}
}
},
@@ -892,7 +912,10 @@
},
"postfix": {
"description": "exclude",
"type": ["string","array"]
"type": [
"string",
"array"
]
}
}
},
@@ -1209,8 +1232,7 @@
"name"
],
"properties": {
"id": {
},
"id": {},
"name": {
"type": "string"
},