mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[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:
@@ -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": [
|
||||
|
@@ -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
|
||||
|
@@ -21,6 +21,7 @@ export interface LayoutOrientedConfigItem {
|
||||
properties: string | string[];
|
||||
includeAll?: boolean;
|
||||
exclude?: string | string[];
|
||||
editable?: boolean;
|
||||
}
|
||||
|
||||
export interface LayoutOrientedConfigLayoutBlock {
|
||||
|
@@ -23,4 +23,5 @@ export interface Property {
|
||||
defaultValue?: any;
|
||||
mandatory: boolean;
|
||||
multiValued: boolean;
|
||||
editable?: boolean;
|
||||
}
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -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) => {
|
||||
|
@@ -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.`
|
||||
|
@@ -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
|
||||
};
|
||||
});
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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"
|
||||
},
|
||||
|
Reference in New Issue
Block a user