From 7c09fb1fb9bce5ea6838d7304c0cb6346fd84346 Mon Sep 17 00:00:00 2001 From: Urse Daniel Date: Mon, 27 Jul 2020 11:29:29 +0300 Subject: [PATCH] added functionality to view a previous version (#5913) --- demo-shell/src/app/app.routes.ts | 13 ++ .../file-view/file-view.component.html | 4 +- .../file-view/file-view.component.ts | 6 + ...sion-manager-dialog-adapter.component.html | 4 +- ...ersion-manager-dialog-adapter.component.ts | 9 +- .../src/app/services/preview.service.ts | 8 +- .../components/version-list.component.md | 2 + .../components/version-manager.component.md | 1 + docs/core/components/viewer.component.md | 1 + .../directives/node-download.directive.md | 4 +- .../version/version-actions.e2e.ts | 16 +- e2e/core/pages/version-manager.page.ts | 8 +- .../viewer-custom-toolbar-info-drawer.e2e.ts | 27 +++- lib/content-services/src/lib/i18n/en.json | 1 + .../version-list.component.html | 8 + .../version-list.component.spec.ts | 7 + .../version-manager/version-list.component.ts | 12 ++ .../version-manager.component.html | 3 +- .../version-manager.component.spec.ts | 7 + .../version-manager.component.ts | 8 + .../node-download.directive.spec.ts | 19 ++- .../directives/node-download.directive.ts | 16 +- .../viewer/components/viewer.component.html | 3 +- .../viewer/components/viewer.component.ts | 139 ++++++------------ lib/core/viewer/services/view-util.service.ts | 118 +++++++++++++++ lib/testing/src/lib/core/pages/viewer.page.ts | 4 + 26 files changed, 337 insertions(+), 111 deletions(-) diff --git a/demo-shell/src/app/app.routes.ts b/demo-shell/src/app/app.routes.ts index c6642140f1..640ef0bbb2 100644 --- a/demo-shell/src/app/app.routes.ts +++ b/demo-shell/src/app/app.routes.ts @@ -74,6 +74,19 @@ export const appRoutes: Routes = [ } ] }, + { + path: 'files/:nodeId/:versionId/view', + component: AppComponent, + canActivate: [AuthGuardEcm], + canActivateChild: [AuthGuardEcm], + outlet: 'overlay', + children: [ + { + path: '', + loadChildren: () => import('./components/file-view/file-view.module').then(m => m.FileViewModule) + } + ] + }, { path: 'preview/blob', component: AppComponent, diff --git a/demo-shell/src/app/components/file-view/file-view.component.html b/demo-shell/src/app/components/file-view/file-view.component.html index 2a51337d31..82c6fe3746 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.html +++ b/demo-shell/src/app/components/file-view/file-view.component.html @@ -108,7 +108,8 @@ + (uploadError)="onUploadError($event)" + (viewVersion)="onViewVersion($event)"> @@ -318,6 +319,7 @@ { const id = params.nodeId; + this.versionId = params.versionId; if (id) { this.nodeApiService.getNode(id).subscribe( (node) => { @@ -92,6 +94,10 @@ export class FileViewComponent implements OnInit { }); } + onViewVersion(versionId: string) { + this.preview.showResource(this.nodeId, versionId); + } + onViewerVisibilityChanged() { const primaryUrl = this.router.parseUrl(this.router.url).root.children[PRIMARY_OUTLET].toString(); this.router.navigateByUrl(primaryUrl); diff --git a/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.html b/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.html index 1e1d692125..a5c2182142 100644 --- a/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.html +++ b/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.html @@ -32,7 +32,9 @@ [newFileVersion]="newFileVersion" [allowDownload]="allowDownload" [showComments]="showComments" - (uploadError)="uploadError($event)"> + (uploadError)="uploadError($event)" + (viewVersion)="onViewVersion($event)" + >
diff --git a/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.ts b/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.ts index 7968c0b19b..86b007606c 100644 --- a/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.ts +++ b/demo-shell/src/app/components/files/version-manager-dialog-adapter.component.ts @@ -19,6 +19,7 @@ import { Component, Inject, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MinimalNodeEntryEntity } from '@alfresco/js-api'; import { MatSnackBar } from '@angular/material/snack-bar'; +import { PreviewService } from '../../services/preview.service'; @Component({ templateUrl: './version-manager-dialog-adapter.component.html', @@ -34,7 +35,8 @@ export class VersionManagerDialogAdapterComponent { readOnly = false; showVersionComparison = false; - constructor(@Inject(MAT_DIALOG_DATA) data: any, + constructor(private previewService: PreviewService, + @Inject(MAT_DIALOG_DATA) data: any, private snackBar: MatSnackBar, private containingDialog?: MatDialogRef) { this.contentEntry = data.contentEntry; @@ -51,6 +53,11 @@ export class VersionManagerDialogAdapterComponent { this.containingDialog.close(); } + onViewVersion(versionId: string) { + this.previewService.showResource(this.contentEntry.id, versionId); + this.close(); + } + hideVersionComparison(isCancelled: boolean | Node) { if (isCancelled) { this.showVersionComparison = false; diff --git a/demo-shell/src/app/services/preview.service.ts b/demo-shell/src/app/services/preview.service.ts index 9e06247d57..aa9dfc057d 100644 --- a/demo-shell/src/app/services/preview.service.ts +++ b/demo-shell/src/app/services/preview.service.ts @@ -26,8 +26,12 @@ export class PreviewService { constructor(private router: Router) {} - showResource(resourceId): void { - this.router.navigate([{ outlets: { overlay: ['files', resourceId, 'view'] } }]); + showResource(resourceId, versionId?): void { + if (versionId) { + this.router.navigate([{outlets: {overlay: ['files', resourceId, versionId, 'view']}}]); + } else { + this.router.navigate([{outlets: {overlay: ['files', resourceId, 'view']}}]); + } } showBlob(name: string, content: Blob): void { diff --git a/docs/content-services/components/version-list.component.md b/docs/content-services/components/version-list.component.md index 7c5d0dc6cb..21e80c5eb9 100644 --- a/docs/content-services/components/version-list.component.md +++ b/docs/content-services/components/version-list.component.md @@ -22,6 +22,7 @@ Displays the version history of a node in a [Version Manager component](version- | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | | allowDownload | `boolean` | true | Enable/disable downloading a version of the current node. | +| allowViewVersions | `boolean` | true | Enable/disable viewing a version of the current node. | | node | [`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) | | The target node. | | showActions | `boolean` | true | Toggles showing/hiding of version actions | | showComments | `boolean` | true | Toggles showing/hiding of comments | @@ -32,6 +33,7 @@ Displays the version history of a node in a [Version Manager component](version- | ---- | ---- | ----------- | | deleted | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md)`>` | Emitted when a version is deleted | | restored | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md)`>` | Emitted when a version is restored | +| viewVersion | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when viewing a version | ## Details diff --git a/docs/content-services/components/version-manager.component.md b/docs/content-services/components/version-manager.component.md index 332abcfbe7..f6bbdf365f 100644 --- a/docs/content-services/components/version-manager.component.md +++ b/docs/content-services/components/version-manager.component.md @@ -42,6 +42,7 @@ Displays the version history of a node with the ability to upload a new version. | uploadCancel | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when an cancelling during upload. | | uploadError | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md)`>` | Emitted when an error occurs during upload. | | uploadSuccess | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md)`>` | Emitted when a file is uploaded successfully. | +| viewVersion | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when viewing a version. | ## Details diff --git a/docs/core/components/viewer.component.md b/docs/core/components/viewer.component.md index 99a7001bbc..0e07ebdc8f 100644 --- a/docs/core/components/viewer.component.md +++ b/docs/core/components/viewer.component.md @@ -103,6 +103,7 @@ See the [Custom layout](#custom-layout) section for full details of all availabl | thumbnailsTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`` | null | The template for the pdf thumbnails. | | urlFile | `string` | "" | If you want to load an external file that does not come from ACS you can use this URL to specify where to load the file from. | | urlFileViewer | `string` | null | Viewer to use with the `urlFile` address (`pdf`, `image`, `media`, `text`). Used when `urlFile` has no filename and extension. | +| versionId | `string` | null | Version Id of the file to load. | ### Events diff --git a/docs/core/directives/node-download.directive.md b/docs/core/directives/node-download.directive.md index cc3569657b..7a1401dedd 100644 --- a/docs/core/directives/node-download.directive.md +++ b/docs/core/directives/node-download.directive.md @@ -14,7 +14,8 @@ Allows folders and/or files to be downloaded, with multiple nodes packed as a '. ```html @@ -31,3 +32,4 @@ Allows folders and/or files to be downloaded, with multiple nodes packed as a '. | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | | nodes | [`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)` \| `[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`[]` | | Nodes to download. | +| version | [`VersionEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/VersionEntry.md) | | [Node's](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md) version to download. | diff --git a/e2e/content-services/version/version-actions.e2e.ts b/e2e/content-services/version/version-actions.e2e.ts index 5e6cc1d203..5eb3d7443c 100644 --- a/e2e/content-services/version/version-actions.e2e.ts +++ b/e2e/content-services/version/version-actions.e2e.ts @@ -23,7 +23,8 @@ import { LoginPage, UploadActions, UserModel, - UsersActions + UsersActions, + ViewerPage } from '@alfresco/adf-testing'; import { browser, by, element } from 'protractor'; import { FileModel } from '../../models/ACS/file.model'; @@ -41,6 +42,7 @@ describe('Version component actions', () => { const uploadDialog = new UploadDialogPage(); const apiService = new ApiService(); const usersActions = new UsersActions(apiService); + const viewerPage = new ViewerPage(); let acsUser: UserModel; @@ -151,6 +153,18 @@ describe('Version component actions', () => { await contentServicesPage.checkContentIsDisplayed(txtFileModel.name); }); + it('[C362240] Should be possible to view a previous document version', async () => { + await contentServicesPage.versionManagerContent(fileModelVersionTwo.name); + await versionManagePage.viewFileVersion('1.0'); + await viewerPage.expectUrlToContain('1.0'); + }); + + it('[C362241] Should be possible to download a previous document version', async () => { + await viewerPage.clickDownloadButton(); + await FileBrowserUtil.isFileDownloaded(fileModelVersionTwo.name); + await viewerPage.clickCloseButton(); + }); + it('[C307033] Should be possible to cancel the upload of a new version', async () => { await browser.refresh(); await contentServicesPage.versionManagerContent(txtFileModel.name); diff --git a/e2e/core/pages/version-manager.page.ts b/e2e/core/pages/version-manager.page.ts index 907464eb4d..9f1054acfd 100644 --- a/e2e/core/pages/version-manager.page.ts +++ b/e2e/core/pages/version-manager.page.ts @@ -17,7 +17,7 @@ import * as path from 'path'; import { BrowserActions, BrowserVisibility, TogglePage } from '@alfresco/adf-testing'; -import { browser, by, element } from 'protractor'; +import { browser, by, element, ElementFinder } from 'protractor'; export class VersionManagePage { @@ -173,6 +173,12 @@ export class VersionManagePage { await BrowserActions.click(restoreButton); } + async viewFileVersion(version): Promise { + await this.clickActionButton(version); + const viewButton: ElementFinder = element(by.id(`adf-version-list-action-view-${version}`)); + await BrowserActions.click(viewButton); + } + async checkActionsArePresent(version: string): Promise { await BrowserVisibility.waitUntilElementIsVisible(element(by.id(`adf-version-list-action-download-${version}`))); await BrowserVisibility.waitUntilElementIsVisible(element(by.id(`adf-version-list-action-delete-${version}`))); diff --git a/e2e/core/viewer/viewer-custom-toolbar-info-drawer.e2e.ts b/e2e/core/viewer/viewer-custom-toolbar-info-drawer.e2e.ts index 8b3ff66d05..04d508f374 100644 --- a/e2e/core/viewer/viewer-custom-toolbar-info-drawer.e2e.ts +++ b/e2e/core/viewer/viewer-custom-toolbar-info-drawer.e2e.ts @@ -16,10 +16,11 @@ */ import { browser } from 'protractor'; -import { ApiService, LoginPage, UploadActions, UserModel, UsersActions, ViewerPage } from '@alfresco/adf-testing'; +import { ApiService, BrowserActions, FileBrowserUtil, LoginPage, UploadActions, UserModel, UsersActions, ViewerPage } from '@alfresco/adf-testing'; import { ContentServicesPage } from '../../core/pages/content-services.page'; import { FileModel } from '../../models/ACS/file.model'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; +import { VersionManagePage } from '../pages/version-manager.page'; describe('Viewer', () => { @@ -32,6 +33,7 @@ describe('Viewer', () => { const uploadActions = new UploadActions(apiService); const usersActions = new UsersActions(apiService); + const versionManagePage = new VersionManagePage(); const acsUser = new UserModel(); let txtFileUploaded; @@ -40,6 +42,11 @@ describe('Viewer', () => { 'location': browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_path }); + const fileModelVersionTwo = new FileModel({ + 'name': browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_name, + 'location': browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_location + }); + beforeAll(async () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await usersActions.createUser(acsUser); @@ -84,4 +91,22 @@ describe('Viewer', () => { await viewerPage.clickOnTab('Versions'); await viewerPage.checkTabIsActive('Versions'); }); + + it('[C362242] Should the Viewer be able to view a previous version of a file', async () => { + await viewerPage.clickCloseButton(); + await contentServicesPage.versionManagerContent(txtFileInfo.name); + await BrowserActions.click(versionManagePage.showNewVersionButton); + await versionManagePage.uploadNewVersionFile(fileModelVersionTwo.location); + await versionManagePage.closeVersionDialog(); + await contentServicesPage.doubleClickRow(txtFileUploaded.entry.name); + await viewerPage.clickInfoButton(); + await viewerPage.clickOnTab('Versions'); + await versionManagePage.viewFileVersion('1.0'); + await viewerPage.expectUrlToContain('1.0'); + }); + + it('[C362265] Should the Viewer be able to download a previous version of a file', async () => { + await viewerPage.clickDownloadButton(); + await FileBrowserUtil.isFileDownloaded(txtFileInfo.name); + }); }); diff --git a/lib/content-services/src/lib/i18n/en.json b/lib/content-services/src/lib/i18n/en.json index 27022215bc..a441716bc6 100644 --- a/lib/content-services/src/lib/i18n/en.json +++ b/lib/content-services/src/lib/i18n/en.json @@ -4,6 +4,7 @@ "RESTORE": "Restore", "DELETE": "Delete", "DOWNLOAD": "Download", + "VIEW": "View", "UPLOAD": { "TITLE": "Upload new version", "TOOLTIP": "Restriction: you must upload a file with the same name to create a new version of it", diff --git a/lib/content-services/src/lib/version-manager/version-list.component.html b/lib/content-services/src/lib/version-manager/version-list.component.html index 0f6ac19452..d3a908b4ef 100644 --- a/lib/content-services/src/lib/version-manager/version-list.component.html +++ b/lib/content-services/src/lib/version-manager/version-list.component.html @@ -12,6 +12,14 @@
+ + +
diff --git a/lib/content-services/src/lib/version-manager/version-manager.component.spec.ts b/lib/content-services/src/lib/version-manager/version-manager.component.spec.ts index 718ac0a735..3423413842 100644 --- a/lib/content-services/src/lib/version-manager/version-manager.component.spec.ts +++ b/lib/content-services/src/lib/version-manager/version-manager.component.spec.ts @@ -73,6 +73,13 @@ describe('VersionManagerComponent', () => { expect(component.uploadState).toBe('open'); }); + it('should be able to view a version', () => { + spyOn(component.viewVersion, 'emit'); + component.onViewVersion('1.0'); + fixture.detectChanges(); + expect(component.viewVersion.emit).toHaveBeenCalledWith('1.0'); + }); + it('should display comments for versions when not configured otherwise', async(() => { fixture.detectChanges(); fixture.whenStable().then(() => { diff --git a/lib/content-services/src/lib/version-manager/version-manager.component.ts b/lib/content-services/src/lib/version-manager/version-manager.component.ts index 845394ed8f..d4d572fcdf 100644 --- a/lib/content-services/src/lib/version-manager/version-manager.component.ts +++ b/lib/content-services/src/lib/version-manager/version-manager.component.ts @@ -75,6 +75,10 @@ export class VersionManagerComponent implements OnInit { @Output() uploadCancel: EventEmitter = new EventEmitter(); + /** Emitted when viewing a version. */ + @Output() + viewVersion: EventEmitter = new EventEmitter(); + @ViewChild('versionList', { static: true }) versionListComponent: VersionListComponent; @@ -117,6 +121,10 @@ export class VersionManagerComponent implements OnInit { this.uploadCancel.emit(true); } + onViewVersion(versionId: string) { + this.viewVersion.emit(versionId); + } + toggleNewVersion() { this.uploadState = this.uploadState === 'open' ? 'close' : 'open'; } diff --git a/lib/core/directives/node-download.directive.spec.ts b/lib/core/directives/node-download.directive.spec.ts index e41255763e..68800aa8a1 100755 --- a/lib/core/directives/node-download.directive.spec.ts +++ b/lib/core/directives/node-download.directive.spec.ts @@ -26,10 +26,11 @@ import { CoreTestingModule } from '../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; @Component({ - template: '
' + template: '
' }) class TestComponent { selection; + version; } describe('NodeDownloadDirective', () => { @@ -91,6 +92,22 @@ describe('NodeDownloadDirective', () => { expect(contentService.getContentUrl).toHaveBeenCalledWith(node.entry.id, true); }); + it('should download selected node version as file', () => { + component.version = { + entry: { + id: '1.0' + } + }; + spyOn(contentService, 'getVersionContentUrl'); + const node = {entry: {id: 'node-id', isFile: true}}; + component.selection = [node]; + + fixture.detectChanges(); + element.triggerEventHandler('click', null); + + expect(contentService.getVersionContentUrl).toHaveBeenCalledWith(node.entry.id, '1.0', true); + }); + it('should download selected shared node as file', () => { spyOn(contentService, 'getContentUrl'); const node = { entry: { nodeId: 'shared-node-id', isFile: true } }; diff --git a/lib/core/directives/node-download.directive.ts b/lib/core/directives/node-download.directive.ts index 6ec248bdb2..47387c122a 100755 --- a/lib/core/directives/node-download.directive.ts +++ b/lib/core/directives/node-download.directive.ts @@ -19,7 +19,7 @@ import { Directive, Input, HostListener } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { DownloadZipDialogComponent } from '../dialogs/download-zip/download-zip.dialog'; -import { NodeEntry } from '@alfresco/js-api'; +import { NodeEntry, VersionEntry } from '@alfresco/js-api'; import { DownloadService } from '../services/download.service'; /** @@ -35,6 +35,10 @@ export class NodeDownloadDirective { @Input('adfNodeDownload') nodes: NodeEntry | NodeEntry[]; + /** Node's version to download. */ + @Input() + version: VersionEntry; + @HostListener('click') onClick() { this.downloadNodes(this.nodes); @@ -101,8 +105,14 @@ export class NodeDownloadDirective { // nodeId for Shared node const id = ( node.entry).nodeId || node.entry.id; - const url = contentApi.getContentUrl(id, true); - const fileName = node.entry.name; + let url, fileName; + if (this.version) { + url = contentApi.getVersionContentUrl(id, this.version.entry.id, true); + fileName = this.version.entry.name; + } else { + url = contentApi.getContentUrl(id, true); + fileName = node.entry.name; + } this.downloadService.downloadUrl(url, fileName); } diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index e8ac50229b..106d635655 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -82,7 +82,8 @@ [attr.aria-label]="'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate" title="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}" data-automation-id="adf-toolbar-download" - [adfNodeDownload]="nodeEntry"> + [adfNodeDownload]="nodeEntry" + [version]="versionEntry"> file_download diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index 158fd424b1..44ddc3858b 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -20,7 +20,7 @@ import { Input, OnChanges, Output, TemplateRef, ViewEncapsulation, OnInit, OnDestroy } from '@angular/core'; -import { RenditionPaging, SharedLinkEntry, Node, RenditionEntry, NodeEntry } from '@alfresco/js-api'; +import { SharedLinkEntry, Node, Version, RenditionEntry, NodeEntry, VersionEntry } from '@alfresco/js-api'; import { BaseEvent } from '../../events'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { LogService } from '../../services/log.service'; @@ -74,6 +74,10 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { @Input() nodeId: string = null; + /** Version Id of the file to load. */ + @Input() + versionId: string = null; + /** Shared link id (to display shared file). */ @Input() sharedLinkId: string = null; @@ -206,6 +210,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { viewerType = 'unknown'; isLoading = false; nodeEntry: NodeEntry; + versionEntry: VersionEntry; extensionTemplates: { template: TemplateRef, isVisible: boolean }[] = []; externalExtensions: string[] = []; @@ -255,6 +260,17 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { ).subscribe((node) => this.onNodeUpdated(node)) ); + this.subscriptions.push( + this.viewUtils.viewerTypeChange.subscribe((type: string) => { + this.viewerType = type; + }) + ); + this.subscriptions.push( + this.viewUtils.urlFileContentChange.subscribe((content: string) => { + this.urlFileContent = content; + }) + ); + this.loadExtensions(); } @@ -298,9 +314,20 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.apiService.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] }).then( (node: NodeEntry) => { this.nodeEntry = node; - this.setUpNodeFile(node.entry).then(() => { - this.isLoading = false; - }); + if (this.versionId) { + this.apiService.versionsApi.getVersion(this.nodeId, this.versionId).then( + (version: VersionEntry) => { + this.versionEntry = version; + this.setUpNodeFile(node.entry, version.entry).then(() => { + this.isLoading = false; + }); + } + ); + } else { + this.setUpNodeFile(node.entry).then(() => { + this.isLoading = false; + }); + } }, () => { this.isLoading = false; @@ -353,21 +380,23 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.scrollTop(); } - private async setUpNodeFile(data: Node) { + private async setUpNodeFile(nodeData: Node, versionData?: Version) { let setupNode; - if (data.content) { - this.mimeType = data.content.mimeType; + if (versionData && versionData.content) { + this.mimeType = versionData.content.mimeType; + } else if (nodeData.content) { + this.mimeType = nodeData.content.mimeType; } - this.fileTitle = this.getDisplayName(data.name); + this.fileTitle = this.getDisplayName(nodeData.name); - this.urlFileContent = this.apiService.contentApi.getContentUrl(data.id); + this.urlFileContent = versionData ? this.apiService.contentApi.getVersionContentUrl(this.nodeId, versionData.id) : this.urlFileContent = this.cacheBusterNumber ? this.urlFileContent + '&' + this.cacheBusterNumber : this.urlFileContent; - this.extension = this.getFileExtension(data.name); + this.extension = this.getFileExtension(versionData ? versionData.name : nodeData.name); - this.fileName = data.name; + this.fileName = versionData ? versionData.name : nodeData.name; this.viewerType = this.getViewerTypeByExtension(this.extension); if (this.viewerType === 'unknown') { @@ -375,12 +404,12 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { } if (this.viewerType === 'unknown') { - setupNode = this.displayNodeRendition(data.id); + setupNode = this.viewUtils.displayNodeRendition(nodeData.id, versionData ? versionData.id : undefined); } this.extensionChange.emit(this.extension); - this.sidebarRightTemplateContext.node = data; - this.sidebarLeftTemplateContext.node = data; + this.sidebarRightTemplateContext.node = nodeData; + this.sidebarLeftTemplateContext.node = nodeData; this.scrollTop(); return setupNode; @@ -603,25 +632,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { } } - private async displayNodeRendition(nodeId: string) { - try { - const rendition = await this.resolveRendition(nodeId, 'pdf'); - if (rendition) { - const renditionId = rendition.entry.id; - - if (renditionId === 'pdf') { - this.viewerType = 'pdf'; - } else if (renditionId === 'imgpreview') { - this.viewerType = 'image'; - } - - this.urlFileContent = this.apiService.contentApi.getRenditionUrl(nodeId, renditionId); - } - } catch (err) { - this.logService.error(err); - } - } - private async displaySharedLinkRendition(sharedId: string) { try { const rendition: RenditionEntry = await this.apiService.renditionsApi.getSharedLinkRendition(sharedId, 'pdf'); @@ -643,69 +653,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { } } - private async resolveRendition(nodeId: string, renditionId: string): Promise { - renditionId = renditionId.toLowerCase(); - - const supportedRendition: RenditionPaging = await this.apiService.renditionsApi.getRenditions(nodeId); - - let rendition: RenditionEntry = supportedRendition.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId); - if (!rendition) { - renditionId = 'imgpreview'; - rendition = supportedRendition.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId); - } - - if (rendition) { - const status: string = rendition.entry.status.toString(); - - if (status === 'NOT_CREATED') { - try { - await this.apiService.renditionsApi.createRendition(nodeId, { id: renditionId }).then(() => { - this.viewerType = 'in_creation'; - }); - rendition = await this.waitRendition(nodeId, renditionId); - } catch (err) { - this.logService.error(err); - } - } - } - - return rendition; - } - - private async waitRendition(nodeId: string, renditionId: string): Promise { - let currentRetry: number = 0; - return new Promise((resolve, reject) => { - const intervalId = setInterval(() => { - currentRetry++; - if (this.maxRetries >= currentRetry) { - this.apiService.renditionsApi.getRendition(nodeId, renditionId).then((rendition: RenditionEntry) => { - const status: string = rendition.entry.status.toString(); - if (status === 'CREATED') { - - if (renditionId === 'pdf') { - this.viewerType = 'pdf'; - } else if (renditionId === 'imgpreview') { - this.viewerType = 'image'; - } - - this.urlFileContent = this.apiService.contentApi.getRenditionUrl(nodeId, renditionId); - - clearInterval(intervalId); - return resolve(rendition); - } - }, () => { - this.viewerType = 'error_in_creation'; - return reject(); - }); - } else { - this.isLoading = false; - this.viewerType = 'error_in_creation'; - clearInterval(intervalId); - } - }, this.TRY_TIMEOUT); - }); - } - checkExtensions(extensionAllowed) { if (typeof extensionAllowed === 'string') { return this.extension.toLowerCase() === extensionAllowed.toLowerCase(); diff --git a/lib/core/viewer/services/view-util.service.ts b/lib/core/viewer/services/view-util.service.ts index 0b8a1dbb41..ff5c73cdb4 100644 --- a/lib/core/viewer/services/view-util.service.ts +++ b/lib/core/viewer/services/view-util.service.ts @@ -19,6 +19,7 @@ import { Injectable } from '@angular/core'; import { RenditionEntry, RenditionPaging } from '@alfresco/js-api'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { LogService } from '../../services/log.service'; +import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -54,6 +55,17 @@ export class ViewUtilService { media: ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/ogg', 'audio/wav'] }; + /** + * Timeout used for setInterval. + */ + TRY_TIMEOUT: number = 10000; + + /** + * Subscribers needed for ViewerComponent to update the viewerType and urlFileContent. + */ + viewerTypeChange: Subject = new Subject(); + urlFileContentChange: Subject = new Subject(); + constructor(private apiService: AlfrescoApiService, private logService: LogService) { } @@ -167,4 +179,110 @@ export class ViewUtilService { return new Promise((resolve) => resolve(rendition)); } + async displayNodeRendition(nodeId: string, versionId?: string) { + try { + const rendition = await this.resolveNodeRendition(nodeId, 'pdf', versionId); + if (rendition) { + const renditionId = rendition.entry.id; + + if (renditionId === 'pdf') { + this.viewerTypeChange.next('pdf'); + } else if (renditionId === 'imgpreview') { + this.viewerTypeChange.next('image'); + } + + const urlFileContent = versionId ? this.apiService.contentApi.getVersionRenditionUrl(nodeId, versionId, renditionId) : + this.apiService.contentApi.getRenditionUrl(nodeId, renditionId); + this.urlFileContentChange.next(urlFileContent); + } + } catch (err) { + this.logService.error(err); + } + } + + private async resolveNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise { + renditionId = renditionId.toLowerCase(); + + const supportedRendition: RenditionPaging = versionId ? await this.apiService.versionsApi.listVersionRenditions(nodeId, versionId) : + await this.apiService.renditionsApi.getRenditions(nodeId); + + let rendition: RenditionEntry = supportedRendition.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId); + if (!rendition) { + renditionId = 'imgpreview'; + rendition = supportedRendition.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId); + } + + if (rendition) { + const status: string = rendition.entry.status.toString(); + + if (status === 'NOT_CREATED') { + try { + if (versionId) { + await this.apiService.versionsApi.createVersionRendition(nodeId, versionId, {id: renditionId}).then(() => { + this.viewerTypeChange.next('in_creation'); + }); + } else { + await this.apiService.renditionsApi.createRendition(nodeId, {id: renditionId}).then(() => { + this.viewerTypeChange.next('in_creation'); + }); + } + rendition = await this.waitNodeRendition(nodeId, renditionId, versionId); + } catch (err) { + this.logService.error(err); + } + } + } + + return rendition; + } + + private async waitNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise { + let currentRetry: number = 0; + return new Promise((resolve, reject) => { + const intervalId = setInterval(() => { + currentRetry++; + if (this.maxRetries >= currentRetry) { + if (!versionId) { + this.apiService.versionsApi.getVersionRendition(nodeId, versionId, renditionId).then((rendition: RenditionEntry) => { + this.handleNodeRendition(rendition, nodeId, renditionId, versionId); + clearInterval(intervalId); + return resolve(rendition); + }, () => { + this.viewerTypeChange.next('error_in_creation'); + return reject(); + }); + } else { + this.apiService.renditionsApi.getRendition(nodeId, renditionId).then((rendition: RenditionEntry) => { + this.handleNodeRendition(rendition, nodeId, renditionId); + clearInterval(intervalId); + return resolve(rendition); + }, () => { + this.viewerTypeChange.next('error_in_creation'); + return reject(); + }); + } + } else { + this.viewerTypeChange.next('error_in_creation'); + clearInterval(intervalId); + } + }, this.TRY_TIMEOUT); + }); + } + + private async handleNodeRendition(rendition: RenditionEntry, nodeId: string, renditionId: string, versionId?: string) { + const status: string = rendition.entry.status.toString(); + if (status === 'CREATED') { + + if (renditionId === 'pdf') { + this.viewerTypeChange.next('pdf'); + } else if (renditionId === 'imgpreview') { + this.viewerTypeChange.next('image'); + } + + const urlFileContent = versionId ? this.apiService.contentApi.getVersionRenditionUrl(nodeId, versionId, renditionId) : + this.apiService.contentApi.getRenditionUrl(nodeId, renditionId); + this.urlFileContentChange.next(urlFileContent); + } + } + } diff --git a/lib/testing/src/lib/core/pages/viewer.page.ts b/lib/testing/src/lib/core/pages/viewer.page.ts index b9147287c6..105d1c0d4d 100644 --- a/lib/testing/src/lib/core/pages/viewer.page.ts +++ b/lib/testing/src/lib/core/pages/viewer.page.ts @@ -637,4 +637,8 @@ export class ViewerPage { const unknownFormatLabel = this.unknownFormat.element(by.css(`.adf-viewer__unknown-label`)); return BrowserActions.getText(unknownFormatLabel); } + + async expectUrlToContain(text: string): Promise { + await expect(browser.getCurrentUrl()).toContain(text); + } }