mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
extensibility docs update (#571)
* docs: custom sidebar tabs * update docs * docs update, simplify extension schema * test fixes
This commit is contained in:
@@ -24,6 +24,13 @@
|
||||
"unfavorite",
|
||||
"Snackbar",
|
||||
"devtools",
|
||||
"gitter",
|
||||
"jira",
|
||||
"markdownlint",
|
||||
"uploader",
|
||||
"nginx",
|
||||
"docx",
|
||||
"SOLR",
|
||||
|
||||
"unshare",
|
||||
"validators",
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
# Alfresco Example Content Application
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
## Introduction
|
||||
|
||||
The Alfresco Content Application is an example application built using
|
||||
@@ -287,7 +289,7 @@ The Header & Toolbar section of the viewer contains a number of features that re
|
||||
|
||||
The File Viewer consists of four separate views that handle displaying the content based on four types of content, covering various [file/mime](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html#supported-file-formats) types:
|
||||
|
||||
- Document View: PDFs are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved.
|
||||
- Document View: PDF files are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved.
|
||||
- Image View: JPEG, PNG, GIF, BMP and SVG images are natively displayed in the application File Viewer.
|
||||
- Media View: MP4, MP3, WAV, OGG and WEBM files are played natively application File Viewer. The File Viewer will download, by default, 50MB of the content at a time to ensure a smooth playback of the content.
|
||||
- Text View: TXT, XML, JS, HTML, JSON and TS files are natively displayed as text in the application File Viewer.
|
||||
@@ -313,7 +315,7 @@ At the bottom of the content the Viewer Controls allow users to interact with th
|
||||
- Media View:
|
||||
- Play/pause
|
||||
- Timeline position
|
||||
- Audio mute/unmute
|
||||
- Toggle audio
|
||||
- Audio volume
|
||||
- Full screen
|
||||
|
||||
@@ -329,7 +331,7 @@ The Properties tab displays the node's metadata info by using the [ContentMetada
|
||||
|
||||

|
||||
|
||||
For more information, please check also the ADF's [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html).
|
||||
For more information, please check also the [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html).
|
||||
|
||||
#### Versions tab
|
||||
|
||||
|
||||
+444
-13
@@ -2,6 +2,8 @@
|
||||
title: Extending
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
<p class="danger">
|
||||
Work is still in progress, the documentation and examples may change.
|
||||
</p>
|
||||
@@ -18,6 +20,7 @@ You can create plugins that change, toggle or extend the following areas:
|
||||
|
||||
* Navigation sidebar links and groups
|
||||
* Context Menu
|
||||
* Sidebar (aka Info Drawer)
|
||||
* Toolbar entries
|
||||
* buttons
|
||||
* menu buttons
|
||||
@@ -116,9 +119,9 @@ To create a new route, populate the `routes` section with the corresponding entr
|
||||
| **id** | Unique identifier. |
|
||||
| **path** | Runtime path of the route. |
|
||||
| **component** | The main [component](#components) to use for the route. |
|
||||
| *layout* | The layout [component](#components) to use for the route. |
|
||||
| *auth* | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. |
|
||||
| *data* | Custom property bag to carry with the route. |
|
||||
| layout | The layout [component](#components) to use for the route. |
|
||||
| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. |
|
||||
| data | Custom property bag to carry with the route. |
|
||||
|
||||
<p class="tip">
|
||||
Use the `app.layout.main` value for the `layout` property to get the default application layout,
|
||||
@@ -150,6 +153,11 @@ You can define the full route schema like in the next example:
|
||||
}
|
||||
```
|
||||
|
||||
<p class="warning">
|
||||
All application routes require at least one authentication guard.
|
||||
If you do not provide a guard the default `['app.auth`]` will be used at runtime.
|
||||
</p>
|
||||
|
||||
### Authentication Guards
|
||||
|
||||
Below is the list of the authentication guards main application registers on startup.
|
||||
@@ -539,9 +547,9 @@ on how to register your own entries to be re-used at runtime.
|
||||
|
||||
The rule in the example below evaluates to `true` if all the conditions are met:
|
||||
|
||||
- user has selected node(s)
|
||||
- user is not using **Trashcan** page
|
||||
- user is not using **Libraries** page
|
||||
* user has selected node(s)
|
||||
* user is not using **Trashcan** page
|
||||
* user is not using **Libraries** page
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -565,19 +573,443 @@ The rule in the example below evaluates to `true` if all the conditions are met:
|
||||
|
||||
## Application Features
|
||||
|
||||
### Extending Create Menu
|
||||
This section contains application-specific features that may vary depending on the final implementation.
|
||||
|
||||
### Extending Navigation Sidebar
|
||||
The ACA supports the following set of extension points:
|
||||
|
||||
### Extending Toolbar
|
||||
* Create menu
|
||||
* Navigation Bar
|
||||
* Toolbar
|
||||
* Context Menu
|
||||
* Viewer
|
||||
* Sidebar (aka Info Drawer)
|
||||
|
||||
### Extending Context Menu
|
||||
All the customisations are stored in the `features` section of the configuration file:
|
||||
|
||||
### Extending Viewer
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"create": [],
|
||||
"navbar": [],
|
||||
"toolbar": [],
|
||||
"contextMenu": [],
|
||||
"viewer": {
|
||||
"toolbar:": [],
|
||||
"openWith": []
|
||||
},
|
||||
"sidebar": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Other applications or external plugins can utilise different subsets of the configuration above.
|
||||
Also, extra entries can be added to the configuration schema.
|
||||
|
||||
### Content Actions
|
||||
|
||||
Most of the UI elements that operate with content, like toolbar buttons or menus,
|
||||
are based on `ContentActionRef` interface implementation:
|
||||
|
||||
```ts
|
||||
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;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
You can define content actions in the `app.extensions.json` file using the structure above.
|
||||
|
||||
### Create Menu
|
||||
|
||||
Provides extension endpoint for the "NEW" menu options.
|
||||
|
||||
You can populate the menu with an extra entries like in the example below:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"create": [
|
||||
{
|
||||
"id": "app.create.folder",
|
||||
"icon": "create_new_folder",
|
||||
"title": "Create Folder",
|
||||
"actions": {
|
||||
"click": "CREATE_FOLDER"
|
||||
},
|
||||
"rules": {
|
||||
"enabled": "app.navigation.folder.canCreate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.create.uploadFile",
|
||||
"icon": "file_upload",
|
||||
"title": "Upload Files",
|
||||
"actions": {
|
||||
"click": "UPLOAD_FILES"
|
||||
},
|
||||
"rules": {
|
||||
"enabled": "app.navigation.folder.canUpload"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to the [Content Actions](#content-actions) section for more details on supported properties.
|
||||
|
||||
<p class="tip">
|
||||
It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customise.
|
||||
</p>
|
||||
|
||||
### Navigation Bar
|
||||
|
||||
Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`).
|
||||
|
||||
```ts
|
||||
export interface NavBarGroupRef {
|
||||
id: string;
|
||||
items: Array<NavBarLinkRef>;
|
||||
|
||||
order?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface NavBarLinkRef {
|
||||
id: string;
|
||||
icon: string;
|
||||
title: string;
|
||||
route: string;
|
||||
|
||||
url?: string; // evaluated at runtime based on route ref
|
||||
description?: string;
|
||||
order?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
You extensions can perform the following actions at runtime:
|
||||
|
||||
* Register new groups with links
|
||||
* Insert new links into existing groups
|
||||
* Update properties of the existing links
|
||||
* Disable existing links or entire groups
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"navbar": [
|
||||
{
|
||||
"id": "app.navbar.primary",
|
||||
"items": [
|
||||
{
|
||||
"id": "app.navbar.personalFiles",
|
||||
"icon": "folder",
|
||||
"title": "Personal Files",
|
||||
"route": "personal-files"
|
||||
},
|
||||
{
|
||||
"id": "app.navbar.libraries",
|
||||
"icon": "group_work",
|
||||
"title": "Libraries",
|
||||
"route": "libraries"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "app.navbar.secondary",
|
||||
"items": [
|
||||
{
|
||||
"id": "app.navbar.shared",
|
||||
"icon": "people",
|
||||
"title": "Shared",
|
||||
"route": "shared"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sidebar (Info Drawer)
|
||||
|
||||
You can provide the following customisations for the Sidebar (aka Info Drawer) component:
|
||||
|
||||
* Add extra tabs with custom components
|
||||
* Disable tabs from the main application or extensions
|
||||
* Replace content or properties of existing tabs
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"sidebar": {
|
||||
{
|
||||
"id": "app.sidebar.properties",
|
||||
"order": 100,
|
||||
"title": "Properties",
|
||||
"component": "app.components.tabs.metadata"
|
||||
},
|
||||
{
|
||||
"id": "app.sidebar.comments",
|
||||
"order": 200,
|
||||
"title": "Comments",
|
||||
"component": "app.components.tabs.comments"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The example above renders two tabs:
|
||||
|
||||
* `Properties` tab that references `app.components.tabs.metadata` component
|
||||
* `Comments` tab that references `app.components.tabs.comments` component
|
||||
|
||||
All corresponding components must be registered for runtime use.
|
||||
|
||||
<p class="tip">
|
||||
See [Registration](#registration) section for more details
|
||||
on how to register your own entries to be re-used at runtime.
|
||||
</p>
|
||||
|
||||
#### Tab properties
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| **id** | Unique identifier. |
|
||||
| **component** | The main [component](#components) to use for the route. |
|
||||
| **title** | Tab title or resource key. |
|
||||
| icon | Tab icon |
|
||||
| disabled | Toggles disabled state. Can be assigned from other plugins. |
|
||||
| order | The order of the element. |
|
||||
|
||||
### Toolbar
|
||||
|
||||
The toolbar extension point is represented by an array of Content Action references.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"toolbar": [
|
||||
{
|
||||
"id": "app.toolbar.preview",
|
||||
"title": "View",
|
||||
"icon": "open_in_browser",
|
||||
"actions": {
|
||||
"click": "VIEW_FILE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canViewFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.toolbar.download",
|
||||
"title": "Download",
|
||||
"icon": "get_app",
|
||||
"actions": {
|
||||
"click": "DOWNLOAD_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canDownload"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The content actions are applied to the toolbars for the following Views:
|
||||
|
||||
* Personal Files
|
||||
* Libraries
|
||||
* Shared
|
||||
* Recent Files
|
||||
* Favorites
|
||||
* Trash
|
||||
* Search Results
|
||||
|
||||
### Context Menu
|
||||
|
||||
Context Menu extensibility is similar to the one of the Toolbar.
|
||||
You may want to define a list of content actions backed by Rules and wired with Application Actions.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"contextMenu": [
|
||||
{
|
||||
"id": "app.context.menu.download",
|
||||
"order": 100,
|
||||
"title": "Download",
|
||||
"icon": "get_app",
|
||||
"actions": {
|
||||
"click": "DOWNLOAD_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canDownload"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that you can re-use any rules and evaluators available.
|
||||
|
||||
In the example above, the context menu action `Download` utilizes the `app.toolbar.canDownload` rule,
|
||||
declared in the `rules` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"id": "app.toolbar.canDownload",
|
||||
"type": "core.every",
|
||||
"parameters": [
|
||||
{ "type": "rule", "value": "app.selection.canDownload" },
|
||||
{ "type": "rule", "value": "app.navigation.isNotTrashcan" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Viewer
|
||||
|
||||
Viewer component in ACA supports the following extension points:
|
||||
|
||||
* `More` toolbar actions
|
||||
* `Open With` actions
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"viewer": {
|
||||
"toolbar:": [],
|
||||
"openWith": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Toolbar actions
|
||||
|
||||
The ADF Viewer component allows providing custom entries for the `More` menu button on the toolbar.
|
||||
The ACA provides an extension point for this menu that you can utilise to populate custom menu items:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"features": {
|
||||
"viewer": {
|
||||
"toolbar:": [
|
||||
{
|
||||
"id": "app.viewer.share",
|
||||
"order": 300,
|
||||
"title": "Share",
|
||||
"icon": "share",
|
||||
"actions": {
|
||||
"click": "SHARE_NODE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.selection.file.canShare"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Open With actions
|
||||
|
||||
#### Toolbar actions
|
||||
You can provide a list of `Open With` actions to render with every instance of the Viewer.
|
||||
|
||||
In the following example, we create a simple `Snackbar` action reference,
|
||||
and invoke it from the custom `Open With` menu entry called `Snackbar`.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "../../../extension.schema.json",
|
||||
"$version": "1.0.0",
|
||||
"$name": "plugin1",
|
||||
|
||||
"actions": [
|
||||
{
|
||||
"id": "plugin1.actions.info",
|
||||
"type": "SNACKBAR_INFO",
|
||||
"payload": "I'm a nice little popup raised by extension."
|
||||
},
|
||||
],
|
||||
|
||||
"features": {
|
||||
"viewer": {
|
||||
"openWith": [
|
||||
{
|
||||
"id": "plugin1.viewer.openWith.action1",
|
||||
"type": "button",
|
||||
"icon": "build",
|
||||
"title": "Snackbar",
|
||||
"actions": {
|
||||
"click": "plugin1.actions.info"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As with other content actions, custom plugins can disable, update or extend `Open With` actions.
|
||||
|
||||
## Registration
|
||||
|
||||
@@ -699,7 +1131,6 @@ extensions.setEvaluators({
|
||||
Now, the `plugin1.rules.hasSelection` evaluator can be used as an inline rule reference,
|
||||
or part of the composite rule like `core.every`.
|
||||
|
||||
|
||||
<p class="tip">
|
||||
See [Registration](#registration) section for more details
|
||||
on how to register your own entries to be re-used at runtime.
|
||||
|
||||
@@ -482,7 +482,7 @@ In the `app.config.json` define a link entry which will point to the custom page
|
||||
{
|
||||
"icon": "work",
|
||||
"label": "Link",
|
||||
"title": "My custome link",
|
||||
"title": "My custom link",
|
||||
"route": {
|
||||
"url": "/custom-route"
|
||||
}
|
||||
|
||||
+6
-12
@@ -332,8 +332,8 @@
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"toolbar": {
|
||||
"description": "Toolbar entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
@@ -352,18 +352,14 @@
|
||||
"items": { "$ref": "#/definitions/sidebarTabRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"content": {
|
||||
"description": "Main application content extensions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"toolbar": {
|
||||
"description": "Toolbar entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"contextActions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"contextMenu": {
|
||||
"description": "Context menu entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
@@ -372,5 +368,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export class ContextMenuComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
.subscribe(selection => {
|
||||
if (selection.count) {
|
||||
this.selection = selection;
|
||||
this.actions = this.extensions.getAllowedContentContextActions();
|
||||
this.actions = this.extensions.getAllowedContextMenuActions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export abstract class PageComponent implements OnInit, OnDestroy {
|
||||
documentDisplayMode$: Observable<string>;
|
||||
sharedPreviewUrl$: Observable<string>;
|
||||
actions: Array<ContentActionRef> = [];
|
||||
viewerActions: Array<ContentActionRef> = [];
|
||||
viewerToolbarActions: Array<ContentActionRef> = [];
|
||||
canUpdateNode = false;
|
||||
canUpload = false;
|
||||
|
||||
@@ -76,8 +76,8 @@ export abstract class PageComponent implements OnInit, OnDestroy {
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(selection => {
|
||||
this.selection = selection;
|
||||
this.actions = this.extensions.getAllowedContentActions();
|
||||
this.viewerActions = this.extensions.getViewerActions();
|
||||
this.actions = this.extensions.getAllowedToolbarActions();
|
||||
this.viewerToolbarActions = this.extensions.getViewerToolbarActions();
|
||||
this.canUpdateNode = this.selection.count === 1 && this.content.canUpdateNode(selection.first);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</adf-viewer-open-with>
|
||||
|
||||
<adf-viewer-more-actions>
|
||||
<ng-container *ngFor="let action of viewerActions; trackBy: trackByActionId">
|
||||
<ng-container *ngFor="let action of viewerToolbarActions; trackBy: trackByActionId">
|
||||
<aca-toolbar-action type="menu-item" [entry]="action"></aca-toolbar-action>
|
||||
</ng-container>
|
||||
</adf-viewer-more-actions>
|
||||
|
||||
@@ -42,13 +42,11 @@ export interface ExtensionConfig {
|
||||
create?: Array<ContentActionRef>;
|
||||
viewer?: {
|
||||
openWith?: Array<ContentActionRef>;
|
||||
actions?: Array<ContentActionRef>;
|
||||
toolbar?: Array<ContentActionRef>;
|
||||
};
|
||||
navbar?: Array<NavBarGroupRef>;
|
||||
sidebar?: Array<SidebarTabRef>;
|
||||
content?: {
|
||||
actions?: Array<ContentActionRef>;
|
||||
contextActions?: Array<ContentActionRef>
|
||||
};
|
||||
toolbar?: Array<ContentActionRef>;
|
||||
contextMenu?: Array<ContentActionRef>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -290,8 +290,7 @@ describe('ExtensionService', () => {
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
features: {
|
||||
content: {
|
||||
actions: [
|
||||
toolbar: [
|
||||
{
|
||||
id: 'aca:toolbar/separator-1',
|
||||
order: 1,
|
||||
@@ -306,10 +305,9 @@ describe('ExtensionService', () => {
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(extensions.contentActions.length).toBe(2);
|
||||
expect(extensions.toolbarActions.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should sort content actions by order', () => {
|
||||
@@ -317,8 +315,7 @@ describe('ExtensionService', () => {
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
features: {
|
||||
content: {
|
||||
actions: [
|
||||
toolbar: [
|
||||
{
|
||||
id: 'aca:toolbar/separator-2',
|
||||
order: 2,
|
||||
@@ -333,14 +330,13 @@ describe('ExtensionService', () => {
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(extensions.contentActions.length).toBe(2);
|
||||
expect(extensions.contentActions[0].id).toBe(
|
||||
expect(extensions.toolbarActions.length).toBe(2);
|
||||
expect(extensions.toolbarActions[0].id).toBe(
|
||||
'aca:toolbar/separator-1'
|
||||
);
|
||||
expect(extensions.contentActions[1].id).toBe(
|
||||
expect(extensions.toolbarActions[1].id).toBe(
|
||||
'aca:toolbar/separator-2'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ import { ActionRef, ContentActionRef, ContentActionType } from './action.extensi
|
||||
import * as core from './evaluators/core.evaluators';
|
||||
import { NodePermissionService } from '../services/node-permission.service';
|
||||
import { SidebarTabRef } from './sidebar.extensions';
|
||||
import { ProfileResolver } from '../services/profile.resolver';
|
||||
|
||||
@Injectable()
|
||||
export class ExtensionService implements RuleContext {
|
||||
@@ -53,9 +54,9 @@ export class ExtensionService implements RuleContext {
|
||||
routes: Array<RouteRef> = [];
|
||||
actions: Array<ActionRef> = [];
|
||||
|
||||
contentActions: Array<ContentActionRef> = [];
|
||||
viewerActions: Array<ContentActionRef> = [];
|
||||
contentContextmenuActions: Array<ContentActionRef> = [];
|
||||
toolbarActions: Array<ContentActionRef> = [];
|
||||
viewerToolbarActions: Array<ContentActionRef> = [];
|
||||
contextMenuActions: Array<ContentActionRef> = [];
|
||||
openWithActions: Array<ContentActionRef> = [];
|
||||
createActions: Array<ContentActionRef> = [];
|
||||
navbar: Array<NavBarGroupRef> = [];
|
||||
@@ -133,9 +134,9 @@ export class ExtensionService implements RuleContext {
|
||||
this.rules = this.loadRules(config);
|
||||
this.actions = this.loadActions(config);
|
||||
this.routes = this.loadRoutes(config);
|
||||
this.contentActions = this.loadContentActions(config);
|
||||
this.viewerActions = this.loadViewerActions(config);
|
||||
this.contentContextmenuActions = this.loadContentContextMenuActions(config);
|
||||
this.toolbarActions = this.loadToolbarActions(config);
|
||||
this.viewerToolbarActions = this.loadViewerToolbarActions(config);
|
||||
this.contextMenuActions = this.loadContextMenuActions(config);
|
||||
this.openWithActions = this.loadViewerOpenWith(config);
|
||||
this.createActions = this.loadCreateActions(config);
|
||||
this.navbar = this.loadNavBar(config);
|
||||
@@ -168,27 +169,27 @@ export class ExtensionService implements RuleContext {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadContentActions(config: ExtensionConfig) {
|
||||
if (config && config.features && config.features.content) {
|
||||
return (config.features.content.actions || [])
|
||||
protected loadToolbarActions(config: ExtensionConfig) {
|
||||
if (config && config.features && config.features.toolbar) {
|
||||
return (config.features.toolbar || [])
|
||||
.sort(this.sortByOrder)
|
||||
.map(this.setActionDefaults);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadViewerActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
protected loadViewerToolbarActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
if (config && config.features && config.features.viewer) {
|
||||
return (config.features.viewer.actions || [])
|
||||
return (config.features.viewer.toolbar || [])
|
||||
.sort(this.sortByOrder)
|
||||
.map(this.setActionDefaults);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadContentContextMenuActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
if (config && config.features && config.features.content) {
|
||||
return (config.features.content.contextActions || [])
|
||||
protected loadContextMenuActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
if (config && config.features && config.features.contextMenu) {
|
||||
return (config.features.contextMenu || [])
|
||||
.sort(this.sortByOrder)
|
||||
.map(this.setActionDefaults);
|
||||
}
|
||||
@@ -312,6 +313,7 @@ export class ExtensionService implements RuleContext {
|
||||
component: this.getComponentById(route.layout || this.defaults.layout),
|
||||
canActivateChild: guards,
|
||||
canActivate: guards,
|
||||
resolve: { profile: ProfileResolver },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@@ -342,8 +344,8 @@ export class ExtensionService implements RuleContext {
|
||||
}
|
||||
|
||||
// evaluates content actions for the selection and parent folder node
|
||||
getAllowedContentActions(): Array<ContentActionRef> {
|
||||
return this.contentActions
|
||||
getAllowedToolbarActions(): Array<ContentActionRef> {
|
||||
return this.toolbarActions
|
||||
.filter(this.filterEnabled)
|
||||
.filter(action => this.filterByRules(action))
|
||||
.map(action => {
|
||||
@@ -364,14 +366,14 @@ export class ExtensionService implements RuleContext {
|
||||
.reduce(this.reduceSeparators, []);
|
||||
}
|
||||
|
||||
getViewerActions(): Array<ContentActionRef> {
|
||||
return this.viewerActions
|
||||
getViewerToolbarActions(): Array<ContentActionRef> {
|
||||
return this.viewerToolbarActions
|
||||
.filter(this.filterEnabled)
|
||||
.filter(action => this.filterByRules(action));
|
||||
}
|
||||
|
||||
getAllowedContentContextActions(): Array<ContentActionRef> {
|
||||
return this.contentContextmenuActions
|
||||
getAllowedContextMenuActions(): Array<ContentActionRef> {
|
||||
return this.contextMenuActions
|
||||
.filter(this.filterEnabled)
|
||||
.filter(action => this.filterByRules(action));
|
||||
}
|
||||
|
||||
@@ -237,8 +237,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"actions": [
|
||||
"toolbar": [
|
||||
{
|
||||
"id": "app.toolbar.preview",
|
||||
"order": 100,
|
||||
@@ -460,9 +459,9 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"contextActions": [
|
||||
"contextMenu": [
|
||||
{
|
||||
"id": "app.contextmenu.download",
|
||||
"id": "app.context.menu.download",
|
||||
"order": 100,
|
||||
"title": "APP.ACTIONS.DOWNLOAD",
|
||||
"icon": "get_app",
|
||||
@@ -474,7 +473,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.preview",
|
||||
"id": "app.context.menu.preview",
|
||||
"order": 200,
|
||||
"title": "APP.ACTIONS.VIEW",
|
||||
"icon": "open_in_browser",
|
||||
@@ -486,7 +485,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.editFolder",
|
||||
"id": "app.context.menu.editFolder",
|
||||
"order": 300,
|
||||
"title": "APP.ACTIONS.EDIT",
|
||||
"icon": "create",
|
||||
@@ -498,7 +497,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.share",
|
||||
"id": "app.context.menu.share",
|
||||
"title": "APP.ACTIONS.SHARE",
|
||||
"order": 400,
|
||||
"icon": "share",
|
||||
@@ -510,7 +509,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.favorite.add",
|
||||
"id": "app.context.menu.favorite.add",
|
||||
"title": "APP.ACTIONS.FAVORITE",
|
||||
"order": 500,
|
||||
"icon": "star_border",
|
||||
@@ -522,7 +521,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.favorite.remove",
|
||||
"id": "app.context.menu.favorite.remove",
|
||||
"title": "APP.ACTIONS.FAVORITE",
|
||||
"order": 600,
|
||||
"icon": "star",
|
||||
@@ -534,7 +533,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.favorite",
|
||||
"id": "app.context.menu.favorite",
|
||||
"comment": "workaround for Recent Files and Search API issue",
|
||||
"type": "custom",
|
||||
"order": 501,
|
||||
@@ -544,7 +543,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.copy",
|
||||
"id": "app.context.menu.copy",
|
||||
"title": "APP.ACTIONS.COPY",
|
||||
"order": 700,
|
||||
"icon": "content_copy",
|
||||
@@ -556,7 +555,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.move",
|
||||
"id": "app.context.menu.move",
|
||||
"title": "APP.ACTIONS.MOVE",
|
||||
"order": 800,
|
||||
"icon": "library_books",
|
||||
@@ -568,7 +567,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.delete",
|
||||
"id": "app.context.menu.delete",
|
||||
"title": "APP.ACTIONS.DELETE",
|
||||
"order": 900,
|
||||
"icon": "delete",
|
||||
@@ -580,7 +579,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.versions",
|
||||
"id": "app.context.menu.versions",
|
||||
"title": "APP.ACTIONS.VERSIONS",
|
||||
"order": 1000,
|
||||
"icon": "history",
|
||||
@@ -592,7 +591,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.permissions",
|
||||
"id": "app.context.menu.permissions",
|
||||
"title": "APP.ACTIONS.PERMISSIONS",
|
||||
"icon": "settings_input_component",
|
||||
"order": 1100,
|
||||
@@ -604,7 +603,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.purgeDeletedNodes",
|
||||
"id": "app.context.menu.purgeDeletedNodes",
|
||||
"order": 1200,
|
||||
"title": "APP.ACTIONS.DELETE_PERMANENT",
|
||||
"icon": "delete_forever",
|
||||
@@ -616,7 +615,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.restoreDeletedNodes",
|
||||
"id": "app.context.menu.restoreDeletedNodes",
|
||||
"order": 1300,
|
||||
"title": "APP.ACTIONS.RESTORE",
|
||||
"icon": "restore",
|
||||
@@ -627,10 +626,9 @@
|
||||
"visible": "app.trashcan.hasSelection"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"viewer": {
|
||||
"actions": [
|
||||
"toolbar": [
|
||||
{
|
||||
"id": "app.viewer.favorite.add",
|
||||
"order": 100,
|
||||
|
||||
@@ -332,8 +332,8 @@
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"toolbar": {
|
||||
"description": "Toolbar entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
@@ -352,18 +352,14 @@
|
||||
"items": { "$ref": "#/definitions/sidebarTabRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"content": {
|
||||
"description": "Main application content extensions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"toolbar": {
|
||||
"description": "Toolbar entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"contextActions": {
|
||||
"description": "Content actions (toolbar, context menus, etc.)",
|
||||
"contextMenu": {
|
||||
"description": "Context menu entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
@@ -372,5 +368,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"actions": [
|
||||
"toolbar": [
|
||||
{
|
||||
"disabled": true,
|
||||
"id": "app.toolbar.createFolder",
|
||||
@@ -124,4 +123,3 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user