[ACA-1695] viewer extensions (#576)

[ACA-1695] viewer extensions
This commit is contained in:
Denys Vuika 2018-08-19 15:14:06 +01:00 committed by GitHub
parent eff9ce13f7
commit e97c8b703c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 299 additions and 0 deletions

View File

@ -817,6 +817,14 @@ on how to register your own entries to be re-used at runtime.
| disabled | Toggles disabled state. Can be assigned from other plugins. | | disabled | Toggles disabled state. Can be assigned from other plugins. |
| order | The order of the element. | | order | The order of the element. |
#### Tab components
Every component you assign for the tab content receives the following additional properties at runtime:
| Name | Type | Description |
| --- | --- | --- |
| node | MinimalNodeEntryEntity | Node entry to be displayed. |
### Toolbar ### Toolbar
The toolbar extension point is represented by an array of Content Action references. The toolbar extension point is represented by an array of Content Action references.
@ -920,6 +928,7 @@ declared in the `rules` section:
Viewer component in ACA supports the following extension points: Viewer component in ACA supports the following extension points:
* Content Viewers
* `More` toolbar actions * `More` toolbar actions
* `Open With` actions * `Open With` actions
@ -931,6 +940,7 @@ Viewer component in ACA supports the following extension points:
"features": { "features": {
"viewer": { "viewer": {
"content": [],
"toolbar:": [], "toolbar:": [],
"openWith": [] "openWith": []
} }
@ -938,6 +948,46 @@ Viewer component in ACA supports the following extension points:
} }
``` ```
#### Content View
You can provide custom components that render particular type of the content based on extensions.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"viewer": {
"content": [
{
"id": "app.viewer.pdf",
"fileExtension": "pdf",
"component": "app.components.tabs.metadata"
},
{
"id": "app.viewer.docx",
"fileExtension": "docx",
"component": "app.components.tabs.comments"
}
]
}
}
}
```
In the example above we replace `PDF` view with the `metadata` tab
and `DOCX` view with the `comments` tab.
Every custom component receives the following properties at runtime:
| Name | Type | Description |
| --- | --- | --- |
| node | MinimalNodeEntryEntity | Node entry to be displayed. |
| url | string | File content URL. |
| extension | string | File name extension. |
#### Toolbar actions #### Toolbar actions
The ADF Viewer component allows providing custom entries for the `More` menu button on the toolbar. The ADF Viewer component allows providing custom entries for the `More` menu button on the toolbar.

View File

@ -268,6 +268,32 @@
} }
} }
} }
},
"viewerExtensionRef": {
"type": "object",
"required": ["id", "component", "fileExtension"],
"properties": {
"id": {
"description": "Unique identifier for the navigation group",
"type": "string"
},
"component": {
"description": "Component id",
"type": "string"
},
"fileExtension": {
"description": "Target file extension",
"type": "string"
},
"order": {
"description": "Group order",
"type": "number"
},
"disabled": {
"description": "Toggles the disabled state",
"type": "boolean"
}
}
} }
}, },
@ -337,6 +363,12 @@
"type": "array", "type": "array",
"items": { "$ref": "#/definitions/contentActionRef" }, "items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1 "minItems": 1
},
"content": {
"description": "Viewer content extensions",
"type": "array",
"items": { "$ref": "#/definitions/viewerExtensionRef" },
"minItems": 1
} }
} }
}, },

View File

@ -0,0 +1,105 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import {
Component,
Input,
ComponentRef,
OnInit,
ComponentFactoryResolver,
ViewChild,
ViewContainerRef,
OnDestroy,
OnChanges
} from '@angular/core';
import { ExtensionService } from '../../extensions/extension.service';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
@Component({
selector: 'app-preview-extension',
template: `<div #content></div>`
})
export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy {
@ViewChild('content', { read: ViewContainerRef })
content: ViewContainerRef;
@Input()
id: string;
@Input()
url: string;
@Input()
extension: string;
@Input()
node: MinimalNodeEntryEntity;
private componentRef: ComponentRef<any>;
constructor(
private extensions: ExtensionService,
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
if (!this.id) {
return;
}
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() {
this.updateInstance();
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
private updateInstance() {
if (this.componentRef && this.componentRef.instance) {
const instance = this.componentRef.instance;
instance.node = this.node;
instance.url = this.url;
instance.extension = this.extension;
}
}
}

View File

@ -28,5 +28,19 @@
<aca-toolbar-action type="menu-item" [entry]="action"></aca-toolbar-action> <aca-toolbar-action type="menu-item" [entry]="action"></aca-toolbar-action>
</ng-container> </ng-container>
</adf-viewer-more-actions> </adf-viewer-more-actions>
<ng-container *ngFor="let ext of contentExtensions">
<adf-viewer-extension [supportedExtensions]="[ext.fileExtension]">
<ng-template let-url="urlFileContent" let-extension="extension">
<app-preview-extension
[id]="ext.component"
[node]="selection.file?.entry"
[url]="url"
[extension]="extension">
</app-preview-extension>
</ng-template>
</adf-viewer-extension>
</ng-container>
</adf-viewer> </adf-viewer>
</ng-container> </ng-container>

View File

@ -35,6 +35,7 @@ import { ExtensionService } from '../../extensions/extension.service';
import { ContentManagementService } from '../../services/content-management.service'; import { ContentManagementService } from '../../services/content-management.service';
import { ContentActionRef } from '../../extensions/action.extensions'; import { ContentActionRef } from '../../extensions/action.extensions';
import { ViewUtilService } from './view-util.service'; import { ViewUtilService } from './view-util.service';
import { ViewerExtensionRef } from '../../extensions/viewer.extensions';
@Component({ @Component({
selector: 'app-preview', selector: 'app-preview',
@ -55,6 +56,7 @@ export class PreviewComponent extends PageComponent implements OnInit {
nextNodeId: string; nextNodeId: string;
navigateMultiple = false; navigateMultiple = false;
openWith: Array<ContentActionRef> = []; openWith: Array<ContentActionRef> = [];
contentExtensions: Array<ViewerExtensionRef> = [];
constructor( constructor(
private contentApi: ContentApiService, private contentApi: ContentApiService,
@ -97,6 +99,7 @@ export class PreviewComponent extends PageComponent implements OnInit {
}); });
this.openWith = this.extensions.openWithActions; this.openWith = this.extensions.openWithActions;
this.contentExtensions = this.extensions.viewerContentExtensions;
} }
/** /**

View File

@ -35,6 +35,7 @@ import { PreviewComponent } from './preview.component';
import { ViewUtilService } from './view-util.service'; import { ViewUtilService } from './view-util.service';
import * as pdfjsLib from 'pdfjs-dist'; import * as pdfjsLib from 'pdfjs-dist';
import { PreviewExtensionComponent } from './preview-extension.component';
pdfjsLib.PDFJS.workerSrc = 'pdf.worker.js'; pdfjsLib.PDFJS.workerSrc = 'pdf.worker.js';
pdfjsLib.PDFJS.disableFontFace = true; pdfjsLib.PDFJS.disableFontFace = true;
@ -57,6 +58,7 @@ const routes: Routes = [
], ],
declarations: [ declarations: [
PreviewComponent, PreviewComponent,
PreviewExtensionComponent
], ],
providers: [ providers: [
ViewUtilService ViewUtilService

View File

@ -28,6 +28,7 @@ import { RouteRef } from './routing.extensions';
import { RuleRef } from './rule.extensions'; import { RuleRef } from './rule.extensions';
import { ActionRef, ContentActionRef } from './action.extensions'; import { ActionRef, ContentActionRef } from './action.extensions';
import { SidebarTabRef } from './sidebar.extensions'; import { SidebarTabRef } from './sidebar.extensions';
import { ViewerExtensionRef } from './viewer.extensions';
export interface ExtensionConfig { export interface ExtensionConfig {
$name: string; $name: string;
@ -43,6 +44,7 @@ export interface ExtensionConfig {
viewer?: { viewer?: {
openWith?: Array<ContentActionRef>; openWith?: Array<ContentActionRef>;
toolbar?: Array<ContentActionRef>; toolbar?: Array<ContentActionRef>;
content?: Array<ViewerExtensionRef>;
}; };
navbar?: Array<NavBarGroupRef>; navbar?: Array<NavBarGroupRef>;
sidebar?: Array<SidebarTabRef>; sidebar?: Array<SidebarTabRef>;

View File

@ -39,6 +39,7 @@ import * as core from './evaluators/core.evaluators';
import { NodePermissionService } from '../services/node-permission.service'; import { NodePermissionService } from '../services/node-permission.service';
import { SidebarTabRef } from './sidebar.extensions'; import { SidebarTabRef } from './sidebar.extensions';
import { ProfileResolver } from '../services/profile.resolver'; import { ProfileResolver } from '../services/profile.resolver';
import { ViewerExtensionRef } from './viewer.extensions';
@Injectable() @Injectable()
export class ExtensionService implements RuleContext { export class ExtensionService implements RuleContext {
@ -56,6 +57,7 @@ export class ExtensionService implements RuleContext {
toolbarActions: Array<ContentActionRef> = []; toolbarActions: Array<ContentActionRef> = [];
viewerToolbarActions: Array<ContentActionRef> = []; viewerToolbarActions: Array<ContentActionRef> = [];
viewerContentExtensions: Array<ViewerExtensionRef> = [];
contextMenuActions: Array<ContentActionRef> = []; contextMenuActions: Array<ContentActionRef> = [];
openWithActions: Array<ContentActionRef> = []; openWithActions: Array<ContentActionRef> = [];
createActions: Array<ContentActionRef> = []; createActions: Array<ContentActionRef> = [];
@ -136,6 +138,7 @@ export class ExtensionService implements RuleContext {
this.routes = this.loadRoutes(config); this.routes = this.loadRoutes(config);
this.toolbarActions = this.loadToolbarActions(config); this.toolbarActions = this.loadToolbarActions(config);
this.viewerToolbarActions = this.loadViewerToolbarActions(config); this.viewerToolbarActions = this.loadViewerToolbarActions(config);
this.viewerContentExtensions = this.loadViewerContentExtensions(config);
this.contextMenuActions = this.loadContextMenuActions(config); this.contextMenuActions = this.loadContextMenuActions(config);
this.openWithActions = this.loadViewerOpenWith(config); this.openWithActions = this.loadViewerOpenWith(config);
this.createActions = this.loadCreateActions(config); this.createActions = this.loadCreateActions(config);
@ -187,6 +190,15 @@ export class ExtensionService implements RuleContext {
return []; return [];
} }
protected loadViewerContentExtensions(config: ExtensionConfig): Array<ViewerExtensionRef> {
if (config && config.features && config.features.viewer) {
return (config.features.viewer.content || [])
.filter(entry => !entry.disabled)
.sort(this.sortByOrder);
}
return [];
}
protected loadContextMenuActions(config: ExtensionConfig): Array<ContentActionRef> { protected loadContextMenuActions(config: ExtensionConfig): Array<ContentActionRef> {
if (config && config.features && config.features.contextMenu) { if (config && config.features && config.features.contextMenu) {
return (config.features.contextMenu || []) return (config.features.contextMenu || [])

View File

@ -0,0 +1,33 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export interface ViewerExtensionRef {
id: string;
fileExtension: string;
component: string;
disabled?: boolean;
order?: number;
}

View File

@ -725,6 +725,20 @@
"visible": "app.toolbar.permissions" "visible": "app.toolbar.permissions"
} }
} }
],
"content": [
{
"id": "app.viewer.pdf",
"disabled": true,
"fileExtension": "pdf",
"component": "app.components.tabs.metadata"
},
{
"id": "app.viewer.docx",
"disabled": true,
"fileExtension": "docx",
"component": "app.components.tabs.comments"
}
] ]
}, },
"sidebar": [ "sidebar": [

View File

@ -268,6 +268,32 @@
} }
} }
} }
},
"viewerExtensionRef": {
"type": "object",
"required": ["id", "component", "fileExtension"],
"properties": {
"id": {
"description": "Unique identifier for the navigation group",
"type": "string"
},
"component": {
"description": "Component id",
"type": "string"
},
"fileExtension": {
"description": "Target file extension",
"type": "string"
},
"order": {
"description": "Group order",
"type": "number"
},
"disabled": {
"description": "Toggles the disabled state",
"type": "boolean"
}
}
} }
}, },
@ -337,6 +363,12 @@
"type": "array", "type": "array",
"items": { "$ref": "#/definitions/contentActionRef" }, "items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1 "minItems": 1
},
"content": {
"description": "Viewer content extensions",
"type": "array",
"items": { "$ref": "#/definitions/viewerExtensionRef" },
"minItems": 1
} }
} }
}, },