mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACA-1646] "dev tools" extension (#567)
* dev tools extension project * code editor integration * latest editor, offline setup * override extension config (session only) * schema support * wire external plugins with experimental flag * update package scripts * sidebar extensions scaffold * propagate extension tabs to info drawer * separate tab components for info drawer * extensibility for info drawer * support tab icons
This commit is contained in:
parent
ed23a21a00
commit
89aec91caf
@ -5,5 +5,5 @@ LABEL maintainer="Denys Vuika <denys.vuika@alfresco.com>"
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY dist/ .
|
||||
COPY dist/app/ .
|
||||
|
||||
|
58
angular.json
58
angular.json
@ -11,7 +11,7 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"outputPath": "dist/app",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
@ -45,6 +45,16 @@
|
||||
"glob": "pdf.worker.js",
|
||||
"input": "node_modules/pdfjs-dist/build",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/monaco-editor/min",
|
||||
"output": "./assets/monaco"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.js",
|
||||
"input": "node_modules/@ngstack/code-editor/workers",
|
||||
"output": "./assets/workers"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
@ -106,9 +116,9 @@
|
||||
"karmaConfig": "./karma.conf.js",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"src/app/ui"
|
||||
]
|
||||
"includePaths": [
|
||||
"src/app/ui"
|
||||
]
|
||||
},
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"scripts": [
|
||||
@ -188,6 +198,46 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aca-dev-tools": {
|
||||
"root": "projects/aca-dev-tools",
|
||||
"sourceRoot": "projects/aca-dev-tools/src",
|
||||
"projectType": "library",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "projects/aca-dev-tools/tsconfig.lib.json",
|
||||
"project": "projects/aca-dev-tools/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"project": "projects/aca-dev-tools/ng-package.prod.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "projects/aca-dev-tools/src/test.ts",
|
||||
"tsConfig": "projects/aca-dev-tools/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-dev-tools/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"projects/aca-dev-tools/tsconfig.lib.json",
|
||||
"projects/aca-dev-tools/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "alfresco-content-app",
|
||||
|
@ -228,6 +228,46 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebarTabRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "component"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier for the navigation group",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Element title",
|
||||
"type": "string"
|
||||
},
|
||||
"component": {
|
||||
"description": "Component id",
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Material icon name",
|
||||
"type": "string"
|
||||
},
|
||||
"disabled": {
|
||||
"description": "Toggles disabled state",
|
||||
"type": "boolean"
|
||||
},
|
||||
"order": {
|
||||
"description": "Element order",
|
||||
"type": "number"
|
||||
},
|
||||
"rules": {
|
||||
"description": "Element rules",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"visible": {
|
||||
"description": "Rule to evaluate the visibility state",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -306,6 +346,12 @@
|
||||
"items": { "$ref": "#/definitions/navBarGroupRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"sidebar": {
|
||||
"description": "Sidebar extensions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/sidebarTabRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"content": {
|
||||
"description": "Main application content extensions",
|
||||
"type": "object",
|
||||
|
1101
package-lock.json
generated
1101
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@ -4,15 +4,12 @@
|
||||
"license": "LGPL-3.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "npm run server-versions && ng serve --open",
|
||||
"start": "npm run server-versions && ng build aca-dev-tools && ng serve --open",
|
||||
"start:prod": "npm run server-versions && ng serve --prod --open",
|
||||
"build": "npm run server-versions && ng build --prod",
|
||||
"build:prod": "npm run server-versions && ng build --prod",
|
||||
"build:dev": "npm run server-versions && ng build",
|
||||
"build:tomcat": "npm run server-versions && ng build --base-href ./",
|
||||
"build:electron": "npm run server-versions && ng build --output-path www --base-href ./",
|
||||
"test": "ng test --code-coverage",
|
||||
"test:ci": "ng test --code-coverage --watch=false && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage",
|
||||
"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",
|
||||
"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",
|
||||
@ -21,7 +18,7 @@
|
||||
"start:docker": "docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000",
|
||||
"stop:docker": "docker-compose stop",
|
||||
"e2e:docker": "npm run start:docker && npm run e2e && npm run stop:docker",
|
||||
"spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts'"
|
||||
"spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts' 'projects/**/*.ts'"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@ -46,6 +43,7 @@
|
||||
"@ngrx/router-store": "^6.1.0",
|
||||
"@ngrx/store": "^6.1.0",
|
||||
"@ngrx/store-devtools": "^6.1.0",
|
||||
"@ngstack/code-editor": "^0.4.3",
|
||||
"@ngx-translate/core": "^10.0.2",
|
||||
"alfresco-js-api": "2.5.0",
|
||||
"core-js": "^2.5.7",
|
||||
@ -53,6 +51,7 @@
|
||||
"minimatch-browser": "^1.0.0",
|
||||
"moment": "^2.22.2",
|
||||
"moment-es6": "1.0.0",
|
||||
"monaco-editor": "^0.14.2",
|
||||
"pdfjs-dist": "2.0.303",
|
||||
"rxjs": "^6.2.2",
|
||||
"web-animations-js": "2.3.1",
|
||||
@ -60,6 +59,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.7.0",
|
||||
"@angular-devkit/build-ng-packagr": "~0.7.0",
|
||||
"@angular/cli": "^6.1.3",
|
||||
"@angular/compiler-cli": "6.1.2",
|
||||
"@angular/language-service": "6.1.2",
|
||||
@ -82,10 +82,13 @@
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"ng-packagr": "^3.0.0",
|
||||
"protractor": "5.3.2",
|
||||
"rimraf": "2.6.2",
|
||||
"selenium-webdriver": "4.0.0-alpha.1",
|
||||
"ts-node": "~4.1.0",
|
||||
"tsickle": ">=0.29.0",
|
||||
"tslib": "^1.9.0",
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "^2.9.2",
|
||||
"wait-on": "2.1.0"
|
||||
|
31
projects/aca-dev-tools/karma.conf.js
Normal file
31
projects/aca-dev-tools/karma.conf.js
Normal 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
|
||||
});
|
||||
};
|
8
projects/aca-dev-tools/ng-package.json
Normal file
8
projects/aca-dev-tools/ng-package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/aca-dev-tools",
|
||||
"deleteDestPath": false,
|
||||
"lib": {
|
||||
"entryFile": "src/public_api.ts"
|
||||
}
|
||||
}
|
7
projects/aca-dev-tools/ng-package.prod.json
Normal file
7
projects/aca-dev-tools/ng-package.prod.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/aca-dev-tools",
|
||||
"lib": {
|
||||
"entryFile": "src/public_api.ts"
|
||||
}
|
||||
}
|
9
projects/aca-dev-tools/package.json
Normal file
9
projects/aca-dev-tools/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "aca-dev-tools",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^6.0.0",
|
||||
"@angular/core": "^6.0.0",
|
||||
"@ngstack/code-editor": "^0.4.1"
|
||||
}
|
||||
}
|
39
projects/aca-dev-tools/src/lib/aca-dev-tools.component.html
Normal file
39
projects/aca-dev-tools/src/lib/aca-dev-tools.component.html
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="inner-layout">
|
||||
<div class="inner-layout__header">
|
||||
<adf-breadcrumb root="app.extensions.json"></adf-breadcrumb>
|
||||
<adf-toolbar class="inline">
|
||||
<button
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
title="Save changes"
|
||||
(click)="saveChanges()">
|
||||
<mat-icon>save_alt</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
title="Revert changes"
|
||||
(click)="revertChanges()">
|
||||
<mat-icon>history</mat-icon>
|
||||
</button>
|
||||
</adf-toolbar>
|
||||
</div>
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
|
||||
<div fxLayout="column" fxFill>
|
||||
<div fxLayout fxFlex>
|
||||
<div fxFlex="100">
|
||||
<ng-container *ngIf="model">
|
||||
<ngs-code-editor
|
||||
[codeModel]="model"
|
||||
(valueChanged)="onCodeChanged($event)">
|
||||
</ngs-code-editor>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
23
projects/aca-dev-tools/src/lib/aca-dev-tools.component.scss
Normal file
23
projects/aca-dev-tools/src/lib/aca-dev-tools.component.scss
Normal file
@ -0,0 +1,23 @@
|
||||
.lib-aca-dev-tools {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.ngs-code-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
|
||||
.editor {
|
||||
// border: 1px solid grey;
|
||||
// min-height: 400px;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AcaDevToolsComponent } from './aca-dev-tools.component';
|
||||
|
||||
describe('AcaDevToolsComponent', () => {
|
||||
let component: AcaDevToolsComponent;
|
||||
let fixture: ComponentFixture<AcaDevToolsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AcaDevToolsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AcaDevToolsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
72
projects/aca-dev-tools/src/lib/aca-dev-tools.component.ts
Normal file
72
projects/aca-dev-tools/src/lib/aca-dev-tools.component.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { CodeModel } from '@ngstack/code-editor';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { forkJoin } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-aca-dev-tools',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'lib-aca-dev-tools' },
|
||||
templateUrl: './aca-dev-tools.component.html',
|
||||
styleUrls: ['./aca-dev-tools.component.scss']
|
||||
})
|
||||
export class AcaDevToolsComponent implements OnInit {
|
||||
model: CodeModel = null;
|
||||
|
||||
private code: string;
|
||||
|
||||
constructor(private route: ActivatedRoute, private http: HttpClient) {}
|
||||
|
||||
ngOnInit() {
|
||||
const routeData = this.route.snapshot.data;
|
||||
if (!routeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const schemaUri = routeData.schemaUri;
|
||||
const getSchema = this.http.get(routeData.schemaPath);
|
||||
const getConfig = this.http.get(routeData.configPath, { responseType: 'text' });
|
||||
|
||||
forkJoin([getSchema, getConfig]).subscribe(
|
||||
([schema, config]) => {
|
||||
let code = config;
|
||||
|
||||
const override = sessionStorage.getItem('aca.extension.config');
|
||||
if (override) {
|
||||
code = override;
|
||||
}
|
||||
|
||||
this.model = {
|
||||
language: 'json',
|
||||
uri: 'app.extensions.json',
|
||||
value: code,
|
||||
schemas: [
|
||||
{
|
||||
uri: schemaUri,
|
||||
schema
|
||||
}
|
||||
]
|
||||
};
|
||||
this.code = code;
|
||||
},
|
||||
err => {
|
||||
console.log(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onCodeChanged(value: string) {
|
||||
this.code = value;
|
||||
}
|
||||
|
||||
saveChanges() {
|
||||
sessionStorage.setItem('aca.extension.config', this.code);
|
||||
// window.location.reload(true);
|
||||
}
|
||||
|
||||
revertChanges() {
|
||||
sessionStorage.removeItem('aca.extension.config');
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
19
projects/aca-dev-tools/src/lib/aca-dev-tools.module.ts
Normal file
19
projects/aca-dev-tools/src/lib/aca-dev-tools.module.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CodeEditorModule } from '@ngstack/code-editor';
|
||||
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';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
FlexLayoutModule,
|
||||
CodeEditorModule.forChild(),
|
||||
CoreModule.forChild(),
|
||||
ContentModule.forChild()
|
||||
],
|
||||
declarations: [AcaDevToolsComponent],
|
||||
exports: [AcaDevToolsComponent],
|
||||
entryComponents: [AcaDevToolsComponent]
|
||||
})
|
||||
export class AcaDevToolsModule {}
|
15
projects/aca-dev-tools/src/lib/aca-dev-tools.service.spec.ts
Normal file
15
projects/aca-dev-tools/src/lib/aca-dev-tools.service.spec.ts
Normal file
@ -0,0 +1,15 @@
|
||||
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();
|
||||
}));
|
||||
});
|
9
projects/aca-dev-tools/src/lib/aca-dev-tools.service.ts
Normal file
9
projects/aca-dev-tools/src/lib/aca-dev-tools.service.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AcaDevToolsService {
|
||||
|
||||
constructor() { }
|
||||
}
|
7
projects/aca-dev-tools/src/public_api.ts
Normal file
7
projects/aca-dev-tools/src/public_api.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* 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';
|
22
projects/aca-dev-tools/src/test.ts
Normal file
22
projects/aca-dev-tools/src/test.ts
Normal 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);
|
33
projects/aca-dev-tools/tsconfig.lib.json
Normal file
33
projects/aca-dev-tools/tsconfig.lib.json
Normal 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"
|
||||
]
|
||||
}
|
17
projects/aca-dev-tools/tsconfig.spec.json
Normal file
17
projects/aca-dev-tools/tsconfig.spec.json
Normal 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"
|
||||
]
|
||||
}
|
17
projects/aca-dev-tools/tslint.json
Normal file
17
projects/aca-dev-tools/tslint.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"lib",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"lib",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
@ -13,7 +13,8 @@
|
||||
"cardview": false,
|
||||
"permissions": false,
|
||||
"share": false,
|
||||
"extensions": false
|
||||
"extensions": false,
|
||||
"external-plugins": false
|
||||
},
|
||||
"headerColor": "#2196F3",
|
||||
"languagePicker": false,
|
||||
|
@ -88,7 +88,7 @@ import { ContextMenuModule } from './components/context-menu/context-menu.module
|
||||
}),
|
||||
MaterialModule,
|
||||
CoreModule.forRoot(),
|
||||
ContentModule,
|
||||
ContentModule.forRoot(),
|
||||
AppStoreModule,
|
||||
CoreExtensionsModule.forRoot(),
|
||||
ExtensionsModule,
|
||||
|
@ -90,7 +90,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -120,7 +120,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: ':folderId/preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -152,7 +152,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -161,7 +161,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: ':folderId/preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -185,7 +185,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -209,7 +209,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
@ -228,7 +228,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
loadChildren: 'app/components/about/about.module#AboutModule',
|
||||
loadChildren: 'src/app/components/about/about.module#AboutModule',
|
||||
data: {
|
||||
title: 'APP.BROWSE.ABOUT.TITLE'
|
||||
}
|
||||
@ -246,7 +246,7 @@ export const APP_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'preview/:nodeId',
|
||||
loadChildren: 'app/components/preview/preview.module#PreviewModule',
|
||||
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
|
||||
data: {
|
||||
title: 'APP.PREVIEW.TITLE',
|
||||
navigateMultiple: true,
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* @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 } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-comments-tab',
|
||||
template: `
|
||||
<adf-comments [nodeId]="node?.id"></adf-comments>
|
||||
`
|
||||
})
|
||||
export class CommentsTabComponent {
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*!
|
||||
* @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,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
ComponentRef,
|
||||
ComponentFactoryResolver,
|
||||
OnChanges,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ExtensionService } from '../../../extensions/extension.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dynamic-tab',
|
||||
template: `<div #content></div>`
|
||||
})
|
||||
export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@ViewChild('content', { read: ViewContainerRef })
|
||||
content: ViewContainerRef;
|
||||
|
||||
@Input()
|
||||
id: string;
|
||||
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
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.updateInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.node) {
|
||||
this.updateInstance();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.componentRef) {
|
||||
this.componentRef.destroy();
|
||||
this.componentRef = null;
|
||||
}
|
||||
}
|
||||
|
||||
private updateInstance() {
|
||||
if (this.componentRef && this.componentRef.instance) {
|
||||
this.componentRef.instance.node = this.node;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,38 +3,12 @@
|
||||
</div>
|
||||
<ng-container *ngIf="!isLoading && !!displayNode">
|
||||
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
|
||||
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate">
|
||||
<adf-content-metadata-card
|
||||
[readOnly]="!canUpdateNode"
|
||||
[displayEmpty]="canUpdateNode"
|
||||
[preset]="'custom'"
|
||||
[node]="displayNode">
|
||||
</adf-content-metadata-card>
|
||||
<adf-info-drawer-tab
|
||||
*ngFor="let tab of tabs"
|
||||
[icon]="tab.icon"
|
||||
[label]="tab.title | translate">
|
||||
<app-dynamic-tab [node]="displayNode" [id]="tab.component">
|
||||
</app-dynamic-tab>
|
||||
</adf-info-drawer-tab>
|
||||
|
||||
<ng-container *ifExperimental="'comments'; else versions">
|
||||
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.COMMENTS' | translate">
|
||||
<adf-comments [nodeId]="displayNode.id"></adf-comments>
|
||||
</adf-info-drawer-tab>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #versions>
|
||||
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.VERSIONS' | translate">
|
||||
<ng-container *ngIf="isFileSelected;else empty">
|
||||
<adf-version-manager
|
||||
[showComments]="'adf-version-manager.allowComments' | adfAppConfig:true"
|
||||
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig:true"
|
||||
[node]="displayNode">
|
||||
</adf-version-manager>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #empty>
|
||||
<div class="adf-manage-versions-empty">
|
||||
<mat-icon class="adf-manage-versions-empty-icon">face</mat-icon>
|
||||
{{ 'VERSION.SELECTION.EMPTY' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</adf-info-drawer-tab>
|
||||
</ng-template>
|
||||
</adf-info-drawer>
|
||||
</ng-container>
|
||||
|
@ -23,41 +23,34 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { ContentApiService } from '../../services/content-api.service';
|
||||
import { ExtensionService } from '../../extensions/extension.service';
|
||||
import { SidebarTabRef } from '../../extensions/sidebar.extensions';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-info-drawer',
|
||||
templateUrl: './info-drawer.component.html'
|
||||
})
|
||||
export class InfoDrawerComponent implements OnChanges {
|
||||
export class InfoDrawerComponent implements OnChanges, OnInit {
|
||||
@Input() nodeId: string;
|
||||
@Input() node: MinimalNodeEntity;
|
||||
|
||||
isLoading = false;
|
||||
displayNode: MinimalNodeEntryEntity;
|
||||
canUpdateNode = false;
|
||||
|
||||
get isFileSelected(): boolean {
|
||||
if (this.node && this.node.entry) {
|
||||
// workaround for shared files type.
|
||||
if (this.node.entry.nodeId) {
|
||||
return true;
|
||||
} else {
|
||||
return this.node.entry.isFile;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
tabs: Array<SidebarTabRef> = [];
|
||||
|
||||
constructor(
|
||||
public permission: NodePermissionService,
|
||||
private contentApi: ContentApiService
|
||||
private contentApi: ContentApiService,
|
||||
private extensions: ExtensionService
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
ngOnInit() {
|
||||
this.tabs = this.extensions.getSidebarTabs();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.node) {
|
||||
const entry = this.node.entry;
|
||||
if (entry.nodeId) {
|
||||
@ -103,6 +96,5 @@ export class InfoDrawerComponent implements OnChanges {
|
||||
|
||||
private setDisplayNode(node: MinimalNodeEntryEntity) {
|
||||
this.displayNode = node;
|
||||
this.canUpdateNode = node && this.permission.check(node, ['update']);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,20 @@ import { ContentMetadataModule, VersionManagerModule } from '@alfresco/adf-conte
|
||||
import { InfoDrawerComponent } from './info-drawer.component';
|
||||
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 { VersionsTabComponent } from './versions-tab/versions-tab.component';
|
||||
import { DynamicTabComponent } from './dynamic-tab/dynamic-tab.component';
|
||||
|
||||
export function components() {
|
||||
return [
|
||||
InfoDrawerComponent,
|
||||
MetadataTabComponent,
|
||||
CommentsTabComponent,
|
||||
VersionsTabComponent,
|
||||
DynamicTabComponent
|
||||
];
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -41,10 +55,13 @@ import { MaterialModule } from '../../material.module';
|
||||
DirectivesModule
|
||||
],
|
||||
declarations: [
|
||||
InfoDrawerComponent
|
||||
...components()
|
||||
],
|
||||
exports: [
|
||||
InfoDrawerComponent
|
||||
...components()
|
||||
],
|
||||
entryComponents: [
|
||||
...components()
|
||||
]
|
||||
})
|
||||
export class AppInfoDrawerModule {}
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* @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, OnChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { NodePermissionService } from '../../../services/node-permission.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-metadata-tab',
|
||||
template: `
|
||||
<adf-content-metadata-card
|
||||
[readOnly]="!canUpdateNode"
|
||||
[displayEmpty]="canUpdateNode"
|
||||
[preset]="'custom'"
|
||||
[node]="node">
|
||||
</adf-content-metadata-card>
|
||||
`,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { 'class': 'app-metadata-tab' }
|
||||
})
|
||||
export class MetadataTabComponent implements OnChanges {
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
canUpdateNode = false;
|
||||
|
||||
constructor(public permission: NodePermissionService) {}
|
||||
|
||||
ngOnChanges() {
|
||||
this.canUpdateNode =
|
||||
this.node && this.permission.check(this.node, ['update']);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* @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, OnChanges, OnInit } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-versions-tab',
|
||||
template: `
|
||||
<ng-container *ngIf="isFileSelected;else empty">
|
||||
<adf-version-manager
|
||||
[showComments]="'adf-version-manager.allowComments' | adfAppConfig:true"
|
||||
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig:true"
|
||||
[node]="node">
|
||||
</adf-version-manager>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #empty>
|
||||
<div class="adf-manage-versions-empty">
|
||||
<mat-icon class="adf-manage-versions-empty-icon">face</mat-icon>
|
||||
{{ 'VERSION.SELECTION.EMPTY' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
export class VersionsTabComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
isFileSelected = false;
|
||||
|
||||
ngOnInit() {
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
if (this.node && this.node.nodeId) {
|
||||
// workaround for shared files type.
|
||||
this.isFileSelected = true;
|
||||
} else {
|
||||
this.isFileSelected = this.node.isFile;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AcaDevToolsModule, AcaDevToolsComponent } from 'aca-dev-tools';
|
||||
import { ExtensionService } from './extensions/extension.service';
|
||||
import { CodeEditorModule } from '@ngstack/code-editor';
|
||||
|
||||
// Main entry point for external extensions only.
|
||||
// For any application-specific code use CoreExtensionsModule instead.
|
||||
|
||||
@NgModule({
|
||||
imports: []
|
||||
imports: [
|
||||
CodeEditorModule.forRoot({
|
||||
// use local Monaco installation
|
||||
baseUrl: 'assets/monaco',
|
||||
// use local Typings Worker
|
||||
typingsWorkerUrl: 'assets/workers/typings-worker.js'
|
||||
}),
|
||||
AcaDevToolsModule
|
||||
]
|
||||
})
|
||||
export class ExtensionsModule {}
|
||||
export class ExtensionsModule {
|
||||
constructor(extensions: ExtensionService) {
|
||||
extensions.setComponents({
|
||||
'app.dev.tools.component': AcaDevToolsComponent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -36,11 +36,17 @@ import { CustomExtensionComponent } from './components/custom-component/custom.c
|
||||
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';
|
||||
|
||||
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
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ import { NavBarGroupRef } from './navbar.extensions';
|
||||
import { RouteRef } from './routing.extensions';
|
||||
import { RuleRef } from './rule.extensions';
|
||||
import { ActionRef, ContentActionRef } from './action.extensions';
|
||||
import { SidebarTabRef } from './sidebar.extensions';
|
||||
|
||||
export interface ExtensionConfig {
|
||||
$name: string;
|
||||
@ -44,6 +45,7 @@ export interface ExtensionConfig {
|
||||
actions?: Array<ContentActionRef>;
|
||||
};
|
||||
navbar?: Array<NavBarGroupRef>;
|
||||
sidebar?: Array<SidebarTabRef>;
|
||||
content?: {
|
||||
actions?: Array<ContentActionRef>;
|
||||
contextActions?: Array<ContentActionRef>
|
||||
|
@ -37,6 +37,7 @@ import { RuleContext, RuleRef, RuleEvaluator, RuleParameter } from './rule.exten
|
||||
import { ActionRef, ContentActionRef, ContentActionType } from './action.extensions';
|
||||
import * as core from './evaluators/core.evaluators';
|
||||
import { NodePermissionService } from '../services/node-permission.service';
|
||||
import { SidebarTabRef } from './sidebar.extensions';
|
||||
|
||||
@Injectable()
|
||||
export class ExtensionService implements RuleContext {
|
||||
@ -58,6 +59,7 @@ export class ExtensionService implements RuleContext {
|
||||
openWithActions: Array<ContentActionRef> = [];
|
||||
createActions: Array<ContentActionRef> = [];
|
||||
navbar: Array<NavBarGroupRef> = [];
|
||||
sidebar: Array<SidebarTabRef> = [];
|
||||
|
||||
authGuards: { [key: string]: Type<{}> } = {};
|
||||
components: { [key: string]: Type<{}> } = {};
|
||||
@ -88,7 +90,15 @@ export class ExtensionService implements RuleContext {
|
||||
this.loadConfig(this.configPath, 0).then(result => {
|
||||
let config = result.config;
|
||||
|
||||
if (config.$references && config.$references.length > 0) {
|
||||
const override = sessionStorage.getItem('aca.extension.config');
|
||||
if (override) {
|
||||
console.log('overriding extension config');
|
||||
config = JSON.parse(override);
|
||||
}
|
||||
|
||||
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(`${this.pluginsPath}/${name}`, idx)
|
||||
);
|
||||
@ -129,6 +139,7 @@ export class ExtensionService implements RuleContext {
|
||||
this.openWithActions = this.loadViewerOpenWith(config);
|
||||
this.createActions = this.loadCreateActions(config);
|
||||
this.navbar = this.loadNavBar(config);
|
||||
this.sidebar = this.loadSidebar(config);
|
||||
}
|
||||
|
||||
protected loadConfig(url: string, order: number): Promise<{ order: number, config: ExtensionConfig }> {
|
||||
@ -166,7 +177,7 @@ export class ExtensionService implements RuleContext {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadViewerActions(config: ExtensionConfig) {
|
||||
protected loadViewerActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
if (config && config.features && config.features.viewer) {
|
||||
return (config.features.viewer.actions || [])
|
||||
.sort(this.sortByOrder)
|
||||
@ -184,7 +195,7 @@ export class ExtensionService implements RuleContext {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadNavBar(config: ExtensionConfig): any {
|
||||
protected loadNavBar(config: ExtensionConfig): Array<NavBarGroupRef> {
|
||||
if (config && config.features) {
|
||||
return (config.features.navbar || [])
|
||||
.filter(entry => !entry.disabled)
|
||||
@ -206,7 +217,16 @@ export class ExtensionService implements RuleContext {
|
||||
};
|
||||
});
|
||||
}
|
||||
return {};
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadSidebar(config: ExtensionConfig): Array<SidebarTabRef> {
|
||||
if (config && config.features) {
|
||||
return (config.features.sidebar || [])
|
||||
.filter(entry => !entry.disabled)
|
||||
.sort(this.sortByOrder);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadViewerOpenWith(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
@ -271,6 +291,10 @@ export class ExtensionService implements RuleContext {
|
||||
return this.navbar;
|
||||
}
|
||||
|
||||
getSidebarTabs(): Array<SidebarTabRef> {
|
||||
return this.sidebar;
|
||||
}
|
||||
|
||||
getComponentById(id: string): Type<{}> {
|
||||
return this.components[id];
|
||||
}
|
||||
|
38
src/app/extensions/sidebar.extensions.ts
Normal file
38
src/app/extensions/sidebar.extensions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* @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 class SidebarTabRef {
|
||||
id: string;
|
||||
title: string;
|
||||
component: string;
|
||||
|
||||
icon?: string;
|
||||
disabled?: boolean;
|
||||
order?: number;
|
||||
rules?: {
|
||||
visible?: string;
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
@ -4,7 +4,8 @@
|
||||
"$version": "1.0.0",
|
||||
"$references": [
|
||||
"plugin1.json",
|
||||
"plugin2.json"
|
||||
"plugin2.json",
|
||||
"dev.tools.json"
|
||||
],
|
||||
|
||||
"rules": [
|
||||
@ -727,6 +728,27 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sidebar": [
|
||||
{
|
||||
"id": "app.sidebar.properties",
|
||||
"order": 100,
|
||||
"title": "APP.INFO_DRAWER.TABS.PROPERTIES",
|
||||
"component": "app.components.tabs.metadata"
|
||||
},
|
||||
{
|
||||
"id": "app.sidebar.comments",
|
||||
"order": 200,
|
||||
"title": "APP.INFO_DRAWER.TABS.COMMENTS",
|
||||
"component": "app.components.tabs.comments"
|
||||
},
|
||||
{
|
||||
"id": "app.sidebar.versions",
|
||||
"order": 300,
|
||||
"disabled": true,
|
||||
"title": "APP.INFO_DRAWER.TABS.VERSIONS",
|
||||
"component": "app.components.tabs.versions"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
376
src/assets/extension.schema.json
Normal file
376
src/assets/extension.schema.json
Normal file
@ -0,0 +1,376 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://github.com/Alfresco/alfresco-content-app/blob/development/extension.schema.json",
|
||||
"title": "ACA Extension Schema",
|
||||
"description": "Provides a validation schema for ACA extensions",
|
||||
|
||||
"definitions": {
|
||||
"ruleRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "type"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique rule definition id",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Rule evaluator type",
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"description": "Rule evaluator parameters",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/ruleParameter" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"ruleParameter": {
|
||||
"type": "object",
|
||||
"required": ["type", "value"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "Rule parameter type",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Rule parameter value",
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"description": "Parameters",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/ruleParameter" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"routeRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "path", "component"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique route reference identifier.",
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"description": "Route path to register.",
|
||||
"type": "string"
|
||||
},
|
||||
"component": {
|
||||
"description": "Unique identifier for the Component to use with the route.",
|
||||
"type": "string"
|
||||
},
|
||||
"layout": {
|
||||
"description": "Unique identifier for the custom layout component to use.",
|
||||
"type": "string"
|
||||
},
|
||||
"auth": {
|
||||
"description": "List of the authentication guards to use with the route.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minLength": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"data": {
|
||||
"description": "Custom data to pass to the activated route so that your components can access it",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"actionRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "type"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique action identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Action type",
|
||||
"type": "string"
|
||||
},
|
||||
"payload": {
|
||||
"description": "Action payload value (string or expression)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentActionRef": {
|
||||
"type": "object",
|
||||
"required": ["id"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique action identifier.",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Element type",
|
||||
"type": "string",
|
||||
"enum": ["default", "button", "separator", "menu", "custom"]
|
||||
},
|
||||
"title": {
|
||||
"description": "Element title",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Element description, used for the tooltips.",
|
||||
"type": "string"
|
||||
},
|
||||
"description-disabled": {
|
||||
"description": "Description to use when element is in the disabled state.",
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"description": "Element order",
|
||||
"type": "number"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Element icon",
|
||||
"type": "string"
|
||||
},
|
||||
"disabled": {
|
||||
"description": "Toggles disabled state",
|
||||
"type": "boolean"
|
||||
},
|
||||
"component": {
|
||||
"description": "Custom component id (requires type to be 'custom')",
|
||||
"type": "string"
|
||||
},
|
||||
"children": {
|
||||
"description": "Child entries for the container types.",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"actions": {
|
||||
"description": "Element actions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"click": {
|
||||
"description": "Action reference for the click handler",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"description": "Element rules",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Rule to evaluate the enabled state",
|
||||
"type": "string"
|
||||
},
|
||||
"visible": {
|
||||
"description": "Rule to evaluate the visibility state",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"navBarLinkRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "icon", "title", "route"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Element icon",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Element title",
|
||||
"type": "string"
|
||||
},
|
||||
"route": {
|
||||
"description": "Route reference identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Element description or tooltip",
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"description": "Element order",
|
||||
"type": "number"
|
||||
},
|
||||
"disabled": {
|
||||
"description": "Toggles the disabled state",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"navBarGroupRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "items"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier for the navigation group",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"description": "Navigation group items",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/navBarLinkRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"order": {
|
||||
"description": "Group order",
|
||||
"type": "number"
|
||||
},
|
||||
"disabled": {
|
||||
"description": "Toggles the disabled state",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebarTabRef": {
|
||||
"type": "object",
|
||||
"required": ["id", "component"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier for the navigation group",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Element title",
|
||||
"type": "string"
|
||||
},
|
||||
"component": {
|
||||
"description": "Component id",
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Material icon name",
|
||||
"type": "string"
|
||||
},
|
||||
"disabled": {
|
||||
"description": "Toggles disabled state",
|
||||
"type": "boolean"
|
||||
},
|
||||
"order": {
|
||||
"description": "Element order",
|
||||
"type": "number"
|
||||
},
|
||||
"rules": {
|
||||
"description": "Element rules",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"visible": {
|
||||
"description": "Rule to evaluate the visibility state",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"type": "object",
|
||||
"required": ["$name", "$version"],
|
||||
"properties": {
|
||||
"$name": {
|
||||
"description": "Extension name",
|
||||
"type": "string"
|
||||
},
|
||||
"$version": {
|
||||
"description": "Extension version",
|
||||
"type": "string"
|
||||
},
|
||||
"$description": {
|
||||
"description": "Brief description on what the extension does"
|
||||
},
|
||||
"$references": {
|
||||
"description": "References to external files",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"rules": {
|
||||
"description": "List of rule definitions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/ruleRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"routes": {
|
||||
"description": "List of custom application routes",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/routeRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"actions": {
|
||||
"description": "List of action definitions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/actionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"features": {
|
||||
"description": "Application-specific features and extensions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"create": {
|
||||
"description": "The [New] menu component extensions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"viewer": {
|
||||
"description": "Viewer component extensions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"openWith": {
|
||||
"description": "The [Open With] menu extensions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"navbar": {
|
||||
"description": "Navigation bar extensions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/navBarGroupRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"sidebar": {
|
||||
"description": "Sidebar extensions",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/sidebarTabRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"content": {
|
||||
"description": "Main application content extensions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"contextActions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
src/assets/plugins/dev.tools.json
Normal file
36
src/assets/plugins/dev.tools.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "dev.tools",
|
||||
"$description": "ACA dev tools",
|
||||
|
||||
"routes": [
|
||||
{
|
||||
"id": "app.dev.tools",
|
||||
"component": "app.dev.tools.component",
|
||||
"path": "dev-tools",
|
||||
"data": {
|
||||
"configPath": "assets/app.extensions.json",
|
||||
"pluginsPath": "assets/plugins",
|
||||
"schemaUri": "../../extension.schema.json",
|
||||
"schemaPath": "assets/extension.schema.json"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"features": {
|
||||
"navbar": [
|
||||
{
|
||||
"id": "app.navbar.dev.tools",
|
||||
"items": [
|
||||
{
|
||||
"id": "app.navbar.dev.tools.main",
|
||||
"icon": "build",
|
||||
"title": "dev tools",
|
||||
"route": "app.dev.tools"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -95,3 +95,38 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
* Need to import at least one locale-data with intl.
|
||||
*/
|
||||
// import 'intl/locale-data/jsonp/en';
|
||||
|
||||
// workaround for https://github.com/Microsoft/monaco-editor/issues/790
|
||||
|
||||
Promise.all = function(values: any): Promise<any> {
|
||||
let resolve: (v: any) => void;
|
||||
let reject: (v: any) => void;
|
||||
const promise = new this((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
let count = 0;
|
||||
let index = 0;
|
||||
const resolvedValues: any[] = [];
|
||||
for (let value of values) {
|
||||
if (!(value && value.then)) {
|
||||
value = this.resolve(value);
|
||||
}
|
||||
value.then(
|
||||
(idx => (val: any) => {
|
||||
resolvedValues[idx] = val;
|
||||
count--;
|
||||
if (!count) {
|
||||
resolve(resolvedValues);
|
||||
}
|
||||
})(index),
|
||||
reject
|
||||
);
|
||||
count++;
|
||||
index++;
|
||||
}
|
||||
if (!count) {
|
||||
resolve(resolvedValues);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
|
@ -2,9 +2,7 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"baseUrl": "./",
|
||||
"module": "es2015",
|
||||
"types": []
|
||||
},
|
||||
"exclude": ["node_modules", "test.ts", "**/*.spec.ts"]
|
||||
"exclude": ["test.ts", "**/*.spec.ts"]
|
||||
}
|
||||
|
@ -2,9 +2,6 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"baseUrl": "./",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": ["jasmine", "node"]
|
||||
},
|
||||
"files": ["test.ts", "polyfills.ts"],
|
||||
|
@ -17,7 +17,15 @@
|
||||
"dom"
|
||||
],
|
||||
"module": "es2015",
|
||||
"baseUrl": "./"
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"aca-dev-tools": [
|
||||
"dist/aca-dev-tools"
|
||||
],
|
||||
"aca-dev-tools/*": [
|
||||
"dist/aca-dev-tools/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
|
Loading…
x
Reference in New Issue
Block a user