Skip to content

Commit

Permalink
[MNT-22613] Viewer wildcard extension (#7280)
Browse files Browse the repository at this point in the history
* support viewer wildcard extensions

* update docs

* fix lint

* fix template
  • Loading branch information
DenysVuika committed Oct 5, 2021
1 parent 2ff3298 commit 2bb7586
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 41 deletions.
23 changes: 23 additions & 0 deletions docs/extensions/components/preview-extension.component.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
further details of how to develop extensions.

Expand Down
13 changes: 11 additions & 2 deletions lib/core/viewer/components/viewer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,17 @@ <h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
fxFlexOrder="1"
fxFlex="1 1 auto">
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-content-container"
[ngSwitch]="viewerType">
<div class="adf-viewer-content-container" [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'">
<adf-pdf-viewer (close)="onBackButtonClick()"
Expand Down
64 changes: 57 additions & 7 deletions lib/core/viewer/components/viewer.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { MatDialog } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { UploadService } from '../../services/upload.service';
import { FileModel } from '../../models';
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';

@Component({
selector: 'adf-viewer-container-toolbar',
Expand Down Expand Up @@ -137,6 +138,7 @@ describe('ViewerComponent', () => {
let element: HTMLElement;
let dialog: MatDialog;
let uploadService: UploadService;
let extensionService: AppExtensionService;

setupTestBed({
imports: [
Expand Down Expand Up @@ -173,12 +175,65 @@ describe('ViewerComponent', () => {
uploadService = TestBed.inject(UploadService);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
dialog = TestBed.inject(MatDialog);
extensionService = TestBed.inject(AppExtensionService);
});

afterEach(() => {
fixture.destroy();
});

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]);

fixture = TestBed.createComponent(ViewerComponent);
element = fixture.nativeElement;
component = fixture.componentInstance;

component.urlFile = 'fake-test-file.pdf';
component.ngOnChanges();

fixture.detectChanges();
await fixture.whenStable();

afterEach(() => {
fixture.destroy();
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) => {
Expand Down Expand Up @@ -244,11 +299,6 @@ describe('ViewerComponent', () => {
});

describe('MimeType handling', () => {

afterEach(() => {
fixture.destroy();
});

it('should display an image file identified by mimetype when the filename has no extension', (done) => {
component.urlFile = 'fake-content-img';
component.mimeType = 'image/png';
Expand Down
80 changes: 48 additions & 32 deletions lib/core/viewer/components/viewer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,39 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
versionEntry: VersionEntry;

extensionTemplates: { template: TemplateRef<any>, isVisible: boolean }[] = [];
externalExtensions: string[] = [];
urlFileContent: string;
otherMenu: any;
extension: string;
sidebarRightTemplateContext: { node: Node } = { node: null };
sidebarLeftTemplateContext: { node: Node } = { node: null };
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;

private cacheBusterNumber;
private cacheBusterNumber: number;
cacheTypeForContent = '';

// Extensions that are supported by the Viewer without conversion
Expand Down Expand Up @@ -316,18 +338,9 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
});

this.closeOverlayManager();
this.loadExtensions();
this.cacheTypeForContent = '';
}

private loadExtensions() {
this.viewerExtensions = this.extensionService.getViewerExtensions();
this.viewerExtensions
.forEach((extension: ViewerExtensionRef) => {
this.externalExtensions.push(extension.fileExtension);
});
}

private getNodeVersionProperty(node: Node): string {
return node?.properties['cm:versionLabel'] ?? '';
}
Expand Down Expand Up @@ -427,13 +440,8 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
this.fileTitle = this.getDisplayName(filenameFromUrl);
this.extension = this.getFileExtension(filenameFromUrl);
this.urlFileContent = this.urlFile;

this.fileName = this.displayName;

this.viewerType = this.urlFileViewer || this.getViewerTypeByExtension(this.extension);
if (this.viewerType === 'unknown') {
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
}
this.viewerType = this.urlFileViewer || this.getViewerType(this.extension, this.mimeType);

this.extensionChange.emit(this.extension);
this.scrollTop();
Expand All @@ -442,8 +450,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
private async setUpNodeFile(nodeData: Node, versionData?: Version) {
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');

let setupNode;

if (versionData && versionData.content) {
this.mimeType = versionData.content.mimeType;
} else if (nodeData.content) {
Expand All @@ -461,13 +467,10 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
this.urlFileContent + '&' + currentFileVersion;

this.extension = this.getFileExtension(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);
if (this.viewerType === 'unknown') {
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
}
let setupNode: Promise<void>;

if (this.viewerType === 'unknown') {
if (versionData) {
Expand All @@ -485,18 +488,23 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
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) {
this.mimeType = details.entry.content.mimeType;
this.fileTitle = this.getDisplayName(details.entry.name);
this.extension = this.getFileExtension(details.entry.name);
this.fileName = details.entry.name;

this.urlFileContent = this.contentApi.getSharedLinkContentUrl(this.sharedLinkId, false);

this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
if (this.viewerType === 'unknown') {
this.viewerType = this.getViewerTypeByExtension(this.extension);
}
this.viewerType = this.getViewerType(this.extension, this.mimeType);

if (this.viewerType === 'unknown') {
this.displaySharedLinkRendition(this.sharedLinkId);
Expand Down Expand Up @@ -552,6 +560,10 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
extension = extension.toLowerCase();
}

if (this.isExternalViewer()) {
return 'external';
}

if (this.isCustomViewerExtension(extension)) {
return 'custom';
}
Expand Down Expand Up @@ -628,8 +640,12 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
return null;
}

private isExternalViewer(): boolean {
return !!this.viewerExtensions.find(ext => ext.fileExtension === '*');
}

isCustomViewerExtension(extension: string): boolean {
const extensions: any = this.externalExtensions || [];
const extensions = this.externalExtensions || [];

if (extension && extensions.length > 0) {
extension = extension.toLowerCase();
Expand Down

0 comments on commit 2bb7586

Please sign in to comment.