mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
extensibility improvements (#4484)
* add missing interfaces to extensions library * separate rule service * api enhancements * fix test * improve APIs
This commit is contained in:
committed by
Eugenio Romano
parent
80aaaef65d
commit
26c5982a1a
@@ -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": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -208,7 +208,8 @@
|
||||
</ng-container>
|
||||
|
||||
<span class="adf-viewer-custom-content" *ngFor="let extensionTemplate of extensionTemplates">
|
||||
<ng-template *ngIf="extensionTemplate.isVisible"
|
||||
<ng-template
|
||||
*ngIf="extensionTemplate.isVisible"
|
||||
[ngTemplateOutlet]="extensionTemplate.template"
|
||||
[ngTemplateOutletContext]="{ urlFileContent: urlFileContent, extension:extension }">
|
||||
</ng-template>
|
||||
|
@@ -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,13 +251,14 @@ 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
29
lib/extensions/src/lib/config/document-list.extensions.ts
Normal file
29
lib/extensions/src/lib/config/document-list.extensions.ts
Normal file
@@ -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;
|
||||
}
|
22
lib/extensions/src/lib/config/icon.extensions.ts
Normal file
22
lib/extensions/src/lib/config/icon.extensions.ts
Normal file
@@ -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;
|
||||
}
|
@@ -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;
|
||||
|
@@ -20,4 +20,9 @@ import { ExtensionElement } from './extension-element';
|
||||
export interface ViewerExtensionRef extends ExtensionElement {
|
||||
fileExtension: string;
|
||||
component: string;
|
||||
|
||||
rules?: {
|
||||
visible?: string;
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
@@ -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) => <ExtensionRef> 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<ViewerExtensionRef>('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;
|
||||
}
|
||||
}
|
||||
|
@@ -105,6 +105,12 @@ export class ExtensionLoaderService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves configuration elements.
|
||||
* Filters element by **enabled** and **order** attributes.
|
||||
* Example:
|
||||
* `getElements<ViewerExtensionRef>(config, 'features.viewer.content')`
|
||||
*/
|
||||
getElements<T extends ExtensionElement>(
|
||||
config: ExtensionConfig,
|
||||
key: string,
|
||||
|
@@ -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 () => {
|
||||
|
@@ -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<RuleRef> = [];
|
||||
routes: Array<RouteRef> = [];
|
||||
actions: Array<ActionRef> = [];
|
||||
features: Array<any> = [];
|
||||
|
||||
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<T extends ExtensionElement>(key: string, fallback: Array<T> = []): Array<T> {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
94
lib/extensions/src/lib/services/rule.service.ts
Normal file
94
lib/extensions/src/lib/services/rule.service.ts
Normal file
@@ -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<RuleRef> = [];
|
||||
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;
|
||||
}
|
||||
}
|
@@ -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';
|
||||
|
Reference in New Issue
Block a user