diff --git a/demo-shell/src/assets/app.extensions.json b/demo-shell/src/assets/app.extensions.json index a7e0183e9d..6b2338374f 100644 --- a/demo-shell/src/assets/app.extensions.json +++ b/demo-shell/src/assets/app.extensions.json @@ -1,9 +1,15 @@ { - "$schema": "../../lib/extensions/config/schema/app-extension.schema.json", + "$schema": "../../../lib/extensions/src/lib/config/schema/app-extension.schema.json", "$references": [ "plugin1.json", "plugin2.json", "monaco-extension.json" ], - "$dependencies": [] + "$dependencies": [], + + "features": { + "viewer": { + "content": [] + } + } } diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index a5755f2983..9a0513af79 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -196,7 +196,7 @@ - + - + diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index ddf16fe264..9afd1ec919 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -31,7 +31,7 @@ import { ViewerSidebarComponent } from './viewer-sidebar.component'; import { ViewerToolbarComponent } from './viewer-toolbar.component'; import { Subscription } from 'rxjs'; import { ViewUtilService } from '../services/view-util.service'; -import { ExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; +import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; @Component({ selector: 'adf-viewer', @@ -238,7 +238,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { private viewUtils: ViewUtilService, private logService: LogService, private location: Location, - private extensionService: ExtensionService, + private extensionService: AppExtensionService, private el: ElementRef) { } @@ -251,14 +251,15 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.apiService.nodeUpdated.subscribe((node) => this.onNodeUpdated(node)) ); - this.extensionLoad(); + this.loadExtensions(); } - private extensionLoad() { - this.viewerExtensions = this.extensionService.getFeature('viewer.content'); - this.viewerExtensions.forEach((currentViewerExtension: ViewerExtensionRef) => { - this.externalExtensions.push(currentViewerExtension.fileExtension); - }); + private loadExtensions() { + this.viewerExtensions = this.extensionService.getViewerExtensions(); + this.viewerExtensions + .forEach((extension: ViewerExtensionRef) => { + this.externalExtensions.push(extension.fileExtension); + }); } ngOnDestroy() { diff --git a/lib/extensions/src/lib/config/document-list.extensions.ts b/lib/extensions/src/lib/config/document-list.extensions.ts new file mode 100644 index 0000000000..d467d1082a --- /dev/null +++ b/lib/extensions/src/lib/config/document-list.extensions.ts @@ -0,0 +1,29 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExtensionElement } from './extension-element'; + +export interface DocumentListPresetRef extends ExtensionElement { + key: string; + type: string; // text|image|date + title?: string; + format?: string; + class?: string; + sortable: boolean; + template: string; + desktopOnly: boolean; +} diff --git a/lib/extensions/src/lib/config/icon.extensions.ts b/lib/extensions/src/lib/config/icon.extensions.ts new file mode 100644 index 0000000000..be61309740 --- /dev/null +++ b/lib/extensions/src/lib/config/icon.extensions.ts @@ -0,0 +1,22 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExtensionElement } from './extension-element'; + +export interface IconRef extends ExtensionElement { + value: string; +} diff --git a/lib/extensions/src/lib/config/rule.extensions.ts b/lib/extensions/src/lib/config/rule.extensions.ts index 10d8c16980..ccf06d8c21 100644 --- a/lib/extensions/src/lib/config/rule.extensions.ts +++ b/lib/extensions/src/lib/config/rule.extensions.ts @@ -19,10 +19,13 @@ import { SelectionState } from '../store/states/selection.state'; import { NavigationState } from '../store/states/navigation.state'; import { NodePermissions } from './permission.extensions'; import { ProfileState } from '../store/states/profile.state'; +import { RepositoryInfo } from '@alfresco/js-api'; export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean; export interface RuleContext { + repository: RepositoryInfo; + auth: any; selection: SelectionState; navigation: NavigationState; profile: ProfileState; diff --git a/lib/extensions/src/lib/config/viewer.extensions.ts b/lib/extensions/src/lib/config/viewer.extensions.ts index 08ebba674f..6a4768dcdf 100644 --- a/lib/extensions/src/lib/config/viewer.extensions.ts +++ b/lib/extensions/src/lib/config/viewer.extensions.ts @@ -20,4 +20,9 @@ import { ExtensionElement } from './extension-element'; export interface ViewerExtensionRef extends ExtensionElement { fileExtension: string; component: string; + + rules?: { + visible?: string; + [key: string]: string; + }; } diff --git a/lib/extensions/src/lib/services/app-extension.service.ts b/lib/extensions/src/lib/services/app-extension.service.ts index 5599e975c5..9b83b7e253 100644 --- a/lib/extensions/src/lib/services/app-extension.service.ts +++ b/lib/extensions/src/lib/services/app-extension.service.ts @@ -19,6 +19,7 @@ import { Injectable } from '@angular/core'; import { ExtensionConfig, ExtensionRef } from '../config/extension.config'; import { ExtensionService } from '../services/extension.service'; import { Observable, BehaviorSubject } from 'rxjs'; +import { ViewerExtensionRef } from '../config/viewer.extensions'; @Injectable({ providedIn: 'root' @@ -47,4 +48,28 @@ export class AppExtensionService { .map((entry) => entry); this._references.next(references); } + + /** + * Provides a list of the Viewer content extensions, + * filtered by disabled state and rules. + */ + getViewerExtensions(): ViewerExtensionRef[] { + return this.extensionService + .getElements('features.viewer.content') + .filter((extension) => !this.isViewerExtensionDisabled(extension)); + } + + protected isViewerExtensionDisabled(extension: ViewerExtensionRef): boolean { + if (extension) { + if (extension.disabled) { + return true; + } + + if (extension.rules && extension.rules.disabled) { + return this.extensionService.evaluateRule(extension.rules.disabled); + } + } + + return false; + } } diff --git a/lib/extensions/src/lib/services/extension-loader.service.ts b/lib/extensions/src/lib/services/extension-loader.service.ts index 0e918a9ebd..d7f62adedd 100644 --- a/lib/extensions/src/lib/services/extension-loader.service.ts +++ b/lib/extensions/src/lib/services/extension-loader.service.ts @@ -105,6 +105,12 @@ export class ExtensionLoaderService { }); } + /** + * Retrieves configuration elements. + * Filters element by **enabled** and **order** attributes. + * Example: + * `getElements(config, 'features.viewer.content')` + */ getElements( config: ExtensionConfig, key: string, diff --git a/lib/extensions/src/lib/services/extension.service.spec.ts b/lib/extensions/src/lib/services/extension.service.spec.ts index 736f4bed54..029c4df1a0 100644 --- a/lib/extensions/src/lib/services/extension.service.spec.ts +++ b/lib/extensions/src/lib/services/extension.service.spec.ts @@ -22,6 +22,7 @@ import { RuleRef } from '../config/rule.extensions'; import { RouteRef } from '../config/routing.extensions'; import { ActionRef } from '../config/action.extensions'; import { ComponentRegisterService } from './component-register.service'; +import { RuleService } from './rule.service'; describe('ExtensionService', () => { const blankConfig: ExtensionConfig = { @@ -36,11 +37,13 @@ describe('ExtensionService', () => { let loader: ExtensionLoaderService; let componentRegister: ComponentRegisterService; let service: ExtensionService; + let ruleService: RuleService; beforeEach(() => { loader = new ExtensionLoaderService(null); componentRegister = new ComponentRegisterService(); - service = new ExtensionService(loader, componentRegister); + ruleService = new RuleService(loader); + service = new ExtensionService(loader, componentRegister, ruleService); }); it('should load and setup a config', async () => { diff --git a/lib/extensions/src/lib/services/extension.service.ts b/lib/extensions/src/lib/services/extension.service.ts index 0ac4de4263..4e4d1b114c 100644 --- a/lib/extensions/src/lib/services/extension.service.ts +++ b/lib/extensions/src/lib/services/extension.service.ts @@ -16,32 +16,35 @@ */ import { Injectable, Type } from '@angular/core'; -import { RuleEvaluator, RuleRef, RuleContext, RuleParameter } from '../config/rule.extensions'; +import { RuleEvaluator, RuleRef, RuleContext } from '../config/rule.extensions'; import { ExtensionConfig } from '../config/extension.config'; import { ExtensionLoaderService } from './extension-loader.service'; import { RouteRef } from '../config/routing.extensions'; import { ActionRef } from '../config/action.extensions'; import * as core from '../evaluators/core.evaluators'; import { ComponentRegisterService } from './component-register.service'; +import { RuleService } from './rule.service'; +import { ExtensionElement } from '../config/extension-element'; @Injectable({ providedIn: 'root' }) export class ExtensionService { + + protected config: ExtensionConfig = null; + configPath = 'assets/app.extensions.json'; pluginsPath = 'assets/plugins'; - rules: Array = []; routes: Array = []; actions: Array = []; features: Array = []; - authGuards: { [key: string]: Type<{}> } = {}; - evaluators: { [key: string]: RuleEvaluator } = {}; constructor( - private loader: ExtensionLoaderService, - private componentRegister: ComponentRegisterService + protected loader: ExtensionLoaderService, + protected componentRegister: ComponentRegisterService, + protected ruleService: RuleService ) { } @@ -68,16 +71,19 @@ export class ExtensionService { return; } + this.config = config; + this.setEvaluators({ 'core.every': core.every, 'core.some': core.some, 'core.not': core.not }); - this.rules = this.loader.getRules(config); this.actions = this.loader.getActions(config); this.routes = this.loader.getRoutes(config); this.features = this.loader.getFeatures(config); + + this.ruleService.setup(config); } /** @@ -90,14 +96,16 @@ export class ExtensionService { return properties.reduce((prev, curr) => prev && prev[curr], this.features) || []; } + getElements(key: string, fallback: Array = []): Array { + return this.loader.getElements(this.config, key, fallback); + } + /** * Adds one or more new rule evaluators to the existing set. * @param values The new evaluators to add */ setEvaluators(values: { [key: string]: RuleEvaluator }) { - if (values) { - this.evaluators = Object.assign({}, this.evaluators, values); - } + this.ruleService.setEvaluators(values); } /** @@ -153,36 +161,17 @@ export class ExtensionService { * @returns RuleEvaluator or null if not found */ getEvaluator(key: string): RuleEvaluator { - if (key && key.startsWith('!')) { - const fn = this.evaluators[key.substring(1)]; - return (context: RuleContext, ...args: RuleParameter[]): boolean => { - return !fn(context, ...args); - }; - } - return this.evaluators[key]; + return this.ruleService.getEvaluator(key); } /** * Evaluates a rule. * @param ruleId ID of the rule to evaluate - * @param context Parameter object for the evaluator with details of app state + * @param context (optional) Custom rule execution context. * @returns True if the rule passed, false otherwise */ - evaluateRule(ruleId: string, context: RuleContext): boolean { - const ruleRef = this.getRuleById(ruleId); - - if (ruleRef) { - const evaluator = this.getEvaluator(ruleRef.type); - if (evaluator) { - return evaluator(context, ...ruleRef.parameters); - } - } else { - const evaluator = this.getEvaluator(ruleId); - if (evaluator) { - return evaluator(context); - } - } - return false; + evaluateRule(ruleId: string, context?: RuleContext): boolean { + return this.ruleService.evaluateRule(ruleId, context); } /** @@ -200,7 +189,7 @@ export class ExtensionService { * @returns The rule or null if not found */ getRuleById(id: string): RuleRef { - return this.rules.find((ref) => ref.id === id); + return this.ruleService.getRuleById(id); } /** diff --git a/lib/extensions/src/lib/services/rule.service.ts b/lib/extensions/src/lib/services/rule.service.ts new file mode 100644 index 0000000000..91904142b0 --- /dev/null +++ b/lib/extensions/src/lib/services/rule.service.ts @@ -0,0 +1,94 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { RuleRef, RuleContext, RuleEvaluator, RuleParameter } from '../config/rule.extensions'; +import { ExtensionConfig } from '../config/extension.config'; +import { ExtensionLoaderService } from './extension-loader.service'; + +@Injectable({ + providedIn: 'root' +}) +export class RuleService { + context: RuleContext = null; + rules: Array = []; + evaluators: { [key: string]: RuleEvaluator } = {}; + + constructor(protected loader: ExtensionLoaderService) {} + + setup(config: ExtensionConfig) { + this.rules = this.loader.getRules(config); + } + + /** + * Adds one or more new rule evaluators to the existing set. + * @param values The new evaluators to add + */ + setEvaluators(values: { [key: string]: RuleEvaluator }) { + if (values) { + this.evaluators = Object.assign({}, this.evaluators, values); + } + } + + /** + * Retrieves a rule using its ID value. + * @param id The ID value to look for + * @returns The rule or null if not found + */ + getRuleById(id: string): RuleRef { + return this.rules.find((ref) => ref.id === id); + } + + /** + * Retrieves a RuleEvaluator function using its key name. + * @param key Key name to look for + * @returns RuleEvaluator or null if not found + */ + getEvaluator(key: string): RuleEvaluator { + if (key && key.startsWith('!')) { + const fn = this.evaluators[key.substring(1)]; + return (context: RuleContext, ...args: RuleParameter[]): boolean => { + return !fn(context, ...args); + }; + } + return this.evaluators[key]; + } + + /** + * Evaluates a rule. + * @param ruleId ID of the rule to evaluate + * @param context (optional) Custom rule execution context. + * @returns True if the rule passed, false otherwise + */ + evaluateRule(ruleId: string, context?: RuleContext): boolean { + const ruleRef = this.getRuleById(ruleId); + context = context || this.context; + + if (ruleRef) { + const evaluator = this.getEvaluator(ruleRef.type); + if (evaluator) { + return evaluator(context, ...ruleRef.parameters); + } + } else { + const evaluator = this.getEvaluator(ruleId); + if (evaluator) { + return evaluator(context); + } + } + return false; + } +} diff --git a/lib/extensions/src/public-api.ts b/lib/extensions/src/public-api.ts index c7acbcf4fb..b182f65c0d 100644 --- a/lib/extensions/src/public-api.ts +++ b/lib/extensions/src/public-api.ts @@ -18,9 +18,11 @@ export * from './lib/extensions.module'; export * from './lib/config/action.extensions'; +export * from './lib/config/document-list.extensions'; export * from './lib/config/extension-element'; export * from './lib/config/extension-utils'; export * from './lib/config/extension.config'; +export * from './lib/config/icon.extensions'; export * from './lib/config/navbar.extensions'; export * from './lib/config/permission.extensions'; export * from './lib/config/routing.extensions';