diff --git a/src/app.config.json b/src/app.config.json
index 39041e5ec..bbccd407a 100644
--- a/src/app.config.json
+++ b/src/app.config.json
@@ -42,6 +42,28 @@
"plugin2.json"
],
"core": {
+ "rules": [
+ {
+ "id": "app.create.canCreateFolder",
+ "type": "app.navigation.folder.canCreate"
+ },
+ {
+ "id": "app.toolbar.canEditFolder",
+ "type": "core.every",
+ "parameters": [
+ { "type": "rule", "value": "app.selection.folder" },
+ { "type": "rule", "value": "app.selection.folder.canUpdate" }
+ ]
+ },
+ {
+ "id": "app.toolbar.canViewFile",
+ "type": "app.selection.file"
+ },
+ {
+ "id": "app.toolbar.canDownload",
+ "type": "app.selection.canDownload"
+ }
+ ],
"routes": [
{
"id": "aca:routes/about",
@@ -81,11 +103,6 @@
"type": "SNACKBAR_INFO",
"payload": "I'm a nice little popup raised by extension."
},
- {
- "id": "aca:actions/error",
- "type": "SNACKBAR_ERROR",
- "payload": "Aw, Snap!"
- },
{
"id": "aca:actions/node-name",
"type": "SNACKBAR_INFO",
@@ -100,14 +117,14 @@
"features": {
"create": [
{
- "disabled": false,
- "id": "aca:create/folder",
- "order": 100,
+ "id": "app.create.folder",
"icon": "create_new_folder",
"title": "ext: Create Folder",
- "target": {
- "permissions": ["create"],
- "action": "aca:actions/create-folder"
+ "actions": {
+ "click": "aca:actions/create-folder"
+ },
+ "rules": {
+ "enabled": "app.create.canCreateFolder"
}
}
],
@@ -210,10 +227,11 @@
"order": 10,
"title": "APP.NEW_MENU.TOOLTIPS.CREATE_FOLDER",
"icon": "create_new_folder",
- "target": {
- "types": [],
- "permissions": ["parent.create"],
- "action": "aca:actions/create-folder"
+ "actions": {
+ "click": "aca:actions/create-folder"
+ },
+ "rules": {
+ "visible": "app.create.canCreateFolder"
}
},
{
@@ -222,10 +240,11 @@
"order": 15,
"title": "APP.ACTIONS.VIEW",
"icon": "open_in_browser",
- "target": {
- "types": ["file"],
- "permissions": [],
- "action": "aca:actions/preview"
+ "actions": {
+ "click": "aca:actions/preview"
+ },
+ "rules": {
+ "visible": "app.toolbar.canViewFile"
}
},
{
@@ -234,11 +253,11 @@
"order": 20,
"title": "APP.ACTIONS.DOWNLOAD",
"icon": "get_app",
- "target": {
- "types": ["file", "folder"],
- "permissions": [],
- "action": "aca:actions/download",
- "multiple": true
+ "actions": {
+ "click": "aca:actions/download"
+ },
+ "rules": {
+ "visible": "app.toolbar.canDownload"
}
},
{
@@ -247,10 +266,11 @@
"order": 30,
"title": "APP.ACTIONS.EDIT",
"icon": "create",
- "target": {
- "types": ["folder"],
- "permissions": ["update"],
- "action": "aca:actions/edit-folder"
+ "actions": {
+ "click": "aca:actions/edit-folder"
+ },
+ "rules": {
+ "visible": "app.toolbar.canEditFolder"
}
},
@@ -270,21 +290,8 @@
"type": "button",
"title": "Settings",
"icon": "settings_applications",
- "target": {
- "types": [],
- "permissions": [],
- "action": "aca:actions/settings"
- }
- },
- {
- "id": "aca:action4",
- "type": "button",
- "title": "Error",
- "icon": "report_problem",
- "target": {
- "types": ["file"],
- "permissions": ["update", "delete"],
- "action": "aca:actions/error"
+ "actions": {
+ "click": "aca:actions/settings"
}
}
]
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 09e65bc1c..2bf23b841 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -85,6 +85,7 @@ import { SearchResultsRowComponent } from './components/search/search-results-ro
import { NodePermissionsDialogComponent } from './dialogs/node-permissions/node-permissions.dialog';
import { NodePermissionsDirective } from './common/directives/node-permissions.directive';
import { PermissionsManagerComponent } from './components/permission-manager/permissions-manager.component';
+import { RuleService } from './extensions/rules/rule.service';
@NgModule({
imports: [
@@ -161,7 +162,8 @@ import { PermissionsManagerComponent } from './components/permission-manager/per
ProfileResolver,
ExperimentalGuard,
ContentApiService,
- ExtensionService
+ ExtensionService,
+ RuleService
],
entryComponents: [
LibraryDialogComponent,
diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts
index e84c547a0..95c3dd909 100644
--- a/src/app/components/page.component.ts
+++ b/src/app/components/page.component.ts
@@ -83,8 +83,7 @@ export abstract class PageComponent implements OnInit, OnDestroy {
if (selection.isEmpty) {
this.infoDrawerOpened = false;
}
- const selectedNodes = selection ? selection.nodes : null;
- this.actions = this.extensions.getAllowedContentActions(selectedNodes, this.node);
+ this.actions = this.extensions.getAllowedContentActions();
this.canUpdateFile = this.selection.file && this.content.canUpdateNode(selection.file);
this.canUpdateNode = this.selection.count === 1 && this.content.canUpdateNode(selection.first);
this.canDelete = !this.selection.isEmpty && this.content.canDeleteNodes(selection.nodes);
diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html
index 851a0b6b4..042d0b5ba 100644
--- a/src/app/components/sidenav/sidenav.component.html
+++ b/src/app/components/sidenav/sidenav.component.html
@@ -10,7 +10,7 @@
diff --git a/src/app/extensions/components/toolbar-action/toolbar-action.component.html b/src/app/extensions/components/toolbar-action/toolbar-action.component.html
index c4e03c64d..9684bf899 100644
--- a/src/app/extensions/components/toolbar-action/toolbar-action.component.html
+++ b/src/app/extensions/components/toolbar-action/toolbar-action.component.html
@@ -3,7 +3,7 @@
mat-icon-button
color="primary"
title="{{ entry.title | translate }}"
- (click)="runAction(entry.target.action)">
+ (click)="runAction(entry.actions.click)">
{{ entry.icon }}
@@ -20,7 +20,7 @@
[overlapTrigger]="false">
diff --git a/src/app/extensions/content-action.extension.ts b/src/app/extensions/content-action.extension.ts
index 0f4478c2d..65e805de7 100644
--- a/src/app/extensions/content-action.extension.ts
+++ b/src/app/extensions/content-action.extension.ts
@@ -38,10 +38,13 @@ export interface ContentActionExtension {
icon?: string;
disabled?: boolean;
children?: Array;
- target: {
- types: Array;
- permissions: Array,
- action: string;
- multiple?: boolean;
+ actions?: {
+ click?: string;
+ [key: string]: string;
+ };
+ rules: {
+ enabled?: string;
+ visible?: string;
+ [key: string]: string;
};
}
diff --git a/src/app/extensions/extension.config.ts b/src/app/extensions/extension.config.ts
new file mode 100644
index 000000000..0a5e9af20
--- /dev/null
+++ b/src/app/extensions/extension.config.ts
@@ -0,0 +1,35 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { RouteExtension } from './route.extension';
+import { ActionExtension } from './action.extension';
+import { RuleRef } from './rules/rule-ref';
+
+export interface ExtensionConfig {
+ rules?: Array;
+ routes?: Array;
+ actions?: Array;
+ features?: { [key: string]: any };
+}
diff --git a/src/app/extensions/extension.service.spec.ts b/src/app/extensions/extension.service.spec.ts
index e0819e4c8..fa1e27278 100644
--- a/src/app/extensions/extension.service.spec.ts
+++ b/src/app/extensions/extension.service.spec.ts
@@ -491,63 +491,6 @@ describe('ExtensionService', () => {
});
});
- describe('permissions', () => {
- it('should approve node permission', () => {
- const node: any = {
- allowableOperations: ['create']
- };
-
- expect(extensions.nodeHasPermission(node, 'create')).toBeTruthy();
- });
-
- it('should not approve node permission', () => {
- const node: any = {
- allowableOperations: ['create']
- };
-
- expect(extensions.nodeHasPermission(node, 'update')).toBeFalsy();
- });
-
- it('should not approve node permission when node missing property', () => {
- const node: any = {
- allowableOperations: null
- };
-
- expect(extensions.nodeHasPermission(node, 'update')).toBeFalsy();
- });
-
- it('should require node to check permission', () => {
- expect(extensions.nodeHasPermission(null, 'create')).toBeFalsy();
- });
-
- it('should require permission value to check', () => {
- const node: any = {
- allowableOperations: ['create']
- };
- expect(extensions.nodeHasPermission(node, null)).toBeFalsy();
- });
-
- it('should approve multiple permissions', () => {
- const node: any = {
- allowableOperations: ['create', 'update', 'delete']
- };
- expect(
- extensions.nodeHasPermissions(node, ['create', 'delete'])
- ).toBeTruthy();
- });
-
- it('should require node to check multiple permissions', () => {
- expect(extensions.nodeHasPermissions(null, ['create'])).toBeFalsy();
- });
-
- it('should require multiple permissions to check', () => {
- const node: any = {
- allowableOperations: ['create', 'update', 'delete']
- };
- expect(extensions.nodeHasPermissions(node, null)).toBeFalsy();
- });
- });
-
describe('sorting', () => {
it('should sort by provided order', () => {
const sorted = [
diff --git a/src/app/extensions/extension.service.ts b/src/app/extensions/extension.service.ts
index 40e7c10b0..6991c81fa 100644
--- a/src/app/extensions/extension.service.ts
+++ b/src/app/extensions/extension.service.ts
@@ -36,7 +36,8 @@ import { AppStore } from '../store/states';
import { Store } from '@ngrx/store';
import { NavigationExtension } from './navigation.extension';
import { Route } from '@angular/router';
-import { Node, MinimalNodeEntity } from 'alfresco-js-api';
+import { Node } from 'alfresco-js-api';
+import { RuleService } from './rules/rule.service';
@Injectable()
export class ExtensionService {
@@ -52,7 +53,8 @@ export class ExtensionService {
constructor(
private config: AppConfigService,
- private store: Store
+ private store: Store,
+ private ruleService: RuleService
) {}
// initialise extension service
@@ -89,6 +91,8 @@ export class ExtensionService {
[]
)
.sort(this.sortByOrder);
+
+ this.ruleService.init();
}
getRouteById(id: string): RouteExtension {
@@ -183,38 +187,28 @@ export class ExtensionService {
// evaluates create actions for the folder node
getFolderCreateActions(folder: Node): Array {
- return this.createActions.filter(this.filterEnabled).map(action => {
- if (
- action.target &&
- action.target.permissions &&
- action.target.permissions.length > 0
- ) {
+ return this.createActions
+ .filter(this.filterEnabled)
+ .filter(action => this.filterByRules(action))
+ .map(action => {
+ let disabled = false;
+
+ if (action.rules && action.rules.enabled) {
+ disabled = !this.ruleService.evaluateRule(action.rules.enabled);
+ }
+
return {
...action,
- disabled: !this.nodeHasPermissions(
- folder,
- action.target.permissions
- ),
- target: {
- ...action.target
- }
+ disabled
};
- }
- return action;
});
}
// evaluates content actions for the selection and parent folder node
- getAllowedContentActions(
- nodes: MinimalNodeEntity[],
- parentNode: Node
- ): Array {
+ getAllowedContentActions(): Array {
return this.contentActions
.filter(this.filterEnabled)
- .filter(action => this.filterByTarget(nodes, action))
- .filter(action =>
- this.filterByPermission(nodes, action, parentNode)
- )
+ .filter(action => this.filterByRules(action))
.reduce(this.reduceSeparators, [])
.map(action => {
if (action.type === ContentActionType.menu) {
@@ -222,14 +216,7 @@ export class ExtensionService {
if (copy.children && copy.children.length > 0) {
copy.children = copy.children
.filter(childAction =>
- this.filterByTarget(nodes, childAction)
- )
- .filter(childAction =>
- this.filterByPermission(
- nodes,
- childAction,
- parentNode
- )
+ this.filterByRules(childAction)
)
.reduce(this.reduceSeparators, []);
}
@@ -301,108 +288,10 @@ export class ExtensionService {
};
}
- filterByTarget(
- nodes: MinimalNodeEntity[],
- action: ContentActionExtension
- ): boolean {
- if (!action) {
- return false;
+ filterByRules(action: ContentActionExtension): boolean {
+ if (action && action.rules && action.rules.visible) {
+ return this.ruleService.evaluateRule(action.rules.visible);
}
-
- if (!action.target) {
- return (
- action.type === ContentActionType.separator ||
- action.type === ContentActionType.menu
- );
- }
-
- const types = action.target.types || [];
-
- if (types.length === 0) {
- return true;
- }
-
- if (nodes && nodes.length > 0) {
- return types.some(type => {
- if (type === 'folder') {
- return action.target.multiple
- ? nodes.some(node => node.entry.isFolder)
- : nodes.length === 1 &&
- nodes.every(node => node.entry.isFolder);
- }
- if (type === 'file') {
- return action.target.multiple
- ? nodes.some(node => node.entry.isFile)
- : nodes.length === 1 &&
- nodes.every(node => node.entry.isFile);
- }
- return false;
- });
- }
-
- return false;
- }
-
- filterByPermission(
- nodes: MinimalNodeEntity[],
- action: ContentActionExtension,
- parentNode: Node
- ): boolean {
- if (!action) {
- return false;
- }
-
- if (!action.target) {
- return (
- action.type === ContentActionType.separator ||
- action.type === ContentActionType.menu
- );
- }
-
- const permissions = action.target.permissions || [];
-
- if (permissions.length === 0) {
- return true;
- }
-
- return permissions.some(permission => {
- if (permission.startsWith('parent.')) {
- if (parentNode) {
- const parentQuery = permission.split('.')[1];
- return this.nodeHasPermission(parentNode, parentQuery);
- }
- return false;
- }
-
- if (nodes && nodes.length > 0) {
- return action.target.multiple
- ? nodes.some(node =>
- this.nodeHasPermission(node.entry, permission)
- )
- : nodes.length === 1 &&
- nodes.every(node =>
- this.nodeHasPermission(node.entry, permission)
- );
- }
-
- return false;
- });
- }
-
- nodeHasPermissions(node: Node, permissions: string[]): boolean {
- if (node && permissions && permissions.length > 0) {
- return permissions.some(permission =>
- this.nodeHasPermission(node, permission)
- );
- }
- return false;
- }
-
- nodeHasPermission(node: Node, permission: string): boolean {
- if (node && permission) {
- const allowableOperations = node.allowableOperations || [];
- return allowableOperations.includes(permission);
- }
- return false;
+ return true;
}
}
diff --git a/src/app/extensions/rules/app.evaluators.ts b/src/app/extensions/rules/app.evaluators.ts
new file mode 100644
index 000000000..51a7560f5
--- /dev/null
+++ b/src/app/extensions/rules/app.evaluators.ts
@@ -0,0 +1,72 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { RuleContext } from './rule-context';
+import { RuleParameter } from './rule-parameter';
+import { Node } from 'alfresco-js-api';
+
+export function canCreateFolder(context: RuleContext, ...args: RuleParameter[]): boolean {
+ const folder = context.navigation.currentFolder;
+ if (folder) {
+ return nodeHasPermission(folder, 'create');
+ }
+ return false;
+}
+
+export function canDownloadSelection(context: RuleContext, ...args: RuleParameter[]): boolean {
+ if (!context.selection.isEmpty) {
+ return context.selection.nodes.every(node => {
+ return node.entry && (node.entry.isFile || node.entry.isFolder);
+ });
+ }
+ return false;
+
+}
+
+export function hasFolderSelected(context: RuleContext, ...args: RuleParameter[]): boolean {
+ const folder = context.selection.folder;
+ return folder ? true : false;
+}
+
+export function hasFileSelected(context: RuleContext, ...args: RuleParameter[]): boolean {
+ const file = context.selection.file;
+ return file ? true : false;
+}
+
+export function canUpdateSelectedFolder(context: RuleContext, ...args: RuleParameter[]): boolean {
+ const folder = context.selection.folder;
+ if (folder && folder.entry) {
+ return nodeHasPermission(folder.entry, 'update');
+ }
+ return false;
+}
+
+export function nodeHasPermission(node: Node, permission: string): boolean {
+ if (node && permission) {
+ const allowableOperations = node.allowableOperations || [];
+ return allowableOperations.includes(permission);
+ }
+ return false;
+}
diff --git a/src/app/extensions/rules/core.evaluators.ts b/src/app/extensions/rules/core.evaluators.ts
new file mode 100644
index 000000000..c69b7888e
--- /dev/null
+++ b/src/app/extensions/rules/core.evaluators.ts
@@ -0,0 +1,47 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { RuleContext } from './rule-context';
+import { RuleParameter } from './rule-parameter';
+
+export function every(context: RuleContext, ...args: RuleParameter[]): boolean {
+ if (!args || args.length === 0) {
+ return false;
+ }
+
+ return args
+ .map(arg => context.evaluators[arg.value])
+ .every(evaluator => evaluator(context));
+}
+
+export function some(context: RuleContext, ...args: RuleParameter[]): boolean {
+ if (!args || args.length === 0) {
+ return false;
+ }
+
+ return args
+ .map(arg => context.evaluators[arg.value])
+ .some(evaluator => evaluator(context));
+}
diff --git a/src/app/extensions/rules/rule-context.ts b/src/app/extensions/rules/rule-context.ts
new file mode 100644
index 000000000..80ac871fe
--- /dev/null
+++ b/src/app/extensions/rules/rule-context.ts
@@ -0,0 +1,34 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { SelectionState } from '../../store/states';
+import { RuleEvaluator } from './rule.service';
+import { NavigationState } from '../../store/states/navigation.state';
+
+export interface RuleContext {
+ selection: SelectionState;
+ navigation: NavigationState;
+ evaluators: { [key: string]: RuleEvaluator };
+}
diff --git a/src/app/extensions/rules/rule-parameter.ts b/src/app/extensions/rules/rule-parameter.ts
new file mode 100644
index 000000000..798976503
--- /dev/null
+++ b/src/app/extensions/rules/rule-parameter.ts
@@ -0,0 +1,29 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+export interface RuleParameter {
+ type: string;
+ value: any;
+}
diff --git a/src/app/extensions/rules/rule-ref.ts b/src/app/extensions/rules/rule-ref.ts
new file mode 100644
index 000000000..da8fdb7fd
--- /dev/null
+++ b/src/app/extensions/rules/rule-ref.ts
@@ -0,0 +1,34 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { RuleParameter } from './rule-parameter';
+import { RuleEvaluator } from './rule.service';
+
+export class RuleRef {
+ type: string;
+ id?: string;
+ parameters?: Array;
+ evaluator?: RuleEvaluator;
+}
diff --git a/src/app/extensions/rules/rule.service.ts b/src/app/extensions/rules/rule.service.ts
new file mode 100644
index 000000000..34daf6063
--- /dev/null
+++ b/src/app/extensions/rules/rule.service.ts
@@ -0,0 +1,97 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Injectable } from '@angular/core';
+import { AppConfigService } from '@alfresco/adf-core';
+import { every, some } from './core.evaluators';
+import { RuleContext } from './rule-context';
+import { RuleRef } from './rule-ref';
+import { createSelector, Store } from '@ngrx/store';
+import {
+ appSelection,
+ appNavigation
+} from '../../store/selectors/app.selectors';
+import { AppStore, SelectionState } from '../../store/states';
+import { NavigationState } from '../../store/states/navigation.state';
+import { canCreateFolder, hasFolderSelected, canUpdateSelectedFolder, hasFileSelected, canDownloadSelection } from './app.evaluators';
+
+export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean;
+
+export const selectionWithFolder = createSelector(
+ appSelection,
+ appNavigation,
+ (selection, navigation) => {
+ return {
+ selection,
+ navigation
+ };
+ }
+);
+
+@Injectable()
+export class RuleService implements RuleContext {
+ rules: Array = [];
+ evaluators: { [key: string]: RuleEvaluator } = {};
+ selection: SelectionState;
+ navigation: NavigationState;
+
+ constructor(
+ private config: AppConfigService,
+ private store: Store
+ ) {
+ this.evaluators['core.every'] = every;
+ this.evaluators['core.some'] = some;
+ this.evaluators['app.selection.canDownload'] = canDownloadSelection;
+ this.evaluators['app.selection.file'] = hasFileSelected;
+ this.evaluators['app.selection.folder'] = hasFolderSelected;
+ this.evaluators['app.selection.folder.canUpdate'] = canUpdateSelectedFolder;
+ this.evaluators['app.navigation.folder.canCreate'] = canCreateFolder;
+
+ this.store
+ .select(selectionWithFolder)
+ .subscribe(result => {
+ this.selection = result.selection;
+ this.navigation = result.navigation;
+ });
+ }
+
+ init() {
+ this.rules = this.config
+ .get>('extensions.core.rules', [])
+ .map(rule => {
+ rule.evaluator = this.evaluators[rule.type];
+ return rule;
+ });
+ }
+
+ evaluateRule(ruleId: string): boolean {
+ const ruleRef = this.rules.find(ref => ref.id === ruleId);
+
+ if (ruleRef.evaluator) {
+ return ruleRef.evaluator(this, ...ruleRef.parameters);
+ }
+ return false;
+ }
+}
diff --git a/src/app/store/selectors/app.selectors.ts b/src/app/store/selectors/app.selectors.ts
index 49277c9d8..da23f79e1 100644
--- a/src/app/store/selectors/app.selectors.ts
+++ b/src/app/store/selectors/app.selectors.ts
@@ -34,4 +34,5 @@ export const appSelection = createSelector(selectApp, state => state.selection)
export const appLanguagePicker = createSelector(selectApp, state => state.languagePicker);
export const selectUser = createSelector(selectApp, state => state.user);
export const sharedUrl = createSelector(selectApp, state => state.sharedUrl);
+export const appNavigation = createSelector(selectApp, state => state.navigation);
export const currentFolder = createSelector(selectApp, state => state.navigation.currentFolder);
diff --git a/src/app/testing/app-testing.module.ts b/src/app/testing/app-testing.module.ts
index ee3142e6d..1376ba23b 100644
--- a/src/app/testing/app-testing.module.ts
+++ b/src/app/testing/app-testing.module.ts
@@ -60,6 +60,7 @@ import { NodeActionsService } from '../common/services/node-actions.service';
import { NodePermissionService } from '../common/services/node-permission.service';
import { ContentApiService } from '../services/content-api.service';
import { ExtensionService } from '../extensions/extension.service';
+import { RuleService } from '../extensions/rules/rule.service';
@NgModule({
imports: [
@@ -112,7 +113,8 @@ import { ExtensionService } from '../extensions/extension.service';
NodeActionsService,
NodePermissionService,
ContentApiService,
- ExtensionService
+ ExtensionService,
+ RuleService
]
})
export class AppTestingModule {}