reusable extensibility lib (#585)

reusable extensibility lib (part 1)
This commit is contained in:
Denys Vuika 2018-08-29 16:38:44 +01:00 committed by GitHub
parent 091e0d3e3f
commit c916ab4cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 1312 additions and 1123 deletions

View File

@ -239,6 +239,46 @@
}
}
}
},
"adf-extensions": {
"root": "projects/adf-extensions",
"sourceRoot": "projects/adf-extensions/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/adf-extensions/tsconfig.lib.json",
"project": "projects/adf-extensions/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/adf-extensions/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/adf-extensions/src/test.ts",
"tsConfig": "projects/adf-extensions/tsconfig.spec.json",
"karmaConfig": "projects/adf-extensions/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/adf-extensions/tsconfig.lib.json",
"projects/adf-extensions/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "alfresco-content-app",
@ -251,4 +291,4 @@
"prefix": "aca"
}
}
}
}

View File

@ -1119,51 +1119,44 @@ As with other content actions, custom plugins can disable, update or extend `Ope
You can use `ExtensionService` to register custom components, authentication guards,
rule evaluators, etc.
It is recommended to register custom content during application startup
by utilising the `APP_INITIALIZER` injection token that comes with Angular.
It is recommended to register custom content from within the module constructor.
In that case all plugins will be available right after main application component is ready.
Update the main application module `app.module.ts`, or create your own module,
and use the following snippet to register custom content:
```typescript
export function setupExtensions(extensions: ExtensionService): Function {
return () =>
new Promise(resolve => {
extensions.setComponents({
'plugin1.components.my': MyComponent1,
'plugin1.layouts.my': MyLayout
});
extensions.setAuthGuards({
'plugin.auth': MyAuthGuard
});
extensions.setEvaluators({
'plugin1.rules.custom1': MyCustom1Evaluator,
'plugin1.rules.custom2': MyCustom2Evaluator
});
resolve(true);
});
}
import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions';
@NgModule({
imports: [ ExtensionsModule.forChild() ]
declarations: [ MyComponent1, MyLayout ],
entryComponents: [ MyComponent1, MyLayout ],
providers: [
{
provide: APP_INITIALIZER,
useFactory: setupExtensions,
deps: [ ExtensionService ],
multi: true
}
]
entryComponents: [ MyComponent1, MyLayout ]
})
export class MyExtensionModule {}
export class MyExtensionModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'plugin1.components.my': MyComponent1,
'plugin1.layouts.my': MyLayout
});
extensions.setAuthGuards({
'plugin.auth': MyAuthGuard
});
extensions.setEvaluators({
'plugin1.rules.custom1': MyCustom1Evaluator,
'plugin1.rules.custom2': MyCustom2Evaluator
});
}
}
```
Use `ExtensionsModule.forChild()` when importing into the child modules,
and `ExtensionsModule.forRoot()` for the main application module.
<p class="warning">
According to Angular rules, all components that are created dynamically at runtime
need to be registered within the `entryComponents` section of the NgModule.

View File

@ -38,7 +38,7 @@ export class InfoDrawer extends Component {
tabLabel: '.mat-tab-label-content',
tabActiveLabel: '.mat-tab-label-active',
activeTabContent: '.mat-tab-body-active .mat-tab-body-content app-dynamic-tab',
activeTabContent: '.mat-tab-body-active .mat-tab-body-content adf-dynamic-tab',
next: '.mat-tab-header-pagination-after .mat-tab-header-pagination-chevron',
previous: '.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron'
};

160
package-lock.json generated
View File

@ -977,19 +977,19 @@
}
},
"@angular-devkit/schematics": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.7.4.tgz",
"integrity": "sha512-Gkm2mBMm6a0mNKqZsAcS42VaO7zNSIXhIKbMyZQIcQ1ZMwbsx+Rs0dliQwFVfEVCNGc1pjh0Idc5O5V/g/N5Fw==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.7.5.tgz",
"integrity": "sha512-E7HkQeJawUskf2gPnogMc+cTdjJ2Iv3QEZOgprh/ExEmBYByWkGDRX5fQOuy8wME8VZqUBvQACZaVkEredn5EA==",
"dev": true,
"requires": {
"@angular-devkit/core": "0.7.4",
"@angular-devkit/core": "0.7.5",
"rxjs": "^6.0.0"
},
"dependencies": {
"@angular-devkit/core": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz",
"integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz",
"integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==",
"dev": true,
"requires": {
"ajv": "~6.4.0",
@ -1384,32 +1384,32 @@
}
},
"@angular/animations": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.3.tgz",
"integrity": "sha512-uLKq+bdfo+/jLW/C6lkUVsB7m+e8j18MjZGHlphI07jW6KvutX+AXdPUI/RMkkWRjZp11aF727PAQ6y3DyqB+Q==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.4.tgz",
"integrity": "sha512-R+akCyIneyqJ5wAf9VaymvxbxM4Iw3YsUdylO9rrr9wAUhzmzWhCSGK9bncwL4+d5rbd0n1u+8A8Gm0dZe1P1A==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/cdk": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.5.tgz",
"integrity": "sha512-bbz8lHzY1NXPqrvHcZTOOD6+BiDNPKXC00xwzls62NPmiueBaWGwQdk7s3sa/0kCyq1ZKrPD3KQIDMyytxlzzw==",
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.6.tgz",
"integrity": "sha512-XKSoeSP4htpOq2UIyF9KDhIJtEQ3wyhZRjDxyRSNmJ9OsuRZxJAGCAzOX5RpMszOyFZgUNVycOi+1lHDe0JrZg==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/cli": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.1.4.tgz",
"integrity": "sha512-9lkFGZGV38p5p/s4p91hc8r4z1WaUKgige8pGNBIuW93esnkjoll5/NKx4siU2wsaPd/4njaP5f2iuUuyYNtbg==",
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.1.5.tgz",
"integrity": "sha512-QNVUSC8mPdiaxubneqNZISy+wec3gwbKoXjcaQ9/45baOnp662j2iJXwiMh6Atn0YUM4u1iUsz1uHyARMtgZmw==",
"dev": true,
"requires": {
"@angular-devkit/architect": "0.7.4",
"@angular-devkit/core": "0.7.4",
"@angular-devkit/schematics": "0.7.4",
"@schematics/angular": "0.7.4",
"@schematics/update": "0.7.4",
"@angular-devkit/architect": "0.7.5",
"@angular-devkit/core": "0.7.5",
"@angular-devkit/schematics": "0.7.5",
"@schematics/angular": "0.7.5",
"@schematics/update": "0.7.5",
"opn": "^5.3.0",
"rxjs": "^6.0.0",
"semver": "^5.1.0",
@ -1418,19 +1418,19 @@
},
"dependencies": {
"@angular-devkit/architect": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.7.4.tgz",
"integrity": "sha512-qcxLtA5XhUCqNyyMOD+s7oIVywNnhUNE1qoopnm6MN0FJ1n7iQMU5TPZBTiXDWQVnbGODObi7tGo7gFnEBML5Q==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.7.5.tgz",
"integrity": "sha512-zwCpGdx3JDE+Y+LiWh9ErRX+fpFPTRHtEd2PDJmfQsdlIWfjxSR5U9vi3+bSRW2n6IFiH2GCYMS31R64rfMwbg==",
"dev": true,
"requires": {
"@angular-devkit/core": "0.7.4",
"@angular-devkit/core": "0.7.5",
"rxjs": "^6.0.0"
}
},
"@angular-devkit/core": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz",
"integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz",
"integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==",
"dev": true,
"requires": {
"ajv": "~6.4.0",
@ -1825,25 +1825,25 @@
}
},
"@angular/common": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.3.tgz",
"integrity": "sha512-1V3pDdEty4hYsdpePlcNUE8rF1w1NP8LW6Q1ICNk86MI472W1U9ZTDFwCYcQYDiYMtzBrgXcnE1q6u1rqTdygQ==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.4.tgz",
"integrity": "sha512-vpedSD5Rbuj9kLq9W/aeQBVugplimTJPPeuW/zUXHWVOOOk4Y7KBw5g4JdYw2ocSoY3z+dRl/6fR0JTi9+muaA==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/compiler": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.3.tgz",
"integrity": "sha512-r8Nv4wo2QNmsGs/sjxcR6z6YG17TfaAAxAl/6yk3z3DNdDM76cBwTi9hurXlmZKPU6/2YFI+ZwvhBrGwaOZd5Q==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.4.tgz",
"integrity": "sha512-HjSK9Jjx6f1jpXy2TALKp2ByAXycZKKD39M9K2g+feTTrpUtd3iDEDLG4S/yok9qs4e1k3L8fxr/qBngQuv23A==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/compiler-cli": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.3.tgz",
"integrity": "sha512-YSVoLMaCF0mvt0CPRGldKMCJDGju82XhsupbDOZsMddVG6hRl8dRbem7OChJp+8GV+UNsfbP/X2o4ERLROHXRA==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.4.tgz",
"integrity": "sha512-ueTe5THcPIKjXOb1+LvqEqh35QPihEGObvJIpudMTqkJHguOr2WXKbbgxzF8QWuIBhOHR9fjtaSgNY5Kk2mfTg==",
"dev": true,
"requires": {
"chokidar": "^1.4.2",
@ -1861,9 +1861,9 @@
}
},
"@angular/core": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.3.tgz",
"integrity": "sha512-pqRfQphqIEExhDWM3RRusvLY6gFN0zdITC7TqQy6Wof6VKgWOvfHiHPbiamw4kpEzflMekuOeNm0s6h6hIUnWA==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.4.tgz",
"integrity": "sha512-8r2LpD4MR0hAYjWkElD/I6iXcugMK/HrpdtopDlRcxW2f6XuMN4mu8eS3g2fu72PwdGhtMcqDDFlbeJ8k599lw==",
"requires": {
"tslib": "^1.9.0"
}
@ -1877,31 +1877,31 @@
}
},
"@angular/forms": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.3.tgz",
"integrity": "sha512-8pHVQ97S0W//GsYsGuGV/SyzkMAF/bcE1AqDg4HXNU3mKOl5lthfM+PxHz23Cdzkg/GVInwRNxUl2Q+6lb8zuw==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.4.tgz",
"integrity": "sha512-O/rYF36zM15fbXPv4Tj7NlYiCazko6+Eb4o9Ls0nJXMOxt8pRwdYjgGMaOtYprtbH89YlnmJU/gav5Z58JG7sw==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/http": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.3.tgz",
"integrity": "sha512-1IZ+8i1gIoPDD57Yv/8fEjzdFfWM7nY5JZGyjWUkWxq/A19zjkR4C1nRCI6KiWHk0QFx2e1Sl6OFqyiphwhalw==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.4.tgz",
"integrity": "sha512-HD3+ouMAqsgA6hFUS8AQDVlZTbkuNuyomGY1I85IpqXmO7GneszN6y5xIf8XO5ke4UYgvk5UhRrKJ9UG9VtUOw==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/language-service": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.3.tgz",
"integrity": "sha512-cAIptulTWn1LGFTmRzBbNY4axhnyQl/YaocX0JqutmI+eaWTofSiDwHVB2spYSDTWhy9fg+OMK2KmhghpZmc1g==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.4.tgz",
"integrity": "sha512-kvnAphJ7VrKJpm1gN3sFjGp/5gQxu/FAw03yD1f1z+C+aHrNKoxaS9pp9NdOIT/DWlxR/BcEDF4gzMNMTJ9/wA==",
"dev": true
},
"@angular/material": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-6.4.5.tgz",
"integrity": "sha512-3wDRfGqlRSo3CBA1XuPXSz7zAwZF5kotXEgbZhIAocv+nsXa73DyPNceAQ++Pu7rFR3ipg6McyggP0OCOgv7NQ==",
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-6.4.6.tgz",
"integrity": "sha512-SUSg9MhLv4IZj6Nh8qoCLDImZugCQ+Jvvt+/cDIaTn6TrT6ZenDHc6jOhbGFesU6FuBDBFIXMiuBPD9kBr7vaA==",
"requires": {
"parse5": "^5.0.0",
"tslib": "^1.7.1"
@ -1916,33 +1916,33 @@
}
},
"@angular/material-moment-adapter": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.5.tgz",
"integrity": "sha512-xgPMnntOfwTX4Uuz7g/GQ4IcRn6tiSJCGSikwTK5yRJjUEGguHmBch57q2Xjr/A+WfmGYZnVfzy3V9t/XsNKqA==",
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.6.tgz",
"integrity": "sha512-Bvj/zwtEjF1bqOF1z/M0VcWYOwlRjmCBjvPVmNZ1Bxf7HE8yyRf8B+AAv46sn/EhhAv/eMNU9bzFcxrR/vEHAg==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/platform-browser": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.3.tgz",
"integrity": "sha512-ml844B5g8UtQaOK2QCdTRSTRHWa1elTbg4ph65GTs0lNMH/tLFNn8tvcPOfMh21ZV3fh6yqA6a9wMjSMTVSiWg==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.4.tgz",
"integrity": "sha512-46UPtC360+3E4eeOQk45qp+r+d0Qnsujyot+XtVKQmTSHTInDlwfIGA9TBTw8GyAs3O65i80LRkDHFz9BM2pmw==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/platform-browser-dynamic": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.3.tgz",
"integrity": "sha512-6b4Y8f4yhuUJrAVsHyYWRbRSaIQUzZWSbYvWLVZeXnt4HKzRhX+zO5yTA/eXCXAce+fRIU239FbkeyqtpuogPQ==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.4.tgz",
"integrity": "sha512-a/WVCsAa0qdtLNuK6h8q+nwtXmJqOlc+dJrBK3vz1umhc80nFZeaUPMTnkRwphk6WVE8xHvjb41PZsI8jF9CBQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/router": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.3.tgz",
"integrity": "sha512-6sb1yH/a2CACcbXZ6d+PYWGgwV3BXupCQXBd8Q5h7o3/r5zz18VATdRqBWtSIOmFr11wekJrGGRMxDCpTDlXvg==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.4.tgz",
"integrity": "sha512-ANdJmpPkr4BMW3/ixJ/qSbsQk4CwR3BPDJp3Iua/xLy7i+9h0bcs11Lpdyo5U3esPYpcpc6TE3ofptyD6xpyGg==",
"requires": {
"tslib": "^1.9.0"
}
@ -2018,20 +2018,20 @@
}
},
"@schematics/angular": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.7.4.tgz",
"integrity": "sha512-/KJH5fPv+40VdadWmVnXR2GlvFZKYH01eYw7XOi77gXM896gk2tGlWmm+6RjUaVyec49ivusmmhRJEjiTyA7NA==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.7.5.tgz",
"integrity": "sha512-NrtvFwHCoWon8KInsvA1jdPu4pVJGa8GAWM/jqnE7HpwPwM7hMML08lV0P8r3NX5t2/i0CKvfp4AAEr5MXorEQ==",
"dev": true,
"requires": {
"@angular-devkit/core": "0.7.4",
"@angular-devkit/schematics": "0.7.4",
"@angular-devkit/core": "0.7.5",
"@angular-devkit/schematics": "0.7.5",
"typescript": ">=2.6.2 <2.10"
},
"dependencies": {
"@angular-devkit/core": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz",
"integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz",
"integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==",
"dev": true,
"requires": {
"ajv": "~6.4.0",
@ -2426,13 +2426,13 @@
}
},
"@schematics/update": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.7.4.tgz",
"integrity": "sha512-HC8AgVrwF4fmtFVQ2y7wheB7k8sqbLNBFmTH51C6+5XJegSFnT5AjkRoWkbHS5Wuifxma1wMjJKqZZrH6vgWFQ==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.7.5.tgz",
"integrity": "sha512-pwNkXGtlzyCV6tsTPe8AgUuMCkmubcz94zgL6pSMdEe122yXBcKnr/PKqG9QzD/gGwmOcHUE9EWcuRtU5kdFpA==",
"dev": true,
"requires": {
"@angular-devkit/core": "0.7.4",
"@angular-devkit/schematics": "0.7.4",
"@angular-devkit/core": "0.7.5",
"@angular-devkit/schematics": "0.7.5",
"npm-registry-client": "^8.5.1",
"rxjs": "^6.0.0",
"semver": "^5.3.0",
@ -2440,9 +2440,9 @@
},
"dependencies": {
"@angular-devkit/core": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz",
"integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz",
"integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==",
"dev": true,
"requires": {
"ajv": "~6.4.0",

View File

@ -4,12 +4,14 @@
"license": "LGPL-3.0",
"scripts": {
"ng": "ng",
"start": "npm run server-versions && ng build aca-dev-tools && ng serve --open",
"start": "npm run server-versions && npm run build.libs.dev && ng serve --open",
"start:prod": "npm run server-versions && ng serve --prod --open",
"build": "npm run server-versions && ng build aca-dev-tools --prod && ng build --prod",
"build:dev": "npm run server-versions && ng build aca-dev-tools && ng build",
"test": "ng test alfresco-content-app --code-coverage",
"test:ci": "ng test alfresco-content-app --code-coverage --watch=false && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage",
"build.libs.dev": "ng build adf-extensions && ng build aca-dev-tools",
"build.libs.prod": "ng build adf-extensions --prod && ng build aca-dev-tools --prod",
"build": "npm run server-versions && npm run build.libs.prod && ng build --prod",
"build:dev": "npm run server-versions && npm run build.libs.dev && ng build",
"test": "npm run build.libs.dev && ng test alfresco-content-app --code-coverage",
"test:ci": "npm run build.libs.dev && ng test alfresco-content-app --code-coverage --watch=false && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage",
"lint": "ng lint",
"server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0",
"_e2e": "ng e2e",
@ -24,19 +26,19 @@
"dependencies": {
"@alfresco/adf-content-services": "2.5.0",
"@alfresco/adf-core": "2.5.0",
"@angular/animations": "6.1.3",
"@angular/cdk": "^6.4.5",
"@angular/common": "6.1.3",
"@angular/compiler": "6.1.3",
"@angular/core": "6.1.3",
"@angular/animations": "6.1.4",
"@angular/cdk": "^6.4.6",
"@angular/common": "6.1.4",
"@angular/compiler": "6.1.4",
"@angular/core": "6.1.4",
"@angular/flex-layout": "^6.0.0-beta.17",
"@angular/forms": "6.1.3",
"@angular/http": "6.1.3",
"@angular/material": "^6.4.5",
"@angular/material-moment-adapter": "^6.4.5",
"@angular/platform-browser": "6.1.3",
"@angular/platform-browser-dynamic": "6.1.3",
"@angular/router": "6.1.3",
"@angular/forms": "6.1.4",
"@angular/http": "6.1.4",
"@angular/material": "^6.4.6",
"@angular/material-moment-adapter": "^6.4.6",
"@angular/platform-browser": "6.1.4",
"@angular/platform-browser-dynamic": "6.1.4",
"@angular/router": "6.1.4",
"@mat-datetimepicker/core": "^2.0.1",
"@mat-datetimepicker/moment": "^2.0.1",
"@ngrx/effects": "^6.1.0",
@ -60,9 +62,9 @@
"devDependencies": {
"@angular-devkit/build-angular": "~0.7.4",
"@angular-devkit/build-ng-packagr": "~0.7.4",
"@angular/cli": "^6.1.4",
"@angular/compiler-cli": "6.1.3",
"@angular/language-service": "6.1.3",
"@angular/cli": "^6.1.5",
"@angular/compiler-cli": "6.1.4",
"@angular/language-service": "6.1.4",
"@types/jasmine": "^2.5.53",
"@types/jasminewd2": "^2.0.2",
"@types/node": "9.3.0",

View File

@ -4,6 +4,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
import { AcaDevToolsComponent } from './aca-dev-tools.component';
import { CoreModule } from '@alfresco/adf-core';
import { ContentModule } from '@alfresco/adf-content-services';
import { ExtensionService } from '@alfresco/adf-extensions';
@NgModule({
imports: [
@ -16,4 +17,10 @@ import { ContentModule } from '@alfresco/adf-content-services';
exports: [AcaDevToolsComponent],
entryComponents: [AcaDevToolsComponent]
})
export class AcaDevToolsModule {}
export class AcaDevToolsModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'app.dev.tools.component': AcaDevToolsComponent
});
}
}

View File

@ -1,15 +0,0 @@
import { TestBed, inject } from '@angular/core/testing';
import { AcaDevToolsService } from './aca-dev-tools.service';
describe('AcaDevToolsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AcaDevToolsService]
});
});
it('should be created', inject([AcaDevToolsService], (service: AcaDevToolsService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,9 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AcaDevToolsService {
constructor() { }
}

View File

@ -2,6 +2,5 @@
* Public API Surface of aca-dev-tools
*/
export * from './lib/aca-dev-tools.service';
export * from './lib/aca-dev-tools.component';
export * from './lib/aca-dev-tools.module';

View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -0,0 +1,9 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/@alfresco/adf-extensions",
"deleteDestPath": false,
"lib": {
"languageLevel": ["dom", "es2017"],
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,8 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/@alfresco/adf-extensions",
"lib": {
"languageLevel": ["dom", "es2017"],
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,10 @@
{
"name": "@alfresco/adf-extensions",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^6.0.0",
"@angular/core": "^6.0.0",
"@angular/http": "^6.1.4",
"alfresco-js-api": "^2.5.0"
}
}

View File

@ -0,0 +1,67 @@
/*!
* @license
* Copyright 2016 - 2018 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 {
Component,
Input,
ComponentRef,
OnInit,
ComponentFactoryResolver,
ViewChild,
ViewContainerRef,
OnDestroy
} from '@angular/core';
import { ExtensionService } from '../../services/extension.service';
@Component({
selector: 'adf-dynamic-component',
template: `<div #content></div>`
})
export class DynamicExtensionComponent implements OnInit, OnDestroy {
@ViewChild('content', { read: ViewContainerRef })
content: ViewContainerRef;
@Input() id: string;
private componentRef: ComponentRef<any>;
constructor(
private extensions: ExtensionService,
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
const componentType = this.extensions.getComponentById(this.id);
if (componentType) {
const factory = this.componentFactoryResolver.resolveComponentFactory(
componentType
);
if (factory) {
this.content.clear();
this.componentRef = this.content.createComponent(factory, 0);
// this.setupWidget(this.componentRef);
}
}
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
}

View File

@ -36,10 +36,10 @@ import {
SimpleChanges
} from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ExtensionService } from '../../../extensions/extension.service';
import { ExtensionService } from '../../services/extension.service';
@Component({
selector: 'app-dynamic-tab',
selector: 'adf-dynamic-tab',
template: `<div #content></div>`
})
export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy {

View File

@ -0,0 +1,51 @@
/*!
* @license
* Copyright 2016 - 2018 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 enum ContentActionType {
default = 'default',
button = 'button',
separator = 'separator',
menu = 'menu',
custom = 'custom'
}
export interface ContentActionRef extends ExtensionElement {
type: ContentActionType;
title?: string;
description?: string;
icon?: string;
children?: Array<ContentActionRef>;
component?: string;
actions?: {
click?: string;
[key: string]: string;
};
rules?: {
enabled?: string;
visible?: string;
[key: string]: string;
};
}
export interface ActionRef {
id: string;
type: string;
payload?: string;
}

View File

@ -0,0 +1,23 @@
/*!
* @license
* Copyright 2016 - 2018 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.
*/
export interface ExtensionElement {
id: string;
order?: number;
disabled?: boolean;
}

View File

@ -1,26 +1,18 @@
/*!
* @license
* Alfresco Example Content Application
* Copyright 2016 - 2018 Alfresco Software, Ltd.
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* 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
*
* 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:
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 <http://www.gnu.org/licenses/>.
* 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 { ContentActionRef, ContentActionType } from './action.extensions';

View File

@ -0,0 +1,35 @@
/*!
* @license
* Copyright 2016 - 2018 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 { RouteRef } from './routing.extensions';
import { RuleRef } from './rule.extensions';
import { ActionRef } from './action.extensions';
export interface ExtensionConfig {
$name: string;
$version: string;
$description?: string;
$references?: Array<string>;
rules?: Array<RuleRef>;
routes?: Array<RouteRef>;
actions?: Array<ActionRef>;
features?: {
[key: string]: any;
};
}

View File

@ -0,0 +1,31 @@
/*!
* @license
* Copyright 2016 - 2018 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 NavBarGroupRef extends ExtensionElement {
items: Array<NavBarLinkRef>;
}
export interface NavBarLinkRef extends ExtensionElement {
icon: string;
title: string;
route: string;
url?: string; // evaluated at runtime based on route ref
description?: string;
}

View File

@ -0,0 +1,20 @@
/*!
* @license
* Copyright 2016 - 2018 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.
*/
export interface NodePermissions {
check(source: any, permissions: string[], options?: any): boolean;
}

View File

@ -0,0 +1,26 @@
/*!
* @license
* Copyright 2016 - 2018 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.
*/
export interface RouteRef {
id: string;
path: string;
component: string;
layout?: string;
auth?: string[];
data?: { [key: string]: string };
}

View File

@ -0,0 +1,44 @@
/*!
* @license
* Copyright 2016 - 2018 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 { 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';
export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean;
export interface RuleContext {
selection: SelectionState;
navigation: NavigationState;
profile: ProfileState;
permissions: NodePermissions;
getEvaluator(key: string): RuleEvaluator;
}
export class RuleRef {
type: string;
id?: string;
parameters?: Array<RuleParameter>;
}
export interface RuleParameter {
type: string;
value: any;
parameters?: Array<RuleParameter>;
}

View File

@ -0,0 +1,29 @@
/*!
* @license
* Copyright 2016 - 2018 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 SidebarTabRef extends ExtensionElement {
title: string;
component: string;
icon?: string;
rules?: {
visible?: string;
[key: string]: string;
};
}

View File

@ -0,0 +1,23 @@
/*!
* @license
* Copyright 2016 - 2018 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 ViewerExtensionRef extends ExtensionElement {
fileExtension: string;
component: string;
}

View File

@ -0,0 +1,63 @@
/*!
* @license
* Copyright 2016 - 2018 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 { RuleContext, RuleParameter } from '../config/rule.extensions';
export function not(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.every(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return !evaluator(context, ...(arg.parameters || []));
});
}
export function every(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.every(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return evaluator(context, ...(arg.parameters || []));
});
}
export function some(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.some(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return evaluator(context, ...(arg.parameters || []));
});
}

View File

@ -0,0 +1,42 @@
/*!
* @license
* Copyright 2016 - 2018 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 { NgModule, ModuleWithProviders } from '@angular/core';
import { ExtensionLoaderService } from './services/extension-loader.service';
import { ExtensionService } from './services/extension.service';
import { DynamicExtensionComponent } from './components/dynamic-component/dynamic.component';
import { DynamicTabComponent } from './components/dynamic-tab/dynamic-tab.component';
@NgModule({
imports: [],
declarations: [DynamicExtensionComponent, DynamicTabComponent],
exports: [DynamicExtensionComponent, DynamicTabComponent]
})
export class ExtensionsModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ExtensionsModule,
providers: [ExtensionLoaderService, ExtensionService]
};
}
static forChild(): ModuleWithProviders {
return {
ngModule: ExtensionsModule
};
}
}

View File

@ -1,38 +1,32 @@
/*!
* @license
* Alfresco Example Content Application
* Copyright 2016 - 2018 Alfresco Software, Ltd.
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
* 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
*
* 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:
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 <http://www.gnu.org/licenses/>.
* 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 { ExtensionConfig } from './extension.config';
import { HttpClient } from '@angular/common/http';
import { ExtensionElement } from './extension-element';
import { ContentActionRef, ContentActionType, ActionRef } from './action.extensions';
import { RuleRef } from './rule.extensions';
import { RouteRef } from './routing.extensions';
import { sortByOrder, filterEnabled, getValue, mergeObjects } from './extension-utils';
import { Injectable } from '@angular/core';
import { ActionRef, ContentActionRef, ContentActionType } from '../config/action.extensions';
import { ExtensionElement } from '../config/extension-element';
import { filterEnabled, getValue, mergeObjects, sortByOrder } from '../config/extension-utils';
import { ExtensionConfig } from '../config/extension.config';
import { RouteRef } from '../config/routing.extensions';
import { RuleRef } from '../config/rule.extensions';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class ExtensionLoaderService {
constructor(private http: HttpClient) {}
@ -47,14 +41,20 @@ export class ExtensionLoaderService {
config = JSON.parse(override);
}
const externalPlugins = localStorage.getItem('experimental.external-plugins') === 'true';
const externalPlugins =
localStorage.getItem('experimental.external-plugins') ===
'true';
if (externalPlugins && config.$references && config.$references.length > 0) {
const plugins = config.$references.map(
(name, idx) => this.loadConfig(`${pluginsPath}/${name}`, idx)
if (
externalPlugins &&
config.$references &&
config.$references.length > 0
) {
const plugins = config.$references.map((name, idx) =>
this.loadConfig(`${pluginsPath}/${name}`, idx)
);
Promise.all(plugins).then((results => {
Promise.all(plugins).then(results => {
const configs = results
.filter(entry => entry)
.sort(sortByOrder)
@ -65,7 +65,7 @@ export class ExtensionLoaderService {
}
resolve(config);
}));
});
} else {
resolve(config);
}

View File

@ -0,0 +1,148 @@
/*!
* @license
* Copyright 2016 - 2018 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, Type } from '@angular/core';
import { RuleEvaluator, RuleRef, RuleContext, RuleParameter } 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';
@Injectable()
export class ExtensionService {
configPath = 'assets/app.extensions.json';
pluginsPath = 'assets/plugins';
rules: Array<RuleRef> = [];
routes: Array<RouteRef> = [];
actions: Array<ActionRef> = [];
authGuards: { [key: string]: Type<{}> } = {};
components: { [key: string]: Type<{}> } = {};
evaluators: { [key: string]: RuleEvaluator } = {};
constructor(private loader: ExtensionLoaderService) {}
async load(): Promise<ExtensionConfig> {
const config = await this.loader.load(
this.configPath,
this.pluginsPath
);
this.setup(config);
return config;
}
setup(config: ExtensionConfig) {
if (!config) {
console.error('Extension configuration not found');
return;
}
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);
}
setEvaluators(values: { [key: string]: RuleEvaluator }) {
if (values) {
this.evaluators = Object.assign({}, this.evaluators, values);
}
}
setAuthGuards(values: { [key: string]: Type<{}> }) {
if (values) {
this.authGuards = Object.assign({}, this.authGuards, values);
}
}
setComponents(values: { [key: string]: Type<{}> }) {
if (values) {
this.components = Object.assign({}, this.components, values);
}
}
getRouteById(id: string): RouteRef {
return this.routes.find(route => route.id === id);
}
getAuthGuards(ids: string[]): Array<Type<{}>> {
return (ids || [])
.map(id => this.authGuards[id])
.filter(guard => guard);
}
getActionById(id: string): ActionRef {
return this.actions.find(action => action.id === id);
}
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];
}
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;
}
getComponentById(id: string): Type<{}> {
return this.components[id];
}
getRuleById(id: string): RuleRef {
return this.rules.find(ref => ref.id === id);
}
runExpression(value: string, context?: any) {
const pattern = new RegExp(/\$\((.*\)?)\)/g);
const matches = pattern.exec(value);
if (matches && matches.length > 1) {
const expression = matches[1];
const fn = new Function('context', `return ${expression}`);
const result = fn(context);
return result;
}
return value;
}
}

View File

@ -0,0 +1,23 @@
/*!
* @license
* Copyright 2016 - 2018 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 { Node } from 'alfresco-js-api';
export interface NavigationState {
currentFolder?: Node;
url?: string;
}

View File

@ -0,0 +1,25 @@
/*!
* @license
* Copyright 2016 - 2018 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.
*/
export interface ProfileState {
id: string;
isAdmin: boolean;
firstName: string;
lastName: string;
userName?: string;
initials?: string;
}

View File

@ -0,0 +1,30 @@
/*!
* @license
* Copyright 2016 - 2018 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 { MinimalNodeEntity, SiteEntry } from 'alfresco-js-api';
export interface SelectionState {
count: number;
nodes: MinimalNodeEntity[];
libraries: SiteEntry[];
isEmpty: boolean;
first?: MinimalNodeEntity;
last?: MinimalNodeEntity;
folder?: MinimalNodeEntity;
file?: MinimalNodeEntity;
library?: SiteEntry;
}

View File

@ -0,0 +1,36 @@
/*!
* @license
* Copyright 2016 - 2018 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.
*/
export * from './lib/config/action.extensions';
export * from './lib/config/extension-element';
export * from './lib/config/extension-utils';
export * from './lib/config/extension.config';
export * from './lib/config/navbar.extensions';
export * from './lib/config/permission.extensions';
export * from './lib/config/routing.extensions';
export * from './lib/config/rule.extensions';
export * from './lib/config/sidebar.extensions';
export * from './lib/config/viewer.extensions';
export * from './lib/services/extension-loader.service';
export * from './lib/services/extension.service';
export * from './lib/store/states/navigation.state';
export * from './lib/store/states/profile.state';
export * from './lib/store/states/selection.state';
export * from './lib/extensions.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,33 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"adf",
"camelCase"
],
"component-selector": [
true,
"element",
"adf",
"kebab-case"
]
}
}

View File

@ -34,7 +34,7 @@ import {
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { ExtensionService } from './extensions/extension.service';
import { AppExtensionService } from './extensions/extension.service';
import {
SetLanguagePickerAction,
SnackbarErrorAction,
@ -59,7 +59,7 @@ export class AppComponent implements OnInit {
private alfrescoApiService: AlfrescoApiService,
private authenticationService: AuthenticationService,
private uploadService: UploadService,
private extensions: ExtensionService
private extensions: AppExtensionService
) {}
ngOnInit() {

View File

@ -63,7 +63,7 @@ import { ExperimentalGuard } from './services/experimental-guard.service';
import { AppStoreModule } from './store/app-store.module';
import { MaterialModule } from './material.module';
import { ContentApiService } from './services/content-api.service';
import { ExtensionsModule } from './extensions.module';
import { AppExtensionsModule } from './extensions.module';
import { CoreExtensionsModule } from './extensions/core.extensions.module';
import { SearchResultsRowComponent } from './components/search/search-results-row/search-results-row.component';
import { NodePermissionsDialogComponent } from './dialogs/node-permissions/node-permissions.dialog';
@ -75,6 +75,7 @@ import { ToggleInfoDrawerComponent } from './components/toolbar/toggle-info-draw
import { DocumentDisplayModeComponent } from './components/toolbar/document-display-mode/document-display-mode.component';
import { ToggleFavoriteComponent } from './components/toolbar/toggle-favorite/toggle-favorite.component';
import { ContextMenuModule } from './components/context-menu/context-menu.module';
import { ExtensionsModule } from '@alfresco/adf-extensions';
@NgModule({
imports: [
@ -91,7 +92,8 @@ import { ContextMenuModule } from './components/context-menu/context-menu.module
ContentModule.forRoot(),
AppStoreModule,
CoreExtensionsModule.forRoot(),
ExtensionsModule,
ExtensionsModule.forRoot(),
AppExtensionsModule,
DirectivesModule,
ContextMenuModule.forRoot(),

View File

@ -41,7 +41,7 @@
</ng-container>
<ng-container *ngSwitchCase="'custom'">
<app-custom-component [id]="entry.component"></app-custom-component>
<adf-dynamic-component [id]="entry.component"></adf-dynamic-component>
</ng-container>
</ng-container>
</div>

View File

@ -31,15 +31,15 @@ import { trigger } from '@angular/animations';
import { FocusKeyManager } from '@angular/cdk/a11y';
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
import { ExtensionService } from '../../extensions/extension.service';
import { AppStore, SelectionState } from '../../store/states';
import { AppExtensionService } from '../../extensions/extension.service';
import { AppStore } from '../../store/states';
import { appSelection } from '../../store/selectors/app.selectors';
import { Store } from '@ngrx/store';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { SelectionState, ContentActionRef } from '@alfresco/adf-extensions';
import { ContextMenuOverlayRef } from './context-menu-overlay';
import { ContentActionRef } from '../../extensions/action.extensions';
import { contextMenuAnimation } from './animations';
import { ContextMenuItemDirective } from './context-menu-item.directive';
@ -99,7 +99,7 @@ export class ContextMenuComponent implements OnInit, OnDestroy, AfterViewInit {
constructor(
private contextMenuOverlayRef: ContextMenuOverlayRef,
private extensions: ExtensionService,
private extensions: AppExtensionService,
private store: Store<AppStore>,
) { }

View File

@ -33,6 +33,7 @@ import { ContextActionsDirective } from './context-menu.directive';
import { ContextMenuService } from './context-menu.service';
import { ContextMenuComponent } from './context-menu.component';
import { ContextMenuItemDirective } from './context-menu-item.directive';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { OutsideEventDirective } from './context-menu-outside-event.directive';
@NgModule({
@ -43,7 +44,8 @@ import { OutsideEventDirective } from './context-menu-outside-event.directive';
MatButtonModule,
BrowserModule,
CoreExtensionsModule.forChild(),
CoreModule.forChild()
CoreModule.forChild(),
ExtensionsModule.forChild()
],
declarations: [
ContextActionsDirective,

View File

@ -27,7 +27,8 @@ import { Component, ViewEncapsulation } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { selectUser, appLanguagePicker } from '../../store/selectors/app.selectors';
import { AppStore, ProfileState } from '../../store/states';
import { AppStore } from '../../store/states';
import { ProfileState } from '@alfresco/adf-extensions';
@Component({
selector: 'aca-current-user',

View File

@ -37,7 +37,7 @@ import { ContentManagementService } from '../../services/content-management.serv
import { AppStore } from '../../store/states/app.state';
import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { map } from 'rxjs/operators';
@Component({
@ -49,7 +49,7 @@ export class FavoritesComponent extends PageComponent implements OnInit {
constructor(
private router: Router,
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
private contentApi: ContentApiService,
content: ContentManagementService,
private breakpointObserver: BreakpointObserver

View File

@ -33,7 +33,7 @@ import { NodeActionsService } from '../../services/node-actions.service';
import { AppStore } from '../../store/states/app.state';
import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { SetCurrentFolderAction } from '../../store/actions';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { debounceTime } from 'rxjs/operators';
@ -55,7 +55,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
private nodeActionsService: NodeActionsService,
private uploadService: UploadService,
content: ContentManagementService,
extensions: ExtensionService,
extensions: AppExtensionService,
private breakpointObserver: BreakpointObserver) {
super(store, extensions, content);
}

View File

@ -7,8 +7,8 @@
*ngFor="let tab of tabs"
[icon]="tab.icon"
[label]="tab.title | translate">
<app-dynamic-tab [node]="displayNode" [id]="tab.component" [attr.data-automation-id]="tab.component">
</app-dynamic-tab>
<adf-dynamic-tab [node]="displayNode" [id]="tab.component" [attr.data-automation-id]="tab.component">
</adf-dynamic-tab>
</adf-info-drawer-tab>
</adf-info-drawer>
</ng-container>

View File

@ -26,8 +26,8 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { SidebarTabRef } from '../../extensions/sidebar.extensions';
import { AppExtensionService } from '../../extensions/extension.service';
import { SidebarTabRef } from '@alfresco/adf-extensions';
@Component({
selector: 'aca-info-drawer',
@ -43,7 +43,7 @@ export class InfoDrawerComponent implements OnChanges, OnInit {
constructor(
private contentApi: ContentApiService,
private extensions: ExtensionService
private extensions: AppExtensionService
) {}
ngOnInit() {

View File

@ -23,25 +23,27 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
ContentMetadataModule,
VersionManagerModule
} from '@alfresco/adf-content-services';
import { CoreModule } from '@alfresco/adf-core';
import { ContentMetadataModule, VersionManagerModule } from '@alfresco/adf-content-services';
import { InfoDrawerComponent } from './info-drawer.component';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { DirectivesModule } from '../../directives/directives.module';
import { MaterialModule } from '../../material.module';
import { MetadataTabComponent } from './metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from './comments-tab/comments-tab.component';
import { InfoDrawerComponent } from './info-drawer.component';
import { MetadataTabComponent } from './metadata-tab/metadata-tab.component';
import { VersionsTabComponent } from './versions-tab/versions-tab.component';
import { DynamicTabComponent } from './dynamic-tab/dynamic-tab.component';
export function components() {
return [
InfoDrawerComponent,
MetadataTabComponent,
CommentsTabComponent,
VersionsTabComponent,
DynamicTabComponent
VersionsTabComponent
];
}
@ -50,18 +52,13 @@ export function components() {
CommonModule,
MaterialModule,
CoreModule.forChild(),
ExtensionsModule.forChild(),
ContentMetadataModule,
VersionManagerModule,
DirectivesModule
],
declarations: [
...components()
],
exports: [
...components()
],
entryComponents: [
...components()
]
declarations: [...components()],
exports: [...components()],
entryComponents: [...components()]
})
export class AppInfoDrawerModule {}

View File

@ -34,7 +34,7 @@ import { AppStore } from '../../store/states/app.state';
import { SiteEntry } from 'alfresco-js-api';
import { ContentManagementService } from '../../services/content-management.service';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { map } from 'rxjs/operators';
@Component({
@ -48,7 +48,7 @@ export class LibrariesComponent extends PageComponent implements OnInit {
content: ContentManagementService,
private contentApi: ContentApiService,
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
private router: Router,
private breakpointObserver: BreakpointObserver) {
super(store, extensions, content);

View File

@ -24,18 +24,17 @@
*/
import { DocumentListComponent, ShareDataRow } from '@alfresco/adf-content-services';
import { ContentActionRef, SelectionState } from '@alfresco/adf-extensions';
import { OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { Observable, Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, Subscription , Observable } from 'rxjs';
import { SetSelectedNodesAction, ViewFileAction } from '../store/actions';
import { appSelection, sharedUrl, currentFolder, infoDrawerOpened, documentDisplayMode } from '../store/selectors/app.selectors';
import { AppStore } from '../store/states/app.state';
import { SelectionState } from '../store/states/selection.state';
import { ExtensionService } from '../extensions/extension.service';
import { AppExtensionService } from '../extensions/extension.service';
import { ContentManagementService } from '../services/content-management.service';
import { ContentActionRef } from '../extensions/action.extensions';
import { SetSelectedNodesAction, ViewFileAction } from '../store/actions';
import { appSelection, currentFolder, documentDisplayMode, infoDrawerOpened, sharedUrl } from '../store/selectors/app.selectors';
import { AppStore } from '../store/states/app.state';
export abstract class PageComponent implements OnInit, OnDestroy {
@ -63,7 +62,7 @@ export abstract class PageComponent implements OnInit, OnDestroy {
constructor(
protected store: Store<AppStore>,
protected extensions: ExtensionService,
protected extensions: AppExtensionService,
protected content: ContentManagementService) {}
ngOnInit() {

View File

@ -34,7 +34,7 @@ import {
OnDestroy,
OnChanges
} from '@angular/core';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
@Component({
@ -60,7 +60,7 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy {
private componentRef: ComponentRef<any>;
constructor(
private extensions: ExtensionService,
private extensions: AppExtensionService,
private componentFactoryResolver: ComponentFactoryResolver
) {}

View File

@ -31,11 +31,10 @@ import { AppStore } from '../../store/states/app.state';
import { SetSelectedNodesAction } from '../../store/actions';
import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { ContentManagementService } from '../../services/content-management.service';
import { ContentActionRef } from '../../extensions/action.extensions';
import { ContentActionRef, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { ViewUtilService } from './view-util.service';
import { ViewerExtensionRef } from '../../extensions/viewer.extensions';
@Component({
selector: 'app-preview',
@ -65,7 +64,7 @@ export class PreviewComponent extends PageComponent implements OnInit {
private router: Router,
private viewUtils: ViewUtilService,
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
content: ContentManagementService) {
super(store, extensions, content);
}

View File

@ -30,7 +30,7 @@ import { ContentManagementService } from '../../services/content-management.serv
import { PageComponent } from '../page.component';
import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
@Component({
templateUrl: './recent-files.component.html'
@ -40,7 +40,7 @@ export class RecentFilesComponent extends PageComponent implements OnInit {
constructor(
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
content: ContentManagementService,
private breakpointObserver: BreakpointObserver
) {

View File

@ -31,7 +31,7 @@ import { PageComponent } from '../../page.component';
import { Store } from '@ngrx/store';
import { AppStore } from '../../../store/states/app.state';
import { NavigateToFolder } from '../../../store/actions';
import { ExtensionService } from '../../../extensions/extension.service';
import { AppExtensionService } from '../../../extensions/extension.service';
import { ContentManagementService } from '../../../services/content-management.service';
@Component({
@ -56,7 +56,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
private queryBuilder: SearchQueryBuilderService,
private route: ActivatedRoute,
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
content: ContentManagementService
) {
super(store, extensions, content);

View File

@ -28,10 +28,11 @@ import { AppConfigService, StorageService, SettingsService } from '@alfresco/adf
import { Validators, FormGroup, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { AppStore, ProfileState } from '../../store/states';
import { AppStore } from '../../store/states';
import { appLanguagePicker, selectHeaderColor, selectAppName, selectUser } from '../../store/selectors/app.selectors';
import { MatCheckboxChange } from '@angular/material';
import { SetLanguagePickerAction } from '../../store/actions';
import { ProfileState } from '@alfresco/adf-extensions';
@Component({
selector: 'aca-settings',

View File

@ -29,7 +29,7 @@ import { ContentManagementService } from '../../services/content-management.serv
import { PageComponent } from '../page.component';
import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
@Component({
templateUrl: './shared-files.component.html'
@ -39,7 +39,7 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
constructor(
store: Store<AppStore>,
extensions: ExtensionService,
extensions: AppExtensionService,
content: ContentManagementService,
private breakpointObserver: BreakpointObserver
) {

View File

@ -25,13 +25,12 @@
import { Subject } from 'rxjs';
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states';
import { currentFolder } from '../../store/selectors/app.selectors';
import { takeUntil } from 'rxjs/operators';
import { NavBarGroupRef } from '../../extensions/navbar.extensions';
import { ContentActionRef } from '../../extensions/action.extensions';
import { ContentActionRef, NavBarGroupRef } from '@alfresco/adf-extensions';
@Component({
selector: 'app-sidenav',
@ -47,7 +46,7 @@ export class SidenavComponent implements OnInit, OnDestroy {
constructor(
private store: Store<AppStore>,
private extensions: ExtensionService
private extensions: AppExtensionService
) {}
ngOnInit() {

View File

@ -25,9 +25,10 @@
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppStore, SelectionState } from '../../../store/states';
import { AppStore } from '../../../store/states';
import { appSelection } from '../../../store/selectors/app.selectors';
import { Observable } from 'rxjs';
import { SelectionState } from '@alfresco/adf-extensions';
@Component({
selector: 'app-toggle-favorite',

View File

@ -30,9 +30,9 @@ import { PageComponent } from '../page.component';
import { Store } from '@ngrx/store';
import { selectUser } from '../../store/selectors/app.selectors';
import { AppStore } from '../../store/states/app.state';
import { ProfileState } from '../../store/states/profile.state';
import { ExtensionService } from '../../extensions/extension.service';
import { AppExtensionService } from '../../extensions/extension.service';
import { Observable } from 'rxjs';
import { ProfileState } from '@alfresco/adf-extensions';
@Component({
templateUrl: './trashcan.component.html'
@ -42,7 +42,7 @@ export class TrashcanComponent extends PageComponent implements OnInit {
user$: Observable<ProfileState>;
constructor(content: ContentManagementService,
extensions: ExtensionService,
extensions: AppExtensionService,
store: Store<AppStore>,
private breakpointObserver: BreakpointObserver) {
super(store, extensions, content);

View File

@ -1,6 +1,5 @@
import { NgModule } from '@angular/core';
import { AcaDevToolsModule, AcaDevToolsComponent } from 'aca-dev-tools';
import { ExtensionService } from './extensions/extension.service';
import { AcaDevToolsModule } from 'aca-dev-tools';
import { CodeEditorModule } from '@ngstack/code-editor';
// Main entry point for external extensions only.
@ -17,10 +16,4 @@ import { CodeEditorModule } from '@ngstack/code-editor';
AcaDevToolsModule
]
})
export class ExtensionsModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'app.dev.tools.component': AcaDevToolsComponent
});
}
}
export class AppExtensionsModule {}

View File

@ -1,60 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
export enum ContentActionType {
default = 'default',
button = 'button',
separator = 'separator',
menu = 'menu',
custom = 'custom'
}
export interface ContentActionRef {
id: string;
type: ContentActionType;
title?: string;
description?: string;
order?: number;
icon?: string;
disabled?: boolean;
children?: Array<ContentActionRef>;
component?: string;
actions?: {
click?: string;
[key: string]: string;
};
rules?: {
enabled?: string;
visible?: string;
[key: string]: string;
};
}
export interface ActionRef {
id: string;
type: string;
payload?: string;
}

View File

@ -1,75 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import {
Component,
Input,
ComponentRef,
OnInit,
ComponentFactoryResolver,
ViewChild,
ViewContainerRef,
OnDestroy
} from '@angular/core';
import { ExtensionService } from '../../extension.service';
@Component({
selector: 'app-custom-component',
template: `<div #content></div>`
})
export class CustomExtensionComponent implements OnInit, OnDestroy {
@ViewChild('content', { read: ViewContainerRef })
content: ViewContainerRef;
@Input() id: string;
private componentRef: ComponentRef<any>;
constructor(
private extensions: ExtensionService,
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
const componentType = this.extensions.getComponentById(this.id);
if (componentType) {
const factory = this.componentFactoryResolver.resolveComponentFactory(
componentType
);
if (factory) {
this.content.clear();
this.componentRef = this.content.createComponent(factory, 0);
// this.setupWidget(this.componentRef);
}
}
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
}

View File

@ -26,7 +26,7 @@
<ng-container *ngFor="let child of entry.children; trackBy: trackByActionId">
<ng-container [ngSwitch]="child.type">
<ng-container *ngSwitchCase="'custom'">
<app-custom-component [id]="child.component"></app-custom-component>
<adf-dynamic-component [id]="child.component"></adf-dynamic-component>
</ng-container>
<ng-container *ngSwitchDefault>
<app-toolbar-button type="menu-item" [actionRef]="child"></app-toolbar-button>
@ -37,7 +37,7 @@
</ng-container>
<ng-container *ngSwitchCase="'custom'">
<app-custom-component [id]="entry.component"></app-custom-component>
<adf-dynamic-component [id]="entry.component"></adf-dynamic-component>
</ng-container>
</ng-container>

View File

@ -31,8 +31,8 @@ import {
} from '@angular/core';
import { AppStore } from '../../../store/states';
import { Store } from '@ngrx/store';
import { ExtensionService } from '../../extension.service';
import { ContentActionRef } from '../../action.extensions';
import { AppExtensionService } from '../../extension.service';
import { ContentActionRef } from '@alfresco/adf-extensions';
@Component({
selector: 'aca-toolbar-action',
@ -47,7 +47,7 @@ export class ToolbarActionComponent {
constructor(
protected store: Store<AppStore>,
protected extensions: ExtensionService
protected extensions: AppExtensionService
) {}
trackByActionId(index: number, action: ContentActionRef) {

View File

@ -24,8 +24,8 @@
*/
import { Component, Input } from '@angular/core';
import { ContentActionRef } from '../../action.extensions';
import { ExtensionService } from '../../extension.service';
import { ContentActionRef } from '@alfresco/adf-extensions';
import { AppExtensionService } from '../../extension.service';
import { Store } from '@ngrx/store';
import { AppStore } from '../../../store/states';
import { appSelection } from '../../../store/selectors/app.selectors';
@ -75,7 +75,7 @@ export class ToolbarButtonComponent {
constructor(
protected store: Store<AppStore>,
private extensions: ExtensionService
private extensions: AppExtensionService
) {}
runAction() {

View File

@ -31,75 +31,32 @@ import { TrashcanComponent } from '../components/trashcan/trashcan.component';
import { ToolbarActionComponent } from './components/toolbar/toolbar-action.component';
import * as app from './evaluators/app.evaluators';
import * as nav from './evaluators/navigation.evaluators';
import { ExtensionService } from './extension.service';
import { CustomExtensionComponent } from './components/custom-component/custom.component';
import { AppExtensionService } from './extension.service';
import { ToggleInfoDrawerComponent } from '../components/toolbar/toggle-info-drawer/toggle-info-drawer.component';
import { ToggleFavoriteComponent } from '../components/toolbar/toggle-favorite/toggle-favorite.component';
import { ToolbarButtonComponent } from './components/toolbar/toolbar-button.component';
import { MetadataTabComponent } from '../components/info-drawer/metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from '../components/info-drawer/comments-tab/comments-tab.component';
import { VersionsTabComponent } from '../components/info-drawer/versions-tab/versions-tab.component';
import { ExtensionLoaderService } from './extension-loader.service';
import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions';
export function setupExtensions(extensions: ExtensionService): Function {
extensions.setComponents({
'app.layout.main': LayoutComponent,
'app.components.trashcan': TrashcanComponent,
'app.components.tabs.metadata': MetadataTabComponent,
'app.components.tabs.comments': CommentsTabComponent,
'app.components.tabs.versions': VersionsTabComponent,
'app.toolbar.toggleInfoDrawer': ToggleInfoDrawerComponent,
'app.toolbar.toggleFavorite': ToggleFavoriteComponent
});
extensions.setAuthGuards({
'app.auth': AuthGuardEcm
});
extensions.setEvaluators({
'app.selection.canDelete': app.canDeleteSelection,
'app.selection.canDownload': app.canDownloadSelection,
'app.selection.notEmpty': app.hasSelection,
'app.selection.canUnshare': app.canUnshareNodes,
'app.selection.canAddFavorite': app.canAddFavorite,
'app.selection.canRemoveFavorite': app.canRemoveFavorite,
'app.selection.first.canUpdate': app.canUpdateSelectedNode,
'app.selection.file': app.hasFileSelected,
'app.selection.file.canShare': app.canShareFile,
'app.selection.library': app.hasLibrarySelected,
'app.selection.folder': app.hasFolderSelected,
'app.selection.folder.canUpdate': app.canUpdateSelectedFolder,
'app.navigation.folder.canCreate': app.canCreateFolder,
'app.navigation.folder.canUpload': app.canUpload,
'app.navigation.isTrashcan': nav.isTrashcan,
'app.navigation.isNotTrashcan': nav.isNotTrashcan,
'app.navigation.isLibraries': nav.isLibraries,
'app.navigation.isNotLibraries': nav.isNotLibraries,
'app.navigation.isSharedFiles': nav.isSharedFiles,
'app.navigation.isNotSharedFiles': nav.isNotSharedFiles,
'app.navigation.isFavorites': nav.isFavorites,
'app.navigation.isNotFavorites': nav.isNotFavorites,
'app.navigation.isRecentFiles': nav.isRecentFiles,
'app.navigation.isNotRecentFiles': nav.isNotRecentFiles,
'app.navigation.isSearchResults': nav.isSearchResults,
'app.navigation.isNotSearchResults': nav.isNotSearchResults
});
return () => extensions.load();
export function setupExtensions(service: AppExtensionService): Function {
return () => service.load();
}
@NgModule({
imports: [CommonModule, CoreModule.forChild()],
imports: [
CommonModule,
CoreModule.forChild(),
ExtensionsModule.forChild()
],
declarations: [
ToolbarActionComponent,
ToolbarButtonComponent,
CustomExtensionComponent
ToolbarButtonComponent
],
exports: [
ToolbarActionComponent,
ToolbarButtonComponent,
CustomExtensionComponent
ToolbarButtonComponent
]
})
export class CoreExtensionsModule {
@ -107,12 +64,11 @@ export class CoreExtensionsModule {
return {
ngModule: CoreExtensionsModule,
providers: [
ExtensionLoaderService,
ExtensionService,
AppExtensionService,
{
provide: APP_INITIALIZER,
useFactory: setupExtensions,
deps: [ExtensionService],
deps: [AppExtensionService],
multi: true
}
]
@ -124,4 +80,50 @@ export class CoreExtensionsModule {
ngModule: CoreExtensionsModule
};
}
constructor(extensions: ExtensionService) {
extensions.setComponents({
'app.layout.main': LayoutComponent,
'app.components.trashcan': TrashcanComponent,
'app.components.tabs.metadata': MetadataTabComponent,
'app.components.tabs.comments': CommentsTabComponent,
'app.components.tabs.versions': VersionsTabComponent,
'app.toolbar.toggleInfoDrawer': ToggleInfoDrawerComponent,
'app.toolbar.toggleFavorite': ToggleFavoriteComponent
});
extensions.setAuthGuards({
'app.auth': AuthGuardEcm
});
extensions.setEvaluators({
'app.selection.canDelete': app.canDeleteSelection,
'app.selection.canDownload': app.canDownloadSelection,
'app.selection.notEmpty': app.hasSelection,
'app.selection.canUnshare': app.canUnshareNodes,
'app.selection.canAddFavorite': app.canAddFavorite,
'app.selection.canRemoveFavorite': app.canRemoveFavorite,
'app.selection.first.canUpdate': app.canUpdateSelectedNode,
'app.selection.file': app.hasFileSelected,
'app.selection.file.canShare': app.canShareFile,
'app.selection.library': app.hasLibrarySelected,
'app.selection.folder': app.hasFolderSelected,
'app.selection.folder.canUpdate': app.canUpdateSelectedFolder,
'app.navigation.folder.canCreate': app.canCreateFolder,
'app.navigation.folder.canUpload': app.canUpload,
'app.navigation.isTrashcan': nav.isTrashcan,
'app.navigation.isNotTrashcan': nav.isNotTrashcan,
'app.navigation.isLibraries': nav.isLibraries,
'app.navigation.isNotLibraries': nav.isNotLibraries,
'app.navigation.isSharedFiles': nav.isSharedFiles,
'app.navigation.isNotSharedFiles': nav.isNotSharedFiles,
'app.navigation.isFavorites': nav.isFavorites,
'app.navigation.isNotFavorites': nav.isNotFavorites,
'app.navigation.isRecentFiles': nav.isRecentFiles,
'app.navigation.isNotRecentFiles': nav.isNotRecentFiles,
'app.navigation.isSearchResults': nav.isSearchResults,
'app.navigation.isNotSearchResults': nav.isNotSearchResults
});
}
}

View File

@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RuleContext, RuleParameter } from '../rule.extensions';
import { RuleContext, RuleParameter } from '@alfresco/adf-extensions';
import {
isNotTrashcan,
isNotSharedFiles,

View File

@ -1,71 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { RuleContext, RuleParameter } from '../rule.extensions';
export function not(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.every(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return !evaluator(context, ...arg.parameters);
});
}
export function every(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.every(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return evaluator(context, ...arg.parameters);
});
}
export function some(context: RuleContext, ...args: RuleParameter[]): boolean {
if (!args || args.length === 0) {
return false;
}
return args
.some(arg => {
const evaluator = context.getEvaluator(arg.value);
if (!evaluator) {
console.warn('evaluator not found: ' + arg.value);
}
return evaluator(context, ...arg.parameters);
});
}

View File

@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RuleContext, RuleParameter } from '../rule.extensions';
import { RuleContext, RuleParameter } from '@alfresco/adf-extensions';
export function isFavorites(
context: RuleContext,

View File

@ -1,31 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
export interface ExtensionElement {
id: string;
order?: number;
disabled?: boolean;
}

View File

@ -1,43 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { RouteRef } from './routing.extensions';
import { RuleRef } from './rule.extensions';
import { ActionRef } from './action.extensions';
export interface ExtensionConfig {
$name: string;
$version: string;
$description?: string;
$references?: Array<string>;
rules?: Array<RuleRef>;
routes?: Array<RouteRef>;
actions?: Array<ActionRef>;
features?: {
[key: string]: any;
};
}

View File

@ -25,25 +25,34 @@
import { TestBed } from '@angular/core/testing';
import { AppTestingModule } from '../testing/app-testing.module';
import { ExtensionService } from './extension.service';
import { AppExtensionService } from './extension.service';
import { Store } from '@ngrx/store';
import { AppStore } from '../store/states';
import { ContentActionType } from './action.extensions';
import { mergeArrays, sortByOrder, filterEnabled, reduceSeparators, reduceEmptyMenus } from './extension-utils';
import { ContentActionType, mergeArrays,
sortByOrder, filterEnabled, reduceSeparators, reduceEmptyMenus, ExtensionService, ExtensionConfig } from '@alfresco/adf-extensions';
describe('ExtensionService', () => {
let extensions: ExtensionService;
describe('AppExtensionService', () => {
let service: AppExtensionService;
let store: Store<AppStore>;
let extensions: ExtensionService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule]
imports: [
AppTestingModule
]
});
store = TestBed.get(Store);
service = TestBed.get(AppExtensionService);
extensions = TestBed.get(ExtensionService);
});
const applyConfig = (config: ExtensionConfig) => {
extensions.setup(config);
service.setup(config);
};
describe('configs', () => {
it('should merge two arrays based on [id] keys', () => {
const left = [
@ -94,7 +103,7 @@ describe('ExtensionService', () => {
describe('actions', () => {
beforeEach(() => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
actions: [
@ -128,7 +137,7 @@ describe('ExtensionService', () => {
it('should run the action via store', () => {
spyOn(store, 'dispatch').and.stub();
extensions.runActionById('aca:actions/create-folder');
service.runActionById('aca:actions/create-folder');
expect(store.dispatch).toHaveBeenCalledWith({
type: 'CREATE_FOLDER',
payload: 'folder-name'
@ -138,7 +147,7 @@ describe('ExtensionService', () => {
it('should still invoke store if action is missing', () => {
spyOn(store, 'dispatch').and.stub();
extensions.runActionById('missing');
service.runActionById('missing');
expect(store.dispatch).toHaveBeenCalled();
});
});
@ -213,12 +222,12 @@ describe('ExtensionService', () => {
});
it('should fetch registered component', () => {
const component = extensions.getComponentById('component-1');
const component = service.getComponentById('component-1');
expect(component).toEqual(component1);
});
it('should not fetch registered component', () => {
const component = extensions.getComponentById('missing');
const component = service.getComponentById('missing');
expect(component).toBeFalsy();
});
});
@ -228,7 +237,7 @@ describe('ExtensionService', () => {
let guard1;
beforeEach(() => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
routes: [
@ -270,7 +279,7 @@ describe('ExtensionService', () => {
});
it('should build application routes', () => {
const routes = extensions.getApplicationRoutes();
const routes = service.getApplicationRoutes();
expect(routes.length).toBe(1);
@ -287,7 +296,7 @@ describe('ExtensionService', () => {
describe('content actions', () => {
it('should load content actions from the config', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -308,11 +317,11 @@ describe('ExtensionService', () => {
}
});
expect(extensions.toolbarActions.length).toBe(2);
expect(service.toolbarActions.length).toBe(2);
});
it('should sort content actions by order', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -333,11 +342,11 @@ describe('ExtensionService', () => {
}
});
expect(extensions.toolbarActions.length).toBe(2);
expect(extensions.toolbarActions[0].id).toBe(
expect(service.toolbarActions.length).toBe(2);
expect(service.toolbarActions[0].id).toBe(
'aca:toolbar/separator-1'
);
expect(extensions.toolbarActions[1].id).toBe(
expect(service.toolbarActions[1].id).toBe(
'aca:toolbar/separator-2'
);
});
@ -345,7 +354,7 @@ describe('ExtensionService', () => {
describe('open with', () => {
it('should load [open with] actions for the viewer', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -367,11 +376,11 @@ describe('ExtensionService', () => {
}
});
expect(extensions.openWithActions.length).toBe(1);
expect(service.openWithActions.length).toBe(1);
});
it('should load only enabled [open with] actions for the viewer', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -403,12 +412,12 @@ describe('ExtensionService', () => {
}
});
expect(extensions.openWithActions.length).toBe(1);
expect(extensions.openWithActions[0].id).toBe('aca:viewer/action2');
expect(service.openWithActions.length).toBe(1);
expect(service.openWithActions[0].id).toBe('aca:viewer/action2');
});
it('should sort [open with] actions by order', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -439,15 +448,15 @@ describe('ExtensionService', () => {
}
});
expect(extensions.openWithActions.length).toBe(2);
expect(extensions.openWithActions[0].id).toBe('aca:viewer/action1');
expect(extensions.openWithActions[1].id).toBe('aca:viewer/action2');
expect(service.openWithActions.length).toBe(2);
expect(service.openWithActions[0].id).toBe('aca:viewer/action1');
expect(service.openWithActions[1].id).toBe('aca:viewer/action2');
});
});
describe('create', () => {
it('should load [create] actions from config', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -463,11 +472,11 @@ describe('ExtensionService', () => {
}
});
expect(extensions.createActions.length).toBe(1);
expect(service.createActions.length).toBe(1);
});
it('should sort [create] actions by order', () => {
extensions.setup({
applyConfig({
$name: 'test',
$version: '1.0.0',
features: {
@ -490,9 +499,9 @@ describe('ExtensionService', () => {
}
});
expect(extensions.createActions.length).toBe(2);
expect(extensions.createActions[0].id).toBe('aca:create/folder-2');
expect(extensions.createActions[1].id).toBe('aca:create/folder');
expect(service.createActions.length).toBe(2);
expect(service.createActions[0].id).toBe('aca:create/folder-2');
expect(service.createActions[1].id).toBe('aca:create/folder');
});
});

View File

@ -26,36 +26,28 @@
import { Injectable, Type } from '@angular/core';
import { Store } from '@ngrx/store';
import { Route } from '@angular/router';
import { ExtensionConfig } from './extension.config';
import { AppStore, SelectionState } from '../store/states';
import { NavigationState } from '../store/states/navigation.state';
import { selectionWithFolder } from '../store/selectors/app.selectors';
import { NavBarGroupRef } from './navbar.extensions';
import { RouteRef } from './routing.extensions';
import { RuleContext, RuleRef, RuleEvaluator, RuleParameter } from './rule.extensions';
import { ActionRef, ContentActionRef, ContentActionType } from './action.extensions';
import * as core from './evaluators/core.evaluators';
import { AppStore } from '../store/states';
import { ruleContext } from '../store/selectors/app.selectors';
import { NodePermissionService } from '../services/node-permission.service';
import { SidebarTabRef } from './sidebar.extensions';
import { ProfileResolver } from '../services/profile.resolver';
import { ViewerExtensionRef } from './viewer.extensions';
import { ExtensionLoaderService } from './extension-loader.service';
import { sortByOrder, filterEnabled, reduceSeparators, reduceEmptyMenus } from './extension-utils';
import {
SelectionState, NavigationState, ExtensionConfig,
RuleContext, RuleEvaluator, ViewerExtensionRef,
ContentActionRef, ContentActionType,
ExtensionLoaderService,
SidebarTabRef, NavBarGroupRef,
sortByOrder, reduceSeparators, reduceEmptyMenus,
ExtensionService,
ProfileState
} from '@alfresco/adf-extensions';
@Injectable()
export class ExtensionService implements RuleContext {
configPath = 'assets/app.extensions.json';
pluginsPath = 'assets/plugins';
export class AppExtensionService implements RuleContext {
defaults = {
layout: 'app.layout.main',
auth: ['app.auth']
};
rules: Array<RuleRef> = [];
routes: Array<RouteRef> = [];
actions: Array<ActionRef> = [];
toolbarActions: Array<ContentActionRef> = [];
viewerToolbarActions: Array<ContentActionRef> = [];
viewerContentExtensions: Array<ViewerExtensionRef> = [];
@ -65,32 +57,25 @@ export class ExtensionService implements RuleContext {
navbar: Array<NavBarGroupRef> = [];
sidebar: Array<SidebarTabRef> = [];
authGuards: { [key: string]: Type<{}> } = {};
components: { [key: string]: Type<{}> } = {};
evaluators: { [key: string]: RuleEvaluator } = {};
selection: SelectionState;
navigation: NavigationState;
profile: ProfileState;
constructor(
private store: Store<AppStore>,
private loader: ExtensionLoaderService,
private extensions: ExtensionService,
public permissions: NodePermissionService) {
this.evaluators = {
'core.every': core.every,
'core.some': core.some,
'core.not': core.not
};
this.store.select(selectionWithFolder).subscribe(result => {
this.store.select(ruleContext).subscribe(result => {
this.selection = result.selection;
this.navigation = result.navigation;
this.profile = result.profile;
});
}
async load() {
const config = await this.loader.load(this.configPath, this.pluginsPath);
const config = await this.extensions.load();
this.setup(config);
}
@ -100,9 +85,6 @@ export class ExtensionService implements RuleContext {
return;
}
this.rules = this.loader.getRules(config);
this.actions = this.loader.getActions(config);
this.routes = this.loader.getRoutes(config);
this.toolbarActions = this.loader.getContentActions(config, 'features.toolbar');
this.viewerToolbarActions = this.loader.getContentActions(config, 'features.viewer.toolbar');
this.viewerContentExtensions = this.loader.getElements<ViewerExtensionRef>(config, 'features.viewer.content');
@ -123,7 +105,7 @@ export class ExtensionService implements RuleContext {
.filter(item => !item.disabled)
.sort(sortByOrder)
.map(item => {
const routeRef = this.getRouteById(item.route);
const routeRef = this.extensions.getRouteById(item.route);
const url = `/${routeRef ? routeRef.path : item.route}`;
return {
...item,
@ -134,34 +116,6 @@ export class ExtensionService implements RuleContext {
});
}
setEvaluators(values: { [key: string]: RuleEvaluator }) {
if (values) {
this.evaluators = Object.assign({}, this.evaluators, values);
}
}
setAuthGuards(values: { [key: string]: Type<{}> }) {
if (values) {
this.authGuards = Object.assign({}, this.authGuards, values);
}
}
setComponents(values: { [key: string]: Type<{}> }) {
if (values) {
this.components = Object.assign({}, this.components, values);
}
}
getRouteById(id: string): RouteRef {
return this.routes.find(route => route.id === id);
}
getAuthGuards(ids: string[]): Array<Type<{}>> {
return (ids || [])
.map(id => this.authGuards[id])
.filter(guard => guard);
}
getNavigationGroups(): Array<NavBarGroupRef> {
return this.navbar;
}
@ -171,12 +125,12 @@ export class ExtensionService implements RuleContext {
}
getComponentById(id: string): Type<{}> {
return this.components[id];
return this.extensions.getComponentById(id);
}
getApplicationRoutes(): Array<Route> {
return this.routes.map(route => {
const guards = this.getAuthGuards(
return this.extensions.routes.map(route => {
const guards = this.extensions.getAuthGuards(
route.auth && route.auth.length > 0
? route.auth
: this.defaults.auth
@ -201,13 +155,12 @@ export class ExtensionService implements RuleContext {
getCreateActions(): Array<ContentActionRef> {
return this.createActions
.filter(filterEnabled)
.filter(action => this.filterByRules(action))
.map(action => {
let disabled = false;
if (action.rules && action.rules.enabled) {
disabled = !this.evaluateRule(action.rules.enabled);
disabled = !this.extensions.evaluateRule(action.rules.enabled, this);
}
return {
@ -220,7 +173,6 @@ export class ExtensionService implements RuleContext {
// evaluates content actions for the selection and parent folder node
getAllowedToolbarActions(): Array<ContentActionRef> {
return this.toolbarActions
.filter(filterEnabled)
.filter(action => this.filterByRules(action))
.map(action => {
if (action.type === ContentActionType.menu) {
@ -242,13 +194,11 @@ export class ExtensionService implements RuleContext {
getViewerToolbarActions(): Array<ContentActionRef> {
return this.viewerToolbarActions
.filter(filterEnabled)
.filter(action => this.filterByRules(action));
}
getAllowedContextMenuActions(): Array<ContentActionRef> {
return this.contextMenuActions
.filter(filterEnabled)
.filter(action => this.filterByRules(action));
}
@ -263,20 +213,16 @@ export class ExtensionService implements RuleContext {
filterByRules(action: ContentActionRef): boolean {
if (action && action.rules && action.rules.visible) {
return this.evaluateRule(action.rules.visible);
return this.extensions.evaluateRule(action.rules.visible, this);
}
return true;
}
getActionById(id: string): ActionRef {
return this.actions.find(action => action.id === id);
}
runActionById(id: string, context?: any) {
const action = this.getActionById(id);
const action = this.extensions.getActionById(id);
if (action) {
const { type, payload } = action;
const expression = this.runExpression(payload, context);
const expression = this.extensions.runExpression(payload, context);
this.store.dispatch({ type, payload: expression });
} else {
@ -284,45 +230,7 @@ export class ExtensionService implements RuleContext {
}
}
runExpression(value: string, context?: any) {
const pattern = new RegExp(/\$\((.*\)?)\)/g);
const matches = pattern.exec(value);
if (matches && matches.length > 1) {
const expression = matches[1];
const fn = new Function('context', `return ${expression}`);
const result = fn(context);
return result;
}
return value;
}
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];
}
evaluateRule(ruleId: string): boolean {
const ruleRef = this.rules.find(ref => ref.id === ruleId);
if (ruleRef) {
const evaluator = this.getEvaluator(ruleRef.type);
if (evaluator) {
return evaluator(this, ...ruleRef.parameters);
}
} else {
const evaluator = this.getEvaluator(ruleId);
if (evaluator) {
return evaluator(this);
}
}
return false;
return this.extensions.getEvaluator(key);
}
}

View File

@ -1,39 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { ExtensionElement } from './extension-element';
export interface NavBarGroupRef extends ExtensionElement {
items: Array<NavBarLinkRef>;
}
export interface NavBarLinkRef extends ExtensionElement {
icon: string;
title: string;
route: string;
url?: string; // evaluated at runtime based on route ref
description?: string;
}

View File

@ -1,28 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
export interface NodePermissions {
check(source: any, permissions: string[], options?: any): boolean;
}

View File

@ -1,34 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
export interface RouteRef {
id: string;
path: string;
component: string;
layout?: string;
auth?: string[];
data?: { [key: string]: string };
}

View File

@ -1,50 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { SelectionState } from '../store/states';
import { NavigationState } from '../store/states/navigation.state';
import { NodePermissions } from './permission.extensions';
export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean;
export interface RuleContext {
selection: SelectionState;
navigation: NavigationState;
permissions: NodePermissions;
getEvaluator(key: string): RuleEvaluator;
}
export class RuleRef {
type: string;
id?: string;
parameters?: Array<RuleParameter>;
}
export interface RuleParameter {
type: string;
value: any;
parameters?: Array<RuleParameter>;
}

View File

@ -1,37 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { ExtensionElement } from './extension-element';
export interface SidebarTabRef extends ExtensionElement {
title: string;
component: string;
icon?: string;
rules?: {
visible?: string;
[key: string]: string;
};
}

View File

@ -1,31 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { ExtensionElement } from './extension-element';
export interface ViewerExtensionRef extends ExtensionElement {
fileExtension: string;
component: string;
}

View File

@ -24,7 +24,7 @@
*/
import { Injectable } from '@angular/core';
import { NodePermissions } from '../extensions/permission.extensions';
import { NodePermissions } from '@alfresco/adf-extensions';
@Injectable()
export class NodePermissionService implements NodePermissions {

View File

@ -39,13 +39,15 @@ export const currentFolder = createSelector(selectApp, state => state.navigation
export const infoDrawerOpened = createSelector(selectApp, state => state.infoDrawerOpened);
export const documentDisplayMode = createSelector(selectApp, state => state.documentDisplayMode);
export const selectionWithFolder = createSelector(
export const ruleContext = createSelector(
appSelection,
appNavigation,
(selection, navigation) => {
selectUser,
(selection, navigation, profile) => {
return {
selection,
navigation
navigation,
profile
};
}
);

View File

@ -24,5 +24,3 @@
*/
export * from './states/app.state';
export * from './states/profile.state';
export * from './states/selection.state';

View File

@ -23,9 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SelectionState } from './selection.state';
import { ProfileState } from './profile.state';
import { NavigationState } from './navigation.state';
import { SelectionState, ProfileState, NavigationState } from '@alfresco/adf-extensions';
export interface AppState {
appName: string;

View File

@ -1,31 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { Node } from 'alfresco-js-api';
export interface NavigationState {
currentFolder?: Node;
url?: string;
}

View File

@ -1,33 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
export interface ProfileState {
id: string;
isAdmin: boolean;
firstName: string;
lastName: string;
userName?: string;
initials?: string;
}

View File

@ -1,38 +0,0 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { MinimalNodeEntity, SiteEntry } from 'alfresco-js-api';
export interface SelectionState {
count: number;
nodes: MinimalNodeEntity[];
libraries: SiteEntry[];
isEmpty: boolean;
first?: MinimalNodeEntity;
last?: MinimalNodeEntity;
folder?: MinimalNodeEntity;
file?: MinimalNodeEntity;
library?: SiteEntry;
}

View File

@ -59,9 +59,9 @@ import { ContentManagementService } from '../services/content-management.service
import { NodeActionsService } from '../services/node-actions.service';
import { NodePermissionService } from '../services/node-permission.service';
import { ContentApiService } from '../services/content-api.service';
import { ExtensionService } from '../extensions/extension.service';
import { AppExtensionService } from '../extensions/extension.service';
import { ViewUtilService } from '../components/preview/view-util.service';
import { ExtensionLoaderService } from '../extensions/extension-loader.service';
import { ExtensionLoaderService, ExtensionService } from '@alfresco/adf-extensions';
@NgModule({
imports: [
@ -114,6 +114,7 @@ import { ExtensionLoaderService } from '../extensions/extension-loader.service';
NodeActionsService,
NodePermissionService,
ContentApiService,
AppExtensionService,
ExtensionService,
ExtensionLoaderService,
ViewUtilService

View File

@ -24,6 +24,12 @@
],
"aca-dev-tools/*": [
"dist/aca-dev-tools/*"
],
"@alfresco/adf-extensions": [
"dist/@alfresco/adf-extensions"
],
"@alfresco/adf-extensions/*": [
"dist/@alfresco/adf-extensions/*"
]
}
},
@ -33,4 +39,4 @@
"angularCompilerOptions": {
"preserveWhitespaces": false
}
}
}