diff --git a/extension.schema.json b/extension.schema.json new file mode 100644 index 000000000..7989236ba --- /dev/null +++ b/extension.schema.json @@ -0,0 +1,300 @@ +{ + "$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" + } + } + }, + "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", "type"], + "properties": { + "id": { + "description": "Unique action identifier.", + "type": "string" + }, + "type": { + "description": "Element type", + "type": "string", + "enum": ["default", "button", "separator", "menu"] + }, + "title": { + "description": "Element title", + "type": "string" + }, + "order": { + "description": "Element order", + "type": "number" + }, + "icon": { + "description": "Element icon", + "type": "string" + }, + "disabled": { + "description": "Toggles disabled state", + "type": "boolean" + }, + "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" + } + } + } + }, + + "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 + } + } + }, + "navbar": { + "description": "Navigation bar extensions", + "type": "array", + "items": { "$ref": "#/definitions/navBarGroupRef" }, + "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 + } + } + } + } + } + } +} diff --git a/src/app.config.json b/src/app.config.json index bbccd407a..a7f3707d2 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -36,275 +36,6 @@ "preserveState": true, "expandedSidenav": true }, - "extensions": { - "external": [ - "plugin1.json", - "plugin2.json" - ], - "core": { - "rules": [ - { - "id": "app.create.canCreateFolder", - "type": "app.navigation.folder.canCreate" - }, - { - "id": "app.toolbar.canEditFolder", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.folder" }, - { "type": "rule", "value": "app.selection.folder.canUpdate" } - ] - }, - { - "id": "app.toolbar.canViewFile", - "type": "app.selection.file" - }, - { - "id": "app.toolbar.canDownload", - "type": "app.selection.canDownload" - } - ], - "routes": [ - { - "id": "aca:routes/about", - "path": "ext/about", - "component": "aca:components/about", - "layout": "aca:layouts/main", - "auth":[ "aca:auth" ], - "data": { - "title": "Custom About" - } - } - ], - "actions": [ - { - "id": "aca:actions/create-folder", - "type": "CREATE_FOLDER", - "payload": null - }, - { - "id": "aca:actions/edit-folder", - "type": "EDIT_FOLDER", - "payload": null - }, - { - "id": "aca:actions/download", - "type": "DOWNLOAD_NODES", - "payload": null - }, - { - "id": "aca:actions/preview", - "type": "VIEW_FILE", - "payload": null - }, - - { - "id": "aca:actions/info", - "type": "SNACKBAR_INFO", - "payload": "I'm a nice little popup raised by extension." - }, - { - "id": "aca:actions/node-name", - "type": "SNACKBAR_INFO", - "payload": "$('Action for ' + context.selection.first.entry.name)" - }, - { - "id": "aca:actions/settings", - "type": "NAVIGATE_URL", - "payload": "/settings" - } - ], - "features": { - "create": [ - { - "id": "app.create.folder", - "icon": "create_new_folder", - "title": "ext: Create Folder", - "actions": { - "click": "aca:actions/create-folder" - }, - "rules": { - "enabled": "app.create.canCreateFolder" - } - } - ], - "navigation": { - "aca:main": [ - { - "id": "aca/personal-files", - "order": 100, - "icon": "folder", - "title": "APP.BROWSE.PERSONAL.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.PERSONAL.SIDENAV_LINK.TOOLTIP", - "route": "personal-files" - }, - { - "id": "aca/libraries", - "order": 101, - "icon": "group_work", - "title": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.TOOLTIP", - "route": "libraries" - } - ], - "aca:secondary": [ - { - "id": "aca/shared", - "order": 100, - "icon": "people", - "title": "APP.BROWSE.SHARED.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.SHARED.SIDENAV_LINK.TOOLTIP", - "route": "shared" - }, - { - "id": "aca/recent-files", - "order": 101, - "icon": "schedule", - "title": "APP.BROWSE.RECENT.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.RECENT.SIDENAV_LINK.TOOLTIP", - "route": "recent-files" - }, - { - "id": "aca/favorites", - "order": 102, - "icon": "star", - "title": "APP.BROWSE.FAVORITES.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.FAVORITES.SIDENAV_LINK.TOOLTIP", - "route": "favorites" - }, - { - "id": "aca/trashcan", - "order": 103, - "icon": "delete", - "title": "APP.BROWSE.TRASHCAN.SIDENAV_LINK.LABEL", - "description": "APP.BROWSE.TRASHCAN.SIDENAV_LINK.TOOLTIP", - "route": "trashcan" - } - ], - "aca:demo": [ - { - "disabled": true, - "id": "aca:demo/link1", - "order": 100, - "icon": "build", - "title": "About (native)", - "description": "Uses native application route", - "route": "about" - }, - { - "disabled": true, - "id": "aca:demo/link2", - "order": 100, - "icon": "build", - "title": "About (custom)", - "description": "Uses custom defined route", - "route": "aca:routes/about" - } - ] - }, - "viewer": { - "open-with": [ - { - "disabled": false, - "id": "aca:viewer/action1", - "order": 100, - "icon": "build", - "title": "Snackbar", - "action": "aca:actions/info" - } - ] - }, - "content": { - "actions": [ - { - "id": "aca:toolbar/separator-1", - "order": 5, - "type": "separator" - }, - { - "id": "aca:toolbar/create-folder", - "type": "button", - "order": 10, - "title": "APP.NEW_MENU.TOOLTIPS.CREATE_FOLDER", - "icon": "create_new_folder", - "actions": { - "click": "aca:actions/create-folder" - }, - "rules": { - "visible": "app.create.canCreateFolder" - } - }, - { - "id": "aca:toolbar/preview", - "type": "button", - "order": 15, - "title": "APP.ACTIONS.VIEW", - "icon": "open_in_browser", - "actions": { - "click": "aca:actions/preview" - }, - "rules": { - "visible": "app.toolbar.canViewFile" - } - }, - { - "id": "aca:toolbar/download", - "type": "button", - "order": 20, - "title": "APP.ACTIONS.DOWNLOAD", - "icon": "get_app", - "actions": { - "click": "aca:actions/download" - }, - "rules": { - "visible": "app.toolbar.canDownload" - } - }, - { - "id": "aca:toolbar/edit-folder", - "type": "button", - "order": 30, - "title": "APP.ACTIONS.EDIT", - "icon": "create", - "actions": { - "click": "aca:actions/edit-folder" - }, - "rules": { - "visible": "app.toolbar.canEditFolder" - } - }, - - { - "id": "aca:toolbar/separator-2", - "order": 200, - "type": "separator" - }, - { - "id": "aca:toolbar/menu-1", - "type": "menu", - "icon": "storage", - "order": 300, - "children": [ - { - "id": "aca:action3", - "type": "button", - "title": "Settings", - "icon": "settings_applications", - "actions": { - "click": "aca:actions/settings" - } - } - ] - }, - { - "id": "aca:toolbar/separator-3", - "type": "separator" - } - ] - } - } - } - }, "languages": [ { "key": "de", @@ -460,6 +191,7 @@ { "field": "modifier", "mincount": 1, "label": "SEARCH.FACET_FIELDS.MODIFIER" }, { "field": "SITE", "mincount": 1, "label": "SEARCH.FACET_FIELDS.FILE_LIBRARY" } ], + "facetQueries": {}, "categories": [ { "id": "modifiedDate", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 963f6ce14..30a1c2292 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -91,8 +91,6 @@ export class AppComponent implements OnInit { pageTitle.setTitle(data.title || ''); }); - this.extensions.init(); - this.router.config.unshift(...this.extensions.getApplicationRoutes()); this.uploadService.fileUploadError.subscribe(error => diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e418f6c77..49cdd7502 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,7 +24,7 @@ */ import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; import { RouterModule, RouteReuseStrategy } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -84,7 +84,11 @@ import { NodePermissionsDialogComponent } from './dialogs/node-permissions/node- import { NodePermissionsDirective } from './directives/node-permissions.directive'; import { PermissionsManagerComponent } from './components/permission-manager/permissions-manager.component'; import { AppRouteReuseStrategy } from './app.routes.strategy'; +import { ExtensionService } from './extensions/extension.service'; +export function setupExtensionServiceFactory(service: ExtensionService): Function { + return () => service.load(); +} @NgModule({ imports: [ BrowserModule, @@ -96,7 +100,7 @@ import { AppRouteReuseStrategy } from './app.routes.strategy'; enableTracing: false // enable for debug only }), MaterialModule, - CoreModule, + CoreModule.forRoot(), ContentModule, AppStoreModule, CoreExtensionsModule, @@ -159,7 +163,13 @@ import { AppRouteReuseStrategy } from './app.routes.strategy'; NodePermissionService, ProfileResolver, ExperimentalGuard, - ContentApiService + ContentApiService, + { + provide: APP_INITIALIZER, + useFactory: setupExtensionServiceFactory, + deps: [ExtensionService], + multi: true + } ], entryComponents: [ LibraryDialogComponent, diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts index c70c169d6..ac20b941d 100644 --- a/src/app/components/page.component.ts +++ b/src/app/components/page.component.ts @@ -36,8 +36,8 @@ import { AppStore } from '../store/states/app.state'; import { SelectionState } from '../store/states/selection.state'; import { Observable } from 'rxjs/Rx'; import { ExtensionService } from '../extensions/extension.service'; -import { ContentActionExtension } from '../extensions/content-action.extension'; import { ContentManagementService } from '../services/content-management.service'; +import { ContentActionRef } from '../extensions/action.extensions'; export abstract class PageComponent implements OnInit, OnDestroy { @@ -52,7 +52,7 @@ export abstract class PageComponent implements OnInit, OnDestroy { selection: SelectionState; displayMode = DisplayMode.List; sharedPreviewUrl$: Observable; - actions: Array = []; + actions: Array = []; canUpdateFile = false; canUpdateNode = false; canDelete = false; diff --git a/src/app/components/preview/preview.component.html b/src/app/components/preview/preview.component.html index 8dd91b2dc..4497904a1 100644 --- a/src/app/components/preview/preview.component.html +++ b/src/app/components/preview/preview.component.html @@ -18,7 +18,7 @@ diff --git a/src/app/components/preview/preview.component.ts b/src/app/components/preview/preview.component.ts index eb3088476..9affe0674 100644 --- a/src/app/components/preview/preview.component.ts +++ b/src/app/components/preview/preview.component.ts @@ -32,8 +32,8 @@ import { DeleteNodesAction, SetSelectedNodesAction } from '../../store/actions'; import { PageComponent } from '../page.component'; import { ContentApiService } from '../../services/content-api.service'; import { ExtensionService } from '../../extensions/extension.service'; -import { OpenWithExtension } from '../../extensions/open-with.extension'; import { ContentManagementService } from '../../services/content-management.service'; +import { ContentActionRef } from '../../extensions/action.extensions'; @Component({ selector: 'app-preview', templateUrl: 'preview.component.html', @@ -52,7 +52,7 @@ export class PreviewComponent extends PageComponent implements OnInit { previousNodeId: string; nextNodeId: string; navigateMultiple = false; - openWith: Array = []; + openWith: Array = []; constructor( private contentApi: ContentApiService, diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html index 042d0b5ba..4a190d3c4 100644 --- a/src/app/components/sidenav/sidenav.component.html +++ b/src/app/components/sidenav/sidenav.component.html @@ -59,13 +59,13 @@
    -