extensibility docs update (#571)

* docs: custom sidebar tabs

* update docs

* docs update, simplify extension schema

* test fixes
This commit is contained in:
Denys Vuika 2018-08-17 13:44:01 +01:00 committed by GitHub
parent c0321fe449
commit 160226094d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 996 additions and 576 deletions

View File

@ -24,6 +24,13 @@
"unfavorite",
"Snackbar",
"devtools",
"gitter",
"jira",
"markdownlint",
"uploader",
"nginx",
"docx",
"SOLR",
"unshare",
"validators",

View File

@ -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
![](images/content-metadata.png)
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
@ -357,7 +359,7 @@ There are 2 ways users can access the Version Manager:
#### Upload new version
A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mimetype as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments.
A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mime type as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments.
Please also check the [UploadVersionButtonComponent](https://alfresco.github.io/adf-component-catalog/components/UploadVersionButtonComponent.html).
#### Actions Menu

View File

@ -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.

View File

@ -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"
}

View File

@ -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,23 +352,17 @@
"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
}
}
"toolbar": {
"description": "Toolbar entries",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"contextMenu": {
"description": "Context menu entries",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
}
}
}

View File

@ -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();
}
});
}

View File

@ -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);
});

View File

@ -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>

View File

@ -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>;
};
}

View File

@ -290,26 +290,24 @@ describe('ExtensionService', () => {
$name: 'test',
$version: '1.0.0',
features: {
content: {
actions: [
{
id: 'aca:toolbar/separator-1',
order: 1,
type: ContentActionType.separator,
title: 'action1',
},
{
id: 'aca:toolbar/separator-2',
order: 2,
type: ContentActionType.separator,
title: 'action2'
}
]
}
toolbar: [
{
id: 'aca:toolbar/separator-1',
order: 1,
type: ContentActionType.separator,
title: 'action1',
},
{
id: 'aca:toolbar/separator-2',
order: 2,
type: ContentActionType.separator,
title: 'action2'
}
]
}
});
expect(extensions.contentActions.length).toBe(2);
expect(extensions.toolbarActions.length).toBe(2);
});
it('should sort content actions by order', () => {
@ -317,30 +315,28 @@ describe('ExtensionService', () => {
$name: 'test',
$version: '1.0.0',
features: {
content: {
actions: [
{
id: 'aca:toolbar/separator-2',
order: 2,
type: ContentActionType.separator,
title: 'action2'
},
{
id: 'aca:toolbar/separator-1',
order: 1,
type: ContentActionType.separator,
title: 'action1'
}
]
}
toolbar: [
{
id: 'aca:toolbar/separator-2',
order: 2,
type: ContentActionType.separator,
title: 'action2'
},
{
id: 'aca:toolbar/separator-1',
order: 1,
type: ContentActionType.separator,
title: 'action1'
}
]
}
});
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'
);
});

View File

@ -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));
}

View File

@ -237,400 +237,398 @@
]
}
],
"content": {
"actions": [
{
"id": "app.toolbar.preview",
"order": 100,
"title": "APP.ACTIONS.VIEW",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
"rules": {
"visible": "app.toolbar.canViewFile"
}
"toolbar": [
{
"id": "app.toolbar.preview",
"order": 100,
"title": "APP.ACTIONS.VIEW",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
{
"id": "app.toolbar.download",
"order": 200,
"title": "APP.ACTIONS.DOWNLOAD",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
"rules": {
"visible": "app.toolbar.canDownload"
}
"rules": {
"visible": "app.toolbar.canViewFile"
}
},
{
"id": "app.toolbar.download",
"order": 200,
"title": "APP.ACTIONS.DOWNLOAD",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
{
"id": "app.toolbar.editFolder",
"order": 300,
"title": "APP.ACTIONS.EDIT",
"icon": "create",
"actions": {
"click": "EDIT_FOLDER"
},
"rules": {
"visible": "app.toolbar.canEditFolder"
}
"rules": {
"visible": "app.toolbar.canDownload"
}
},
{
"id": "app.toolbar.editFolder",
"order": 300,
"title": "APP.ACTIONS.EDIT",
"icon": "create",
"actions": {
"click": "EDIT_FOLDER"
},
{
"id": "app.toolbar.purgeDeletedNodes",
"order": 400,
"title": "APP.ACTIONS.DELETE_PERMANENT",
"icon": "delete_forever",
"actions": {
"click": "PURGE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
"rules": {
"visible": "app.toolbar.canEditFolder"
}
},
{
"id": "app.toolbar.purgeDeletedNodes",
"order": 400,
"title": "APP.ACTIONS.DELETE_PERMANENT",
"icon": "delete_forever",
"actions": {
"click": "PURGE_DELETED_NODES"
},
{
"id": "app.toolbar.restoreDeletedNodes",
"order": 500,
"title": "APP.ACTIONS.RESTORE",
"icon": "restore",
"actions": {
"click": "RESTORE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
"rules": {
"visible": "app.trashcan.hasSelection"
}
},
{
"id": "app.toolbar.restoreDeletedNodes",
"order": 500,
"title": "APP.ACTIONS.RESTORE",
"icon": "restore",
"actions": {
"click": "RESTORE_DELETED_NODES"
},
{
"id": "app.toolbar.createLibrary",
"order": 600,
"title": "Create Library",
"icon": "create_new_folder",
"actions": {
"click": "CREATE_LIBRARY"
},
"rules": {
"visible": "app.navigation.isLibraries"
}
"rules": {
"visible": "app.trashcan.hasSelection"
}
},
{
"id": "app.toolbar.createLibrary",
"order": 600,
"title": "Create Library",
"icon": "create_new_folder",
"actions": {
"click": "CREATE_LIBRARY"
},
{
"id": "app.toolbar.info",
"type": "custom",
"order": 700,
"component": "app.toolbar.toggleInfoDrawer",
"rules": {
"visible": "app.toolbar.info"
}
},
{
"id": "app.toolbar.more",
"type": "menu",
"order": 10000,
"icon": "more_vert",
"title": "APP.ACTIONS.MORE",
"children": [
{
"id": "app.toolbar.favorite",
"comment": "workaround for Recent Files and Search API issue",
"type": "custom",
"order": 100,
"component": "app.toolbar.toggleFavorite",
"rules": {
"visible": "app.toolbar.favorite.canToggle"
}
},
{
"id": "app.toolbar.favorite.add",
"order": 200,
"title": "APP.ACTIONS.FAVORITE",
"icon": "star_border",
"actions": {
"click": "ADD_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canAdd"
}
},
{
"id": "app.toolbar.favorite.remove",
"order": 300,
"title": "APP.ACTIONS.FAVORITE",
"icon": "star",
"actions": {
"click": "REMOVE_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canRemove"
}
},
{
"id": "app.toolbar.copy",
"order": 400,
"title": "APP.ACTIONS.COPY",
"icon": "content_copy",
"actions": {
"click": "COPY_NODES"
},
"rules": {
"visible": "app.toolbar.canCopyNode"
}
},
{
"id": "app.toolbar.move",
"order": 500,
"title": "APP.ACTIONS.MOVE",
"icon": "library_books",
"actions": {
"click": "MOVE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.toolbar.share",
"order": 600,
"title": "APP.ACTIONS.SHARE",
"icon": "share",
"actions": {
"click": "SHARE_NODE"
},
"rules": {
"visible": "app.selection.file.canShare"
}
},
{
"id": "app.toolbar.unshare",
"order": 700,
"title": "APP.ACTIONS.UNSHARE",
"icon": "stop_screen_share",
"actions": {
"click": "UNSHARE_NODES"
},
"rules": {
"visible": "app.selection.canUnshare"
}
},
{
"id": "app.toolbar.delete",
"order": 800,
"title": "APP.ACTIONS.DELETE",
"icon": "delete",
"actions": {
"click": "DELETE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.toolbar.deleteLibrary",
"order": 900,
"title": "APP.ACTIONS.DELETE",
"icon": "delete",
"actions": {
"click": "DELETE_LIBRARY"
},
"rules": {
"visible": "app.selection.library"
}
},
{
"id": "app.toolbar.versions",
"order": 1000,
"title": "APP.ACTIONS.VERSIONS",
"icon": "history",
"actions": {
"click": "MANAGE_VERSIONS"
},
"rules": {
"visible": "app.toolbar.versions"
}
},
{
"id": "app.toolbar.permissions",
"order": 1100,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
"rules": {
"visible": "app.toolbar.permissions"
}
"rules": {
"visible": "app.navigation.isLibraries"
}
},
{
"id": "app.toolbar.info",
"type": "custom",
"order": 700,
"component": "app.toolbar.toggleInfoDrawer",
"rules": {
"visible": "app.toolbar.info"
}
},
{
"id": "app.toolbar.more",
"type": "menu",
"order": 10000,
"icon": "more_vert",
"title": "APP.ACTIONS.MORE",
"children": [
{
"id": "app.toolbar.favorite",
"comment": "workaround for Recent Files and Search API issue",
"type": "custom",
"order": 100,
"component": "app.toolbar.toggleFavorite",
"rules": {
"visible": "app.toolbar.favorite.canToggle"
}
]
},
{
"id": "app.toolbar.favorite.add",
"order": 200,
"title": "APP.ACTIONS.FAVORITE",
"icon": "star_border",
"actions": {
"click": "ADD_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canAdd"
}
},
{
"id": "app.toolbar.favorite.remove",
"order": 300,
"title": "APP.ACTIONS.FAVORITE",
"icon": "star",
"actions": {
"click": "REMOVE_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canRemove"
}
},
{
"id": "app.toolbar.copy",
"order": 400,
"title": "APP.ACTIONS.COPY",
"icon": "content_copy",
"actions": {
"click": "COPY_NODES"
},
"rules": {
"visible": "app.toolbar.canCopyNode"
}
},
{
"id": "app.toolbar.move",
"order": 500,
"title": "APP.ACTIONS.MOVE",
"icon": "library_books",
"actions": {
"click": "MOVE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.toolbar.share",
"order": 600,
"title": "APP.ACTIONS.SHARE",
"icon": "share",
"actions": {
"click": "SHARE_NODE"
},
"rules": {
"visible": "app.selection.file.canShare"
}
},
{
"id": "app.toolbar.unshare",
"order": 700,
"title": "APP.ACTIONS.UNSHARE",
"icon": "stop_screen_share",
"actions": {
"click": "UNSHARE_NODES"
},
"rules": {
"visible": "app.selection.canUnshare"
}
},
{
"id": "app.toolbar.delete",
"order": 800,
"title": "APP.ACTIONS.DELETE",
"icon": "delete",
"actions": {
"click": "DELETE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.toolbar.deleteLibrary",
"order": 900,
"title": "APP.ACTIONS.DELETE",
"icon": "delete",
"actions": {
"click": "DELETE_LIBRARY"
},
"rules": {
"visible": "app.selection.library"
}
},
{
"id": "app.toolbar.versions",
"order": 1000,
"title": "APP.ACTIONS.VERSIONS",
"icon": "history",
"actions": {
"click": "MANAGE_VERSIONS"
},
"rules": {
"visible": "app.toolbar.versions"
}
},
{
"id": "app.toolbar.permissions",
"order": 1100,
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"actions": {
"click": "MANAGE_PERMISSIONS"
},
"rules": {
"visible": "app.toolbar.permissions"
}
}
]
}
],
"contextMenu": [
{
"id": "app.context.menu.download",
"order": 100,
"title": "APP.ACTIONS.DOWNLOAD",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
"rules": {
"visible": "app.toolbar.canDownload"
}
],
"contextActions": [
{
"id": "app.contextmenu.download",
"order": 100,
"title": "APP.ACTIONS.DOWNLOAD",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
"rules": {
"visible": "app.toolbar.canDownload"
}
},
{
"id": "app.context.menu.preview",
"order": 200,
"title": "APP.ACTIONS.VIEW",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
{
"id": "app.contextmenu.preview",
"order": 200,
"title": "APP.ACTIONS.VIEW",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
"rules": {
"visible": "app.toolbar.canViewFile"
}
},
{
"id": "app.contextmenu.editFolder",
"order": 300,
"title": "APP.ACTIONS.EDIT",
"icon": "create",
"actions": {
"click": "EDIT_FOLDER"
},
"rules": {
"visible": "app.toolbar.canEditFolder"
}
},
{
"id": "app.contextmenu.share",
"title": "APP.ACTIONS.SHARE",
"order": 400,
"icon": "share",
"actions": {
"click": "SHARE_NODE"
},
"rules": {
"visible": "app.selection.file.canShare"
}
},
{
"id": "app.contextmenu.favorite.add",
"title": "APP.ACTIONS.FAVORITE",
"order": 500,
"icon": "star_border",
"actions": {
"click": "ADD_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canAdd"
}
},
{
"id": "app.contextmenu.favorite.remove",
"title": "APP.ACTIONS.FAVORITE",
"order": 600,
"icon": "star",
"actions": {
"click": "REMOVE_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canRemove"
}
},
{
"id": "app.contextmenu.favorite",
"comment": "workaround for Recent Files and Search API issue",
"type": "custom",
"order": 501,
"component": "app.toolbar.toggleFavorite",
"rules": {
"visible": "app.toolbar.favorite.canToggle"
}
},
{
"id": "app.contextmenu.copy",
"title": "APP.ACTIONS.COPY",
"order": 700,
"icon": "content_copy",
"actions": {
"click": "COPY_NODES"
},
"rules": {
"visible": "app.toolbar.canCopyNode"
}
},
{
"id": "app.contextmenu.move",
"title": "APP.ACTIONS.MOVE",
"order": 800,
"icon": "library_books",
"actions": {
"click": "MOVE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.contextmenu.delete",
"title": "APP.ACTIONS.DELETE",
"order": 900,
"icon": "delete",
"actions": {
"click": "DELETE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.contextmenu.versions",
"title": "APP.ACTIONS.VERSIONS",
"order": 1000,
"icon": "history",
"actions": {
"click": "MANAGE_VERSIONS"
},
"rules": {
"visible": "app.toolbar.versions"
}
},
{
"id": "app.contextmenu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"order": 1100,
"actions": {
"click": "MANAGE_PERMISSIONS"
},
"rules": {
"visible": "app.toolbar.permissions"
}
},
{
"id": "app.contextmenu.purgeDeletedNodes",
"order": 1200,
"title": "APP.ACTIONS.DELETE_PERMANENT",
"icon": "delete_forever",
"actions": {
"click": "PURGE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
},
{
"id": "app.contextmenu.restoreDeletedNodes",
"order": 1300,
"title": "APP.ACTIONS.RESTORE",
"icon": "restore",
"actions": {
"click": "RESTORE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
"rules": {
"visible": "app.toolbar.canViewFile"
}
]
},
},
{
"id": "app.context.menu.editFolder",
"order": 300,
"title": "APP.ACTIONS.EDIT",
"icon": "create",
"actions": {
"click": "EDIT_FOLDER"
},
"rules": {
"visible": "app.toolbar.canEditFolder"
}
},
{
"id": "app.context.menu.share",
"title": "APP.ACTIONS.SHARE",
"order": 400,
"icon": "share",
"actions": {
"click": "SHARE_NODE"
},
"rules": {
"visible": "app.selection.file.canShare"
}
},
{
"id": "app.context.menu.favorite.add",
"title": "APP.ACTIONS.FAVORITE",
"order": 500,
"icon": "star_border",
"actions": {
"click": "ADD_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canAdd"
}
},
{
"id": "app.context.menu.favorite.remove",
"title": "APP.ACTIONS.FAVORITE",
"order": 600,
"icon": "star",
"actions": {
"click": "REMOVE_FAVORITE"
},
"rules": {
"visible": "app.toolbar.favorite.canRemove"
}
},
{
"id": "app.context.menu.favorite",
"comment": "workaround for Recent Files and Search API issue",
"type": "custom",
"order": 501,
"component": "app.toolbar.toggleFavorite",
"rules": {
"visible": "app.toolbar.favorite.canToggle"
}
},
{
"id": "app.context.menu.copy",
"title": "APP.ACTIONS.COPY",
"order": 700,
"icon": "content_copy",
"actions": {
"click": "COPY_NODES"
},
"rules": {
"visible": "app.toolbar.canCopyNode"
}
},
{
"id": "app.context.menu.move",
"title": "APP.ACTIONS.MOVE",
"order": 800,
"icon": "library_books",
"actions": {
"click": "MOVE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.context.menu.delete",
"title": "APP.ACTIONS.DELETE",
"order": 900,
"icon": "delete",
"actions": {
"click": "DELETE_NODES"
},
"rules": {
"visible": "app.selection.canDelete"
}
},
{
"id": "app.context.menu.versions",
"title": "APP.ACTIONS.VERSIONS",
"order": 1000,
"icon": "history",
"actions": {
"click": "MANAGE_VERSIONS"
},
"rules": {
"visible": "app.toolbar.versions"
}
},
{
"id": "app.context.menu.permissions",
"title": "APP.ACTIONS.PERMISSIONS",
"icon": "settings_input_component",
"order": 1100,
"actions": {
"click": "MANAGE_PERMISSIONS"
},
"rules": {
"visible": "app.toolbar.permissions"
}
},
{
"id": "app.context.menu.purgeDeletedNodes",
"order": 1200,
"title": "APP.ACTIONS.DELETE_PERMANENT",
"icon": "delete_forever",
"actions": {
"click": "PURGE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
},
{
"id": "app.context.menu.restoreDeletedNodes",
"order": 1300,
"title": "APP.ACTIONS.RESTORE",
"icon": "restore",
"actions": {
"click": "RESTORE_DELETED_NODES"
},
"rules": {
"visible": "app.trashcan.hasSelection"
}
}
],
"viewer": {
"actions": [
"toolbar": [
{
"id": "app.viewer.favorite.add",
"order": 100,

View File

@ -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,23 +352,17 @@
"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
}
}
"toolbar": {
"description": "Toolbar entries",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"contextMenu": {
"description": "Context menu entries",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
}
}
}

View File

@ -50,78 +50,76 @@
]
}
],
"content": {
"actions": [
{
"disabled": true,
"id": "app.toolbar.createFolder",
"type": "button",
"order": 10,
"title": "APP.NEW_MENU.MENU_ITEMS.CREATE_FOLDER",
"description": "APP.NEW_MENU.TOOLTIPS.CREATE_FOLDER",
"icon": "create_new_folder",
"actions": {
"click": "CREATE_FOLDER"
},
"rules": {
"visible": "app.navigation.folder.canCreate"
}
"toolbar": [
{
"disabled": true,
"id": "app.toolbar.createFolder",
"type": "button",
"order": 10,
"title": "APP.NEW_MENU.MENU_ITEMS.CREATE_FOLDER",
"description": "APP.NEW_MENU.TOOLTIPS.CREATE_FOLDER",
"icon": "create_new_folder",
"actions": {
"click": "CREATE_FOLDER"
},
{
"disabled": true,
"id": "app.toolbar.uploadFile",
"order": 11,
"type": "button",
"icon": "file_upload",
"title": "APP.NEW_MENU.MENU_ITEMS.UPLOAD_FILE",
"description": "APP.NEW_MENU.TOOLTIPS.UPLOAD_FILES",
"actions": {
"click": "UPLOAD_FILES"
},
"rules": {
"visible": "app.navigation.folder.canUpload"
}
},
{
"disabled": true,
"id": "app.toolbar.uploadFolder",
"order": 12,
"type": "button",
"icon": "cloud_upload",
"title": "APP.NEW_MENU.MENU_ITEMS.UPLOAD_FOLDER",
"description": "APP.NEW_MENU.TOOLTIPS.UPLOAD_FOLDERS",
"actions": {
"click": "UPLOAD_FOLDER"
},
"rules": {
"visible": "app.navigation.folder.canUpload"
}
},
{
"disabled": true,
"id": "plugin1.toolbar.menu1",
"type": "menu",
"icon": "storage",
"order": 300,
"children": [
{
"id": "plugin1.toolbar.menu1.settings",
"type": "button",
"title": "Settings",
"icon": "settings_applications",
"actions": {
"click": "plugin1.actions.settings"
}
}
]
},
{
"disabled": true,
"id": "plugin1.toolbar.separator3",
"order": 301,
"type": "separator"
"rules": {
"visible": "app.navigation.folder.canCreate"
}
]
}
},
{
"disabled": true,
"id": "app.toolbar.uploadFile",
"order": 11,
"type": "button",
"icon": "file_upload",
"title": "APP.NEW_MENU.MENU_ITEMS.UPLOAD_FILE",
"description": "APP.NEW_MENU.TOOLTIPS.UPLOAD_FILES",
"actions": {
"click": "UPLOAD_FILES"
},
"rules": {
"visible": "app.navigation.folder.canUpload"
}
},
{
"disabled": true,
"id": "app.toolbar.uploadFolder",
"order": 12,
"type": "button",
"icon": "cloud_upload",
"title": "APP.NEW_MENU.MENU_ITEMS.UPLOAD_FOLDER",
"description": "APP.NEW_MENU.TOOLTIPS.UPLOAD_FOLDERS",
"actions": {
"click": "UPLOAD_FOLDER"
},
"rules": {
"visible": "app.navigation.folder.canUpload"
}
},
{
"disabled": true,
"id": "plugin1.toolbar.menu1",
"type": "menu",
"icon": "storage",
"order": 300,
"children": [
{
"id": "plugin1.toolbar.menu1.settings",
"type": "button",
"title": "Settings",
"icon": "settings_applications",
"actions": {
"click": "plugin1.actions.settings"
}
}
]
},
{
"disabled": true,
"id": "plugin1.toolbar.separator3",
"order": 301,
"type": "separator"
}
]
}
}