mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[MNT-22613] Viewer wildcard extension (#7280)
* support viewer wildcard extensions * update docs * fix lint * fix template
This commit is contained in:
parent
2ff3298a85
commit
2bb7586f93
@ -96,6 +96,29 @@ You also need to provide a [viewer component](../../core/components/viewer.compo
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use `*` wildcard to register a single component that opens all files:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$version": "1.0.0",
|
||||||
|
"$name": "my viewer extension",
|
||||||
|
"$description": "my viewer plugin",
|
||||||
|
"features": {
|
||||||
|
"viewer": {
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"id": "dev.tools.viewer.viewer",
|
||||||
|
"fileExtension": ["*"],
|
||||||
|
"component": "your-extension.main.component"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> It is recommended to use wildcard replacement only when introducing your own Viewer implementation.
|
||||||
|
|
||||||
See the [App extensions](../../user-guide/app-extensions.md) page for
|
See the [App extensions](../../user-guide/app-extensions.md) page for
|
||||||
further details of how to develop extensions.
|
further details of how to develop extensions.
|
||||||
|
|
||||||
|
@ -199,8 +199,17 @@
|
|||||||
fxFlexOrder="1"
|
fxFlexOrder="1"
|
||||||
fxFlex="1 1 auto">
|
fxFlex="1 1 auto">
|
||||||
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
|
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
|
||||||
<div class="adf-viewer-content-container"
|
<div class="adf-viewer-content-container" [ngSwitch]="viewerType">
|
||||||
[ngSwitch]="viewerType">
|
<ng-container *ngSwitchCase="'external'">
|
||||||
|
<adf-preview-extension
|
||||||
|
*ngIf="!!externalViewer"
|
||||||
|
[id]="externalViewer.component"
|
||||||
|
[node]="nodeEntry?.entry"
|
||||||
|
[url]="urlFileContent"
|
||||||
|
[extension]="externalViewer.fileExtension"
|
||||||
|
[attr.data-automation-id]="externalViewer.component">
|
||||||
|
</adf-preview-extension>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'pdf'">
|
<ng-container *ngSwitchCase="'pdf'">
|
||||||
<adf-pdf-viewer (close)="onBackButtonClick()"
|
<adf-pdf-viewer (close)="onBackButtonClick()"
|
||||||
|
@ -33,6 +33,7 @@ import { MatDialog } from '@angular/material/dialog';
|
|||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { UploadService } from '../../services/upload.service';
|
import { UploadService } from '../../services/upload.service';
|
||||||
import { FileModel } from '../../models';
|
import { FileModel } from '../../models';
|
||||||
|
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-viewer-container-toolbar',
|
selector: 'adf-viewer-container-toolbar',
|
||||||
@ -137,6 +138,7 @@ describe('ViewerComponent', () => {
|
|||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
let uploadService: UploadService;
|
let uploadService: UploadService;
|
||||||
|
let extensionService: AppExtensionService;
|
||||||
|
|
||||||
setupTestBed({
|
setupTestBed({
|
||||||
imports: [
|
imports: [
|
||||||
@ -173,12 +175,65 @@ describe('ViewerComponent', () => {
|
|||||||
uploadService = TestBed.inject(UploadService);
|
uploadService = TestBed.inject(UploadService);
|
||||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||||
dialog = TestBed.inject(MatDialog);
|
dialog = TestBed.inject(MatDialog);
|
||||||
|
extensionService = TestBed.inject(AppExtensionService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Extension Type Test', () => {
|
describe('Extension Type Test', () => {
|
||||||
|
it('should use external viewer via wildcard notation', async () => {
|
||||||
|
const extension: ViewerExtensionRef = {
|
||||||
|
component: 'custom.component',
|
||||||
|
id: 'custom.component.id',
|
||||||
|
fileExtension: '*'
|
||||||
|
};
|
||||||
|
spyOn(extensionService, 'getViewerExtensions').and.returnValue([extension]);
|
||||||
|
|
||||||
afterEach(() => {
|
fixture = TestBed.createComponent(ViewerComponent);
|
||||||
fixture.destroy();
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(component.externalExtensions.includes('*')).toBe(true);
|
||||||
|
expect(component.externalViewer).toBe(extension);
|
||||||
|
expect(component.viewerType).toBe('external');
|
||||||
|
expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use first external viewer provided', async () => {
|
||||||
|
const extensions: ViewerExtensionRef[] = [
|
||||||
|
{
|
||||||
|
component: 'custom.component.1',
|
||||||
|
id: 'custom.component.id',
|
||||||
|
fileExtension: '*'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'custom.component.2',
|
||||||
|
id: 'custom.component.id',
|
||||||
|
fileExtension: '*'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
spyOn(extensionService, 'getViewerExtensions').and.returnValue(extensions);
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ViewerComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(element.querySelector('[data-automation-id="custom.component.1"]')).not.toBeNull();
|
||||||
|
expect(element.querySelector('[data-automation-id="custom.component.2"]')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extension file pdf be loaded', (done) => {
|
it('should extension file pdf be loaded', (done) => {
|
||||||
@ -244,11 +299,6 @@ describe('ViewerComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('MimeType handling', () => {
|
describe('MimeType handling', () => {
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
fixture.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display an image file identified by mimetype when the filename has no extension', (done) => {
|
it('should display an image file identified by mimetype when the filename has no extension', (done) => {
|
||||||
component.urlFile = 'fake-content-img';
|
component.urlFile = 'fake-content-img';
|
||||||
component.mimeType = 'image/png';
|
component.mimeType = 'image/png';
|
||||||
|
@ -225,17 +225,39 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
versionEntry: VersionEntry;
|
versionEntry: VersionEntry;
|
||||||
|
|
||||||
extensionTemplates: { template: TemplateRef<any>, isVisible: boolean }[] = [];
|
extensionTemplates: { template: TemplateRef<any>, isVisible: boolean }[] = [];
|
||||||
externalExtensions: string[] = [];
|
|
||||||
urlFileContent: string;
|
urlFileContent: string;
|
||||||
otherMenu: any;
|
otherMenu: any;
|
||||||
extension: string;
|
extension: string;
|
||||||
sidebarRightTemplateContext: { node: Node } = { node: null };
|
sidebarRightTemplateContext: { node: Node } = { node: null };
|
||||||
sidebarLeftTemplateContext: { node: Node } = { node: null };
|
sidebarLeftTemplateContext: { node: Node } = { node: null };
|
||||||
fileTitle: string;
|
fileTitle: string;
|
||||||
viewerExtensions: Array<ViewerExtensionRef> = [];
|
|
||||||
|
/**
|
||||||
|
* Returns a list of the active Viewer content extensions.
|
||||||
|
*/
|
||||||
|
get viewerExtensions(): ViewerExtensionRef[] {
|
||||||
|
return this.extensionService.getViewerExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of file extensions supported by external plugins.
|
||||||
|
*/
|
||||||
|
get externalExtensions(): string[] {
|
||||||
|
return this.viewerExtensions.map(ext => ext.fileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _externalViewer: ViewerExtensionRef;
|
||||||
|
get externalViewer(): ViewerExtensionRef {
|
||||||
|
if (!this._externalViewer) {
|
||||||
|
this._externalViewer = this.viewerExtensions.find(ext => ext.fileExtension === '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._externalViewer;
|
||||||
|
}
|
||||||
|
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
|
|
||||||
private cacheBusterNumber;
|
private cacheBusterNumber: number;
|
||||||
cacheTypeForContent = '';
|
cacheTypeForContent = '';
|
||||||
|
|
||||||
// Extensions that are supported by the Viewer without conversion
|
// Extensions that are supported by the Viewer without conversion
|
||||||
@ -316,18 +338,9 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.closeOverlayManager();
|
this.closeOverlayManager();
|
||||||
this.loadExtensions();
|
|
||||||
this.cacheTypeForContent = '';
|
this.cacheTypeForContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadExtensions() {
|
|
||||||
this.viewerExtensions = this.extensionService.getViewerExtensions();
|
|
||||||
this.viewerExtensions
|
|
||||||
.forEach((extension: ViewerExtensionRef) => {
|
|
||||||
this.externalExtensions.push(extension.fileExtension);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private getNodeVersionProperty(node: Node): string {
|
private getNodeVersionProperty(node: Node): string {
|
||||||
return node?.properties['cm:versionLabel'] ?? '';
|
return node?.properties['cm:versionLabel'] ?? '';
|
||||||
}
|
}
|
||||||
@ -427,13 +440,8 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
this.fileTitle = this.getDisplayName(filenameFromUrl);
|
this.fileTitle = this.getDisplayName(filenameFromUrl);
|
||||||
this.extension = this.getFileExtension(filenameFromUrl);
|
this.extension = this.getFileExtension(filenameFromUrl);
|
||||||
this.urlFileContent = this.urlFile;
|
this.urlFileContent = this.urlFile;
|
||||||
|
|
||||||
this.fileName = this.displayName;
|
this.fileName = this.displayName;
|
||||||
|
this.viewerType = this.urlFileViewer || this.getViewerType(this.extension, this.mimeType);
|
||||||
this.viewerType = this.urlFileViewer || this.getViewerTypeByExtension(this.extension);
|
|
||||||
if (this.viewerType === 'unknown') {
|
|
||||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extensionChange.emit(this.extension);
|
this.extensionChange.emit(this.extension);
|
||||||
this.scrollTop();
|
this.scrollTop();
|
||||||
@ -442,8 +450,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
private async setUpNodeFile(nodeData: Node, versionData?: Version) {
|
private async setUpNodeFile(nodeData: Node, versionData?: Version) {
|
||||||
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');
|
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');
|
||||||
|
|
||||||
let setupNode;
|
|
||||||
|
|
||||||
if (versionData && versionData.content) {
|
if (versionData && versionData.content) {
|
||||||
this.mimeType = versionData.content.mimeType;
|
this.mimeType = versionData.content.mimeType;
|
||||||
} else if (nodeData.content) {
|
} else if (nodeData.content) {
|
||||||
@ -461,13 +467,10 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
this.urlFileContent + '&' + currentFileVersion;
|
this.urlFileContent + '&' + currentFileVersion;
|
||||||
|
|
||||||
this.extension = this.getFileExtension(versionData ? versionData.name : nodeData.name);
|
this.extension = this.getFileExtension(versionData ? versionData.name : nodeData.name);
|
||||||
|
|
||||||
this.fileName = versionData ? versionData.name : nodeData.name;
|
this.fileName = versionData ? versionData.name : nodeData.name;
|
||||||
|
this.viewerType = this.getViewerType(this.extension, this.mimeType);
|
||||||
|
|
||||||
this.viewerType = this.getViewerTypeByExtension(this.extension);
|
let setupNode: Promise<void>;
|
||||||
if (this.viewerType === 'unknown') {
|
|
||||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.viewerType === 'unknown') {
|
if (this.viewerType === 'unknown') {
|
||||||
if (versionData) {
|
if (versionData) {
|
||||||
@ -485,18 +488,23 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
return setupNode;
|
return setupNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getViewerType(extension: string, mimeType: string): string {
|
||||||
|
let viewerType = this.getViewerTypeByExtension(extension);
|
||||||
|
|
||||||
|
if (viewerType === 'unknown') {
|
||||||
|
viewerType = this.getViewerTypeByMimeType(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewerType;
|
||||||
|
}
|
||||||
|
|
||||||
private setUpSharedLinkFile(details: any) {
|
private setUpSharedLinkFile(details: any) {
|
||||||
this.mimeType = details.entry.content.mimeType;
|
this.mimeType = details.entry.content.mimeType;
|
||||||
this.fileTitle = this.getDisplayName(details.entry.name);
|
this.fileTitle = this.getDisplayName(details.entry.name);
|
||||||
this.extension = this.getFileExtension(details.entry.name);
|
this.extension = this.getFileExtension(details.entry.name);
|
||||||
this.fileName = details.entry.name;
|
this.fileName = details.entry.name;
|
||||||
|
|
||||||
this.urlFileContent = this.contentApi.getSharedLinkContentUrl(this.sharedLinkId, false);
|
this.urlFileContent = this.contentApi.getSharedLinkContentUrl(this.sharedLinkId, false);
|
||||||
|
this.viewerType = this.getViewerType(this.extension, this.mimeType);
|
||||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
|
||||||
if (this.viewerType === 'unknown') {
|
|
||||||
this.viewerType = this.getViewerTypeByExtension(this.extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.viewerType === 'unknown') {
|
if (this.viewerType === 'unknown') {
|
||||||
this.displaySharedLinkRendition(this.sharedLinkId);
|
this.displaySharedLinkRendition(this.sharedLinkId);
|
||||||
@ -552,6 +560,10 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
extension = extension.toLowerCase();
|
extension = extension.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isExternalViewer()) {
|
||||||
|
return 'external';
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isCustomViewerExtension(extension)) {
|
if (this.isCustomViewerExtension(extension)) {
|
||||||
return 'custom';
|
return 'custom';
|
||||||
}
|
}
|
||||||
@ -628,8 +640,12 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isExternalViewer(): boolean {
|
||||||
|
return !!this.viewerExtensions.find(ext => ext.fileExtension === '*');
|
||||||
|
}
|
||||||
|
|
||||||
isCustomViewerExtension(extension: string): boolean {
|
isCustomViewerExtension(extension: string): boolean {
|
||||||
const extensions: any = this.externalExtensions || [];
|
const extensions = this.externalExtensions || [];
|
||||||
|
|
||||||
if (extension && extensions.length > 0) {
|
if (extension && extensions.length > 0) {
|
||||||
extension = extension.toLowerCase();
|
extension = extension.toLowerCase();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user