mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[AAE-10778] Refactor Viewer (#7992)
* refactor version 1 many todo
* split render from viewer
move alfresco render in content pack
* refactor part 2
* test fixed
* fix doc
* [AAE-10778] Fix lint issues
* [AAE-10778] Fix lint issue: remove duplicated declaration
* [AAE-10778] Fix lint issue: use flex shorthand rule
* [AAE-10778] Fix FormService and WidgetComponent imports
* [AAE-10778] Fix import FormModel, FormService, FormFieldModel from adf-core
* [AAE-10778] Implement missing oninit, onchanges and ondestroy
* [AAE-10778] Replace adf-viewer with adf-alfresco-viewer, update escape command to close the viewer
* [AAE-10778] Fix unit test: fix the class name to match the 'adf-viewer-render.image-viewer-scaling' get from the appConfigService
* [AAE-10778] Fix image-viewer unit tests: replace ContentService with UrlService
* [AAE-10778] Fix unit test 'should if the extension change extension Change event be fired': emit file extension when the filename extension change
* [AAE-10778] Fix unit test: expect for internalFileName value instead of display-name id because the display name logic has been moved to the alfresco-viewer.component
* [AAE-10778] Fix unit test: remove display name it because the unknown display name value is no longer handled after refactoring
* [AAE-10778] Fix e2e: [C260096] Should the Viewer able to accept a customToolbar
* [AAE-10778] Update selector to fix e2e: '[C362265] Should the Viewer be able to download a previous version of a file'
* [AAE-10778] Update selector to fix e2e: '[C260038] Should display first page, toolbar and pagination when opening a .pdf file'
* fix aftrer rebase
* fix unit test
* [AAE-10778] Add adf viewer component that is node agnostic, show adf-alfresco-viewer or adf-viewer into file-view-component if blob or node are set
* [AAE-10778] Update viewer export path
* [AAE-10778] Update selectors since have been updated in the viewer component
* [AAE-10778] Call adf-viewer from alfresco-viewer, project adf-alfresco-viewer content to adf-viewer
* [AAE-10778] Remove full screen unit tests from alfresco-viewer component becase that logic is handled in the viewer.component
* [AAE-10778] Export toolbar custom actions component
* [AAE-10778] Pass mimeType as input to adf-viewer to update mime icon
* [AAE-10778] Remove e2e because the custom name behaviour has been removed from the file-view.component (9f21b6dc69
\#diff-4b438dc59784dce9eb7634cfeca6d8db61362966343bd3d6895a3edafdf4cfd5L129)
* [AAE-10778] Use two-way binding for showViewer change to fix C260100
* [AAE-10778] Update prefix css selectors to adf-viewer because are related to the adf-viewer component
* [AAE-10778] Update prefix css selectors to adf-viewer in the unit tests because are related to the adf-viewer component
* [AAE-10778] Update the output name to showViewerChange to navigate to primary url after closing the viewer
* [AAE-10778] Pass right and left sidebar template context to viewer component (fix C362242)
* [AAE-10778] Add allowFullScreen input to disable/enable full screen behaviour
* [AAE-10778] Handle loading visualization only inside the viewer-render component
* [AAE-10778] PDF viewer: fix mat-progress-bar is not showed during the pdf loading, center progress bar
* [AAE-10778] Remove isLoading from unit tests because no longer exists
* [AAE-10778] Remove viewerType input from adf-viewer, viewerType will be handled by viewer-render
* [AAE-10778] Remove console.log
* [AAE-10778] Remove check full screen button is not displayed on the media file because is not needed anymore, we don't need to check for the fullscreen button in the viewer component
* [AAE-10778] Check for node rendtion before to assign to urlFileContent and mimeType
* [AAE-10778] Process Services Cloud: register file-viewer widget that uses adf-alfresco-viewer component to display content from ACS
* [AAE-10778] Core: rename file-viewer widget into base-viewer, base-viewer no longer accept nodeId, but will accept urlFile and blobFile
* [AAE-10778] Process Services: register file-viewer widget that uses adf-alfresco-viewer component to display content from ACS
* [AAE-10778] Base viewer widget: show viewer only if there's a file input
* [AAE-10778] Viewer component: check for fileName when urlFile is provided as Input
* [AAE-10778] Viewer component documentation
* [AAE-10778] Update upgrade guide with viewer changes
* [AAE-10778] Fix double quote lint issue after rebase
---------
Co-authored-by: Amedeo Lepore <amedeo.lepore@hyland.com>
Co-authored-by: Amedeo Lepore <amedeo.lepore85@gmail.com>
This commit is contained in:
parent
52520bb61e
commit
4043d55fc4
@ -1,4 +1,3 @@
|
|||||||
<adf-viewer
|
<adf-alfresco-viewer
|
||||||
[overlayMode]="true"
|
|
||||||
[nodeId]="nodeId">
|
[nodeId]="nodeId">
|
||||||
</adf-viewer>
|
</adf-alfresco-viewer>
|
||||||
|
@ -1,340 +1,382 @@
|
|||||||
<ng-container *ngIf="nodeId || content">
|
<ng-container *ngIf="nodeId">
|
||||||
|
|
||||||
<ng-template let-node="node" #sidebarRightTemplate>
|
<adf-alfresco-viewer
|
||||||
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
|
(showViewerChange)="onViewerClosed()"
|
||||||
|
|
||||||
<adf-info-drawer-tab label="APP.INFO_DRAWER.COMMENTS">
|
|
||||||
<adf-node-comments
|
|
||||||
[nodeId]="nodeId"
|
|
||||||
[readOnly]="!isCommentEnabled"
|
|
||||||
>
|
|
||||||
</adf-node-comments>
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
|
|
||||||
<adf-info-drawer-tab label="APP.INFO_DRAWER.PROPERTIES">
|
|
||||||
<adf-content-metadata-card *ngIf="isPreset" [node]="node"
|
|
||||||
[multi]="multi"
|
|
||||||
[preset]="customPreset"
|
|
||||||
[readOnly]="isReadOnly"
|
|
||||||
[displayAspect]="showAspect"
|
|
||||||
[displayDefaultProperties]="displayDefaultProperties"
|
|
||||||
[displayEmpty]="displayEmptyMetadata"></adf-content-metadata-card>
|
|
||||||
|
|
||||||
<adf-content-metadata-card *ngIf="!isPreset" [node]="node"
|
|
||||||
[multi]="multi"
|
|
||||||
[readOnly]="isReadOnly"
|
|
||||||
[displayAspect]="showAspect"
|
|
||||||
[displayDefaultProperties]="displayDefaultProperties"
|
|
||||||
[displayEmpty]="displayEmptyMetadata"></adf-content-metadata-card>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-metadata-default-properties"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleDisplayProperties()"
|
|
||||||
[checked]="displayDefaultProperties">
|
|
||||||
Display Default Properties
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-metadata-empty"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleEmptyMetadata()"
|
|
||||||
[checked]="displayEmptyMetadata">
|
|
||||||
Display Empty Metadata
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-metadata-multi"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleMulti()"
|
|
||||||
[checked]="multi">
|
|
||||||
Multi accordion
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-metadata-readonly"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleReadOnly()"
|
|
||||||
[checked]="isReadOnly">
|
|
||||||
Read Only
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-toggle-custom-preset"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="togglePreset()"
|
|
||||||
[checked]="isPreset">
|
|
||||||
Custom preset
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
|
|
||||||
<mat-form-field floatPlaceholder="float">
|
|
||||||
<input matInput
|
|
||||||
placeholder="Display Aspect"
|
|
||||||
[(ngModel)]="desiredAspect">
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<button mat-raised-button (click)="applyAspect()" color="primary">
|
|
||||||
Apply Aspect
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<ng-container *ngIf="isPreset">
|
|
||||||
<mat-form-field floatPlaceholder="float">
|
|
||||||
<input matInput
|
|
||||||
placeholder="Custom Preset"
|
|
||||||
[(ngModel)]="customPreset"
|
|
||||||
data-automation-id="adf-text-custom-preset">
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<button mat-raised-button id="adf-metadata-aplly" (click)="applyCustomPreset()" color="primary">
|
|
||||||
Apply
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</ng-container>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
|
|
||||||
<adf-info-drawer-tab label="APP.INFO_DRAWER.VERSIONS">
|
|
||||||
<mat-card>
|
|
||||||
<mat-card-content>
|
|
||||||
<adf-version-manager [node]="node"
|
|
||||||
(uploadError)="onUploadError($event)"
|
|
||||||
(viewVersion)="onViewVersion($event)">
|
|
||||||
</adf-version-manager>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
</adf-info-drawer>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
|
|
||||||
<ng-template let-node="node" #sidebarLeftTemplate>
|
|
||||||
<adf-info-drawer [title]="'Viewer Options'">
|
|
||||||
|
|
||||||
<adf-info-drawer-tab label="Settings">
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-custoname"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleCustomName()"
|
|
||||||
[checked]="customName">
|
|
||||||
Custom Name
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<ng-container *ngIf="customName">
|
|
||||||
<mat-form-field floatLabel="never">
|
|
||||||
<input matInput
|
|
||||||
placeholder="Custom Name"
|
|
||||||
[(ngModel)]="displayName"
|
|
||||||
data-automation-id="adf-text-custom-name">
|
|
||||||
</mat-form-field>
|
|
||||||
</ng-container>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-fileurl"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleFileUrl()"
|
|
||||||
[checked]="fileUrlSwitch">
|
|
||||||
Url File
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<ng-container *ngIf="fileUrlSwitch">
|
|
||||||
<mat-form-field floatPlaceholder="float">
|
|
||||||
<input matInput
|
|
||||||
placeholder="Url File"
|
|
||||||
[(ngModel)]="urlFile"
|
|
||||||
data-automation-id="adf-text-file-url">
|
|
||||||
</mat-form-field>
|
|
||||||
</ng-container>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-toolbar"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleShowToolbar()"
|
|
||||||
[checked]="showToolbar">
|
|
||||||
Show Toolbar
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-goback"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleAllowGoBack()"
|
|
||||||
[checked]="allowGoBack">
|
|
||||||
Allow GoBack
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-openwith"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleOpenWith()"
|
|
||||||
[checked]="openWith">
|
|
||||||
Open With
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-moreactions"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleOpenMoreActions()"
|
|
||||||
[checked]="moreActions">
|
|
||||||
More Actions
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-moreactionsmenu"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleMoreActionsMenu()"
|
|
||||||
[checked]="moreActionsMenu">
|
|
||||||
More Actions Menu
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-download"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleAllowDownload()"
|
|
||||||
[checked]="allowDownload">
|
|
||||||
Allow Download
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-print"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleAllowPrint()"
|
|
||||||
[checked]="allowPrint">
|
|
||||||
Allow Print
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-allowsidebar"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleAllowRightSidebar()"
|
|
||||||
[checked]="allowRightSidebar">
|
|
||||||
Allow Right Sidebar
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-switch-allowLeftSidebar"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleAllowLeftSidebar()"
|
|
||||||
[checked]="allowLeftSidebar">
|
|
||||||
Allow Left Sidebar
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-toggle-custom-toolbar"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleToolbar()"
|
|
||||||
[checked]="customToolbar">
|
|
||||||
Custom Toolbar
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-tab-with-icon"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleShowTabWithIcon()"
|
|
||||||
[checked]="showTabWithIcon">
|
|
||||||
Show tab with icon
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<mat-slide-toggle
|
|
||||||
id="adf-icon-and-label-tab"
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleShowTabWithIconAndLabel()"
|
|
||||||
[checked]="showTabWithIconAndLabel">
|
|
||||||
Show tab with icon and label
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<button mat-raised-button id="adf-switch-showrightsidebar" (click)="toggleShowRightSidebar()"
|
|
||||||
color="primary">
|
|
||||||
Toggle Right Sidebar
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="toggle">
|
|
||||||
<button mat-raised-button id="adf-switch-showleftsidebar" (click)="hideLeftSidebar()"
|
|
||||||
color="primary">
|
|
||||||
Hide Left Sidebar
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
|
|
||||||
<adf-info-drawer-tab
|
|
||||||
*ngIf="showTabWithIcon"
|
|
||||||
[label]=""
|
|
||||||
[icon]="'face'"
|
|
||||||
data-automation-id="adf-settings-tab">
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
|
|
||||||
<adf-info-drawer-tab
|
|
||||||
*ngIf="showTabWithIconAndLabel"
|
|
||||||
[label]="'Comments'"
|
|
||||||
[icon]="'comment'"
|
|
||||||
data-automation-id="adf-settings-tab">
|
|
||||||
</adf-info-drawer-tab>
|
|
||||||
</adf-info-drawer>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<adf-viewer
|
|
||||||
[blobFile]="content"
|
|
||||||
[nodeId]="nodeId"
|
[nodeId]="nodeId"
|
||||||
[versionId]="versionId"
|
[versionId]="versionId"
|
||||||
[showRightSidebar]="showRightSidebar"
|
[showRightSidebar]="showRightSidebar"
|
||||||
[showLeftSidebar]="showLeftSidebar"
|
[showLeftSidebar]="showLeftSidebar"
|
||||||
[allowGoBack]="allowGoBack"
|
[allowGoBack]="allowGoBack"
|
||||||
[displayName]="displayName"
|
|
||||||
[showToolbar]="showToolbar"
|
[showToolbar]="showToolbar"
|
||||||
[allowPrint]="allowPrint"
|
[allowPrint]="allowPrint"
|
||||||
[allowDownload]="allowDownload"
|
[allowDownload]="allowDownload"
|
||||||
[allowRightSidebar]="allowRightSidebar"
|
[allowRightSidebar]="allowRightSidebar"
|
||||||
[allowLeftSidebar]="allowLeftSidebar"
|
[allowLeftSidebar]="allowLeftSidebar"
|
||||||
[urlFile]="urlFile"
|
[sidebarLeftTemplate]="sidebarLeftTemplate"
|
||||||
(showViewerChange)="onViewerVisibilityChanged()"
|
[sidebarRightTemplate]="sidebarRightTemplate">
|
||||||
|
|
||||||
|
<adf-viewer-toolbar *ngIf="customToolbar" data-automation-id="adf-viewer-custom-toolbar">
|
||||||
|
<h1>My custom toolbar</h1>
|
||||||
|
</adf-viewer-toolbar>
|
||||||
|
|
||||||
|
<adf-viewer-toolbar-actions *ngIf="moreActions">
|
||||||
|
<button mat-icon-button id="adf-viewer-time">
|
||||||
|
<mat-icon>alarm</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button id="adf-viewer-upload">
|
||||||
|
<mat-icon>backup</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button id="adf-viewer-bug">
|
||||||
|
<mat-icon>bug_report</mat-icon>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-toolbar-actions>
|
||||||
|
|
||||||
|
<adf-viewer-more-actions *ngIf="moreActionsMenu">
|
||||||
|
<button mat-menu-item id="adf-viewer-more-menu-alarm">
|
||||||
|
<mat-icon>alarm</mat-icon>
|
||||||
|
<span>Alarm</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item id="adf-viewer-more-menu-backup">
|
||||||
|
<mat-icon>backup</mat-icon>
|
||||||
|
<span>Backup</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item id="adf-viewer-more-menu-bug">
|
||||||
|
<mat-icon>bug_report</mat-icon>
|
||||||
|
<span>Bug report</span>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-more-actions>
|
||||||
|
|
||||||
|
<ng-container *ngIf="openWith">
|
||||||
|
<adf-viewer-open-with>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>Option 1</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<mat-icon>voicemail</mat-icon>
|
||||||
|
<span>Option 2</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>notifications_off</mat-icon>
|
||||||
|
<span>Option 3</span>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-open-with>
|
||||||
|
</ng-container>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template let-node="node" #sidebarRightTemplate>
|
||||||
|
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
|
||||||
|
|
||||||
|
<adf-info-drawer-tab label="APP.INFO_DRAWER.COMMENTS">
|
||||||
|
<adf-node-comments
|
||||||
|
[nodeId]="nodeId"
|
||||||
|
[readOnly]="!isCommentEnabled"
|
||||||
|
>
|
||||||
|
</adf-node-comments>
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
|
||||||
|
<adf-info-drawer-tab label="APP.INFO_DRAWER.PROPERTIES">
|
||||||
|
<adf-content-metadata-card *ngIf="isPreset" [node]="node"
|
||||||
|
[multi]="multi"
|
||||||
|
[preset]="customPreset"
|
||||||
|
[readOnly]="isReadOnly"
|
||||||
|
[displayAspect]="showAspect"
|
||||||
|
[displayDefaultProperties]="displayDefaultProperties"
|
||||||
|
[displayEmpty]="displayEmptyMetadata"></adf-content-metadata-card>
|
||||||
|
|
||||||
|
<adf-content-metadata-card *ngIf="!isPreset" [node]="node"
|
||||||
|
[multi]="multi"
|
||||||
|
[readOnly]="isReadOnly"
|
||||||
|
[displayAspect]="showAspect"
|
||||||
|
[displayDefaultProperties]="displayDefaultProperties"
|
||||||
|
[displayEmpty]="displayEmptyMetadata"></adf-content-metadata-card>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-metadata-default-properties"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleDisplayProperties()"
|
||||||
|
[checked]="displayDefaultProperties">
|
||||||
|
Display Default Properties
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-metadata-empty"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleEmptyMetadata()"
|
||||||
|
[checked]="displayEmptyMetadata">
|
||||||
|
Display Empty Metadata
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-metadata-multi"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleMulti()"
|
||||||
|
[checked]="multi">
|
||||||
|
Multi accordion
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-metadata-readonly"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleReadOnly()"
|
||||||
|
[checked]="isReadOnly">
|
||||||
|
Read Only
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-toggle-custom-preset"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="togglePreset()"
|
||||||
|
[checked]="isPreset">
|
||||||
|
Custom preset
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
|
||||||
|
<mat-form-field floatPlaceholder="float">
|
||||||
|
<input matInput
|
||||||
|
placeholder="Display Aspect"
|
||||||
|
[(ngModel)]="desiredAspect">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<button mat-raised-button (click)="applyAspect()" color="primary">
|
||||||
|
Apply Aspect
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<ng-container *ngIf="isPreset">
|
||||||
|
<mat-form-field floatPlaceholder="float">
|
||||||
|
<input matInput
|
||||||
|
placeholder="Custom Preset"
|
||||||
|
[(ngModel)]="customPreset"
|
||||||
|
data-automation-id="adf-text-custom-preset">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<button mat-raised-button id="adf-metadata-aplly" (click)="applyCustomPreset()" color="primary">
|
||||||
|
Apply
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
|
||||||
|
<adf-info-drawer-tab label="APP.INFO_DRAWER.VERSIONS">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<adf-version-manager [node]="node"
|
||||||
|
(uploadError)="onUploadError($event)"
|
||||||
|
(viewVersion)="onViewVersion($event)">
|
||||||
|
</adf-version-manager>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
</adf-info-drawer>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #sidebarLeftTemplate>
|
||||||
|
<adf-info-drawer [title]="'Viewer Options'">
|
||||||
|
|
||||||
|
<adf-info-drawer-tab label="Settings">
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-fileurl"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleFileUrl()"
|
||||||
|
[checked]="fileUrlSwitch">
|
||||||
|
Url File
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<ng-container *ngIf="fileUrlSwitch">
|
||||||
|
<mat-form-field floatPlaceholder="float">
|
||||||
|
<input matInput
|
||||||
|
placeholder="Url File"
|
||||||
|
[(ngModel)]="urlFile"
|
||||||
|
data-automation-id="adf-text-file-url">
|
||||||
|
</mat-form-field>
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-toolbar"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleShowToolbar()"
|
||||||
|
[checked]="showToolbar">
|
||||||
|
Show Toolbar
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-goback"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleAllowGoBack()"
|
||||||
|
[checked]="allowGoBack">
|
||||||
|
Allow GoBack
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-openwith"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleOpenWith()"
|
||||||
|
[checked]="openWith">
|
||||||
|
Open With
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-moreactions"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleOpenMoreActions()"
|
||||||
|
[checked]="moreActions">
|
||||||
|
More Actions
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-moreactionsmenu"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleMoreActionsMenu()"
|
||||||
|
[checked]="moreActionsMenu">
|
||||||
|
More Actions Menu
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-download"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleAllowDownload()"
|
||||||
|
[checked]="allowDownload">
|
||||||
|
Allow Download
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-print"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleAllowPrint()"
|
||||||
|
[checked]="allowPrint">
|
||||||
|
Allow Print
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-allowsidebar"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleAllowRightSidebar()"
|
||||||
|
[checked]="allowRightSidebar">
|
||||||
|
Allow Right Sidebar
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-switch-allowLeftSidebar"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleAllowLeftSidebar()"
|
||||||
|
[checked]="allowLeftSidebar">
|
||||||
|
Allow Left Sidebar
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-toggle-custom-toolbar"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleToolbar()"
|
||||||
|
[checked]="customToolbar">
|
||||||
|
Custom Toolbar
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-tab-with-icon"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleShowTabWithIcon()"
|
||||||
|
[checked]="showTabWithIcon">
|
||||||
|
Show tab with icon
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<mat-slide-toggle
|
||||||
|
id="adf-icon-and-label-tab"
|
||||||
|
[color]="'primary'"
|
||||||
|
(change)="toggleShowTabWithIconAndLabel()"
|
||||||
|
[checked]="showTabWithIconAndLabel">
|
||||||
|
Show tab with icon and label
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<button mat-raised-button id="adf-switch-showrightsidebar" (click)="toggleShowRightSidebar()"
|
||||||
|
color="primary">
|
||||||
|
Toggle Right Sidebar
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toggle">
|
||||||
|
<button mat-raised-button id="adf-switch-showleftsidebar" (click)="hideLeftSidebar()"
|
||||||
|
color="primary">
|
||||||
|
Hide Left Sidebar
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
|
||||||
|
<adf-info-drawer-tab
|
||||||
|
*ngIf="showTabWithIcon"
|
||||||
|
[label]=""
|
||||||
|
[icon]="'face'"
|
||||||
|
data-automation-id="adf-settings-tab">
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
|
||||||
|
<adf-info-drawer-tab
|
||||||
|
*ngIf="showTabWithIconAndLabel"
|
||||||
|
[label]="'Comments'"
|
||||||
|
[icon]="'comment'"
|
||||||
|
data-automation-id="adf-settings-tab">
|
||||||
|
</adf-info-drawer-tab>
|
||||||
|
</adf-info-drawer>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-container *ngIf="blobFile">
|
||||||
|
<adf-viewer
|
||||||
|
[blobFile]="blobFile"
|
||||||
|
[fileName]="fileName"
|
||||||
|
[showRightSidebar]="showRightSidebar"
|
||||||
|
[showLeftSidebar]="showLeftSidebar"
|
||||||
|
[allowGoBack]="allowGoBack"
|
||||||
|
[showToolbar]="showToolbar"
|
||||||
|
[allowRightSidebar]="allowRightSidebar"
|
||||||
|
[allowLeftSidebar]="allowLeftSidebar"
|
||||||
|
(showViewerChange)="onViewerClosed()"
|
||||||
[sidebarLeftTemplate]="sidebarLeftTemplate"
|
[sidebarLeftTemplate]="sidebarLeftTemplate"
|
||||||
[sidebarRightTemplate]="sidebarRightTemplate">
|
[sidebarRightTemplate]="sidebarRightTemplate">
|
||||||
|
|
||||||
@ -386,36 +428,5 @@
|
|||||||
</adf-viewer-open-with>
|
</adf-viewer-open-with>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!--
|
|
||||||
<adf-viewer-extension [supportedExtensions]="['json']">
|
|
||||||
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
|
||||||
<h1>JSON VIEWER</h1>
|
|
||||||
</ng-template>
|
|
||||||
</adf-viewer-extension>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<adf-viewer-extension [supportedExtensions]="['png']">
|
|
||||||
<ng-template>
|
|
||||||
<h1>PNG Viewer</h1>
|
|
||||||
</ng-template>
|
|
||||||
</adf-viewer-extension>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<adf-viewer-extension [supportedExtensions]="['pdf']">
|
|
||||||
<ng-template>
|
|
||||||
<h1>PDF Viewer</h1>
|
|
||||||
</ng-template>
|
|
||||||
</adf-viewer-extension>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<extension-viewer [supportedExtensions]="['obj','3DS']" #extension>
|
|
||||||
<ng-template let-urlFileContent="urlFileContent" let-extension="extension" >
|
|
||||||
<threed-viewer [urlFile]="urlFileContent" [extension]="extension" ></threed-viewer>
|
|
||||||
</ng-template>
|
|
||||||
</extension-viewer>
|
|
||||||
-->
|
|
||||||
</adf-viewer>
|
</adf-viewer>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -45,7 +45,6 @@ export class FileViewComponent implements OnInit {
|
|||||||
customPreset: string = null;
|
customPreset: string = null;
|
||||||
displayDefaultProperties = true;
|
displayDefaultProperties = true;
|
||||||
showToolbar = true;
|
showToolbar = true;
|
||||||
displayName = null;
|
|
||||||
urlFile = null;
|
urlFile = null;
|
||||||
allowGoBack = true;
|
allowGoBack = true;
|
||||||
openWith = false;
|
openWith = false;
|
||||||
@ -55,7 +54,6 @@ export class FileViewComponent implements OnInit {
|
|||||||
allowLeftSidebar = true;
|
allowLeftSidebar = true;
|
||||||
moreActions = true;
|
moreActions = true;
|
||||||
moreActionsMenu = false;
|
moreActionsMenu = false;
|
||||||
customName = false;
|
|
||||||
fileUrlSwitch = false;
|
fileUrlSwitch = false;
|
||||||
showLeftSidebar = null;
|
showLeftSidebar = null;
|
||||||
showRightSidebar = false;
|
showRightSidebar = false;
|
||||||
@ -65,8 +63,9 @@ export class FileViewComponent implements OnInit {
|
|||||||
showTabWithIconAndLabel = false;
|
showTabWithIconAndLabel = false;
|
||||||
desiredAspect: string = null;
|
desiredAspect: string = null;
|
||||||
showAspect: string = null;
|
showAspect: string = null;
|
||||||
content: Blob;
|
|
||||||
name: string;
|
name: string;
|
||||||
|
fileName: string;
|
||||||
|
blobFile: Blob;
|
||||||
|
|
||||||
constructor(private router: Router,
|
constructor(private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -93,9 +92,9 @@ export class FileViewComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
() => this.router.navigate(['/files', id])
|
() => this.router.navigate(['/files', id])
|
||||||
);
|
);
|
||||||
} else if (this.preview.content) {
|
} else if (this.preview?.content) {
|
||||||
this.content = this.preview.content;
|
this.blobFile = this.preview.content;
|
||||||
this.displayName = this.preview.name;
|
this.fileName = this.preview.name;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -104,7 +103,7 @@ export class FileViewComponent implements OnInit {
|
|||||||
this.preview.showResource(this.nodeId, versionId);
|
this.preview.showResource(this.nodeId, versionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
onViewerVisibilityChanged() {
|
onViewerClosed() {
|
||||||
const primaryUrl = this.router.parseUrl(this.router.url).root.children[PRIMARY_OUTLET].toString();
|
const primaryUrl = this.router.parseUrl(this.router.url).root.children[PRIMARY_OUTLET].toString();
|
||||||
this.router.navigateByUrl(primaryUrl);
|
this.router.navigateByUrl(primaryUrl);
|
||||||
}
|
}
|
||||||
@ -182,14 +181,6 @@ export class FileViewComponent implements OnInit {
|
|||||||
this.showTabWithIconAndLabel = !this.showTabWithIconAndLabel;
|
this.showTabWithIconAndLabel = !this.showTabWithIconAndLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCustomName() {
|
|
||||||
this.customName = !this.customName;
|
|
||||||
|
|
||||||
if (!this.customName) {
|
|
||||||
this.displayName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleFileUrl() {
|
toggleFileUrl() {
|
||||||
this.fileUrlSwitch = !this.fileUrlSwitch;
|
this.fileUrlSwitch = !this.fileUrlSwitch;
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ const routes: Routes = [
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
CoreModule,
|
CoreModule,
|
||||||
|
ContentModule,
|
||||||
InfoDrawerModule,
|
InfoDrawerModule,
|
||||||
ContentModule,
|
ContentModule,
|
||||||
ContentDirectiveModule,
|
ContentDirectiveModule,
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
(preview)="showPreview($event)">
|
(preview)="showPreview($event)">
|
||||||
</adf-document-list>
|
</adf-document-list>
|
||||||
|
|
||||||
<adf-viewer
|
<adf-alfresco-viewer
|
||||||
[(showViewer)]="showViewer"
|
[(showViewer)]="showViewer"
|
||||||
[overlayMode]="isOverlay"
|
[overlayMode]="isOverlay"
|
||||||
[nodeId]="nodeId">
|
[nodeId]="nodeId">
|
||||||
</adf-viewer>
|
</adf-alfresco-viewer>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<ng-container *ngIf="sharedLinkId">
|
<ng-container *ngIf="sharedLinkId">
|
||||||
<adf-viewer
|
<adf-alfresco-viewer
|
||||||
(invalidSharedLink)="redirectTo404()"
|
(invalidSharedLink)="redirectTo404()"
|
||||||
[sharedLinkId]="sharedLinkId"
|
[sharedLinkId]="sharedLinkId"
|
||||||
[allowGoBack]="false">
|
[allowGoBack]="false">
|
||||||
</adf-viewer>
|
</adf-alfresco-viewer>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
474
docs/content-services/components/alfresco-viewer.component.md
Normal file
474
docs/content-services/components/alfresco-viewer.component.md
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
---
|
||||||
|
Title: Alfresco Viewer component
|
||||||
|
Added: 6.0.0
|
||||||
|
Status: Active
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Alfresco Viewer component](../../../lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts "Defined in alfresco-viewer.component.ts")
|
||||||
|
|
||||||
|
Displays content from an ACS repository.
|
||||||
|
|
||||||
|
See it live: [Viewer Quickstart](https://embed.plnkr.co/iTuG1lFIXfsP95l6bDW6/)
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Basic usage](#basic-usage)
|
||||||
|
- [Transclusions](#transclusions)
|
||||||
|
- [Class members](#class-members)
|
||||||
|
- [Properties](#properties)
|
||||||
|
- [Events](#events)
|
||||||
|
- [Keyboard shortcuts](#keyboard-shortcuts)
|
||||||
|
- [Details](#details)
|
||||||
|
- [Integrating with the Document List component](#integrating-with-the-document-list-component)
|
||||||
|
- [Supported file formats](#supported-file-formats)
|
||||||
|
- [Content Renditions](#content-renditions)
|
||||||
|
- [Configuring PDF.js library](#configuring-pdfjs-library)
|
||||||
|
- [Extending the Viewer](#extending-the-viewer)
|
||||||
|
- [Custom layout](#custom-layout)
|
||||||
|
- [Printing](#printing)
|
||||||
|
- [See also](#see-also)
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
Using with node id:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer
|
||||||
|
[showViewer]="true"
|
||||||
|
[overlayMode]="true"
|
||||||
|
[nodeId]="'d367023a-7ebe-4f3a-a7d0-4f27c43f1045'">
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Using with shared link:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer
|
||||||
|
[overlayMode]="true"
|
||||||
|
[sharedLinkId]="'WWDg_afiTU6lHEgr4fAbQA'">
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that if you have a URL which contains a shared link ID, you should extract the
|
||||||
|
ID portion and use it with the `sharedLinkId` property rather than using the whole
|
||||||
|
URL with `urlFile`.
|
||||||
|
|
||||||
|
### [Transclusions](../../user-guide/transclusion.md)
|
||||||
|
|
||||||
|
The [Alfresco Viewer component](viewer.component.md) lets you transclude content for the toolbar (and toolbar buttons),
|
||||||
|
the sidebar, thumbnails, and the "Open with" and "More actions" menus.
|
||||||
|
See the [Custom layout](#custom-layout) section for full details of all available tranclusions.
|
||||||
|
|
||||||
|
## Class members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| allowDownload | `boolean` | true | Toggles downloading. |
|
||||||
|
| allowGoBack | `boolean` | true | Allows `back` navigation |
|
||||||
|
| allowLeftSidebar | `boolean` | false | Allow the left the sidebar. |
|
||||||
|
| allowNavigate | `boolean` | false | Toggles before/next navigation. You can use the arrow buttons to navigate between documents in the collection. |
|
||||||
|
| allowPrint | `boolean` | false | Toggles printing. |
|
||||||
|
| allowRightSidebar | `boolean` | false | Allow the right sidebar. |
|
||||||
|
| canNavigateBefore | `boolean` | true | Toggles the "before" ("<") button. Requires `allowNavigate` to be enabled. |
|
||||||
|
| canNavigateNext | `boolean` | true | Toggles the next (">") button. Requires `allowNavigate` to be enabled. |
|
||||||
|
| maxRetries | `number` | 30 | Number of times the Viewer will retry fetching content Rendition. There is a delay of at least one second between attempts. |
|
||||||
|
| nodeId | `string` | null | Node Id of the file to load. |
|
||||||
|
| overlayMode | `boolean` | false | If `true` then show the Viewer as a full page over the current content. Otherwise fit inside the parent div. |
|
||||||
|
| sharedLinkId | `string` | null | Shared link id (to display shared file). |
|
||||||
|
| showLeftSidebar | `boolean` | false | Toggles left sidebar visibility. Requires `allowLeftSidebar` to be set to `true`. |
|
||||||
|
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
||||||
|
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
||||||
|
| showViewer | `boolean` | true | Hide or show the viewer |
|
||||||
|
| sidebarLeftTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
||||||
|
| sidebarRightTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
||||||
|
| versionId | `string` | null | Version Id of the file to load. |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| close | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when the viewer close |
|
||||||
|
| invalidSharedLink | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the shared link used is not valid. |
|
||||||
|
| navigateBefore | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Before' ("<") button. |
|
||||||
|
| navigateNext | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Next' (">") button. |
|
||||||
|
|
||||||
|
## Keyboard shortcuts
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
| Esc | Close the viewer (overlay mode only). |
|
||||||
|
| Left | Invoke 'Navigate before' action. |
|
||||||
|
| Right | Invoke 'Navigate next' action. |
|
||||||
|
| Ctrl+F | Activate full-screen mode. |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
### Integrating with the Document List component
|
||||||
|
|
||||||
|
Below is the most simple integration of the Viewer and
|
||||||
|
[Document List](../../content-services/components/document-list.component.md) components within your custom component:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-document-list
|
||||||
|
currentFolderId="-my-"
|
||||||
|
(preview)="showPreview($event)">
|
||||||
|
</adf-document-list>
|
||||||
|
|
||||||
|
<adf-alfresco-viewer
|
||||||
|
[(showViewer)]="showViewer"
|
||||||
|
[overlayMode]="true"
|
||||||
|
[nodeId]="nodeId">
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The component controller class implementation might look like the following:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export class OverlayViewerComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
showViewer: boolean = false;
|
||||||
|
|
||||||
|
nodeId: string;
|
||||||
|
|
||||||
|
showPreview(event) {
|
||||||
|
if (event.value.entry.isFile) {
|
||||||
|
this.nodeId = event.value.entry.id;
|
||||||
|
this.showViewer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported file formats
|
||||||
|
|
||||||
|
The [Alfresco Viewer component](viewer.component.md) consists of separate Views that handle particular file types or type families based on either a file extension or a mime type:
|
||||||
|
|
||||||
|
- PDF View
|
||||||
|
- application/pdf
|
||||||
|
- \*.pdf
|
||||||
|
- Image View
|
||||||
|
- image/png
|
||||||
|
- image/jpeg
|
||||||
|
- image/gif
|
||||||
|
- image/bmp
|
||||||
|
- image/svg+xml
|
||||||
|
- \*.png
|
||||||
|
- \*.jpg
|
||||||
|
- \*.jpeg
|
||||||
|
- \*.gif
|
||||||
|
- \*.bpm
|
||||||
|
- \*.svg
|
||||||
|
- Text View
|
||||||
|
- text/plain
|
||||||
|
- text/csv
|
||||||
|
- text/xml
|
||||||
|
- text/html
|
||||||
|
- application/x-javascript
|
||||||
|
- \*.txt
|
||||||
|
- \*.xml
|
||||||
|
- \*.js
|
||||||
|
- \*.html
|
||||||
|
- \*.json
|
||||||
|
- \*.ts
|
||||||
|
- Media View
|
||||||
|
- video/mp4
|
||||||
|
- video/webm
|
||||||
|
- video/ogg
|
||||||
|
- audio/mpeg
|
||||||
|
- audio/ogg
|
||||||
|
- audio/wav
|
||||||
|
- \*.wav
|
||||||
|
- \*.mp4
|
||||||
|
- \*.mp3
|
||||||
|
- \*.webm
|
||||||
|
- \*.ogg
|
||||||
|
|
||||||
|
### Content Renditions
|
||||||
|
|
||||||
|
For those extensions and mime types that cannot be natively displayed in the browser, the Viewer will try to fetch the corresponding rendition using the [renditions service api](../services/renditions.service.md).
|
||||||
|
|
||||||
|
For the full list of supported types please refer to: [File types that support preview and thumbnail generation](http://docs.alfresco.com/5.2/references/valid-transformations-preview.html).
|
||||||
|
|
||||||
|
### Configuring PDF.js library
|
||||||
|
|
||||||
|
Configure your webpack-enabled application with the PDF.js library as follows.
|
||||||
|
|
||||||
|
1. Install pdfjs-dist
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install pdfjs-dist
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update `vendors.ts` by appending the following code. This will enable the [Alfresco Viewer component](viewer.component.md)
|
||||||
|
and compatibility mode for all browsers. It will also configure the web worker for the PDF.js
|
||||||
|
library to render PDF files in the background:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// PDF.js
|
||||||
|
require('pdfjs-dist/web/compatibility.js');
|
||||||
|
const pdfjsLib = require('pdfjs-dist');
|
||||||
|
pdfjsLib.PDFJS.workerSrc = './pdf.worker.js';
|
||||||
|
require('pdfjs-dist/web/pdf_viewer.js');
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Update the `plugins` section of the `webpack.common.js` file with the following lines:
|
||||||
|
|
||||||
|
```js
|
||||||
|
new CopyWebpackPlugin([
|
||||||
|
...
|
||||||
|
{
|
||||||
|
from: 'node_modules/pdfjs-dist/build/pdf.worker.js',
|
||||||
|
to: 'pdf.worker.js'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extending the Viewer
|
||||||
|
|
||||||
|
#### Internal extension mechanism
|
||||||
|
|
||||||
|
The Viewer supports dynamically-loaded viewer preview extensions, to know more about it [Preview Extension component](../../extensions/components/preview-extension.component.md). This
|
||||||
|
|
||||||
|
#### Code extension mechanism]
|
||||||
|
|
||||||
|
You can define your own custom handler to handle other file formats that are not yet supported by
|
||||||
|
the [Alfresco Viewer component](viewer.component.md). Below is an example that shows how to use the `adf-viewer-extension`
|
||||||
|
to handle 3D data files:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-viewer-extension [supportedExtensions]="['obj','3ds']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
||||||
|
<threed-viewer
|
||||||
|
[urlFile]="urlFileContent"
|
||||||
|
[extension]="extension">
|
||||||
|
</threed-viewer>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-extension>
|
||||||
|
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: you need to add the `ng2-3d-editor` dependency to your `package.json` file to make the example above work.
|
||||||
|
|
||||||
|
You can define multiple `adf-viewer-extension` templates if required:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-viewer-extension [supportedExtensions]="['xls','xlsx']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent">
|
||||||
|
<my-custom-xls-component
|
||||||
|
urlFileContent="urlFileContent">
|
||||||
|
</my-custom-xls-component>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-extension>
|
||||||
|
|
||||||
|
<adf-viewer-extension [supportedExtensions]="['txt']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent" >
|
||||||
|
<my-custom-txt-component
|
||||||
|
urlFileContent="urlFileContent">
|
||||||
|
</my-custom-txt-component>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-extension>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom layout
|
||||||
|
|
||||||
|
The [Alfresco Viewer Component](viewer.component.md) lets you transclude custom content in several different places as
|
||||||
|
explained in the sections below.
|
||||||
|
|
||||||
|
#### Custom toolbar
|
||||||
|
|
||||||
|
You can replace the standard viewer toolbar with your own custom implementation:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-alfresco-viewer-toolbar>
|
||||||
|
<h1>toolbar</h1>
|
||||||
|
</adf-alfresco-viewer-toolbar>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Everything you put inside the "adf-alfresco-viewer-toolbar" tags will be rendered instead of the
|
||||||
|
standard toolbar.
|
||||||
|
|
||||||
|
#### Custom toolbar buttons
|
||||||
|
|
||||||
|
If you are happy with the custom toolbar's behaviour but want to add some extra buttons
|
||||||
|
then you can do so as shown in the following example:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-alfresco-viewer-toolbar-actions>
|
||||||
|
<button mat-icon-button>
|
||||||
|
<mat-icon>alarm</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button>
|
||||||
|
<mat-icon>backup</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button>
|
||||||
|
<mat-icon>bug_report</mat-icon>
|
||||||
|
</button>
|
||||||
|
</adf-alfresco-viewer-toolbar-actions>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The result should look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Custom sidebar
|
||||||
|
|
||||||
|
The [Alfresco Viewer Component](viewer.component.md) also supports custom sidebar components and layouts.
|
||||||
|
Set the `allowRightSidebar` property to `true` to enable this feature.
|
||||||
|
|
||||||
|
The custom sidebar can be injected in two different ways. The first way is to use
|
||||||
|
transclusion, which will display all content placed inside the `<adf-alfresco-viewer-sidebar>` element:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [allowRightSidebar]="true">
|
||||||
|
<adf-alfresco-viewer-sidebar>
|
||||||
|
<h1>My info</h1>
|
||||||
|
</adf-alfresco-viewer-sidebar>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The second way to customize the sidebar is to use template injection but note that this only works
|
||||||
|
when using the viewer with `nodeId`.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ng-template let-node="node" #sidebarTemplate>
|
||||||
|
<adf-content-metadata-card [node]="node"></adf-content-metadata-card>
|
||||||
|
</ng-template>
|
||||||
|
<adf-alfresco-viewer [allowRightSidebar]="true" [sidebarRightTemplate]="sidebarTemplate"></adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom thumbnails
|
||||||
|
|
||||||
|
The PDF viewer comes with its own default list of thumbnails but you can replace this
|
||||||
|
by providing a custom template and binding to the context property `viewer` to access the PDFJS.PDFViewer
|
||||||
|
instance.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Provide the custom template as in the following example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'custom-thumbnails',
|
||||||
|
template: '<p> Custom Thumbnails Component </p>'
|
||||||
|
})
|
||||||
|
export class CustomThumbnailsComponent {
|
||||||
|
@Input() pdfViewer: any;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ng-template #customThumbnailsTemplate let-pdfViewer="viewer">
|
||||||
|
<custom-thumbnails [pdfViewer]="pdfViewer"></custom-thumbnails>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<adf-alfresco-viewer [thumbnailsTemplate]="customThumbnailsTemplate"></adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom "Open With" menu
|
||||||
|
|
||||||
|
You can enable a custom "Open With" menu by providing at least one action inside the
|
||||||
|
`adf-alfresco-viewer-open-with` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-alfresco-viewer-open-with>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>Option 1</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<mat-icon>voicemail</mat-icon>
|
||||||
|
<span>Option 2</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>notifications_off</mat-icon>
|
||||||
|
<span>Option 3</span>
|
||||||
|
</button>
|
||||||
|
</adf-alfresco-viewer-open-with>
|
||||||
|
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Custom "More actions" menu
|
||||||
|
|
||||||
|
You can enable a custom "More actions" menu by providing at least one action inside the `adf-alfresco-viewer-more-actions` tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-alfresco-viewer-more-actions>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>Action One</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<mat-icon>voicemail</mat-icon>
|
||||||
|
<span>Action Two</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>notifications_off</mat-icon>
|
||||||
|
<span>Action Three</span>
|
||||||
|
</button>
|
||||||
|
</adf-alfresco-viewer-more-actions>
|
||||||
|
|
||||||
|
</adv-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Custom zoom scaling
|
||||||
|
|
||||||
|
You can set a default zoom scaling value for pdf viewer by adding the following code in `app.config.json`.
|
||||||
|
Note: For the pdf viewer the value has to be within the range of 25 - 1000.
|
||||||
|
|
||||||
|
"adf-alfresco-viewer": {
|
||||||
|
"pdf-viewer-scaling": 150
|
||||||
|
}
|
||||||
|
|
||||||
|
In the same way you can set a default zoom scaling value for the image viewer by adding the following code in `app.config.json`.
|
||||||
|
|
||||||
|
"adf-alfresco-viewer": {
|
||||||
|
"image-viewer-scaling": 150
|
||||||
|
}
|
||||||
|
|
||||||
|
By default the viewer's zoom scaling is set to 100%.
|
||||||
|
|
||||||
|
### Printing
|
||||||
|
|
||||||
|
You can configure the Viewer to let the user print the displayed content. The
|
||||||
|
component will show a "Print" button if the `allowPrint` property is set to
|
||||||
|
true.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-alfresco-viewer [allowPrint]="true">
|
||||||
|
...
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the `print` event to get notification when the user prints some
|
||||||
|
content.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Document List component](../../content-services/components/document-list.component.md)
|
||||||
|
- [Preview Extension component](../../extensions/components/preview-extension.component.md)
|
@ -99,7 +99,7 @@ Displays the documents from a repository.
|
|||||||
| nodeClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user clicks a list node |
|
| nodeClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user clicks a list node |
|
||||||
| nodeDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user double-clicks a list node |
|
| nodeDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user double-clicks a list node |
|
||||||
| nodeSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`[]>` | Emitted when the node selection change |
|
| nodeSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md)`[]>` | Emitted when the node selection change |
|
||||||
| preview | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user acts upon files with either single or double click (depends on `navigation-mode`). Useful for integration with the [Viewer component](../../core/components/viewer.component.md). |
|
| preview | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user acts upon files with either single or double click (depends on `navigation-mode`). Useful for integration with the Viewer component. |
|
||||||
| ready | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md)`>` | Emitted when the Document List has loaded all items and is ready for use |
|
| ready | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md)`>` | Emitted when the Document List has loaded all items and is ready for use |
|
||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
@ -25,7 +25,7 @@ Stores information from all the custom search and faceted search widgets, compil
|
|||||||
- **Returns** `QueryBody` - The finished query
|
- **Returns** `QueryBody` - The finished query
|
||||||
- **execute**(queryBody?: `QueryBody`)<br/>
|
- **execute**(queryBody?: `QueryBody`)<br/>
|
||||||
Builds and executes the current query.
|
Builds and executes the current query.
|
||||||
- _queryBody:_ `QueryBody` - (Optional)
|
- _queryBody:_ `QueryBody` - (Optional) (Optional)
|
||||||
- **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`<br/>
|
- **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`<br/>
|
||||||
|
|
||||||
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined` -
|
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined` -
|
||||||
@ -92,7 +92,7 @@ Stores information from all the custom search and faceted search widgets, compil
|
|||||||
|
|
||||||
- **update**(queryBody?: `QueryBody`)<br/>
|
- **update**(queryBody?: `QueryBody`)<br/>
|
||||||
Builds the current query and triggers the `updated` event.
|
Builds the current query and triggers the `updated` event.
|
||||||
- _queryBody:_ `QueryBody` - (Optional)
|
- _queryBody:_ `QueryBody` - (Optional) (Optional)
|
||||||
- **updateSelectedConfiguration**(index: `number`)<br/>
|
- **updateSelectedConfiguration**(index: `number`)<br/>
|
||||||
|
|
||||||
- _index:_ `number` -
|
- _index:_ `number` -
|
||||||
|
275
docs/core/components/viewer-render.component.md
Normal file
275
docs/core/components/viewer-render.component.md
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
---
|
||||||
|
Title: Viewer Render component
|
||||||
|
Added: v6.0.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2022-11-25
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Viewer render component](../../../lib/core/src/lib/viewer/components/viewer-render.component.ts "Defined in viewer-render.component.ts")
|
||||||
|
|
||||||
|
Displays content from an ACS repository.
|
||||||
|
|
||||||
|
See it live: [Viewer Quickstart](https://embed.plnkr.co/iTuG1lFIXfsP95l6bDW6/)
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Basic usage](#basic-usage)
|
||||||
|
- [Class members](#class-members)
|
||||||
|
- [Properties](#properties)
|
||||||
|
- [Events](#events)
|
||||||
|
- [Keyboard shortcuts](#keyboard-shortcuts)
|
||||||
|
- [Details](#details)
|
||||||
|
- [Custom file parameters](#custom-file-parameters)
|
||||||
|
- [Supported file formats](#supported-file-formats)
|
||||||
|
- [Configuring PDF.js library](#configuring-pdfjs-library)
|
||||||
|
- [Extending the Viewer](#extending-the-viewer)
|
||||||
|
- [Custom layout](#custom-layout)
|
||||||
|
- [See also](#see-also)
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
Using with file url:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-viewer-render
|
||||||
|
[overlayMode]="true"
|
||||||
|
[urlFile]="'filename.pdf'">
|
||||||
|
</adf-viewer-render>
|
||||||
|
```
|
||||||
|
|
||||||
|
Using with file [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-viewer-render
|
||||||
|
[overlayMode]="true"
|
||||||
|
[blobFile]="myBlobVar">
|
||||||
|
</adf-viewer-render>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| allowFullScreen | `boolean` | true | Toggles the 'Full Screen' feature. |
|
||||||
|
| allowThumbnails | `boolean` | true | Toggles PDF thumbnails. |
|
||||||
|
| blobFile | [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) | | Loads a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) File |
|
||||||
|
| fileName | `string` | | Override Content filename. |
|
||||||
|
| isLoading | `boolean` | false | Override loading status |
|
||||||
|
| mimeType | `string` | | MIME type of the file content (when not determined by the filename extension). |
|
||||||
|
| readOnly | `boolean` | true | Enable when where is possible the editing functionalities |
|
||||||
|
| thumbnailsTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the pdf thumbnails. |
|
||||||
|
| tracks | [`Track`](../../../lib/core/src/lib/viewer/models/viewer.model.ts)`[]` | \[] | media subtitles for the media player |
|
||||||
|
| 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. |
|
||||||
|
| viewerType | `string` | "unknown" | Override Content view type. Viewer to use with the `urlFile` address (`pdf`, `image`, `media`, `text`). |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| close | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when the img is submitted in the img viewer. |
|
||||||
|
| extensionChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the filename extension changes. |
|
||||||
|
| submitFile | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)`>` | Emitted when the img is submitted in the img viewer. |
|
||||||
|
|
||||||
|
## Keyboard shortcuts
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
| Esc | Close the viewer (overlay mode only). |
|
||||||
|
| Left | Invoke 'Navigate before' action. |
|
||||||
|
| Right | Invoke 'Navigate next' action. |
|
||||||
|
| Ctrl+F | Activate full-screen mode. |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The component controller class implementation might look like the following:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export class OverlayViewerComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
showViewer: boolean = false;
|
||||||
|
|
||||||
|
nodeId: string;
|
||||||
|
|
||||||
|
showPreview(event) {
|
||||||
|
if (event.value.entry.isFile) {
|
||||||
|
this.nodeId = event.value.entry.id;
|
||||||
|
this.showViewer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom file parameters
|
||||||
|
|
||||||
|
You can provide custom file parameters, for example a value for the `mimeType` or `displayName` when using URL values that have no file names or extensions:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-viewer-render
|
||||||
|
[displayName]="fileName"
|
||||||
|
[allowGoBack]="false"
|
||||||
|
[mimeType]="mimeType"
|
||||||
|
[urlFile]="fileUrl">
|
||||||
|
</adf-viewer-render>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported file formats
|
||||||
|
|
||||||
|
The [Viewer render component](viewer.component.md) consists of separate Views that handle particular file types or type families based on either a file extension or a mime type:
|
||||||
|
|
||||||
|
- PDF View
|
||||||
|
- application/pdf
|
||||||
|
- \*.pdf
|
||||||
|
- Image View
|
||||||
|
- image/png
|
||||||
|
- image/jpeg
|
||||||
|
- image/gif
|
||||||
|
- image/bmp
|
||||||
|
- image/svg+xml
|
||||||
|
- \*.png
|
||||||
|
- \*.jpg
|
||||||
|
- \*.jpeg
|
||||||
|
- \*.gif
|
||||||
|
- \*.bpm
|
||||||
|
- \*.svg
|
||||||
|
- Text View
|
||||||
|
- text/plain
|
||||||
|
- text/csv
|
||||||
|
- text/xml
|
||||||
|
- text/html
|
||||||
|
- application/x-javascript
|
||||||
|
- \*.txt
|
||||||
|
- \*.xml
|
||||||
|
- \*.js
|
||||||
|
- \*.html
|
||||||
|
- \*.json
|
||||||
|
- \*.ts
|
||||||
|
- Media View
|
||||||
|
- video/mp4
|
||||||
|
- video/webm
|
||||||
|
- video/ogg
|
||||||
|
- audio/mpeg
|
||||||
|
- audio/ogg
|
||||||
|
- audio/wav
|
||||||
|
- \*.wav
|
||||||
|
- \*.mp4
|
||||||
|
- \*.mp3
|
||||||
|
- \*.webm
|
||||||
|
- \*.ogg
|
||||||
|
|
||||||
|
### Configuring PDF.js library
|
||||||
|
|
||||||
|
Configure your webpack-enabled application with the PDF.js library as follows.
|
||||||
|
|
||||||
|
1. Install pdfjs-dist
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install pdfjs-dist
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update `vendors.ts` by appending the following code. This will enable the [Viewer render component](viewer.component.md)
|
||||||
|
and compatibility mode for all browsers. It will also configure the web worker for the PDF.js
|
||||||
|
library to render PDF files in the background:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// PDF.js
|
||||||
|
require('pdfjs-dist/web/compatibility.js');
|
||||||
|
const pdfjsLib = require('pdfjs-dist');
|
||||||
|
pdfjsLib.PDFJS.workerSrc = './pdf.worker.js';
|
||||||
|
require('pdfjs-dist/web/pdf_viewer.js');
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Update the `plugins` section of the `webpack.common.js` file with the following lines:
|
||||||
|
|
||||||
|
```js
|
||||||
|
new CopyWebpackPlugin([
|
||||||
|
...
|
||||||
|
{
|
||||||
|
from: 'node_modules/pdfjs-dist/build/pdf.worker.js',
|
||||||
|
to: 'pdf.worker.js'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
The [Viewer render component](viewer.component.md) should now be able to display PDF files.
|
||||||
|
|
||||||
|
### Extending the Viewer
|
||||||
|
|
||||||
|
#### Internal extension mechanism
|
||||||
|
|
||||||
|
The Viewer supports dynamically-loaded viewer preview extensions, to know more about it [Preview Extension component](../../extensions/components/preview-extension.component.md). This
|
||||||
|
|
||||||
|
#### Code extension mechanism]
|
||||||
|
|
||||||
|
You can define your own custom handler to handle other file formats that are not yet supported by
|
||||||
|
the [Viewer render component](viewer.component.md). Below is an example that shows how to use the `adf-viewer-render-extension`
|
||||||
|
to handle 3D data files:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-viewer-render [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-viewer-extension [supportedExtensions]="['obj','3ds']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
||||||
|
<threed-viewer
|
||||||
|
[urlFile]="urlFileContent"
|
||||||
|
[extension]="extension">
|
||||||
|
</threed-viewer>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-extension>
|
||||||
|
|
||||||
|
</adf-viewer-render>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: you need to add the `ng2-3d-editor` dependency to your `package.json` file to make the example above work.
|
||||||
|
|
||||||
|
You can define multiple `adf-viewer-render-extension` templates if required:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-viewer-render [nodeId]="nodeId">
|
||||||
|
|
||||||
|
<adf-viewer-extension [supportedExtensions]="['xls','xlsx']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent">
|
||||||
|
<my-custom-xls-component
|
||||||
|
urlFileContent="urlFileContent">
|
||||||
|
</my-custom-xls-component>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-extension>
|
||||||
|
|
||||||
|
<adf-viewer-render-extension [supportedExtensions]="['txt']" #extension>
|
||||||
|
<ng-template let-urlFileContent="urlFileContent" >
|
||||||
|
<my-custom-txt-component
|
||||||
|
urlFileContent="urlFileContent">
|
||||||
|
</my-custom-txt-component>
|
||||||
|
</ng-template>
|
||||||
|
</adf-viewer-render-extension>
|
||||||
|
</adf-viewer-render>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom layout
|
||||||
|
|
||||||
|
The [Viewer render component](viewer.component.md) lets you transclude custom content in several different places as
|
||||||
|
explained in the sections below.
|
||||||
|
|
||||||
|
#### Custom zoom scaling
|
||||||
|
|
||||||
|
You can set a default zoom scaling value for pdf viewer by adding the following code in `app.config.json`.
|
||||||
|
Note: For the pdf viewer the value has to be within the range of 25 - 1000.
|
||||||
|
|
||||||
|
"adf-viewer-render": {
|
||||||
|
"pdf-viewer-scaling": 150
|
||||||
|
}
|
||||||
|
|
||||||
|
In the same way you can set a default zoom scaling value for the image viewer by adding the following code in `app.config.json`.
|
||||||
|
|
||||||
|
"adf-viewer-render": {
|
||||||
|
"image-viewer-scaling": 150
|
||||||
|
}
|
||||||
|
|
||||||
|
By default the viewer's zoom scaling is set to 100%.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Alfresco Viewer component](../../content-services/components/document-list.component.md)
|
||||||
|
- [Viewer Render component](../../core/components/viewer-render.component.md)
|
@ -2,14 +2,13 @@
|
|||||||
Title: Viewer component
|
Title: Viewer component
|
||||||
Added: v2.0.0
|
Added: v2.0.0
|
||||||
Status: Active
|
Status: Active
|
||||||
Last reviewed: 2019-03-25
|
Last reviewed: 2023-01-30
|
||||||
---
|
---
|
||||||
|
|
||||||
# [Viewer component](../../../lib/core/src/lib/viewer/components/viewer.component.ts "Defined in viewer.component.ts")
|
# [Viewer component](../../../lib/core/src/lib/viewer/components/viewer.component.ts "Defined in viewer.component.ts")
|
||||||
|
|
||||||
Displays content from an ACS repository.
|
Displays content from blob file or url file.
|
||||||
|
|
||||||
See it live: [Viewer Quickstart](https://embed.plnkr.co/iTuG1lFIXfsP95l6bDW6/)
|
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
@ -23,22 +22,21 @@ See it live: [Viewer Quickstart](https://embed.plnkr.co/iTuG1lFIXfsP95l6bDW6/)
|
|||||||
- [Integrating with the Document List component](#integrating-with-the-document-list-component)
|
- [Integrating with the Document List component](#integrating-with-the-document-list-component)
|
||||||
- [Custom file parameters](#custom-file-parameters)
|
- [Custom file parameters](#custom-file-parameters)
|
||||||
- [Supported file formats](#supported-file-formats)
|
- [Supported file formats](#supported-file-formats)
|
||||||
- [Content Renditions](#content-renditions)
|
|
||||||
- [Configuring PDF.js library](#configuring-pdfjs-library)
|
- [Configuring PDF.js library](#configuring-pdfjs-library)
|
||||||
- [Extending the Viewer](#extending-the-viewer)
|
- [Extending the Viewer](#extending-the-viewer)
|
||||||
- [Custom layout](#custom-layout)
|
- [Custom layout](#custom-layout)
|
||||||
- [Printing](#printing)
|
|
||||||
- [See also](#see-also)
|
- [See also](#see-also)
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Using with node id:
|
Using with blob file:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer
|
<adf-viewer
|
||||||
[showViewer]="true"
|
[showViewer]="true"
|
||||||
[overlayMode]="true"
|
[overlayMode]="true"
|
||||||
[nodeId]="'d367023a-7ebe-4f3a-a7d0-4f27c43f1045'">
|
[blobFile]="blobFile"
|
||||||
|
[fileName]="'filename.pdf'">
|
||||||
</adf-viewer>
|
</adf-viewer>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -47,23 +45,10 @@ Using with file url:
|
|||||||
```html
|
```html
|
||||||
<adf-viewer
|
<adf-viewer
|
||||||
[overlayMode]="true"
|
[overlayMode]="true"
|
||||||
[urlFile]="'filename.pdf'">
|
[urlFile]="'https://fileUrl.com/filename.pdf'">
|
||||||
</adf-viewer>
|
</adf-viewer>
|
||||||
```
|
```
|
||||||
|
|
||||||
Using with shared link:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<adf-viewer
|
|
||||||
[overlayMode]="true"
|
|
||||||
[sharedLinkId]="'WWDg_afiTU6lHEgr4fAbQA'">
|
|
||||||
</adf-viewer>
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if you have a URL which contains a shared link ID, you should extract the
|
|
||||||
ID portion and use it with the `sharedLinkId` property rather than using the whole
|
|
||||||
URL with `urlFile`.
|
|
||||||
|
|
||||||
### [Transclusions](../../user-guide/transclusion.md)
|
### [Transclusions](../../user-guide/transclusion.md)
|
||||||
|
|
||||||
The [Viewer component](viewer.component.md) lets you transclude content for the toolbar (and toolbar buttons),
|
The [Viewer component](viewer.component.md) lets you transclude content for the toolbar (and toolbar buttons),
|
||||||
@ -81,41 +66,30 @@ See the [Custom layout](#custom-layout) section for full details of all availabl
|
|||||||
| allowGoBack | `boolean` | true | Allows `back` navigation |
|
| allowGoBack | `boolean` | true | Allows `back` navigation |
|
||||||
| allowLeftSidebar | `boolean` | false | Allow the left the sidebar. |
|
| allowLeftSidebar | `boolean` | false | Allow the left the sidebar. |
|
||||||
| allowNavigate | `boolean` | false | Toggles before/next navigation. You can use the arrow buttons to navigate between documents in the collection. |
|
| allowNavigate | `boolean` | false | Toggles before/next navigation. You can use the arrow buttons to navigate between documents in the collection. |
|
||||||
| allowPrint | `boolean` | false | Toggles printing. |
|
|
||||||
| allowRightSidebar | `boolean` | false | Allow the right sidebar. |
|
| allowRightSidebar | `boolean` | false | Allow the right sidebar. |
|
||||||
| allowThumbnails | `boolean` | true | Toggles PDF thumbnails. |
|
|
||||||
| blobFile | [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) | | Loads a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) File |
|
| blobFile | [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) | | Loads a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) File |
|
||||||
| canNavigateBefore | `boolean` | true | Toggles the "before" ("<") button. Requires `allowNavigate` to be enabled. |
|
| canNavigateBefore | `boolean` | true | Toggles the "before" ("<") button. Requires `allowNavigate` to be enabled. |
|
||||||
| canNavigateNext | `boolean` | true | Toggles the next (">") button. Requires `allowNavigate` to be enabled. |
|
| canNavigateNext | `boolean` | true | Toggles the next (">") button. Requires `allowNavigate` to be enabled. |
|
||||||
| displayName | `string` | | Specifies the name of the file when it is not available from the URL. |
|
|
||||||
| fileName | `string` | | Content filename. |
|
| fileName | `string` | | Content filename. |
|
||||||
| maxRetries | `number` | 30 | Number of times the Viewer will retry fetching content Rendition. There is a delay of at least one second between attempts. |
|
|
||||||
| mimeType | `string` | | MIME type of the file content (when not determined by the filename extension). |
|
| mimeType | `string` | | MIME type of the file content (when not determined by the filename extension). |
|
||||||
| nodeId | `string` | null | Node Id of the file to load. |
|
|
||||||
| overlayMode | `boolean` | false | If `true` then show the Viewer as a full page over the current content. Otherwise fit inside the parent div. |
|
| overlayMode | `boolean` | false | If `true` then show the Viewer as a full page over the current content. Otherwise fit inside the parent div. |
|
||||||
| sharedLinkId | `string` | null | Shared link id (to display shared file). |
|
|
||||||
| showLeftSidebar | `boolean` | false | Toggles left sidebar visibility. Requires `allowLeftSidebar` to be set to `true`. |
|
| showLeftSidebar | `boolean` | false | Toggles left sidebar visibility. Requires `allowLeftSidebar` to be set to `true`. |
|
||||||
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
||||||
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
||||||
| showViewer | `boolean` | true | Hide or show the viewer |
|
| showViewer | `boolean` | true | Hide or show the viewer |
|
||||||
| sidebarLeftTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
| sidebarLeftTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the left sidebar. The template context contains the loaded data. |
|
||||||
| sidebarRightTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
| sidebarRightTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the right sidebar. The template context contains the loaded data. |
|
||||||
| thumbnailsTemplate | [`TemplateRef`](https://angular.io/api/core/TemplateRef)`<any>` | null | The template for the pdf thumbnails. |
|
| tracks | [`Track`](../../../lib/core/src/lib/viewer/models/viewer.model.ts)`[]` | \[] | media subtitles for the media player |
|
||||||
| 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. |
|
| 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
|
### Events
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| extensionChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the filename extension changes. |
|
|
||||||
| goBack | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`BaseEvent`](../../../lib/core/src/lib/events/base.event.ts)`<any>>` | Emitted when user clicks the 'Back' button. |
|
|
||||||
| invalidSharedLink | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the shared link used is not valid. |
|
|
||||||
| navigateBefore | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Before' ("<") button. |
|
| navigateBefore | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Before' ("<") button. |
|
||||||
| navigateNext | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Next' (">") button. |
|
| navigateNext | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<KeyboardEvent \| MouseEvent>` | Emitted when user clicks 'Navigate Next' (">") button. |
|
||||||
| print | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`BaseEvent`](../../../lib/core/src/lib/events/base.event.ts)`<any>>` | Emitted when user clicks the 'Print' button. |
|
|
||||||
| showViewerChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when the viewer is shown or hidden. |
|
| showViewerChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when the viewer is shown or hidden. |
|
||||||
|
| submitFile | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)`>` | Emitted when the img is submitted in the img viewer. |
|
||||||
|
|
||||||
## Keyboard shortcuts
|
## Keyboard shortcuts
|
||||||
|
|
||||||
@ -128,50 +102,13 @@ See the [Custom layout](#custom-layout) section for full details of all availabl
|
|||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
### Integrating with the Document List component
|
|
||||||
|
|
||||||
Below is the most simple integration of the Viewer and
|
|
||||||
[Document List](../../content-services/components/document-list.component.md) components within your custom component:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<adf-document-list
|
|
||||||
currentFolderId="-my-"
|
|
||||||
(preview)="showPreview($event)">
|
|
||||||
</adf-document-list>
|
|
||||||
|
|
||||||
<adf-viewer
|
|
||||||
[(showViewer)]="showViewer"
|
|
||||||
[overlayMode]="true"
|
|
||||||
[nodeId]="nodeId">
|
|
||||||
</adf-viewer>
|
|
||||||
```
|
|
||||||
|
|
||||||
The component controller class implementation might look like the following:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export class OverlayViewerComponent {
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
showViewer: boolean = false;
|
|
||||||
|
|
||||||
nodeId: string;
|
|
||||||
|
|
||||||
showPreview(event) {
|
|
||||||
if (event.value.entry.isFile) {
|
|
||||||
this.nodeId = event.value.entry.id;
|
|
||||||
this.showViewer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom file parameters
|
### Custom file parameters
|
||||||
|
|
||||||
You can provide custom file parameters, for example a value for the `mimeType` or `displayName` when using URL values that have no file names or extensions:
|
You can provide custom file parameters, for example a value for the `mimeType` or `fileName` when using URL values that have no file names or extensions:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer
|
<adf-viewer
|
||||||
[displayName]="fileName"
|
[fileName]="fileName"
|
||||||
[allowGoBack]="false"
|
[allowGoBack]="false"
|
||||||
[mimeType]="mimeType"
|
[mimeType]="mimeType"
|
||||||
[urlFile]="fileUrl">
|
[urlFile]="fileUrl">
|
||||||
@ -222,12 +159,6 @@ The [Viewer component](viewer.component.md) consists of separate Views that hand
|
|||||||
- \*.webm
|
- \*.webm
|
||||||
- \*.ogg
|
- \*.ogg
|
||||||
|
|
||||||
### Content Renditions
|
|
||||||
|
|
||||||
For those extensions and mime types that cannot be natively displayed in the browser, the Viewer will try to fetch the corresponding rendition using the [renditions service api](../services/renditions.service.md).
|
|
||||||
|
|
||||||
For the full list of supported types please refer to: [File types that support preview and thumbnail generation](http://docs.alfresco.com/5.2/references/valid-transformations-preview.html).
|
|
||||||
|
|
||||||
### Configuring PDF.js library
|
### Configuring PDF.js library
|
||||||
|
|
||||||
Configure your webpack-enabled application with the PDF.js library as follows.
|
Configure your webpack-enabled application with the PDF.js library as follows.
|
||||||
@ -277,7 +208,7 @@ the [Viewer component](viewer.component.md). Below is an example that shows how
|
|||||||
to handle 3D data files:
|
to handle 3D data files:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer [nodeId]="nodeId">
|
<adf-viewer [urlFile]="urlFile">
|
||||||
|
|
||||||
<adf-viewer-extension [supportedExtensions]="['obj','3ds']" #extension>
|
<adf-viewer-extension [supportedExtensions]="['obj','3ds']" #extension>
|
||||||
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
||||||
@ -296,7 +227,7 @@ Note: you need to add the `ng2-3d-editor` dependency to your `package.json` file
|
|||||||
You can define multiple `adf-viewer-extension` templates if required:
|
You can define multiple `adf-viewer-extension` templates if required:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer [nodeId]="nodeId">
|
<adf-viewer [urlFile]="urlFile">
|
||||||
|
|
||||||
<adf-viewer-extension [supportedExtensions]="['xls','xlsx']" #extension>
|
<adf-viewer-extension [supportedExtensions]="['xls','xlsx']" #extension>
|
||||||
<ng-template let-urlFileContent="urlFileContent">
|
<ng-template let-urlFileContent="urlFileContent">
|
||||||
@ -377,55 +308,13 @@ transclusion, which will display all content placed inside the `<adf-viewer-side
|
|||||||
</adf-viewer>
|
</adf-viewer>
|
||||||
```
|
```
|
||||||
|
|
||||||
The second way to customize the sidebar is to use template injection but note that this only works
|
|
||||||
when using the viewer with `nodeId`.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ng-template let-node="node" #sidebarTemplate>
|
|
||||||
<adf-content-metadata-card [node]="node"></adf-content-metadata-card>
|
|
||||||
</ng-template>
|
|
||||||
<adf-viewer [allowRightSidebar]="true" [sidebarRightTemplate]="sidebarTemplate"></adf-viewer>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom thumbnails
|
|
||||||
|
|
||||||
The PDF viewer comes with its own default list of thumbnails but you can replace this
|
|
||||||
by providing a custom template and binding to the context property `viewer` to access the PDFJS.PDFViewer
|
|
||||||
instance.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Provide the custom template as in the following example:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'custom-thumbnails',
|
|
||||||
template: '<p> Custom Thumbnails Component </p>'
|
|
||||||
})
|
|
||||||
export class CustomThumbnailsComponent {
|
|
||||||
@Input() pdfViewer: any;
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ng-template #customThumbnailsTemplate let-pdfViewer="viewer">
|
|
||||||
<custom-thumbnails [pdfViewer]="pdfViewer"></custom-thumbnails>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<adf-viewer [thumbnailsTemplate]="customThumbnailsTemplate"></adf-viewer>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom "Open With" menu
|
#### Custom "Open With" menu
|
||||||
|
|
||||||
You can enable a custom "Open With" menu by providing at least one action inside the
|
You can enable a custom "Open With" menu by providing at least one action inside the
|
||||||
`adf-viewer-open-with` tag:
|
`adf-viewer-open-with` tag:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer [nodeId]="nodeId">
|
<adf-viewer [urlFile]="urlFile">
|
||||||
|
|
||||||
<adf-viewer-open-with>
|
<adf-viewer-open-with>
|
||||||
<button mat-menu-item>
|
<button mat-menu-item>
|
||||||
@ -452,7 +341,7 @@ You can enable a custom "Open With" menu by providing at least one action inside
|
|||||||
You can enable a custom "More actions" menu by providing at least one action inside the `adf-viewer-more-actions` tag:
|
You can enable a custom "More actions" menu by providing at least one action inside the `adf-viewer-more-actions` tag:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<adf-viewer [nodeId]="nodeId">
|
<adf-viewer [urlFile]="urlFile">
|
||||||
|
|
||||||
<adf-viewer-more-actions>
|
<adf-viewer-more-actions>
|
||||||
<button mat-menu-item>
|
<button mat-menu-item>
|
||||||
@ -491,21 +380,6 @@ In the same way you can set a default zoom scaling value for the image viewer by
|
|||||||
|
|
||||||
By default the viewer's zoom scaling is set to 100%.
|
By default the viewer's zoom scaling is set to 100%.
|
||||||
|
|
||||||
### Printing
|
|
||||||
|
|
||||||
You can configure the Viewer to let the user print the displayed content. The
|
|
||||||
component will show a "Print" button if the `allowPrint` property is set to
|
|
||||||
true.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<adf-viewer [allowPrint]="true">
|
|
||||||
...
|
|
||||||
</adf-viewer>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use the `print` event to get notification when the user prints some
|
|
||||||
content.
|
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Document List component](../../content-services/components/document-list.component.md)
|
- [Document List component](../../content-services/components/document-list.component.md)
|
||||||
|
@ -20,7 +20,6 @@ an example of a real working viewer extension project.
|
|||||||
| ---- | ---- | ------------- | ----------- |
|
| ---- | ---- | ------------- | ----------- |
|
||||||
| extension | `string` | | File extension (.jpg, .png, etc) for the viewer. |
|
| extension | `string` | | File extension (.jpg, .png, etc) for the viewer. |
|
||||||
| id | `string` | | ID string of the component to preview. |
|
| id | `string` | | ID string of the component to preview. |
|
||||||
| node | `Node` | | Node containing the content to display. |
|
|
||||||
| url | `string` | | URL of the content in the repository. |
|
| url | `string` | | URL of the content in the repository. |
|
||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
@ -222,10 +222,15 @@ The ```adf-comments``` has now two specialization in :
|
|||||||
- ```adf-node-comments``` [Node Comments Componen](../content-services/components/node-comments.component.md)
|
- ```adf-node-comments``` [Node Comments Componen](../content-services/components/node-comments.component.md)
|
||||||
- ```adf-task-comments``` [Task Comments Component](../process-services/components/task-comments.component.md)
|
- ```adf-task-comments``` [Task Comments Component](../process-services/components/task-comments.component.md)
|
||||||
|
|
||||||
|
### ViewerComponent
|
||||||
|
From v.6.0.0 and after [`ViewerComponent`](../../docs/core/components/viewer.component.md) no longer show content from ACS, so instead of taking `nodeId` as `@Input`, it takes `blobFile` and `urlFile`. For more details check the [`PR`](https://github.com/Alfresco/alfresco-ng2-components/pull/7992).
|
||||||
|
If you need to display content from ACS you can use instead the new [`AlfrescoViewerComponent`](../../docs/content-services/components/alfresco-viewer.component.md)
|
||||||
|
|
||||||
## Renamed items
|
## Renamed items
|
||||||
|
|
||||||
### New Classes or Services
|
### New Classes or Services
|
||||||
|
- [`AlfrescoViewerComponent`](../../docs/content-services/components/alfresco-viewer.component.md)
|
||||||
|
- [`ViewerRenderComponent`](../..docs/core/components/viewer-render.component.md)
|
||||||
|
|
||||||
### Properties and methods
|
### Properties and methods
|
||||||
|
|
||||||
|
@ -335,7 +335,6 @@ describe('Content Services Viewer', () => {
|
|||||||
await viewerPage.checkFileNameIsDisplayed(mp4File.name);
|
await viewerPage.checkFileNameIsDisplayed(mp4File.name);
|
||||||
await viewerPage.checkDownloadButtonIsDisplayed();
|
await viewerPage.checkDownloadButtonIsDisplayed();
|
||||||
await viewerPage.checkInfoButtonIsDisplayed();
|
await viewerPage.checkInfoButtonIsDisplayed();
|
||||||
await viewerPage.checkFullScreenButtonIsNotDisplayed();
|
|
||||||
|
|
||||||
await viewerPage.checkZoomInButtonIsNotDisplayed();
|
await viewerPage.checkZoomInButtonIsNotDisplayed();
|
||||||
|
|
||||||
|
@ -132,17 +132,6 @@ describe('Viewer', () => {
|
|||||||
await viewerPage.enableMoreActions();
|
await viewerPage.enableMoreActions();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[C260074] Should show a custom file name when displayName property is used', async () => {
|
|
||||||
await viewerPage.checkFileNameIsDisplayed(pngFile.name);
|
|
||||||
|
|
||||||
await viewerPage.enableCustomName();
|
|
||||||
|
|
||||||
await viewerPage.enterCustomName('test custom title');
|
|
||||||
await viewerPage.checkFileNameIsDisplayed('test custom title');
|
|
||||||
|
|
||||||
await viewerPage.disableCustomName();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('[C260090] Should showSidebar allow right info-drawer to be shown', async () => {
|
it('[C260090] Should showSidebar allow right info-drawer to be shown', async () => {
|
||||||
await viewerPage.clickToggleRightSidebar();
|
await viewerPage.clickToggleRightSidebar();
|
||||||
await viewerPage.checkInfoSideBarIsDisplayed();
|
await viewerPage.checkInfoSideBarIsDisplayed();
|
||||||
@ -182,7 +171,7 @@ describe('Viewer', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[C260100] Should be possible to disable Overlay viewer', async () => {
|
fit('[C260100] Should be possible to disable Overlay viewer', async () => {
|
||||||
await navigationBarPage.clickOverlayViewerButton();
|
await navigationBarPage.clickOverlayViewerButton();
|
||||||
await dataTable.doubleClickRow('Name', fileForOverlay.name);
|
await dataTable.doubleClickRow('Name', fileForOverlay.name);
|
||||||
await viewerPage.checkOverlayViewerIsDisplayed();
|
await viewerPage.checkOverlayViewerIsDisplayed();
|
||||||
|
@ -48,6 +48,7 @@ import { ContentPipeModule } from './pipes/content-pipe.module';
|
|||||||
import { NodeCommentsModule } from './node-comments/node-comments.module';
|
import { NodeCommentsModule } from './node-comments/node-comments.module';
|
||||||
import { TreeModule } from './tree/tree.module';
|
import { TreeModule } from './tree/tree.module';
|
||||||
import { SearchTextModule } from './search-text/search-text-input.module';
|
import { SearchTextModule } from './search-text/search-text-input.module';
|
||||||
|
import { AlfrescoViewerModule } from './viewer/alfresco-viewer.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -79,7 +80,8 @@ import { SearchTextModule } from './search-text/search-text-input.module';
|
|||||||
VersionCompatibilityModule,
|
VersionCompatibilityModule,
|
||||||
NodeCommentsModule,
|
NodeCommentsModule,
|
||||||
TreeModule,
|
TreeModule,
|
||||||
SearchTextModule
|
SearchTextModule,
|
||||||
|
AlfrescoViewerModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@ -115,7 +117,8 @@ import { SearchTextModule } from './search-text/search-text-input.module';
|
|||||||
VersionCompatibilityModule,
|
VersionCompatibilityModule,
|
||||||
NodeCommentsModule,
|
NodeCommentsModule,
|
||||||
TreeModule,
|
TreeModule,
|
||||||
SearchTextModule
|
SearchTextModule,
|
||||||
|
AlfrescoViewerModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ContentModule {
|
export class ContentModule {
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { ExtensionsModule } from '@alfresco/adf-extensions';
|
||||||
|
|
||||||
|
import { MaterialModule } from '../material.module';
|
||||||
|
import { A11yModule } from '@angular/cdk/a11y';
|
||||||
|
import { AlfrescoViewerComponent } from './components/alfresco-viewer.component';
|
||||||
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CoreModule,
|
||||||
|
CommonModule,
|
||||||
|
MaterialModule,
|
||||||
|
TranslateModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
A11yModule,
|
||||||
|
ExtensionsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AlfrescoViewerComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AlfrescoViewerComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AlfrescoViewerModule {
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
<adf-viewer
|
||||||
|
#adfViewer
|
||||||
|
[(showViewer)]="showViewer"
|
||||||
|
[allowGoBack]="allowGoBack"
|
||||||
|
[showToolbar]="showToolbar"
|
||||||
|
[overlayMode]="overlayMode"
|
||||||
|
[allowNavigate]="allowNavigate"
|
||||||
|
[canNavigateBefore]="canNavigateBefore"
|
||||||
|
[canNavigateNext]="canNavigateNext"
|
||||||
|
[allowLeftSidebar]="allowLeftSidebar"
|
||||||
|
[allowRightSidebar]="allowRightSidebar"
|
||||||
|
[showRightSidebar]="showRightSidebar"
|
||||||
|
[showLeftSidebar]="showLeftSidebar"
|
||||||
|
[allowFullScreen]="allowFullScreen"
|
||||||
|
[sidebarRightTemplate]="sidebarRightTemplate"
|
||||||
|
[sidebarLeftTemplate]="sidebarLeftTemplate"
|
||||||
|
[sidebarRightTemplateContext]="sidebarRightTemplateContext"
|
||||||
|
[sidebarLeftTemplateContext]="sidebarLeftTemplateContext"
|
||||||
|
[fileName]="fileName"
|
||||||
|
[mimeType]="mimeType"
|
||||||
|
[urlFile]="urlFileContent"
|
||||||
|
[tracks]="tracks"
|
||||||
|
[readOnly]="readOnly"
|
||||||
|
(navigateBefore)="onNavigateBeforeClick($event)"
|
||||||
|
(navigateNext)="onNavigateNextClick($event)"
|
||||||
|
(showViewerChange)="onClose()"
|
||||||
|
(submitFile)="onSubmitFile($event)">
|
||||||
|
|
||||||
|
<adf-viewer-toolbar *ngIf="toolbar">
|
||||||
|
<ng-content select="adf-viewer-toolbar"></ng-content>
|
||||||
|
</adf-viewer-toolbar>
|
||||||
|
|
||||||
|
<adf-viewer-toolbar-actions *ngIf="toolbarActions">
|
||||||
|
<ng-content select="adf-viewer-toolbar-actions"></ng-content>
|
||||||
|
</adf-viewer-toolbar-actions>
|
||||||
|
|
||||||
|
<adf-viewer-more-actions *ngIf="moreActions">
|
||||||
|
<ng-content select="adf-viewer-more-actions"></ng-content>
|
||||||
|
</adf-viewer-more-actions>
|
||||||
|
|
||||||
|
<adf-viewer-open-with *ngIf="openWith">
|
||||||
|
<ng-content select="adf-viewer-open-with"></ng-content>
|
||||||
|
</adf-viewer-open-with>
|
||||||
|
|
||||||
|
<adf-viewer-sidebar *ngIf="sidebar">
|
||||||
|
<ng-content select="adf-viewer-sidebar"></ng-content>
|
||||||
|
</adf-viewer-sidebar>
|
||||||
|
|
||||||
|
<adf-viewer-toolbar-custom-actions>
|
||||||
|
<button id="adf-alfresco-viewer-download"
|
||||||
|
*ngIf="allowDownload"
|
||||||
|
mat-icon-button
|
||||||
|
[attr.aria-label]="'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate"
|
||||||
|
title="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}"
|
||||||
|
data-automation-id="adf-toolbar-download"
|
||||||
|
[adfNodeDownload]="nodeEntry"
|
||||||
|
[version]="versionEntry">
|
||||||
|
<mat-icon>file_download</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="adf-alfresco-viewer-print"
|
||||||
|
*ngIf="allowPrint"
|
||||||
|
mat-icon-button
|
||||||
|
[attr.aria-label]="'ADF_VIEWER.ACTIONS.PRINT' | translate"
|
||||||
|
title="{{ 'ADF_VIEWER.ACTIONS.PRINT' | translate }}"
|
||||||
|
data-automation-id="adf-toolbar-print"
|
||||||
|
(click)="onPrintContent($event)">
|
||||||
|
<mat-icon>print</mat-icon>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-toolbar-custom-actions>
|
||||||
|
</adf-viewer>
|
@ -0,0 +1 @@
|
|||||||
|
/* stylelint-disable-next-line no-empty-source */
|
@ -0,0 +1,907 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { NodeEntry, VersionEntry } from '@alfresco/js-api';
|
||||||
|
import { AlfrescoViewerComponent, RenditionViewerService } from '@alfresco/adf-content-services';
|
||||||
|
import {
|
||||||
|
NodesApiService,
|
||||||
|
CoreTestingModule,
|
||||||
|
setupTestBed,
|
||||||
|
EventMock,
|
||||||
|
FileModel, UploadService, ViewUtilService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { throwError } from 'rxjs';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-container-toolbar',
|
||||||
|
template: `
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-viewer-toolbar>
|
||||||
|
<div class="custom-toolbar-element"></div>
|
||||||
|
</adf-viewer-toolbar>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ViewerWithCustomToolbarComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-container-toolbar-actions',
|
||||||
|
template: `
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-viewer-toolbar-actions>
|
||||||
|
<button mat-icon-button id="custom-button">
|
||||||
|
<mat-icon>alarm</mat-icon>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-toolbar-actions>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ViewerWithCustomToolbarActionsComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-container-sidebar',
|
||||||
|
template: `
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-viewer-sidebar>
|
||||||
|
<div class="custom-sidebar"></div>
|
||||||
|
</adf-viewer-sidebar>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ViewerWithCustomSidebarComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-dialog-dummy',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
class DummyDialogComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-container-open-with',
|
||||||
|
template: `
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-viewer-open-with>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>Option 1</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<mat-icon>voicemail</mat-icon>
|
||||||
|
<span>Option 2</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>notifications_off</mat-icon>
|
||||||
|
<span>Option 3</span>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-open-with>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ViewerWithCustomOpenWithComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-container-more-actions',
|
||||||
|
template: `
|
||||||
|
<adf-alfresco-viewer>
|
||||||
|
<adf-viewer-more-actions>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>Action One</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<mat-icon>voicemail</mat-icon>
|
||||||
|
<span>Action Two</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>
|
||||||
|
<mat-icon>notifications_off</mat-icon>
|
||||||
|
<span>Action Three</span>
|
||||||
|
</button>
|
||||||
|
</adf-viewer-more-actions>
|
||||||
|
</adf-alfresco-viewer>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ViewerWithCustomMoreActionsComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe('AlfrescoViewerComponent', () => {
|
||||||
|
|
||||||
|
let component: AlfrescoViewerComponent;
|
||||||
|
let fixture: ComponentFixture<AlfrescoViewerComponent>;
|
||||||
|
let element: HTMLElement;
|
||||||
|
|
||||||
|
let nodesApiService: NodesApiService;
|
||||||
|
let dialog: MatDialog;
|
||||||
|
let uploadService: UploadService;
|
||||||
|
let extensionService: AppExtensionService;
|
||||||
|
let renditionService: RenditionViewerService;
|
||||||
|
let viewUtilService: ViewUtilService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule,
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
CoreTestingModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ViewerWithCustomToolbarComponent,
|
||||||
|
ViewerWithCustomSidebarComponent,
|
||||||
|
ViewerWithCustomOpenWithComponent,
|
||||||
|
ViewerWithCustomMoreActionsComponent,
|
||||||
|
ViewerWithCustomToolbarActionsComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: RenditionViewerService, useValue: {
|
||||||
|
getNodeRendition: () => throwError('thrown'),
|
||||||
|
generateMediaTracksRendition: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{provide: Location, useClass: SpyLocation},
|
||||||
|
MatDialog
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AlfrescoViewerComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
uploadService = TestBed.inject(UploadService);
|
||||||
|
nodesApiService = TestBed.inject(NodesApiService);
|
||||||
|
dialog = TestBed.inject(MatDialog);
|
||||||
|
extensionService = TestBed.inject(AppExtensionService);
|
||||||
|
renditionService = TestBed.inject(RenditionViewerService);
|
||||||
|
viewUtilService = TestBed.inject(ViewUtilService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('Extension Type Test', () => {
|
||||||
|
|
||||||
|
|
||||||
|
it('should use external viewer to display node by id', fakeAsync(() => {
|
||||||
|
const extension: ViewerExtensionRef = {
|
||||||
|
component: 'custom.component',
|
||||||
|
id: 'custom.component.id',
|
||||||
|
fileExtension: '*'
|
||||||
|
};
|
||||||
|
spyOn(extensionService, 'getViewerExtensions').and.returnValue([extension]);
|
||||||
|
spyOn(renditionService, 'getNodeRendition');
|
||||||
|
spyOn(renditionService, 'generateMediaTracksRendition');
|
||||||
|
spyOn(viewUtilService, 'getViewerType').and.returnValue('external');
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(AlfrescoViewerComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
spyOn(component.nodesApi, 'getNode').and.callFake(() => Promise.resolve(new NodeEntry({entry: {}})));
|
||||||
|
|
||||||
|
component.nodeId = '37f7f34d-4e64-4db6-bb3f-5c89f7844251';
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
expect(component.nodesApi.getNode).toHaveBeenCalled();
|
||||||
|
expect(renditionService.getNodeRendition).not.toHaveBeenCalled();
|
||||||
|
expect(renditionService.generateMediaTracksRendition).not.toHaveBeenCalled();
|
||||||
|
expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull();
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MimeType handling', () => {
|
||||||
|
|
||||||
|
it('should node without content show unkonwn', (done) => {
|
||||||
|
const displayName = 'the-name';
|
||||||
|
const contentUrl = '/content/url/path';
|
||||||
|
|
||||||
|
component.nodeId = '12';
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(new NodeEntry({
|
||||||
|
entry: {content: {name: displayName, id: '12'}}
|
||||||
|
})));
|
||||||
|
|
||||||
|
spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl);
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change display name every time node changes', fakeAsync(() => {
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValues(
|
||||||
|
Promise.resolve(new NodeEntry({entry: {name: 'file1', content: {}}})),
|
||||||
|
Promise.resolve(new NodeEntry({entry: {name: 'file2', content: {}}}))
|
||||||
|
);
|
||||||
|
|
||||||
|
component.showViewer = true;
|
||||||
|
|
||||||
|
component.nodeId = 'id1';
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file1');
|
||||||
|
|
||||||
|
component.nodeId = 'id2';
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file2');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should append version of the file to the file content URL', fakeAsync(() => {
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValue(
|
||||||
|
Promise.resolve(new NodeEntry({
|
||||||
|
entry: {
|
||||||
|
name: 'file1.pdf',
|
||||||
|
content: {},
|
||||||
|
properties: {'cm:versionLabel': '10'}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
spyOn(component['versionsApi'], 'getVersion').and.returnValue(Promise.resolve(undefined));
|
||||||
|
|
||||||
|
component.nodeId = 'id1';
|
||||||
|
component.showViewer = true;
|
||||||
|
|
||||||
|
component.versionId = null;
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file1.pdf');
|
||||||
|
expect(component.urlFileContent).toContain('/public/alfresco/versions/1/nodes/id1/content?attachment=false&10');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should change display name every time node\`s version changes', fakeAsync(() => {
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValue(
|
||||||
|
Promise.resolve(new NodeEntry({entry: {name: 'node1', content: {}}}))
|
||||||
|
);
|
||||||
|
|
||||||
|
spyOn(component['versionsApi'], 'getVersion').and.returnValues(
|
||||||
|
Promise.resolve(new VersionEntry({entry: {name: 'file1', content: {}}})),
|
||||||
|
Promise.resolve(new VersionEntry({entry: {name: 'file2', content: {}}}))
|
||||||
|
);
|
||||||
|
|
||||||
|
component.nodeId = 'id1';
|
||||||
|
component.showViewer = true;
|
||||||
|
|
||||||
|
component.versionId = '1.0';
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file1');
|
||||||
|
|
||||||
|
component.versionId = '1.1';
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file2');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update node only if node name changed', fakeAsync(() => {
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValues(
|
||||||
|
Promise.resolve(new NodeEntry({entry: {name: 'file1', content: {}}}))
|
||||||
|
);
|
||||||
|
|
||||||
|
component.showViewer = true;
|
||||||
|
|
||||||
|
component.nodeId = 'id1';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.fileName).toBe('file1');
|
||||||
|
|
||||||
|
nodesApiService.nodeUpdated.next({id: 'id1', name: 'file2'} as any);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.fileName).toBe('file2');
|
||||||
|
|
||||||
|
nodesApiService.nodeUpdated.next({id: 'id1', name: 'file3'} as any);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.fileName).toBe('file3');
|
||||||
|
|
||||||
|
nodesApiService.nodeUpdated.next({id: 'id2', name: 'file4'} as any);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.fileName).toBe('file3');
|
||||||
|
expect(component.nodeId).toBe('id1');
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Viewer Example Component Rendering', () => {
|
||||||
|
|
||||||
|
it('should use custom toolbar', (done) => {
|
||||||
|
const customFixture = TestBed.createComponent(ViewerWithCustomToolbarComponent);
|
||||||
|
const customElement: HTMLElement = customFixture.nativeElement;
|
||||||
|
|
||||||
|
customFixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(customElement.querySelector('.custom-toolbar-element')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom toolbar actions', (done) => {
|
||||||
|
const customFixture = TestBed.createComponent(ViewerWithCustomToolbarActionsComponent);
|
||||||
|
const customElement: HTMLElement = customFixture.nativeElement;
|
||||||
|
|
||||||
|
customFixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(customElement.querySelector('#custom-button')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom info drawer', (done) => {
|
||||||
|
const customFixture = TestBed.createComponent(ViewerWithCustomSidebarComponent);
|
||||||
|
const customElement: HTMLElement = customFixture.nativeElement;
|
||||||
|
|
||||||
|
customFixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(customElement.querySelector('.custom-info-drawer-element')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom open with menu', (done) => {
|
||||||
|
const customFixture = TestBed.createComponent(ViewerWithCustomOpenWithComponent);
|
||||||
|
const customElement: HTMLElement = customFixture.nativeElement;
|
||||||
|
|
||||||
|
customFixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(customElement.querySelector('.adf-viewer-container-open-with')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom more actions menu', (done) => {
|
||||||
|
const customFixture = TestBed.createComponent(ViewerWithCustomMoreActionsComponent);
|
||||||
|
const customElement: HTMLElement = customFixture.nativeElement;
|
||||||
|
|
||||||
|
customFixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(customElement.querySelector('.adf-viewer-container-more-actions')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('error handling', () => {
|
||||||
|
|
||||||
|
it('should show unknown view when node file not found', (done) => {
|
||||||
|
spyOn(component['nodesApi'], 'getNode')
|
||||||
|
.and.returnValue(Promise.reject({}));
|
||||||
|
|
||||||
|
component.nodeId = 'the-node-id-of-the-file-to-preview';
|
||||||
|
component.mimeType = null;
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show unknown view when sharedLink file not found', (done) => {
|
||||||
|
spyOn(component['sharedLinksApi'], 'getSharedLink')
|
||||||
|
.and.returnValue(Promise.reject({}));
|
||||||
|
|
||||||
|
component.sharedLinkId = 'the-Shared-Link-id';
|
||||||
|
component.mimeType = null;
|
||||||
|
component.nodeId = null;
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should raise an event when the shared link is invalid', fakeAsync(() => {
|
||||||
|
spyOn(component['sharedLinksApi'], 'getSharedLink')
|
||||||
|
.and.returnValue(Promise.reject({}));
|
||||||
|
|
||||||
|
component.sharedLinkId = 'the-Shared-Link-id';
|
||||||
|
component.mimeType = null;
|
||||||
|
component.nodeId = null;
|
||||||
|
|
||||||
|
component.invalidSharedLink.subscribe((emittedValue) => {
|
||||||
|
expect(emittedValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
}));
|
||||||
|
//
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Toolbar', () => {
|
||||||
|
|
||||||
|
it('should show only next file button', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = false;
|
||||||
|
component.canNavigateNext = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton).not.toBeNull();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should provide tooltip for next file button', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = false;
|
||||||
|
component.canNavigateNext = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton.title).toBe('ADF_VIEWER.ACTIONS.NEXT_FILE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show only previous file button', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = true;
|
||||||
|
component.canNavigateNext = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton).toBeNull();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should provide tooltip for the previous file button', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = true;
|
||||||
|
component.canNavigateNext = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton.title).toBe('ADF_VIEWER.ACTIONS.PREV_FILE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show both file navigation buttons', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = true;
|
||||||
|
component.canNavigateNext = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton).not.toBeNull();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show navigation buttons', async () => {
|
||||||
|
component.allowNavigate = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton).toBeNull();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should now show navigation buttons even if navigation enabled', async () => {
|
||||||
|
component.allowNavigate = true;
|
||||||
|
component.canNavigateBefore = false;
|
||||||
|
component.canNavigateNext = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const nextButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-next-file"]');
|
||||||
|
expect(nextButton).toBeNull();
|
||||||
|
|
||||||
|
const prevButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-toolbar-pref-file"]');
|
||||||
|
expect(prevButton).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render fullscreen button', () => {
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-fullscreen"]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render default download button', (done) => {
|
||||||
|
component.allowDownload = true;
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render default download button', (done) => {
|
||||||
|
component.allowDownload = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render default print button', (done) => {
|
||||||
|
component.allowPrint = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render default print button', (done) => {
|
||||||
|
component.allowPrint = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke print action with the toolbar button', (done) => {
|
||||||
|
component.allowPrint = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
spyOn(component, 'onPrintContent').and.stub();
|
||||||
|
|
||||||
|
const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-print"]') as HTMLButtonElement;
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(component.onPrintContent).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get and assign node for download', (done) => {
|
||||||
|
component.nodeId = '12';
|
||||||
|
const displayName = 'the-name';
|
||||||
|
const nodeDetails = {
|
||||||
|
entry: {name: displayName, id: '12', content: {mimeType: 'txt'}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentUrl = '/content/url/path';
|
||||||
|
|
||||||
|
const node = new NodeEntry(nodeDetails);
|
||||||
|
|
||||||
|
spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(node));
|
||||||
|
spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl);
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.nodeEntry).toBe(node);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render close viewer button if it is not a shared link', (done) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeDefined();
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render close viewer button if it is a shared link', (done) => {
|
||||||
|
spyOn(component['sharedLinksApi'], 'getSharedLink')
|
||||||
|
.and.returnValue(Promise.reject({}));
|
||||||
|
|
||||||
|
component.sharedLinkId = 'the-Shared-Link-id';
|
||||||
|
component.mimeType = null;
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Base component', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.mimeType = 'application/pdf';
|
||||||
|
component.nodeId = 'id1';
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SideBar Test', () => {
|
||||||
|
|
||||||
|
it('should NOT display sidebar if is not allowed', (done) => {
|
||||||
|
component.showRightSidebar = true;
|
||||||
|
component.allowRightSidebar = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const sidebar = element.querySelector('#adf-right-sidebar');
|
||||||
|
expect(sidebar).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display sidebar on the right side', (done) => {
|
||||||
|
component.allowRightSidebar = true;
|
||||||
|
component.showRightSidebar = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const sidebar = element.querySelector('#adf-right-sidebar');
|
||||||
|
expect(getComputedStyle(sidebar).order).toEqual('4');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT display left sidebar if is not allowed', (done) => {
|
||||||
|
component.showLeftSidebar = true;
|
||||||
|
component.allowLeftSidebar = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const sidebar = element.querySelector('#adf-left-sidebar');
|
||||||
|
expect(sidebar).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display sidebar on the left side', (done) => {
|
||||||
|
component.allowLeftSidebar = true;
|
||||||
|
component.showLeftSidebar = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const sidebar = element.querySelector('#adf-left-sidebar');
|
||||||
|
expect(getComputedStyle(sidebar).order).toEqual('1');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('View', () => {
|
||||||
|
|
||||||
|
describe('Overlay mode true', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.overlayMode = true;
|
||||||
|
component.fileName = 'fake-test-file.pdf';
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should header be present if is overlay mode', () => {
|
||||||
|
expect(element.querySelector('.adf-viewer-toolbar')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should Name File be present if is overlay mode ', (done) => {
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('fake-test-file.pdf');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should Close button be present if overlay mode', (done) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('.adf-viewer-close-button')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should Click on close button hide the viewer', (done) => {
|
||||||
|
const closeButton: any = element.querySelector('.adf-viewer-close-button');
|
||||||
|
closeButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should Esc button hide the viewer', (done) => {
|
||||||
|
EventMock.keyDown(27);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not close the viewer on Escape event if dialog was opened', (done) => {
|
||||||
|
const event = new KeyboardEvent('keydown', {
|
||||||
|
bubbles: true,
|
||||||
|
keyCode: 27
|
||||||
|
} as KeyboardEventInit);
|
||||||
|
|
||||||
|
const dialogRef = dialog.open(DummyDialogComponent);
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(() => {
|
||||||
|
EventMock.keyDown(27);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
document.body.dispatchEvent(event);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Overlay mode false', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.overlayMode = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should Esc button not hide the viewer if is not overlay mode', (done) => {
|
||||||
|
EventMock.keyDown(27);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Attribute', () => {
|
||||||
|
|
||||||
|
it('should FileNodeId present not thrown any error ', () => {
|
||||||
|
component.showViewer = true;
|
||||||
|
component.nodeId = 'file-node-id';
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
component.ngOnChanges();
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should showViewer default value be true', () => {
|
||||||
|
expect(component.showViewer).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should viewer be hide if showViewer value is false', () => {
|
||||||
|
component.showViewer = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Events', () => {
|
||||||
|
|
||||||
|
it('should update version when emitted by image-viewer and user has update permissions', () => {
|
||||||
|
spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => {
|
||||||
|
});
|
||||||
|
spyOn(uploadService, 'addToQueue');
|
||||||
|
component.readOnly = false;
|
||||||
|
component.nodeEntry = new NodeEntry({
|
||||||
|
entry: {
|
||||||
|
name: 'fakeImage.png',
|
||||||
|
id: '12',
|
||||||
|
content: {mimeType: 'img/png'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
|
||||||
|
const fakeBlob = new Blob([data], {type: 'image/png'});
|
||||||
|
const newImageFile: File = new File([fakeBlob], component?.nodeEntry?.entry?.name, {type: component?.nodeEntry?.entry?.content?.mimeType});
|
||||||
|
const newFile = new FileModel(
|
||||||
|
newImageFile,
|
||||||
|
{
|
||||||
|
majorVersion: false,
|
||||||
|
newVersion: true,
|
||||||
|
parentId: component?.nodeEntry?.entry?.parentId,
|
||||||
|
nodeType: component?.nodeEntry?.entry?.content?.mimeType
|
||||||
|
},
|
||||||
|
component.nodeEntry.entry?.id
|
||||||
|
);
|
||||||
|
component.onSubmitFile(fakeBlob);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(uploadService.addToQueue).toHaveBeenCalledWith(...[newFile]);
|
||||||
|
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update version when emitted by image-viewer and user doesn`t have update permissions', () => {
|
||||||
|
spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => {
|
||||||
|
});
|
||||||
|
component.readOnly = true;
|
||||||
|
component.nodeEntry = new NodeEntry({
|
||||||
|
entry: {
|
||||||
|
name: 'fakeImage.png',
|
||||||
|
id: '12',
|
||||||
|
content: {mimeType: 'img/png'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
|
||||||
|
const fakeBlob = new Blob([data], {type: 'image/png'});
|
||||||
|
component.onSubmitFile(fakeBlob);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,451 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
ViewChild,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
AlfrescoApiService, ContentService,
|
||||||
|
FileModel,
|
||||||
|
LogService,
|
||||||
|
NodesApiService,
|
||||||
|
Track,
|
||||||
|
UploadService,
|
||||||
|
ViewerComponent,
|
||||||
|
ViewerMoreActionsComponent,
|
||||||
|
ViewerOpenWithComponent,
|
||||||
|
ViewerSidebarComponent,
|
||||||
|
ViewerToolbarActionsComponent,
|
||||||
|
ViewerToolbarComponent,
|
||||||
|
ViewUtilService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import {
|
||||||
|
ContentApi,
|
||||||
|
Node,
|
||||||
|
NodeEntry,
|
||||||
|
NodesApi,
|
||||||
|
RenditionEntry,
|
||||||
|
SharedlinksApi,
|
||||||
|
Version,
|
||||||
|
VersionEntry,
|
||||||
|
VersionsApi
|
||||||
|
} from '@alfresco/js-api';
|
||||||
|
import { RenditionViewerService } from '../services/rendition-viewer.service';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-alfresco-viewer',
|
||||||
|
templateUrl: './alfresco-viewer.component.html',
|
||||||
|
styleUrls: ['./alfresco-viewer.component.scss'],
|
||||||
|
host: {class: 'adf-alfresco-viewer'},
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
providers: [ViewUtilService]
|
||||||
|
})
|
||||||
|
export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild('adfViewer')
|
||||||
|
adfViewer: ViewerComponent<{node: Node}>;
|
||||||
|
|
||||||
|
@ContentChild(ViewerToolbarComponent)
|
||||||
|
toolbar: ViewerToolbarComponent;
|
||||||
|
|
||||||
|
@ContentChild(ViewerSidebarComponent)
|
||||||
|
sidebar: ViewerSidebarComponent;
|
||||||
|
|
||||||
|
@ContentChild(ViewerToolbarActionsComponent)
|
||||||
|
toolbarActions: ViewerToolbarActionsComponent;
|
||||||
|
|
||||||
|
@ContentChild(ViewerMoreActionsComponent)
|
||||||
|
moreActions: ViewerMoreActionsComponent;
|
||||||
|
|
||||||
|
@ContentChild(ViewerOpenWithComponent)
|
||||||
|
openWith: ViewerOpenWithComponent;
|
||||||
|
|
||||||
|
/** Node Id of the file to load. */
|
||||||
|
@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;
|
||||||
|
|
||||||
|
/** Hide or show the viewer */
|
||||||
|
@Input()
|
||||||
|
showViewer = true;
|
||||||
|
|
||||||
|
/** Number of times the Viewer will retry fetching content Rendition.
|
||||||
|
* There is a delay of at least one second between attempts.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
maxRetries = 30;
|
||||||
|
|
||||||
|
/** Allows `back` navigation */
|
||||||
|
@Input()
|
||||||
|
allowGoBack = true;
|
||||||
|
|
||||||
|
/** Hide or show the toolbar */
|
||||||
|
@Input()
|
||||||
|
showToolbar = true;
|
||||||
|
|
||||||
|
/** If `true` then show the Viewer as a full page over the current content.
|
||||||
|
* Otherwise fit inside the parent div.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
overlayMode = false;
|
||||||
|
|
||||||
|
/** Toggles before/next navigation. You can use the arrow buttons to navigate
|
||||||
|
* between documents in the collection.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
allowNavigate = false;
|
||||||
|
|
||||||
|
/** Toggles the "before" ("<") button. Requires `allowNavigate` to be enabled. */
|
||||||
|
@Input()
|
||||||
|
canNavigateBefore = true;
|
||||||
|
|
||||||
|
/** Toggles the next (">") button. Requires `allowNavigate` to be enabled. */
|
||||||
|
@Input()
|
||||||
|
canNavigateNext = true;
|
||||||
|
|
||||||
|
/** Allow the left the sidebar. */
|
||||||
|
@Input()
|
||||||
|
allowLeftSidebar = false;
|
||||||
|
|
||||||
|
/** Allow the right sidebar. */
|
||||||
|
@Input()
|
||||||
|
allowRightSidebar = false;
|
||||||
|
|
||||||
|
/** Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. */
|
||||||
|
@Input()
|
||||||
|
showRightSidebar = false;
|
||||||
|
|
||||||
|
/** Toggles left sidebar visibility. Requires `allowLeftSidebar` to be set to `true`. */
|
||||||
|
@Input()
|
||||||
|
showLeftSidebar = false;
|
||||||
|
|
||||||
|
/** Toggles downloading. */
|
||||||
|
@Input()
|
||||||
|
allowDownload = true;
|
||||||
|
|
||||||
|
/** Toggles printing. */
|
||||||
|
@Input()
|
||||||
|
allowPrint = false;
|
||||||
|
|
||||||
|
/** Toggles the 'Full Screen' feature. */
|
||||||
|
@Input()
|
||||||
|
allowFullScreen = true;
|
||||||
|
|
||||||
|
/** The template for the right sidebar. The template context contains the loaded node data. */
|
||||||
|
@Input()
|
||||||
|
sidebarRightTemplate: TemplateRef<any> = null;
|
||||||
|
|
||||||
|
/** The template for the left sidebar. The template context contains the loaded node data. */
|
||||||
|
@Input()
|
||||||
|
sidebarLeftTemplate: TemplateRef<any> = null;
|
||||||
|
|
||||||
|
/** Emitted when the shared link used is not valid. */
|
||||||
|
@Output()
|
||||||
|
invalidSharedLink = new EventEmitter();
|
||||||
|
|
||||||
|
/** Emitted when user clicks 'Navigate Before' ("<") button. */
|
||||||
|
@Output()
|
||||||
|
navigateBefore = new EventEmitter<MouseEvent | KeyboardEvent>();
|
||||||
|
|
||||||
|
/** Emitted when user clicks 'Navigate Next' (">") button. */
|
||||||
|
@Output()
|
||||||
|
navigateNext = new EventEmitter<MouseEvent | KeyboardEvent>();
|
||||||
|
|
||||||
|
/** Emitted when the viewer close */
|
||||||
|
@Output()
|
||||||
|
showViewerChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
|
private cacheBusterNumber: number;
|
||||||
|
|
||||||
|
versionEntry: VersionEntry;
|
||||||
|
urlFileContent: string;
|
||||||
|
fileName: string;
|
||||||
|
mimeType: string;
|
||||||
|
nodeEntry: NodeEntry;
|
||||||
|
tracks: Track[] = [];
|
||||||
|
readOnly: boolean = true;
|
||||||
|
|
||||||
|
sidebarRightTemplateContext: { node: Node } = {node: null};
|
||||||
|
sidebarLeftTemplateContext: { node: Node } = {node: null};
|
||||||
|
|
||||||
|
_sharedLinksApi: SharedlinksApi;
|
||||||
|
get sharedLinksApi(): SharedlinksApi {
|
||||||
|
this._sharedLinksApi = this._sharedLinksApi ?? new SharedlinksApi(this.apiService.getInstance());
|
||||||
|
return this._sharedLinksApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_versionsApi: VersionsApi;
|
||||||
|
get versionsApi(): VersionsApi {
|
||||||
|
this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance());
|
||||||
|
return this._versionsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodesApi: NodesApi;
|
||||||
|
get nodesApi(): NodesApi {
|
||||||
|
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
|
||||||
|
return this._nodesApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_contentApi: ContentApi;
|
||||||
|
get contentApi(): ContentApi {
|
||||||
|
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
|
||||||
|
return this._contentApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private apiService: AlfrescoApiService,
|
||||||
|
private nodesApiService: NodesApiService,
|
||||||
|
private renditionViewerService: RenditionViewerService,
|
||||||
|
private viewUtilService: ViewUtilService,
|
||||||
|
private logService: LogService,
|
||||||
|
private contentService: ContentService,
|
||||||
|
private uploadService: UploadService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private cdr: ChangeDetectorRef) {
|
||||||
|
renditionViewerService.maxRetries = this.maxRetries;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.nodesApiService.nodeUpdated.pipe(
|
||||||
|
filter((node) => node && node.id === this.nodeId &&
|
||||||
|
(node.name !== this.fileName ||
|
||||||
|
this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(node))),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
).subscribe((node) => this.onNodeUpdated(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onNodeUpdated(node: Node) {
|
||||||
|
if (node && node.id === this.nodeId) {
|
||||||
|
this.generateCacheBusterNumber();
|
||||||
|
|
||||||
|
await this.setUpNodeFile(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNodeVersionProperty(node: Node): string {
|
||||||
|
return node?.properties['cm:versionLabel'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setupSharedLink() {
|
||||||
|
this.allowGoBack = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sharedLinkEntry = await this.sharedLinksApi.getSharedLink(this.sharedLinkId);
|
||||||
|
await this.setUpSharedLinkFile(sharedLinkEntry);
|
||||||
|
} catch (error) {
|
||||||
|
this.logService.error('This sharedLink does not exist');
|
||||||
|
this.invalidSharedLink.next();
|
||||||
|
this.mimeType = 'invalid-link';
|
||||||
|
this.urlFileContent = 'invalid-file';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setupNode() {
|
||||||
|
try {
|
||||||
|
this.nodeEntry = await this.nodesApi.getNode(this.nodeId, {include: ['allowableOperations']});
|
||||||
|
if (this.versionId) {
|
||||||
|
this.versionEntry = await this.versionsApi.getVersion(this.nodeId, this.versionId);
|
||||||
|
await this.setUpNodeFile(this.nodeEntry.entry, this.versionEntry.entry);
|
||||||
|
} else {
|
||||||
|
await this.setUpNodeFile(this.nodeEntry.entry);
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.urlFileContent = 'invalid-node';
|
||||||
|
this.logService.error('This node does not exist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setUpNodeFile(nodeData: Node, versionData?: Version): Promise<void> {
|
||||||
|
|
||||||
|
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');
|
||||||
|
let mimeType;
|
||||||
|
let urlFileContent;
|
||||||
|
|
||||||
|
if (versionData && versionData.content) {
|
||||||
|
mimeType = versionData.content.mimeType;
|
||||||
|
} else if (nodeData.content) {
|
||||||
|
mimeType = nodeData.content.mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentFileVersion = this.nodeEntry?.entry?.properties && this.nodeEntry.entry.properties['cm:versionLabel'] ?
|
||||||
|
encodeURI(this.nodeEntry?.entry?.properties['cm:versionLabel']) : encodeURI('1.0');
|
||||||
|
|
||||||
|
urlFileContent = versionData ? this.contentApi.getVersionContentUrl(this.nodeId, versionData.id) :
|
||||||
|
this.contentApi.getContentUrl(this.nodeId);
|
||||||
|
urlFileContent = this.cacheBusterNumber ? urlFileContent + '&' + currentFileVersion + '&' + this.cacheBusterNumber :
|
||||||
|
urlFileContent + '&' + currentFileVersion;
|
||||||
|
|
||||||
|
const fileExtension = this.viewUtilService.getFileExtension(versionData ? versionData.name : nodeData.name);
|
||||||
|
this.fileName = versionData ? versionData.name : nodeData.name;
|
||||||
|
const viewerType = this.viewUtilService.getViewerType(fileExtension, mimeType);
|
||||||
|
|
||||||
|
if (viewerType === 'unknown') {
|
||||||
|
let nodeRendition;
|
||||||
|
if (versionData) {
|
||||||
|
nodeRendition = await this.renditionViewerService.getNodeRendition(nodeData.id, versionData.id);
|
||||||
|
} else {
|
||||||
|
nodeRendition = await this.renditionViewerService.getNodeRendition(nodeData.id);
|
||||||
|
}
|
||||||
|
if(nodeRendition){
|
||||||
|
urlFileContent = nodeRendition.url;
|
||||||
|
mimeType = nodeRendition.mimeType;
|
||||||
|
}
|
||||||
|
} else if (viewerType === 'media') {
|
||||||
|
this.tracks = await this.renditionViewerService.generateMediaTracksRendition(this.nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
this.urlFileContent = urlFileContent;
|
||||||
|
this.sidebarRightTemplateContext.node = nodeData;
|
||||||
|
this.sidebarLeftTemplateContext.node = nodeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setUpSharedLinkFile(details: any) {
|
||||||
|
let mimeType = details.entry.content.mimeType;
|
||||||
|
const fileExtension = this.viewUtilService.getFileExtension(details.entry.name);
|
||||||
|
this.fileName = details.entry.name;
|
||||||
|
let urlFileContent = this.contentApi.getSharedLinkContentUrl(this.sharedLinkId, false);
|
||||||
|
const viewerType = this.viewUtilService.getViewerType(fileExtension, mimeType);
|
||||||
|
|
||||||
|
if (viewerType === 'unknown') {
|
||||||
|
({
|
||||||
|
url: urlFileContent,
|
||||||
|
mimeType
|
||||||
|
} = await this.getSharedLinkRendition(this.sharedLinkId));
|
||||||
|
}
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
this.urlFileContent = urlFileContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getSharedLinkRendition(sharedId: string): Promise<{ url: string; mimeType: string }> {
|
||||||
|
try {
|
||||||
|
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'pdf');
|
||||||
|
if (rendition.entry.status.toString() === 'CREATED') {
|
||||||
|
const urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'pdf');
|
||||||
|
return {url: urlFileContent, mimeType: 'application/pdf'};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logService.error(error);
|
||||||
|
try {
|
||||||
|
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'imgpreview');
|
||||||
|
if (rendition.entry.status.toString() === 'CREATED') {
|
||||||
|
const urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'imgpreview');
|
||||||
|
return {url: urlFileContent, mimeType: 'image/png'};
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (renditionError) {
|
||||||
|
this.logService.error(renditionError);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateCacheBusterNumber() {
|
||||||
|
this.cacheBusterNumber = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close the viewer
|
||||||
|
*/
|
||||||
|
onClose() {
|
||||||
|
this.showViewerChange.emit(this.showViewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPrintContent(event: MouseEvent) {
|
||||||
|
if (this.allowPrint) {
|
||||||
|
if (!event.defaultPrevented) {
|
||||||
|
this.renditionViewerService.printFileGeneric(this.nodeId, this.mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmitFile(newImageBlob: Blob) {
|
||||||
|
if (this?.nodeEntry?.entry?.id && !this.readOnly) {
|
||||||
|
const newImageFile: File = new File([newImageBlob], this?.nodeEntry?.entry?.name, {type: this?.nodeEntry?.entry?.content?.mimeType});
|
||||||
|
const newFile = new FileModel(
|
||||||
|
newImageFile,
|
||||||
|
{
|
||||||
|
majorVersion: false,
|
||||||
|
newVersion: true,
|
||||||
|
parentId: this?.nodeEntry?.entry?.parentId,
|
||||||
|
nodeType: this?.nodeEntry?.entry?.content?.mimeType
|
||||||
|
},
|
||||||
|
this?.nodeEntry?.entry?.id
|
||||||
|
);
|
||||||
|
this.uploadService.addToQueue(...[newFile]);
|
||||||
|
this.uploadService.uploadFilesInTheQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onNavigateBeforeClick(event: MouseEvent | KeyboardEvent) {
|
||||||
|
this.navigateBefore.next(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onNavigateNextClick(event: MouseEvent | KeyboardEvent) {
|
||||||
|
this.navigateNext.next(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSourceDefined(): boolean {
|
||||||
|
return !!(this.nodeId || this.sharedLinkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
if (this.showViewer) {
|
||||||
|
if (!this.isSourceDefined()) {
|
||||||
|
throw new Error('A content source attribute value is missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.nodeId) {
|
||||||
|
this.setupNode();
|
||||||
|
} else if (this.sharedLinkId) {
|
||||||
|
this.setupSharedLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
lib/content-services/src/lib/viewer/index.ts
Normal file
18
lib/content-services/src/lib/viewer/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './public-api';
|
21
lib/content-services/src/lib/viewer/public-api.ts
Normal file
21
lib/content-services/src/lib/viewer/public-api.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './services/rendition-viewer.service';
|
||||||
|
export * from './components/alfresco-viewer.component';
|
||||||
|
|
||||||
|
export * from './alfresco-viewer.module';
|
@ -0,0 +1,306 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ContentApi, RenditionEntry, RenditionPaging, RenditionsApi, VersionsApi } from '@alfresco/js-api';
|
||||||
|
import { AlfrescoApiService , LogService, Track,TranslationService, ViewUtilService } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class RenditionViewerService {
|
||||||
|
|
||||||
|
static TARGET = '_new';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content groups based on categorization of files that can be viewed in the web browser. This
|
||||||
|
* implementation or grouping is tied to the definition the ng component: ViewerRenderComponent
|
||||||
|
*/
|
||||||
|
static ContentGroup = {
|
||||||
|
IMAGE: 'image',
|
||||||
|
MEDIA: 'media',
|
||||||
|
PDF: 'pdf',
|
||||||
|
TEXT: 'text'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the rendition with the media subtitles in the supported format
|
||||||
|
*/
|
||||||
|
static SUBTITLES_RENDITION_NAME = 'webvtt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on ViewerRenderComponent Implementation, this value is used to determine how many times we try
|
||||||
|
* to get the rendition of a file for preview, or printing.
|
||||||
|
*/
|
||||||
|
maxRetries = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout used for setInterval.
|
||||||
|
*/
|
||||||
|
private TRY_TIMEOUT: number = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
_renditionsApi: RenditionsApi;
|
||||||
|
get renditionsApi(): RenditionsApi {
|
||||||
|
this._renditionsApi = this._renditionsApi ?? new RenditionsApi(this.apiService.getInstance());
|
||||||
|
return this._renditionsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_contentApi: ContentApi;
|
||||||
|
get contentApi(): ContentApi {
|
||||||
|
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
|
||||||
|
return this._contentApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_versionsApi: VersionsApi;
|
||||||
|
private DEFAULT_RENDITION: string = 'imgpreview';
|
||||||
|
|
||||||
|
get versionsApi(): VersionsApi {
|
||||||
|
this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance());
|
||||||
|
return this._versionsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private apiService: AlfrescoApiService,
|
||||||
|
private logService: LogService,
|
||||||
|
private translateService: TranslationService,
|
||||||
|
private viewUtilsService: ViewUtilService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getRenditionUrl(nodeId: string, type: string, renditionExists: boolean): string {
|
||||||
|
return (renditionExists && type !== RenditionViewerService.ContentGroup.IMAGE) ?
|
||||||
|
this.contentApi.getRenditionUrl(nodeId, RenditionViewerService.ContentGroup.PDF) :
|
||||||
|
this.contentApi.getContentUrl(nodeId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitRendition(nodeId: string, renditionId: string, retries: number): Promise<RenditionEntry> {
|
||||||
|
const rendition = await this.renditionsApi.getRendition(nodeId, renditionId);
|
||||||
|
|
||||||
|
if (this.maxRetries < retries) {
|
||||||
|
const status = rendition.entry.status.toString();
|
||||||
|
|
||||||
|
if (status === 'CREATED') {
|
||||||
|
return rendition;
|
||||||
|
} else {
|
||||||
|
retries += 1;
|
||||||
|
await this.wait(1000);
|
||||||
|
return this.waitRendition(nodeId, renditionId, retries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private wait(ms: number): Promise<any> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRendition(nodeId: string, renditionId: string): Promise<RenditionEntry> {
|
||||||
|
const renditionPaging: RenditionPaging = await this.renditionsApi.listRenditions(nodeId);
|
||||||
|
let rendition: RenditionEntry = renditionPaging.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId);
|
||||||
|
|
||||||
|
if (rendition) {
|
||||||
|
const status = rendition.entry.status.toString();
|
||||||
|
|
||||||
|
if (status === 'NOT_CREATED') {
|
||||||
|
try {
|
||||||
|
await this.renditionsApi.createRendition(nodeId, {id: renditionId});
|
||||||
|
rendition = await this.waitRendition(nodeId, renditionId, 0);
|
||||||
|
} catch (err) {
|
||||||
|
this.logService.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Promise<RenditionEntry>((resolve) => resolve(rendition));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNodeRendition(nodeId: string, versionId?: string): Promise<{ url: string; mimeType: string }> {
|
||||||
|
try {
|
||||||
|
return versionId ? await this.resolveNodeRendition(nodeId, 'pdf', versionId) :
|
||||||
|
await this.resolveNodeRendition(nodeId, 'pdf');
|
||||||
|
} catch (err) {
|
||||||
|
this.logService.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resolveNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise<{ url: string; mimeType: string }> {
|
||||||
|
renditionId = renditionId.toLowerCase();
|
||||||
|
|
||||||
|
const supportedRendition: RenditionPaging = versionId ? await this.versionsApi.listVersionRenditions(nodeId, versionId) :
|
||||||
|
await this.renditionsApi.listRenditions(nodeId);
|
||||||
|
|
||||||
|
let rendition = this.findRenditionById(supportedRendition, renditionId);
|
||||||
|
if (!rendition) {
|
||||||
|
renditionId = this.DEFAULT_RENDITION;
|
||||||
|
rendition = this.findRenditionById(supportedRendition, this.DEFAULT_RENDITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendition) {
|
||||||
|
const status: string = rendition.entry.status.toString();
|
||||||
|
const mimeType: string = rendition.entry.content.mimeType;
|
||||||
|
|
||||||
|
if (status === 'NOT_CREATED') {
|
||||||
|
return {url: await this.requestCreateRendition(nodeId, renditionId, versionId), mimeType:mimeType};
|
||||||
|
} else {
|
||||||
|
return {url: await this.handleNodeRendition(nodeId, renditionId, versionId), mimeType:mimeType};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async requestCreateRendition(nodeId: string, renditionId: string, versionId: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
if (versionId) {
|
||||||
|
await this.versionsApi.createVersionRendition(nodeId, versionId, {id: renditionId});
|
||||||
|
} else {
|
||||||
|
await this.renditionsApi.createRendition(nodeId, {id: renditionId});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return versionId ? await this.waitNodeRendition(nodeId, renditionId, versionId) : await this.waitNodeRendition(nodeId, renditionId);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.logService.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private findRenditionById(supportedRendition: RenditionPaging, renditionId: string) {
|
||||||
|
const rendition: RenditionEntry = supportedRendition.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId);
|
||||||
|
return rendition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise<string> {
|
||||||
|
let currentRetry: number = 0;
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
currentRetry++;
|
||||||
|
if (this.maxRetries >= currentRetry) {
|
||||||
|
if (versionId) {
|
||||||
|
this.versionsApi.getVersionRendition(nodeId, versionId, renditionId).then((rendition: RenditionEntry) => {
|
||||||
|
const status: string = rendition.entry.status.toString();
|
||||||
|
|
||||||
|
if (status === 'CREATED') {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
return resolve(this.handleNodeRendition(nodeId, rendition.entry.content.mimeType, versionId));
|
||||||
|
}
|
||||||
|
}, () => reject());
|
||||||
|
} else {
|
||||||
|
this.renditionsApi.getRendition(nodeId, renditionId).then((rendition: RenditionEntry) => {
|
||||||
|
const status: string = rendition.entry.status.toString();
|
||||||
|
|
||||||
|
if (status === 'CREATED') {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
return resolve(this.handleNodeRendition(nodeId, renditionId, versionId));
|
||||||
|
}
|
||||||
|
}, () => reject());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
return reject();
|
||||||
|
}
|
||||||
|
}, this.TRY_TIMEOUT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise<string> {
|
||||||
|
|
||||||
|
const url = versionId ? this.contentApi.getVersionRenditionUrl(nodeId, versionId, renditionId) :
|
||||||
|
this.contentApi.getRenditionUrl(nodeId, renditionId);
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateMediaTracksRendition(nodeId: string): Promise<Track[]> {
|
||||||
|
return this.isRenditionAvailable(nodeId, RenditionViewerService.SUBTITLES_RENDITION_NAME)
|
||||||
|
.then((value) => {
|
||||||
|
const tracks = [];
|
||||||
|
if (value) {
|
||||||
|
tracks.push({
|
||||||
|
kind: 'subtitles',
|
||||||
|
src: this.contentApi.getRenditionUrl(nodeId, RenditionViewerService.SUBTITLES_RENDITION_NAME),
|
||||||
|
label: this.translateService.instant('ADF_VIEWER.SUBTITLES')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tracks;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.logService.error('Error while retrieving ' + RenditionViewerService.SUBTITLES_RENDITION_NAME + ' rendition');
|
||||||
|
this.logService.error(err);
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async isRenditionAvailable(nodeId: string, renditionId: string): Promise<boolean> {
|
||||||
|
const renditionPaging: RenditionPaging = await this.renditionsApi.listRenditions(nodeId);
|
||||||
|
const rendition: RenditionEntry = renditionPaging.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId);
|
||||||
|
|
||||||
|
return rendition?.entry?.status?.toString() === 'CREATED' || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method takes a url to trigger the print dialog against, and the type of artifact that it
|
||||||
|
* is.
|
||||||
|
* This URL should be one that can be rendered in the browser, for example PDF, Image, or Text
|
||||||
|
*/
|
||||||
|
printFile(url: string, type: string): void {
|
||||||
|
const pwa = window.open(url, RenditionViewerService.TARGET);
|
||||||
|
if (pwa) {
|
||||||
|
// Because of the way chrome focus and close image window vs. pdf preview window
|
||||||
|
if (type === RenditionViewerService.ContentGroup.IMAGE) {
|
||||||
|
pwa.onfocus = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
pwa.close();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pwa.onload = () => {
|
||||||
|
pwa.print();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the File Print dialog from anywhere other than the preview service, which resolves the
|
||||||
|
* rendition of the object that can be printed from a web browser.
|
||||||
|
* These are: images, PDF files, or PDF rendition of files.
|
||||||
|
* We also force PDF rendition for TEXT type objects, otherwise the default URL is to download.
|
||||||
|
* TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled)
|
||||||
|
*/
|
||||||
|
printFileGeneric(objectId: string, mimeType: string): void {
|
||||||
|
const nodeId = objectId;
|
||||||
|
const type: string = this.viewUtilsService.getViewerTypeByMimeType(mimeType);
|
||||||
|
|
||||||
|
this.getRendition(nodeId, RenditionViewerService.ContentGroup.PDF)
|
||||||
|
.then((value) => {
|
||||||
|
const url: string = this.getRenditionUrl(nodeId, type, (!!value));
|
||||||
|
const printType = (type === RenditionViewerService.ContentGroup.PDF
|
||||||
|
|| type === RenditionViewerService.ContentGroup.TEXT)
|
||||||
|
? RenditionViewerService.ContentGroup.PDF : type;
|
||||||
|
this.printFile(url, printType);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.logService.error('Error with Printing');
|
||||||
|
this.logService.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -44,5 +44,6 @@ export * from './lib/common/index';
|
|||||||
export * from './lib/tree/index';
|
export * from './lib/tree/index';
|
||||||
export * from './lib/category/index';
|
export * from './lib/category/index';
|
||||||
export * from './lib/search-text/index';
|
export * from './lib/search-text/index';
|
||||||
|
export * from './lib/viewer/index';
|
||||||
|
|
||||||
export * from './lib/content.module';
|
export * from './lib/content.module';
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<div class="adf-base-viewer-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
|
||||||
|
[class.adf-readonly]="field.readOnly">
|
||||||
|
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk"
|
||||||
|
*ngIf="isRequired()">*</span></label>
|
||||||
|
<adf-viewer [urlFile]="field.value.urlFile" [blobFile]="field.value.blobFile" [fileName]="field.value.fileName" [showViewer]="field.value?.blobFile || field.value?.urlFile" [allowGoBack]="false"></adf-viewer>
|
||||||
|
<error-widget [error]="field.validationSummary"></error-widget>
|
||||||
|
</div>
|
@ -0,0 +1,19 @@
|
|||||||
|
base-viewer-widget {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.adf-base-viewer-widget {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
adf-viewer.adf-viewer {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.adf-viewer-container {
|
||||||
|
.adf-viewer-content > div {
|
||||||
|
height: 90vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FormModel } from '../core/form.model';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { FormFieldModel } from '../core/form-field.model';
|
||||||
|
import { FormService } from '../../../services/form.service';
|
||||||
|
import { CoreTestingModule } from '../../../../testing/core.testing.module';
|
||||||
|
import { BaseViewerWidgetComponent } from './base-viewer.widget';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
describe('BaseViewerWidgetComponent', () => {
|
||||||
|
const fakeForm = new FormModel();
|
||||||
|
let widget: BaseViewerWidgetComponent;
|
||||||
|
let formServiceStub: Partial<FormService>;
|
||||||
|
let fixture: ComponentFixture<BaseViewerWidgetComponent>;
|
||||||
|
|
||||||
|
const fakePngAnswer: any = {
|
||||||
|
id: '1933',
|
||||||
|
link: false,
|
||||||
|
isExternal: false,
|
||||||
|
relatedContent: false,
|
||||||
|
contentAvailable: true,
|
||||||
|
name: 'a_png_file.png',
|
||||||
|
simpleType: 'image',
|
||||||
|
mimeType: 'image/png',
|
||||||
|
previewStatus: 'queued',
|
||||||
|
thumbnailStatus: 'queued',
|
||||||
|
created: '2022-10-14T17:17:37.099Z',
|
||||||
|
createdBy: { id: 1001, firstName: 'Admin', lastName: 'admin', email: 'admin@example.com' }
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
CoreTestingModule,
|
||||||
|
TranslateModule.forRoot()
|
||||||
|
],
|
||||||
|
declarations: [ BaseViewerWidgetComponent ],
|
||||||
|
providers: [ { provide: FormService, useValue: formServiceStub } ]
|
||||||
|
});
|
||||||
|
|
||||||
|
formServiceStub = TestBed.inject(FormService);
|
||||||
|
fixture = TestBed.createComponent(BaseViewerWidgetComponent);
|
||||||
|
widget = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the file id corretly when the field value is an array', (done) => {
|
||||||
|
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: [fakePngAnswer] });
|
||||||
|
widget.field = fakeField;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(widget.field.value).toBe('1933');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the file id corretly when the field value is a string', (done) => {
|
||||||
|
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: 'fakeValue' });
|
||||||
|
widget.field = fakeField;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(widget.field.value).toBe('fakeValue');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,55 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormService } from '../../../services/form.service';
|
||||||
|
import { WidgetComponent } from '../widget.component';
|
||||||
|
|
||||||
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'base-viewer-widget',
|
||||||
|
templateUrl: './base-viewer.widget.html',
|
||||||
|
styleUrls: ['./base-viewer.widget.scss'],
|
||||||
|
host: {
|
||||||
|
'(click)': 'event($event)',
|
||||||
|
'(blur)': 'event($event)',
|
||||||
|
'(change)': 'event($event)',
|
||||||
|
'(focus)': 'event($event)',
|
||||||
|
'(focusin)': 'event($event)',
|
||||||
|
'(focusout)': 'event($event)',
|
||||||
|
'(input)': 'event($event)',
|
||||||
|
'(invalid)': 'event($event)',
|
||||||
|
'(select)': 'event($event)'
|
||||||
|
},
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class BaseViewerWidgetComponent extends WidgetComponent implements OnInit {
|
||||||
|
constructor(formService: FormService) {
|
||||||
|
super(formService);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.field &&
|
||||||
|
this.field.value &&
|
||||||
|
Array.isArray(this.field.value) &&
|
||||||
|
this.field.value.length) {
|
||||||
|
const file = this.field.value[0];
|
||||||
|
this.field.value = file.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,7 +39,9 @@ export class FormFieldTypes {
|
|||||||
static DOCUMENT: string = 'document';
|
static DOCUMENT: string = 'document';
|
||||||
static DATETIME: string = 'datetime';
|
static DATETIME: string = 'datetime';
|
||||||
static ATTACH_FOLDER: string = 'select-folder';
|
static ATTACH_FOLDER: string = 'select-folder';
|
||||||
static FILE_VIEWER: string = 'file-viewer';
|
static PROPERTIES_VIEWER: string = 'properties-viewer';
|
||||||
|
static ALFRESCO_FILE_VIEWER: string = 'file-viewer';
|
||||||
|
static VIEWER: string = 'base-viewer';
|
||||||
|
|
||||||
static READONLY_TYPES: string[] = [
|
static READONLY_TYPES: string[] = [
|
||||||
FormFieldTypes.HYPERLINK,
|
FormFieldTypes.HYPERLINK,
|
||||||
|
@ -29,7 +29,7 @@ import { InputMaskDirective } from './text/text-mask.component';
|
|||||||
import { TextWidgetComponent } from './text/text.widget';
|
import { TextWidgetComponent } from './text/text.widget';
|
||||||
import { DateTimeWidgetComponent } from './date-time/date-time.widget';
|
import { DateTimeWidgetComponent } from './date-time/date-time.widget';
|
||||||
import { JsonWidgetComponent } from './json/json.widget';
|
import { JsonWidgetComponent } from './json/json.widget';
|
||||||
import { FileViewerWidgetComponent } from './file-viewer/file-viewer.widget';
|
import { BaseViewerWidgetComponent } from './base-viewer/base-viewer.widget';
|
||||||
import { DisplayRichTextWidgetComponent } from './display-rich-text/display-rich-text.widget';
|
import { DisplayRichTextWidgetComponent } from './display-rich-text/display-rich-text.widget';
|
||||||
|
|
||||||
// core
|
// core
|
||||||
@ -49,7 +49,7 @@ export * from './amount/amount.widget';
|
|||||||
export * from './error/error.component';
|
export * from './error/error.component';
|
||||||
export * from './date-time/date-time.widget';
|
export * from './date-time/date-time.widget';
|
||||||
export * from './json/json.widget';
|
export * from './json/json.widget';
|
||||||
export * from './file-viewer/file-viewer.widget';
|
export * from './base-viewer/base-viewer.widget';
|
||||||
export * from './display-rich-text/display-rich-text.widget';
|
export * from './display-rich-text/display-rich-text.widget';
|
||||||
export * from './text/text-mask.component';
|
export * from './text/text-mask.component';
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ export const WIDGET_DIRECTIVES: any[] = [
|
|||||||
ErrorWidgetComponent,
|
ErrorWidgetComponent,
|
||||||
DateTimeWidgetComponent,
|
DateTimeWidgetComponent,
|
||||||
JsonWidgetComponent,
|
JsonWidgetComponent,
|
||||||
FileViewerWidgetComponent,
|
BaseViewerWidgetComponent,
|
||||||
DisplayRichTextWidgetComponent
|
DisplayRichTextWidgetComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ export class FormRenderingService extends DynamicComponentMapper {
|
|||||||
json: DynamicComponentResolver.fromType(widgets.JsonWidgetComponent),
|
json: DynamicComponentResolver.fromType(widgets.JsonWidgetComponent),
|
||||||
readonly: DynamicComponentResolver.fromType(widgets.TextWidgetComponent),
|
readonly: DynamicComponentResolver.fromType(widgets.TextWidgetComponent),
|
||||||
datetime: DynamicComponentResolver.fromType(widgets.DateTimeWidgetComponent),
|
datetime: DynamicComponentResolver.fromType(widgets.DateTimeWidgetComponent),
|
||||||
'file-viewer': DynamicComponentResolver.fromType(widgets.FileViewerWidgetComponent),
|
'base-viewer': DynamicComponentResolver.fromType(widgets.BaseViewerWidgetComponent),
|
||||||
'display-rich-text': DynamicComponentResolver.fromType(widgets.DisplayRichTextWidgetComponent)
|
'display-rich-text': DynamicComponentResolver.fromType(widgets.DisplayRichTextWidgetComponent)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
42
lib/core/src/lib/services/url.service.ts
Normal file
42
lib/core/src/lib/services/url.service.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UrlService {
|
||||||
|
|
||||||
|
constructor(private sanitizer: DomSanitizer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a trusted object URL from the Blob.
|
||||||
|
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
|
||||||
|
*
|
||||||
|
* @param blob Data to wrap into object URL
|
||||||
|
* @returns URL string
|
||||||
|
*/
|
||||||
|
createTrustedUrl(blob: Blob): string {
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
return this.sanitizer.bypassSecurityTrustUrl(url) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<div id="adf-image-container" (keydown)="onKeyDown($event)" class="adf-image-container" tabindex="0" role="img" [attr.aria-label]="nameFile" data-automation-id="adf-image-container">
|
<div id="adf-image-container" (keydown)="onKeyDown($event)" class="adf-image-container" tabindex="0" role="img" [attr.aria-label]="fileName" data-automation-id="adf-image-container">
|
||||||
<img #image id="viewer-image" [src]="urlFile" [alt]="nameFile" (error)="onImageError()" />
|
<img #image id="viewer-image" [src]="urlFile" [alt]="fileName" (error)="onImageError()" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="adf-image-viewer__toolbar" *ngIf="showToolbar">
|
<div class="adf-image-viewer__toolbar" *ngIf="showToolbar">
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<mat-icon>zoom_out</mat-icon>
|
<mat-icon>zoom_out</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="adf-viewer__toolbar-page-scale" data-automation-id="adf-page-scale">
|
<div class="adf-image-viewer__toolbar-page-scale" data-automation-id="adf-page-scale">
|
||||||
{{ currentScaleText }}
|
{{ currentScaleText }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -48,5 +48,17 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-page-scale {
|
||||||
|
cursor: default;
|
||||||
|
width: 79px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: var(--theme-body-1-font-size);
|
||||||
|
border: 1px solid var(--theme-border-color);
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { SimpleChange } from '@angular/core';
|
import { SimpleChange } from '@angular/core';
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { ContentService } from '../../services/content.service';
|
import { UrlService } from '../../services/url.service';
|
||||||
import { ImgViewerComponent } from './img-viewer.component';
|
import { ImgViewerComponent } from './img-viewer.component';
|
||||||
import { setupTestBed, CoreTestingModule } from '../../testing';
|
import { setupTestBed, CoreTestingModule } from '../../testing';
|
||||||
import { AppConfigService } from '../../app-config/app-config.service';
|
import { AppConfigService } from '../../app-config/app-config.service';
|
||||||
@ -27,7 +27,7 @@ import { By } from '@angular/platform-browser';
|
|||||||
describe('Test Img viewer component ', () => {
|
describe('Test Img viewer component ', () => {
|
||||||
|
|
||||||
let component: ImgViewerComponent;
|
let component: ImgViewerComponent;
|
||||||
let service: ContentService;
|
let urlService: UrlService;
|
||||||
let fixture: ComponentFixture<ImgViewerComponent>;
|
let fixture: ComponentFixture<ImgViewerComponent>;
|
||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
describe('Zoom customization', () => {
|
describe('Zoom customization', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = TestBed.inject(ContentService);
|
urlService = TestBed.inject(UrlService);
|
||||||
fixture = TestBed.createComponent(ImgViewerComponent);
|
fixture = TestBed.createComponent(ImgViewerComponent);
|
||||||
|
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
@ -68,7 +68,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
|
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
|
||||||
appConfig.config['adf-viewer.image-viewer-scaling'] = 70;
|
appConfig.config['adf-viewer-render.image-viewer-scaling'] = 70;
|
||||||
component.initializeScaling();
|
component.initializeScaling();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
describe('Url', () => {
|
describe('Url', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = TestBed.inject(ContentService);
|
urlService = TestBed.inject(UrlService);
|
||||||
fixture = TestBed.createComponent(ImgViewerComponent);
|
fixture = TestBed.createComponent(ImgViewerComponent);
|
||||||
|
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
@ -115,7 +115,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
describe('Blob', () => {
|
describe('Blob', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = TestBed.inject(ContentService);
|
urlService = TestBed.inject(UrlService);
|
||||||
fixture = TestBed.createComponent(ImgViewerComponent);
|
fixture = TestBed.createComponent(ImgViewerComponent);
|
||||||
|
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
@ -138,7 +138,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('The file Name should be present in the alt attribute', () => {
|
it('The file Name should be present in the alt attribute', () => {
|
||||||
component.nameFile = 'fake-name';
|
component.fileName = 'fake-name';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name');
|
expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name');
|
||||||
});
|
});
|
||||||
@ -146,7 +146,7 @@ describe('Test Img viewer component ', () => {
|
|||||||
it('If blob is passed should not thrown an error', () => {
|
it('If blob is passed should not thrown an error', () => {
|
||||||
const blob = createFakeBlob();
|
const blob = createFakeBlob();
|
||||||
|
|
||||||
spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url');
|
spyOn(urlService, 'createTrustedUrl').and.returnValue('fake-blob-url');
|
||||||
const change = new SimpleChange(null, blob, true);
|
const change = new SimpleChange(null, blob, true);
|
||||||
expect(() => {
|
expect(() => {
|
||||||
component.ngOnChanges({ blobFile: change });
|
component.ngOnChanges({ blobFile: change });
|
||||||
|
@ -25,8 +25,8 @@ import {
|
|||||||
Output,
|
Output,
|
||||||
EventEmitter, AfterViewInit, ViewChild, HostListener, OnDestroy
|
EventEmitter, AfterViewInit, ViewChild, HostListener, OnDestroy
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ContentService } from '../../services/content.service';
|
|
||||||
import { AppConfigService } from '../../app-config/app-config.service';
|
import { AppConfigService } from '../../app-config/app-config.service';
|
||||||
|
import { UrlService } from '../../services/url.service';
|
||||||
import Cropper from 'cropperjs';
|
import Cropper from 'cropperjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -51,7 +51,7 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
blobFile: Blob;
|
blobFile: Blob;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
nameFile: string;
|
fileName: string;
|
||||||
|
|
||||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||||
@Output()
|
@Output()
|
||||||
@ -74,12 +74,12 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private contentService: ContentService) {
|
private urlService: UrlService) {
|
||||||
this.initializeScaling();
|
this.initializeScaling();
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeScaling() {
|
initializeScaling() {
|
||||||
const scaling = this.appConfigService.get<number>('adf-viewer.image-viewer-scaling', undefined) / 100;
|
const scaling = this.appConfigService.get<number>('adf-viewer-render.image-viewer-scaling', undefined) / 100;
|
||||||
if (scaling) {
|
if (scaling) {
|
||||||
this.scale = scaling;
|
this.scale = scaling;
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
const blobFile = changes['blobFile'];
|
const blobFile = changes['blobFile'];
|
||||||
if (blobFile && blobFile.currentValue) {
|
if (blobFile && blobFile.currentValue) {
|
||||||
this.urlFile = this.contentService.createTrustedUrl(this.blobFile);
|
this.urlFile = this.urlService.createTrustedUrl(this.blobFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.urlFile && !this.blobFile) {
|
if (!this.urlFile && !this.blobFile) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<video controls [ngClass]="{'adf-audio-file': mimeType && mimeType.startsWith('audio')}">
|
<video controls [ngClass]="{'adf-audio-file': mimeType && mimeType.startsWith('audio')}">
|
||||||
<source [src]="urlFile" [type]="mimeType" (error)="onMediaPlayerError()"/>
|
<source [src]="urlFile" [type]="mimeType" (error)="onMediaPlayerError($event)"/>
|
||||||
<track *ngFor="let track of tracks" [kind]="track.kind" [label]="track.label" [srclang]="track.srclang" [src]="track.src"/>
|
<track *ngFor="let track of tracks" [kind]="track.kind" [label]="track.label" [srclang]="track.srclang" [src]="track.src"/>
|
||||||
</video>
|
</video>
|
||||||
|
@ -18,13 +18,12 @@
|
|||||||
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation, Output, EventEmitter } from '@angular/core';
|
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation, Output, EventEmitter } from '@angular/core';
|
||||||
import { ContentService } from '../../services/content.service';
|
import { ContentService } from '../../services/content.service';
|
||||||
import { Track } from '../models/viewer.model';
|
import { Track } from '../models/viewer.model';
|
||||||
import { ViewUtilService } from '../services/view-util.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-media-player',
|
selector: 'adf-media-player',
|
||||||
templateUrl: './media-player.component.html',
|
templateUrl: './media-player.component.html',
|
||||||
styleUrls: ['./media-player.component.scss'],
|
styleUrls: ['./media-player.component.scss'],
|
||||||
host: { class: 'adf-media-player' },
|
host: {class: 'adf-media-player'},
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class MediaPlayerComponent implements OnChanges {
|
export class MediaPlayerComponent implements OnChanges {
|
||||||
@ -39,39 +38,32 @@ export class MediaPlayerComponent implements OnChanges {
|
|||||||
mimeType: string;
|
mimeType: string;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
nameFile: string;
|
fileName: string;
|
||||||
|
|
||||||
@Input()
|
|
||||||
nodeId: string;
|
|
||||||
|
|
||||||
|
/** media subtitles for the media player*/
|
||||||
@Input()
|
@Input()
|
||||||
tracks: Track[] = [];
|
tracks: Track[] = [];
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
error = new EventEmitter<any>();
|
error = new EventEmitter<any>();
|
||||||
|
|
||||||
constructor(private contentService: ContentService, private viewUtils: ViewUtilService) {
|
constructor(private contentService: ContentService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
const blobFile = changes['blobFile'];
|
const blobFile = changes['blobFile'];
|
||||||
const nodeId = changes['nodeId'];
|
|
||||||
|
|
||||||
if (blobFile && blobFile.currentValue) {
|
if (blobFile && blobFile.currentValue) {
|
||||||
this.urlFile = this.contentService.createTrustedUrl(this.blobFile);
|
this.urlFile = this.contentService.createTrustedUrl(this.blobFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeId && nodeId.currentValue) {
|
|
||||||
this.viewUtils.generateMediaTracks(this.nodeId).then((tracks) => this.tracks = tracks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.urlFile && !this.blobFile) {
|
if (!this.urlFile && !this.blobFile) {
|
||||||
throw new Error('Attribute urlFile or blobFile is required');
|
throw new Error('Attribute urlFile or blobFile is required');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMediaPlayerError() {
|
onMediaPlayerError(event: any) {
|
||||||
this.error.emit();
|
this.error.emit(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,8 +593,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.adf-hidden, [hidden] {
|
.adf-hidden, [hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
<span>{{ 'ADF_VIEWER.PAGE_LABEL.OF' | translate }} {{ totalPages }}</span>
|
<span>{{ 'ADF_VIEWER.PAGE_LABEL.OF' | translate }} {{ totalPages }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="adf-viewer__toolbar-page-scale" data-automation-id="adf-page-scale">
|
<div class="adf-pdf-viewer__toolbar-page-scale" data-automation-id="adf-page-scale">
|
||||||
{{ currentScaleText }}
|
{{ currentScaleText }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
.adf-viewer-content-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-pdf-viewer {
|
.adf-pdf-viewer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -19,6 +14,35 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 190px;
|
width: 190px;
|
||||||
background-color: rgba(0, 0, 0, 0.12);
|
background-color: rgba(0, 0, 0, 0.12);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.adf-info-drawer-layout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
background: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-info-drawer-layout-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-info-drawer-layout-content {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-info-drawer-content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-info-drawer-layout-content > *:last-child {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.adf-thumbnails-template {
|
.adf-thumbnails-template {
|
||||||
@ -51,6 +75,17 @@
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
|
||||||
|
.mat-progress-bar {
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||||
|
-ms-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__toolbar {
|
&__toolbar {
|
||||||
@ -87,5 +122,17 @@
|
|||||||
outline-color: gray;
|
outline-color: gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-page-scale {
|
||||||
|
cursor: default;
|
||||||
|
width: 79px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: var(--theme-body-1-font-size);
|
||||||
|
border: 1px solid var(--theme-border-color);
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
blobFile: Blob;
|
blobFile: Blob;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
nameFile: string;
|
fileName: string;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showToolbar: boolean = true;
|
showToolbar: boolean = true;
|
||||||
|
102
lib/core/src/lib/viewer/components/viewer-render.component.html
Normal file
102
lib/core/src/lib/viewer/components/viewer-render.component.html
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<div *ngIf="isLoading"
|
||||||
|
class="adf-viewer-render-main"
|
||||||
|
fxFlexOrder="1"
|
||||||
|
fxFlex="1 1 auto">
|
||||||
|
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
|
||||||
|
<div class="adf-viewer-render-content-container">
|
||||||
|
<ng-container *ngIf="isLoading">
|
||||||
|
<div class="adf-viewer-render__loading-screen"
|
||||||
|
fxFlex="1 1 auto">
|
||||||
|
<h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
|
||||||
|
<div>
|
||||||
|
<mat-spinner></mat-spinner>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!isLoading"
|
||||||
|
class="adf-viewer-render-main"
|
||||||
|
fxFlexOrder="1"
|
||||||
|
fxFlex="1 1 auto">
|
||||||
|
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
|
||||||
|
<div class="adf-viewer-render-content-container" [ngSwitch]="viewerType">
|
||||||
|
<ng-container *ngSwitchCase="'external'">
|
||||||
|
<adf-preview-extension
|
||||||
|
*ngIf="!!externalViewer"
|
||||||
|
[id]="externalViewer.component"
|
||||||
|
[url]="urlFile"
|
||||||
|
[extension]="externalViewer.fileExtension"
|
||||||
|
[attr.data-automation-id]="externalViewer.component">
|
||||||
|
</adf-preview-extension>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'pdf'">
|
||||||
|
<adf-pdf-viewer [thumbnailsTemplate]="thumbnailsTemplate"
|
||||||
|
[allowThumbnails]="allowThumbnails"
|
||||||
|
[blobFile]="blobFile"
|
||||||
|
[urlFile]="urlFile"
|
||||||
|
[fileName]="internalFileName"
|
||||||
|
[cacheType]="cacheTypeForContent"
|
||||||
|
(close)="onClose()"
|
||||||
|
(error)="onUnsupportedFile()">
|
||||||
|
|
||||||
|
</adf-pdf-viewer>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'image'">
|
||||||
|
<adf-img-viewer [urlFile]="urlFile"
|
||||||
|
[readOnly]="readOnly"
|
||||||
|
[fileName]="internalFileName"
|
||||||
|
[blobFile]="blobFile"
|
||||||
|
(error)="onUnsupportedFile()"
|
||||||
|
(submit)="onSubmitFile($event)"
|
||||||
|
></adf-img-viewer>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'media'">
|
||||||
|
<adf-media-player id="adf-mdedia-player"
|
||||||
|
[urlFile]="urlFile"
|
||||||
|
[tracks]="tracks"
|
||||||
|
[mimeType]="mimeType"
|
||||||
|
[blobFile]="blobFile"
|
||||||
|
[fileName]="internalFileName"
|
||||||
|
(error)="onUnsupportedFile()">
|
||||||
|
</adf-media-player>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'text'">
|
||||||
|
<adf-txt-viewer [urlFile]="urlFile"
|
||||||
|
[blobFile]="blobFile">
|
||||||
|
|
||||||
|
</adf-txt-viewer>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'custom'">
|
||||||
|
<ng-container *ngFor="let ext of viewerExtensions">
|
||||||
|
<adf-preview-extension *ngIf="checkExtensions(ext.fileExtension)"
|
||||||
|
[id]="ext.component"
|
||||||
|
[url]="urlFile"
|
||||||
|
[extension]="extension"
|
||||||
|
[attr.data-automation-id]="ext.component">
|
||||||
|
</adf-preview-extension>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<span class="adf-viewer-render-custom-content"
|
||||||
|
*ngFor="let extensionTemplate of extensionTemplates">
|
||||||
|
<ng-template *ngIf="extensionTemplate.isVisible"
|
||||||
|
[ngTemplateOutlet]="extensionTemplate.template"
|
||||||
|
[ngTemplateOutletContext]="{ urlFile: urlFile, extension:extension }">
|
||||||
|
</ng-template>
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchDefault>
|
||||||
|
<adf-viewer-unknown-format></adf-viewer-unknown-format>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,69 @@
|
|||||||
|
/* stylelint-disable scss/at-extend-no-missing-placeholder */
|
||||||
|
.adf-full-screen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--theme-card-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-viewer-render {
|
||||||
|
&-main {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-layout-content {
|
||||||
|
@extend .adf-full-screen;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: var(--theme-background-color);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
margin: 0 auto;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 93vh;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-overlay-container {
|
||||||
|
.adf-viewer-render-content {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-screen {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 85vh;
|
||||||
|
|
||||||
|
.mat-spinner {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-custom-content {
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-unknown-content {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,445 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
|
import { Component, ViewChild } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RenderingQueueServices } from '../services/rendering-queue.services';
|
||||||
|
import { ViewerRenderComponent } from './viewer-render.component';
|
||||||
|
import { setupTestBed } from '../../testing/setup-test-bed';
|
||||||
|
import { CoreTestingModule } from '../../testing/core.testing.module';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-double-viewer',
|
||||||
|
template: `
|
||||||
|
<adf-viewer-render [urlFile]="urlFileViewer1" #viewer1></adf-viewer-render>
|
||||||
|
<adf-viewer-render [urlFile]="urlFileViewer2" #viewer2></adf-viewer-render>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class DoubleViewerComponent {
|
||||||
|
@ViewChild('viewer1')
|
||||||
|
viewer1: ViewerRenderComponent;
|
||||||
|
|
||||||
|
@ViewChild('viewer2')
|
||||||
|
viewer2: ViewerRenderComponent;
|
||||||
|
|
||||||
|
urlFileViewer1: string;
|
||||||
|
urlFileViewer2: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe('ViewerComponent', () => {
|
||||||
|
|
||||||
|
let component: ViewerRenderComponent;
|
||||||
|
let fixture: ComponentFixture<ViewerRenderComponent>;
|
||||||
|
let element: HTMLElement;
|
||||||
|
|
||||||
|
let extensionService: AppExtensionService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule,
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
CoreTestingModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DoubleViewerComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
RenderingQueueServices,
|
||||||
|
{provide: Location, useClass: SpyLocation},
|
||||||
|
MatDialog
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ViewerRenderComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
extensionService = TestBed.inject(AppExtensionService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Double viewer Test', () => {
|
||||||
|
|
||||||
|
it('should not reload the content of all the viewer after type change', async () => {
|
||||||
|
const fixtureDouble = TestBed.createComponent(DoubleViewerComponent);
|
||||||
|
|
||||||
|
await fixtureDouble.detectChanges();
|
||||||
|
await fixtureDouble.whenStable();
|
||||||
|
|
||||||
|
fixtureDouble.componentInstance.urlFileViewer1 = 'fake-test-file.pdf';
|
||||||
|
fixtureDouble.componentInstance.urlFileViewer2 = 'fake-test-file-two.xls';
|
||||||
|
|
||||||
|
fixtureDouble.componentInstance.viewer1.ngOnChanges();
|
||||||
|
fixtureDouble.componentInstance.viewer2.ngOnChanges();
|
||||||
|
|
||||||
|
await fixtureDouble.detectChanges();
|
||||||
|
await fixtureDouble.whenStable();
|
||||||
|
|
||||||
|
expect(fixtureDouble.componentInstance.viewer1.viewerType).toBe('pdf');
|
||||||
|
expect(fixtureDouble.componentInstance.viewer2.viewerType).toBe('unknown');
|
||||||
|
|
||||||
|
fixtureDouble.componentInstance.urlFileViewer1 = 'fake-test-file.pdf';
|
||||||
|
fixtureDouble.componentInstance.urlFileViewer2 = 'fake-test-file-two.png';
|
||||||
|
|
||||||
|
await fixtureDouble.detectChanges();
|
||||||
|
await fixtureDouble.whenStable();
|
||||||
|
|
||||||
|
fixtureDouble.componentInstance.viewer1.ngOnChanges();
|
||||||
|
fixtureDouble.componentInstance.viewer2.ngOnChanges();
|
||||||
|
|
||||||
|
expect(fixtureDouble.componentInstance.viewer1.viewerType).toBe('pdf');
|
||||||
|
expect(fixtureDouble.componentInstance.viewer2.viewerType).toBe('image');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Extension Type Test', () => {
|
||||||
|
it('should display pdf 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(ViewerRenderComponent);
|
||||||
|
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 display pdf with the 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(ViewerRenderComponent);
|
||||||
|
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 display url with the external viewer provided', async () => {
|
||||||
|
const extension: ViewerExtensionRef = {
|
||||||
|
component: 'custom.component',
|
||||||
|
id: 'custom.component.id',
|
||||||
|
fileExtension: '*'
|
||||||
|
};
|
||||||
|
spyOn(extensionService, 'getViewerExtensions').and.returnValue([extension]);
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ViewerRenderComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
component.urlFile = 'http://localhost:4200/alfresco';
|
||||||
|
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 extension file pdf be loaded', (done) => {
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extension file png be loaded', (done) => {
|
||||||
|
component.urlFile = 'fake-url-file.png';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extension file mp4 be loaded', (done) => {
|
||||||
|
component.urlFile = 'fake-url-file.mp4';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extension file txt be loaded', (done) => {
|
||||||
|
component.urlFile = 'fake-test-file.txt';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-txt-viewer')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display [unknown format] for unsupported extensions', (done) => {
|
||||||
|
component.urlFile = 'fake-url-file.unsupported';
|
||||||
|
component.mimeType = '';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MimeType handling', () => {
|
||||||
|
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';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display a image file identified by mimetype when the file extension is wrong', (done) => {
|
||||||
|
component.urlFile = 'fake-content-img.bin';
|
||||||
|
component.mimeType = 'image/png';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the txt viewer if the file identified by mimetype is a txt when the filename has wrong extension', (done) => {
|
||||||
|
component.urlFile = 'fake-content-txt.bin';
|
||||||
|
component.mimeType = 'text/plain';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-txt-viewer')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the media player if the file identified by mimetype is a media when the filename has wrong extension', (done) => {
|
||||||
|
component.urlFile = 'fake-content-video.bin';
|
||||||
|
component.mimeType = 'video/mp4';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 25000);
|
||||||
|
|
||||||
|
it('should display the media player if the file identified by mimetype is a media when the filename has no extension', (done) => {
|
||||||
|
component.urlFile = 'fake-content-video';
|
||||||
|
component.mimeType = 'video/mp4';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 25000);
|
||||||
|
|
||||||
|
it('should display a PDF file identified by mimetype when the filename has no extension', (done) => {
|
||||||
|
component.urlFile = 'fake-content-pdf';
|
||||||
|
component.mimeType = 'application/pdf';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 25000);
|
||||||
|
|
||||||
|
it('should display a PDF file identified by mimetype when the file extension is wrong', (done) => {
|
||||||
|
component.urlFile = 'fake-content-pdf.bin';
|
||||||
|
component.mimeType = 'application/pdf';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 25000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Base component', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.mimeType = 'application/pdf';
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Attribute', () => {
|
||||||
|
|
||||||
|
it('should urlFile present not thrown any error ', () => {
|
||||||
|
expect(() => {
|
||||||
|
component.ngOnChanges();
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('error handling', () => {
|
||||||
|
|
||||||
|
it('should switch to the unknown template if the type specific viewers throw an error', (done) => {
|
||||||
|
component.urlFile = 'fake-url-file.icns';
|
||||||
|
component.mimeType = 'image/png';
|
||||||
|
component.ngOnChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onUnsupportedFile();
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Events', () => {
|
||||||
|
|
||||||
|
it('should if the extension change extension Change event be fired ', (done) => {
|
||||||
|
|
||||||
|
component.extensionChange.subscribe((fileExtension) => {
|
||||||
|
expect(fileExtension).toEqual('png');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.urlFile = 'fake-url-file.png';
|
||||||
|
|
||||||
|
component.ngOnChanges();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('display name property override by urlFile', () => {
|
||||||
|
|
||||||
|
it('should fileName override the default name if is present and urlFile is set', () => {
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.fileName = 'test name';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
expect(component.internalFileName).toEqual('test name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the urlFile name if fileName is NOT set and urlFile is set', () => {
|
||||||
|
component.urlFile = 'fake-test-file.pdf';
|
||||||
|
component.fileName = '';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
expect(component.internalFileName).toEqual('fake-test-file.pdf');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('display name property override by blobFile', () => {
|
||||||
|
|
||||||
|
it('should fileName override the name if is present and blobFile is set', () => {
|
||||||
|
component.fileName = 'blob file display name';
|
||||||
|
component.blobFile = new Blob(['This is my blob content'], {type: 'text/plain'});
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngOnChanges();
|
||||||
|
|
||||||
|
expect(component.internalFileName).toEqual('blob file display name');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
190
lib/core/src/lib/viewer/components/viewer-render.component.ts
Normal file
190
lib/core/src/lib/viewer/components/viewer-render.component.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, EventEmitter,
|
||||||
|
Input, OnChanges, Output, TemplateRef,
|
||||||
|
ViewEncapsulation, OnInit, OnDestroy
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { ViewUtilService } from '../services/view-util.service';
|
||||||
|
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { Track } from '../models/viewer.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-render',
|
||||||
|
templateUrl: './viewer-render.component.html',
|
||||||
|
styleUrls: ['./viewer-render.component.scss'],
|
||||||
|
host: {class: 'adf-viewer-render'},
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
providers: [ViewUtilService]
|
||||||
|
})
|
||||||
|
export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
|
||||||
|
/** 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.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
urlFile = '';
|
||||||
|
|
||||||
|
/** Loads a Blob File */
|
||||||
|
@Input()
|
||||||
|
blobFile: Blob;
|
||||||
|
|
||||||
|
/** Toggles the 'Full Screen' feature. */
|
||||||
|
@Input()
|
||||||
|
allowFullScreen = true;
|
||||||
|
|
||||||
|
/** Toggles PDF thumbnails. */
|
||||||
|
@Input()
|
||||||
|
allowThumbnails = true;
|
||||||
|
|
||||||
|
/** The template for the pdf thumbnails. */
|
||||||
|
@Input()
|
||||||
|
thumbnailsTemplate: TemplateRef<any> = null;
|
||||||
|
|
||||||
|
/** MIME type of the file content (when not determined by the filename extension). */
|
||||||
|
@Input()
|
||||||
|
mimeType: string;
|
||||||
|
|
||||||
|
/** Override Content filename. */
|
||||||
|
@Input()
|
||||||
|
fileName: string;
|
||||||
|
|
||||||
|
/** Override loading status */
|
||||||
|
@Input()
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
/** Enable when where is possible the editing functionalities */
|
||||||
|
@Input()
|
||||||
|
readOnly = true;
|
||||||
|
|
||||||
|
/** media subtitles for the media player*/
|
||||||
|
@Input()
|
||||||
|
tracks: Track[] = [];
|
||||||
|
|
||||||
|
/** Emitted when the filename extension changes. */
|
||||||
|
@Output()
|
||||||
|
extensionChange = new EventEmitter<string>();
|
||||||
|
|
||||||
|
/** Emitted when the img is submitted in the img viewer. */
|
||||||
|
@Output()
|
||||||
|
submitFile = new EventEmitter<Blob>();
|
||||||
|
|
||||||
|
/** Emitted when the img is submitted in the img viewer. */
|
||||||
|
@Output()
|
||||||
|
close = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
extensionTemplates: { template: TemplateRef<any>; isVisible: boolean }[] = [];
|
||||||
|
extension: string;
|
||||||
|
internalFileName: string;
|
||||||
|
viewerType: string = 'unknown';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheTypeForContent = '';
|
||||||
|
|
||||||
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor(private viewUtilService: ViewUtilService,
|
||||||
|
private extensionService: AppExtensionService,
|
||||||
|
public dialog: MatDialog) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.cacheTypeForContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
this.isLoading = !this.blobFile && !this.urlFile;
|
||||||
|
|
||||||
|
if (this.blobFile) {
|
||||||
|
this.setUpBlobData();
|
||||||
|
} else if (this.urlFile) {
|
||||||
|
this.setUpUrlFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setUpBlobData() {
|
||||||
|
this.internalFileName = this.fileName;
|
||||||
|
this.viewerType = this.viewUtilService.getViewerTypeByMimeType(this.blobFile.type);
|
||||||
|
|
||||||
|
this.extensionChange.emit(this.blobFile.type);
|
||||||
|
this.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setUpUrlFile() {
|
||||||
|
this.internalFileName = this.fileName ? this.fileName : this.viewUtilService.getFilenameFromUrl(this.urlFile);
|
||||||
|
this.extension = this.viewUtilService.getFileExtension(this.internalFileName);
|
||||||
|
this.viewerType = this.viewUtilService.getViewerType(this.extension, this.mimeType);
|
||||||
|
|
||||||
|
this.extensionChange.emit(this.extension);
|
||||||
|
this.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTop() {
|
||||||
|
window.scrollTo(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkExtensions(extensionAllowed) {
|
||||||
|
if (typeof extensionAllowed === 'string') {
|
||||||
|
return this.extension.toLowerCase() === extensionAllowed.toLowerCase();
|
||||||
|
} else if (extensionAllowed.length > 0) {
|
||||||
|
return extensionAllowed.find((currentExtension) => this.extension.toLowerCase() === currentExtension.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmitFile(newImageBlob: Blob) {
|
||||||
|
this.submitFile.next(newImageBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnsupportedFile() {
|
||||||
|
this.viewerType = 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.close.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-viewer-toolbar-custom-actions',
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
host: { class: 'adf-viewer-toolbar-custom-actions' },
|
||||||
|
template: `<ng-content></ng-content>`
|
||||||
|
})
|
||||||
|
export class ViewerToolbarCustomActionsComponent{
|
||||||
|
}
|
@ -30,7 +30,7 @@
|
|||||||
[attr.aria-label]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
|
[attr.aria-label]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
title="{{ 'ADF_VIEWER.ACTIONS.CLOSE' | translate }}"
|
title="{{ 'ADF_VIEWER.ACTIONS.CLOSE' | translate }}"
|
||||||
(click)="onBackButtonClick()">
|
(click)="onClose()">
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</adf-toolbar-title>
|
</adf-toolbar-title>
|
||||||
@ -50,7 +50,7 @@
|
|||||||
[src]="mimeType | adfMimeTypeIcon"
|
[src]="mimeType | adfMimeTypeIcon"
|
||||||
data-automation-id="adf-file-thumbnail">
|
data-automation-id="adf-file-thumbnail">
|
||||||
<span class="adf-viewer__display-name"
|
<span class="adf-viewer__display-name"
|
||||||
id="adf-viewer-display-name">{{ fileTitle }}</span>
|
id="adf-viewer-display-name">{{ fileName }}</span>
|
||||||
<button *ngIf="allowNavigate && canNavigateNext"
|
<button *ngIf="allowNavigate && canNavigateNext"
|
||||||
data-automation-id="adf-toolbar-next-file"
|
data-automation-id="adf-toolbar-next-file"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
@ -80,29 +80,10 @@
|
|||||||
|
|
||||||
<adf-toolbar-divider></adf-toolbar-divider>
|
<adf-toolbar-divider></adf-toolbar-divider>
|
||||||
|
|
||||||
<button id="adf-viewer-download"
|
<ng-content select="adf-viewer-toolbar-custom-actions"></ng-content>
|
||||||
*ngIf="allowDownload"
|
|
||||||
mat-icon-button
|
|
||||||
[attr.aria-label]="'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate"
|
|
||||||
title="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}"
|
|
||||||
data-automation-id="adf-toolbar-download"
|
|
||||||
[adfNodeDownload]="nodeEntry"
|
|
||||||
[version]="versionEntry">
|
|
||||||
<mat-icon>file_download</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button id="adf-viewer-print"
|
|
||||||
*ngIf="allowPrint"
|
|
||||||
mat-icon-button
|
|
||||||
[attr.aria-label]="'ADF_VIEWER.ACTIONS.PRINT' | translate"
|
|
||||||
title="{{ 'ADF_VIEWER.ACTIONS.PRINT' | translate }}"
|
|
||||||
data-automation-id="adf-toolbar-print"
|
|
||||||
(click)="printContent()">
|
|
||||||
<mat-icon>print</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button id="adf-viewer-fullscreen"
|
<button id="adf-viewer-fullscreen"
|
||||||
*ngIf="viewerType !== 'media' && allowFullScreen"
|
*ngIf="allowFullScreen"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
[attr.aria-label]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate"
|
[attr.aria-label]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate"
|
||||||
title="{{ 'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate }}"
|
title="{{ 'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate }}"
|
||||||
@ -120,7 +101,7 @@
|
|||||||
title="{{ 'ADF_VIEWER.ACTIONS.INFO' | translate }}"
|
title="{{ 'ADF_VIEWER.ACTIONS.INFO' | translate }}"
|
||||||
data-automation-id="adf-toolbar-sidebar"
|
data-automation-id="adf-toolbar-sidebar"
|
||||||
[color]="showRightSidebar ? 'accent' : null"
|
[color]="showRightSidebar ? 'accent' : null"
|
||||||
(click)="toggleSidebar()">
|
(click)="toggleRightSidebar()">
|
||||||
<mat-icon>info_outline</mat-icon>
|
<mat-icon>info_outline</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -146,6 +127,7 @@
|
|||||||
|
|
||||||
<div fxLayout="row"
|
<div fxLayout="row"
|
||||||
fxFlex="1 1 auto">
|
fxFlex="1 1 auto">
|
||||||
|
|
||||||
<ng-container *ngIf="allowRightSidebar && showRightSidebar">
|
<ng-container *ngIf="allowRightSidebar && showRightSidebar">
|
||||||
<div class="adf-viewer__sidebar"
|
<div class="adf-viewer__sidebar"
|
||||||
[ngClass]="'adf-viewer__sidebar__right'"
|
[ngClass]="'adf-viewer__sidebar__right'"
|
||||||
@ -174,115 +156,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div *ngIf="isLoading"
|
<adf-viewer-render
|
||||||
class="adf-viewer-main"
|
fxFlexOrder="1"
|
||||||
fxFlexOrder="1"
|
fxFlex="1 1 auto"
|
||||||
fxFlex="1 1 auto">
|
(close)="onClose()"
|
||||||
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
|
[mimeType]="mimeType"
|
||||||
<div class="adf-viewer-content-container">
|
[fileName]="fileName"
|
||||||
<ng-container *ngIf="isLoading">
|
[blobFile]="blobFile"
|
||||||
<div class="adf-viewer__loading-screen"
|
[readOnly]="readOnly"
|
||||||
fxFlex="1 1 auto">
|
(submitFile)="onSubmitFile($event)"
|
||||||
<h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
|
[urlFile]="urlFile"
|
||||||
<div>
|
[tracks]="tracks">
|
||||||
<mat-spinner></mat-spinner>
|
</adf-viewer-render>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!isLoading"
|
|
||||||
class="adf-viewer-main"
|
|
||||||
fxFlexOrder="1"
|
|
||||||
fxFlex="1 1 auto">
|
|
||||||
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
|
|
||||||
<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()"
|
|
||||||
[thumbnailsTemplate]="thumbnailsTemplate"
|
|
||||||
[allowThumbnails]="allowThumbnails"
|
|
||||||
[blobFile]="blobFile"
|
|
||||||
[urlFile]="urlFileContent"
|
|
||||||
[nameFile]="displayName"
|
|
||||||
[cacheType]="cacheTypeForContent"
|
|
||||||
(error)="onUnsupportedFile()"></adf-pdf-viewer>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'image'">
|
|
||||||
<adf-img-viewer [urlFile]="urlFileContent"
|
|
||||||
[nameFile]="displayName || fileName"
|
|
||||||
[blobFile]="blobFile"
|
|
||||||
[readOnly]="readOnly"
|
|
||||||
(error)="onUnsupportedFile()"
|
|
||||||
(submit)="onSubmitFile($event)"
|
|
||||||
></adf-img-viewer>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'media'">
|
|
||||||
<adf-media-player id="adf-mdedia-player"
|
|
||||||
[urlFile]="urlFileContent"
|
|
||||||
[nodeId]="nodeEntry?.entry?.id"
|
|
||||||
[mimeType]="mimeType"
|
|
||||||
[blobFile]="blobFile"
|
|
||||||
[nameFile]="displayName"
|
|
||||||
(error)="onUnsupportedFile()"></adf-media-player>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'text'">
|
|
||||||
<adf-txt-viewer [urlFile]="urlFileContent"
|
|
||||||
[blobFile]="blobFile"></adf-txt-viewer>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'in_creation'">
|
|
||||||
<div class="adf-viewer__loading-screen"
|
|
||||||
fxFlex="1 1 auto">
|
|
||||||
<h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
|
|
||||||
<div>
|
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'custom'">
|
|
||||||
<ng-container *ngFor="let ext of viewerExtensions">
|
|
||||||
<adf-preview-extension *ngIf="checkExtensions(ext.fileExtension)"
|
|
||||||
[id]="ext.component"
|
|
||||||
[node]="nodeEntry.entry"
|
|
||||||
[url]="urlFileContent"
|
|
||||||
[extension]="extension"
|
|
||||||
[attr.data-automation-id]="ext.component">
|
|
||||||
</adf-preview-extension>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<span class="adf-viewer-custom-content"
|
|
||||||
*ngFor="let extensionTemplate of extensionTemplates">
|
|
||||||
<ng-template *ngIf="extensionTemplate.isVisible"
|
|
||||||
[ngTemplateOutlet]="extensionTemplate.template"
|
|
||||||
[ngTemplateOutletContext]="{ urlFileContent: urlFileContent, extension:extension }">
|
|
||||||
</ng-template>
|
|
||||||
</span>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngSwitchDefault>
|
|
||||||
<adf-viewer-unknown-format></adf-viewer-unknown-format>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,32 +112,6 @@
|
|||||||
@extend .adf-full-screen;
|
@extend .adf-full-screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-content-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-custom-content {
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-unknown-content {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__loading-screen {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 85vh;
|
|
||||||
|
|
||||||
.mat-spinner {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__sidebar {
|
&__sidebar {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
display: block;
|
display: block;
|
||||||
@ -154,52 +128,4 @@
|
|||||||
border-right: 1px solid var(--theme-border-color);
|
border-right: 1px solid var(--theme-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__thumbnails {
|
|
||||||
width: 180px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0;
|
|
||||||
background: #e6e6e6;
|
|
||||||
|
|
||||||
.adf-info-drawer-layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
background: #e6e6e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-info-drawer-layout-header {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-info-drawer-layout-content {
|
|
||||||
padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-info-drawer-content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adf-info-drawer-layout-content > *:last-child {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__toolbar {
|
|
||||||
&-page-scale {
|
|
||||||
cursor: default;
|
|
||||||
width: 79px;
|
|
||||||
height: 24px;
|
|
||||||
font-size: var(--theme-body-1-font-size);
|
|
||||||
border: 1px solid var(--theme-border-color);
|
|
||||||
text-align: center;
|
|
||||||
line-height: 24px;
|
|
||||||
margin-left: 4px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,45 +16,39 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component, ContentChild, EventEmitter, HostListener, ElementRef,
|
Component,
|
||||||
Input, OnChanges, Output, TemplateRef,
|
ContentChild,
|
||||||
ViewEncapsulation, OnInit, OnDestroy, ChangeDetectorRef
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
|
TemplateRef,
|
||||||
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import { fromEvent, Subject } from 'rxjs';
|
||||||
SharedLinkEntry,
|
|
||||||
Node,
|
|
||||||
Version,
|
|
||||||
RenditionEntry,
|
|
||||||
NodeEntry,
|
|
||||||
VersionEntry,
|
|
||||||
SharedlinksApi, VersionsApi, NodesApi, ContentApi
|
|
||||||
} from '@alfresco/js-api';
|
|
||||||
import { BaseEvent } from '../../events';
|
|
||||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
|
||||||
import { LogService } from '../../common/services/log.service';
|
|
||||||
import { ViewerMoreActionsComponent } from './viewer-more-actions.component';
|
|
||||||
import { ViewerOpenWithComponent } from './viewer-open-with.component';
|
|
||||||
import { ViewerSidebarComponent } from './viewer-sidebar.component';
|
|
||||||
import { ViewerToolbarComponent } from './viewer-toolbar.component';
|
|
||||||
import { fromEvent, Subject } from 'rxjs';
|
|
||||||
import { ViewUtilService } from '../services/view-util.service';
|
|
||||||
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
|
||||||
import { filter, skipWhile, takeUntil } from 'rxjs/operators';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ContentService } from '../../services/content.service';
|
import { ViewerToolbarComponent } from './viewer-toolbar.component';
|
||||||
import { UploadService } from '../../services/upload.service';
|
import { ViewerOpenWithComponent } from './viewer-open-with.component';
|
||||||
import { FileModel } from '../../models';
|
import { ViewerMoreActionsComponent } from './viewer-more-actions.component';
|
||||||
import { NodesApiService } from '../../services/nodes-api.service';
|
import { ViewerSidebarComponent } from './viewer-sidebar.component';
|
||||||
|
import { filter, skipWhile, takeUntil } from 'rxjs/operators';
|
||||||
|
import { Track } from '../models/viewer.model';
|
||||||
|
import { ViewUtilService } from '../services/view-util.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-viewer',
|
selector: 'adf-viewer',
|
||||||
templateUrl: './viewer.component.html',
|
templateUrl: './viewer.component.html',
|
||||||
styleUrls: ['./viewer.component.scss'],
|
styleUrls: ['./viewer.component.scss'],
|
||||||
host: { class: 'adf-viewer' },
|
host: {class: 'adf-viewer'},
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
providers: [ViewUtilService]
|
providers: [ViewUtilService]
|
||||||
})
|
})
|
||||||
export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
||||||
|
|
||||||
@ContentChild(ViewerToolbarComponent)
|
@ContentChild(ViewerToolbarComponent)
|
||||||
toolbar: ViewerToolbarComponent;
|
toolbar: ViewerToolbarComponent;
|
||||||
@ -74,27 +68,29 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
urlFile = '';
|
urlFile = '';
|
||||||
|
|
||||||
/** Viewer to use with the `urlFile` address (`pdf`, `image`, `media`, `text`).
|
|
||||||
* Used when `urlFile` has no filename and extension.
|
|
||||||
*/
|
|
||||||
@Input()
|
|
||||||
urlFileViewer: string = null;
|
|
||||||
|
|
||||||
/** Loads a Blob File */
|
/** Loads a Blob File */
|
||||||
@Input()
|
@Input()
|
||||||
blobFile: Blob;
|
blobFile: Blob;
|
||||||
|
|
||||||
/** Node Id of the file to load. */
|
/** Override Content filename. */
|
||||||
@Input()
|
@Input()
|
||||||
nodeId: string = null;
|
fileName: string;
|
||||||
|
|
||||||
/** Version Id of the file to load. */
|
/** Hide or show the viewer */
|
||||||
@Input()
|
@Input()
|
||||||
versionId: string = null;
|
showViewer = true;
|
||||||
|
|
||||||
/** Shared link id (to display shared file). */
|
/** Allows `back` navigation */
|
||||||
@Input()
|
@Input()
|
||||||
sharedLinkId: string = null;
|
allowGoBack = true;
|
||||||
|
|
||||||
|
/** Toggles the 'Full Screen' feature. */
|
||||||
|
@Input()
|
||||||
|
allowFullScreen = true;
|
||||||
|
|
||||||
|
/** Hide or show the toolbar */
|
||||||
|
@Input()
|
||||||
|
showToolbar = true;
|
||||||
|
|
||||||
/** If `true` then show the Viewer as a full page over the current content.
|
/** If `true` then show the Viewer as a full page over the current content.
|
||||||
* Otherwise fit inside the parent div.
|
* Otherwise fit inside the parent div.
|
||||||
@ -102,35 +98,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
overlayMode = false;
|
overlayMode = false;
|
||||||
|
|
||||||
/** Hide or show the viewer */
|
|
||||||
@Input()
|
|
||||||
showViewer = true;
|
|
||||||
|
|
||||||
/** Hide or show the toolbar */
|
|
||||||
@Input()
|
|
||||||
showToolbar = true;
|
|
||||||
|
|
||||||
/** Specifies the name of the file when it is not available from the URL. */
|
|
||||||
@Input()
|
|
||||||
displayName: string;
|
|
||||||
|
|
||||||
/** @deprecated 3.2.0 */
|
|
||||||
/** Allows `back` navigation */
|
|
||||||
@Input()
|
|
||||||
allowGoBack = true;
|
|
||||||
|
|
||||||
/** Toggles downloading. */
|
|
||||||
@Input()
|
|
||||||
allowDownload = true;
|
|
||||||
|
|
||||||
/** Toggles printing. */
|
|
||||||
@Input()
|
|
||||||
allowPrint = false;
|
|
||||||
|
|
||||||
/** Toggles the 'Full Screen' feature. */
|
|
||||||
@Input()
|
|
||||||
allowFullScreen = true;
|
|
||||||
|
|
||||||
/** Toggles before/next navigation. You can use the arrow buttons to navigate
|
/** Toggles before/next navigation. You can use the arrow buttons to navigate
|
||||||
* between documents in the collection.
|
* between documents in the collection.
|
||||||
*/
|
*/
|
||||||
@ -153,10 +120,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
allowRightSidebar = false;
|
allowRightSidebar = false;
|
||||||
|
|
||||||
/** Toggles PDF thumbnails. */
|
|
||||||
@Input()
|
|
||||||
allowThumbnails = true;
|
|
||||||
|
|
||||||
/** Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. */
|
/** Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. */
|
||||||
@Input()
|
@Input()
|
||||||
showRightSidebar = false;
|
showRightSidebar = false;
|
||||||
@ -173,39 +136,28 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
sidebarLeftTemplate: TemplateRef<any> = null;
|
sidebarLeftTemplate: TemplateRef<any> = null;
|
||||||
|
|
||||||
/** The template for the pdf thumbnails. */
|
/** Enable when where is possible the editing functionalities */
|
||||||
@Input()
|
@Input()
|
||||||
thumbnailsTemplate: TemplateRef<any> = null;
|
readOnly = true;
|
||||||
|
|
||||||
|
/** media subtitles for the media player*/
|
||||||
|
@Input()
|
||||||
|
tracks: Track[] = [];
|
||||||
|
|
||||||
/** MIME type of the file content (when not determined by the filename extension). */
|
|
||||||
@Input()
|
@Input()
|
||||||
mimeType: string;
|
mimeType: string;
|
||||||
|
|
||||||
/** Content filename. */
|
/**
|
||||||
@Input()
|
* Context object available for binding by the local sidebarRightTemplate with let declarations.
|
||||||
fileName: string;
|
|
||||||
|
|
||||||
/** Number of times the Viewer will retry fetching content Rendition.
|
|
||||||
* There is a delay of at least one second between attempts.
|
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
maxRetries = 30;
|
sidebarRightTemplateContext: T = null;
|
||||||
|
|
||||||
/** Emitted when user clicks the 'Back' button. */
|
/**
|
||||||
@Output()
|
* Context object available for binding by the local sidebarLeftTemplate with let declarations.
|
||||||
goBack = new EventEmitter<BaseEvent<any>>();
|
*/
|
||||||
|
@Input()
|
||||||
/** Emitted when user clicks the 'Print' button. */
|
sidebarLeftTemplateContext: T = null;
|
||||||
@Output()
|
|
||||||
print = new EventEmitter<BaseEvent<any>>();
|
|
||||||
|
|
||||||
/** Emitted when the viewer is shown or hidden. */
|
|
||||||
@Output()
|
|
||||||
showViewerChange = new EventEmitter<boolean>();
|
|
||||||
|
|
||||||
/** Emitted when the filename extension changes. */
|
|
||||||
@Output()
|
|
||||||
extensionChange = new EventEmitter<string>();
|
|
||||||
|
|
||||||
/** Emitted when user clicks 'Navigate Before' ("<") button. */
|
/** Emitted when user clicks 'Navigate Before' ("<") button. */
|
||||||
@Output()
|
@Output()
|
||||||
@ -215,377 +167,64 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
@Output()
|
@Output()
|
||||||
navigateNext = new EventEmitter<MouseEvent | KeyboardEvent>();
|
navigateNext = new EventEmitter<MouseEvent | KeyboardEvent>();
|
||||||
|
|
||||||
/** Emitted when the shared link used is not valid. */
|
/** Emitted when the viewer close */
|
||||||
@Output()
|
@Output()
|
||||||
invalidSharedLink = new EventEmitter();
|
showViewerChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
viewerType = 'unknown';
|
/** Emitted when the img is submitted in the img viewer. */
|
||||||
isLoading = false;
|
@Output()
|
||||||
nodeEntry: NodeEntry;
|
submitFile = new EventEmitter<Blob>();
|
||||||
versionEntry: VersionEntry;
|
|
||||||
|
|
||||||
extensionTemplates: { template: TemplateRef<any>; isVisible: boolean }[] = [];
|
|
||||||
urlFileContent: string;
|
|
||||||
otherMenu: any;
|
|
||||||
extension: string;
|
|
||||||
sidebarRightTemplateContext: { node: Node } = { node: null };
|
|
||||||
sidebarLeftTemplateContext: { node: Node } = { node: null };
|
|
||||||
fileTitle: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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: number;
|
|
||||||
cacheTypeForContent = '';
|
|
||||||
|
|
||||||
// Extensions that are supported by the Viewer without conversion
|
|
||||||
private extensions = {
|
|
||||||
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm', 'svg'],
|
|
||||||
media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'],
|
|
||||||
text: ['txt', 'xml', 'html', 'json', 'ts', 'css', 'md'],
|
|
||||||
pdf: ['pdf']
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mime types that are supported by the Viewer without conversion
|
|
||||||
private mimeTypes = {
|
|
||||||
text: ['text/plain', 'text/csv', 'text/xml', 'text/html', 'application/x-javascript'],
|
|
||||||
pdf: ['application/pdf'],
|
|
||||||
image: ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/svg+xml'],
|
|
||||||
media: ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/mp3', 'audio/ogg', 'audio/wav']
|
|
||||||
};
|
|
||||||
|
|
||||||
private onDestroy$ = new Subject<boolean>();
|
private onDestroy$ = new Subject<boolean>();
|
||||||
private shouldCloseViewer = true;
|
|
||||||
|
private closeViewer = true;
|
||||||
private keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown');
|
private keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown');
|
||||||
|
|
||||||
_sharedLinksApi: SharedlinksApi;
|
constructor(private el: ElementRef,
|
||||||
get sharedLinksApi(): SharedlinksApi {
|
|
||||||
this._sharedLinksApi = this._sharedLinksApi ?? new SharedlinksApi(this.apiService.getInstance());
|
|
||||||
return this._sharedLinksApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
_versionsApi: VersionsApi;
|
|
||||||
get versionsApi(): VersionsApi {
|
|
||||||
this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance());
|
|
||||||
return this._versionsApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
_nodesApi: NodesApi;
|
|
||||||
get nodesApi(): NodesApi {
|
|
||||||
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
|
|
||||||
return this._nodesApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
_contentApi: ContentApi;
|
|
||||||
get contentApi(): ContentApi {
|
|
||||||
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
|
|
||||||
return this._contentApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService,
|
|
||||||
private nodesApiService: NodesApiService,
|
|
||||||
private viewUtilService: ViewUtilService,
|
|
||||||
private logService: LogService,
|
|
||||||
private extensionService: AppExtensionService,
|
|
||||||
private contentService: ContentService,
|
|
||||||
private uploadService: UploadService,
|
|
||||||
private el: ElementRef,
|
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private cdr: ChangeDetectorRef) {
|
private viewUtilsService: ViewUtilService
|
||||||
viewUtilService.maxRetries = this.maxRetries;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
isSourceDefined(): boolean {
|
ngOnChanges(changes: SimpleChanges){
|
||||||
return !!(this.urlFile || this.blobFile || this.nodeId || this.sharedLinkId);
|
const { blobFile, urlFile } = changes;
|
||||||
|
|
||||||
|
if(blobFile?.currentValue){
|
||||||
|
this.mimeType = blobFile.currentValue.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(urlFile?.currentValue){
|
||||||
|
this.fileName = this.fileName ? this.fileName : this.viewUtilsService.getFilenameFromUrl(urlFile.currentValue);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit(): void {
|
||||||
this.nodesApiService.nodeUpdated.pipe(
|
|
||||||
filter((node) => node && node.id === this.nodeId &&
|
|
||||||
(node.name !== this.fileName ||
|
|
||||||
this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(node))),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
).subscribe((node) => this.onNodeUpdated(node));
|
|
||||||
|
|
||||||
this.viewUtilService.viewerTypeChange.pipe(takeUntil(this.onDestroy$)).subscribe((type: string) => {
|
|
||||||
this.viewerType = type;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.viewUtilService.urlFileContentChange.pipe(takeUntil(this.onDestroy$)).subscribe((content: string) => {
|
|
||||||
this.urlFileContent = content;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.closeOverlayManager();
|
this.closeOverlayManager();
|
||||||
this.cacheTypeForContent = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getNodeVersionProperty(node: Node): string {
|
private closeOverlayManager() {
|
||||||
return node?.properties['cm:versionLabel'] ?? '';
|
this.dialog.afterOpened.pipe(
|
||||||
}
|
skipWhile(() => !this.overlayMode),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
).subscribe(() => this.closeViewer = false);
|
||||||
|
|
||||||
ngOnDestroy() {
|
this.dialog.afterAllClosed.pipe(
|
||||||
this.onDestroy$.next(true);
|
skipWhile(() => !this.overlayMode),
|
||||||
this.onDestroy$.complete();
|
takeUntil(this.onDestroy$)
|
||||||
}
|
).subscribe(() => this.closeViewer = true);
|
||||||
|
|
||||||
private onNodeUpdated(node: Node) {
|
this.keyDown$.pipe(
|
||||||
if (node && node.id === this.nodeId) {
|
skipWhile(() => !this.overlayMode),
|
||||||
this.cacheTypeForContent = 'no-cache';
|
filter((e: KeyboardEvent) => e.keyCode === 27),
|
||||||
this.generateCacheBusterNumber();
|
takeUntil(this.onDestroy$)
|
||||||
this.isLoading = true;
|
).subscribe((event: KeyboardEvent) => {
|
||||||
this.setUpNodeFile(node).then(() => {
|
event.preventDefault();
|
||||||
this.isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
if (this.closeViewer) {
|
||||||
if (this.showViewer) {
|
this.onClose();
|
||||||
if (!this.isSourceDefined()) {
|
|
||||||
throw new Error('A content source attribute value is missing.');
|
|
||||||
}
|
}
|
||||||
this.isLoading = true;
|
});
|
||||||
|
|
||||||
if (this.blobFile) {
|
|
||||||
this.setUpBlobData();
|
|
||||||
this.isLoading = false;
|
|
||||||
} else if (this.urlFile) {
|
|
||||||
this.setUpUrlFile();
|
|
||||||
this.isLoading = false;
|
|
||||||
} else if (this.nodeId) {
|
|
||||||
this.setupNode();
|
|
||||||
} else if (this.sharedLinkId) {
|
|
||||||
this.setupSharedLink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupSharedLink() {
|
|
||||||
this.allowGoBack = false;
|
|
||||||
|
|
||||||
this.sharedLinksApi.getSharedLink(this.sharedLinkId).then(
|
|
||||||
(sharedLinkEntry: SharedLinkEntry) => {
|
|
||||||
this.setUpSharedLinkFile(sharedLinkEntry);
|
|
||||||
this.isLoading = false;
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.isLoading = false;
|
|
||||||
this.logService.error('This sharedLink does not exist');
|
|
||||||
this.invalidSharedLink.next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupNode() {
|
|
||||||
this.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] }).then(
|
|
||||||
(node: NodeEntry) => {
|
|
||||||
this.nodeEntry = node;
|
|
||||||
if (this.versionId) {
|
|
||||||
this.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.cdr.detectChanges();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.isLoading = false;
|
|
||||||
this.logService.error('This node does not exist');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private setUpBlobData() {
|
|
||||||
this.fileTitle = this.getDisplayName('Unknown');
|
|
||||||
this.mimeType = this.blobFile.type;
|
|
||||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
|
||||||
|
|
||||||
this.allowDownload = false;
|
|
||||||
// TODO: wrap blob into the data url and allow downloading
|
|
||||||
|
|
||||||
this.extensionChange.emit(this.mimeType);
|
|
||||||
this.scrollTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private setUpUrlFile() {
|
|
||||||
const filenameFromUrl = this.getFilenameFromUrl(this.urlFile);
|
|
||||||
this.fileTitle = this.getDisplayName(filenameFromUrl);
|
|
||||||
this.extension = this.getFileExtension(filenameFromUrl);
|
|
||||||
this.urlFileContent = this.urlFile;
|
|
||||||
this.fileName = this.displayName;
|
|
||||||
this.viewerType = this.urlFileViewer || this.getViewerType(this.extension, this.mimeType);
|
|
||||||
|
|
||||||
this.extensionChange.emit(this.extension);
|
|
||||||
this.scrollTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setUpNodeFile(nodeData: Node, versionData?: Version): Promise<void> {
|
|
||||||
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');
|
|
||||||
|
|
||||||
if (versionData && versionData.content) {
|
|
||||||
this.mimeType = versionData.content.mimeType;
|
|
||||||
} else if (nodeData.content) {
|
|
||||||
this.mimeType = nodeData.content.mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fileTitle = this.getDisplayName(versionData ? versionData.name : nodeData.name);
|
|
||||||
|
|
||||||
const currentFileVersion = this.nodeEntry?.entry?.properties && this.nodeEntry.entry.properties['cm:versionLabel'] ?
|
|
||||||
encodeURI(this.nodeEntry?.entry?.properties['cm:versionLabel']) : encodeURI('1.0');
|
|
||||||
|
|
||||||
this.urlFileContent = versionData ? this.contentApi.getVersionContentUrl(this.nodeId, versionData.id) :
|
|
||||||
this.contentApi.getContentUrl(this.nodeId);
|
|
||||||
this.urlFileContent = this.cacheBusterNumber ? this.urlFileContent + '&' + currentFileVersion + '&' + this.cacheBusterNumber :
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (this.viewerType === 'unknown') {
|
|
||||||
if (versionData) {
|
|
||||||
await this.viewUtilService.displayNodeRendition(nodeData.id, versionData.id);
|
|
||||||
} else {
|
|
||||||
await this.viewUtilService.displayNodeRendition(nodeData.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extensionChange.emit(this.extension);
|
|
||||||
this.sidebarRightTemplateContext.node = nodeData;
|
|
||||||
this.sidebarLeftTemplateContext.node = nodeData;
|
|
||||||
this.scrollTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
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.getViewerType(this.extension, this.mimeType);
|
|
||||||
|
|
||||||
if (this.viewerType === 'unknown') {
|
|
||||||
this.displaySharedLinkRendition(this.sharedLinkId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extensionChange.emit(this.extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSidebar() {
|
|
||||||
this.showRightSidebar = !this.showRightSidebar;
|
|
||||||
if (this.showRightSidebar && this.nodeId) {
|
|
||||||
this.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] })
|
|
||||||
.then((nodeEntry: NodeEntry) => {
|
|
||||||
this.sidebarRightTemplateContext.node = nodeEntry.entry;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleLeftSidebar() {
|
|
||||||
this.showLeftSidebar = !this.showLeftSidebar;
|
|
||||||
if (this.showRightSidebar && this.nodeId) {
|
|
||||||
this.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] })
|
|
||||||
.then((nodeEntry: NodeEntry) => {
|
|
||||||
this.sidebarLeftTemplateContext.node = nodeEntry.entry;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDisplayName(name) {
|
|
||||||
return this.displayName || name;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollTop() {
|
|
||||||
window.scrollTo(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewerTypeByMimeType(mimeType: string) {
|
|
||||||
if (mimeType) {
|
|
||||||
mimeType = mimeType.toLowerCase();
|
|
||||||
|
|
||||||
const editorTypes = Object.keys(this.mimeTypes);
|
|
||||||
for (const type of editorTypes) {
|
|
||||||
if (this.mimeTypes[type].indexOf(mimeType) >= 0) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewerTypeByExtension(extension: string): string {
|
|
||||||
if (extension) {
|
|
||||||
extension = extension.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isExternalViewer()) {
|
|
||||||
return 'external';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isCustomViewerExtension(extension)) {
|
|
||||||
return 'custom';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.extensions.image.indexOf(extension) >= 0) {
|
|
||||||
return 'image';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.extensions.media.indexOf(extension) >= 0) {
|
|
||||||
return 'media';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.extensions.text.indexOf(extension) >= 0) {
|
|
||||||
return 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.extensions.pdf.indexOf(extension) >= 0) {
|
|
||||||
return 'pdf';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
onBackButtonClick() {
|
|
||||||
this.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onNavigateBeforeClick(event: MouseEvent | KeyboardEvent) {
|
onNavigateBeforeClick(event: MouseEvent | KeyboardEvent) {
|
||||||
@ -599,64 +238,19 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* close the viewer
|
* close the viewer
|
||||||
*/
|
*/
|
||||||
close() {
|
onClose() {
|
||||||
if (this.otherMenu) {
|
|
||||||
this.otherMenu.hidden = false;
|
|
||||||
}
|
|
||||||
this.showViewer = false;
|
this.showViewer = false;
|
||||||
this.showViewerChange.emit(this.showViewer);
|
this.showViewerChange.emit(this.showViewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
toggleRightSidebar() {
|
||||||
* get File name from url
|
this.showRightSidebar = !this.showRightSidebar;
|
||||||
*
|
|
||||||
* @param url - url file
|
|
||||||
*/
|
|
||||||
getFilenameFromUrl(url: string): string {
|
|
||||||
const anchor = url.indexOf('#');
|
|
||||||
const query = url.indexOf('?');
|
|
||||||
const end = Math.min(
|
|
||||||
anchor > 0 ? anchor : url.length,
|
|
||||||
query > 0 ? query : url.length);
|
|
||||||
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
toggleLeftSidebar() {
|
||||||
* Get file extension from the string.
|
this.showLeftSidebar = !this.showLeftSidebar;
|
||||||
* Supports the URL formats like:
|
|
||||||
* http://localhost/test.jpg?cache=1000
|
|
||||||
* http://localhost/test.jpg#cache=1000
|
|
||||||
*
|
|
||||||
* @param fileName - file name
|
|
||||||
*/
|
|
||||||
getFileExtension(fileName: string): string {
|
|
||||||
if (fileName) {
|
|
||||||
const match = fileName.match(/\.([^\./\?\#]+)($|\?|\#)/);
|
|
||||||
return match ? match[1] : null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isExternalViewer(): boolean {
|
|
||||||
return !!this.viewerExtensions.find(ext => ext.fileExtension === '*');
|
|
||||||
}
|
|
||||||
|
|
||||||
isCustomViewerExtension(extension: string): boolean {
|
|
||||||
const extensions = this.externalExtensions || [];
|
|
||||||
|
|
||||||
if (extension && extensions.length > 0) {
|
|
||||||
extension = extension.toLowerCase();
|
|
||||||
return extensions.flat().indexOf(extension) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keyboard event listener
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
@HostListener('document:keyup', ['$event'])
|
@HostListener('document:keyup', ['$event'])
|
||||||
handleKeyboardEvent(event: KeyboardEvent) {
|
handleKeyboardEvent(event: KeyboardEvent) {
|
||||||
if (event && event.defaultPrevented) {
|
if (event && event.defaultPrevented) {
|
||||||
@ -684,17 +278,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printContent() {
|
|
||||||
if (this.allowPrint) {
|
|
||||||
const args = new BaseEvent();
|
|
||||||
this.print.next(args);
|
|
||||||
|
|
||||||
if (!args.defaultPrevented) {
|
|
||||||
this.viewUtilService.printFileGeneric(this.nodeId, this.mimeType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers full screen mode with a main content area displayed.
|
* Triggers full screen mode with a main content area displayed.
|
||||||
*/
|
*/
|
||||||
@ -715,82 +298,13 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async displaySharedLinkRendition(sharedId: string) {
|
|
||||||
try {
|
|
||||||
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'pdf');
|
|
||||||
if (rendition.entry.status.toString() === 'CREATED') {
|
|
||||||
this.viewerType = 'pdf';
|
|
||||||
this.urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'pdf');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logService.error(error);
|
|
||||||
try {
|
|
||||||
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'imgpreview');
|
|
||||||
if (rendition.entry.status.toString() === 'CREATED') {
|
|
||||||
this.viewerType = 'image';
|
|
||||||
this.urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'imgpreview');
|
|
||||||
}
|
|
||||||
} catch (renditionError) {
|
|
||||||
this.logService.error(renditionError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkExtensions(extensionAllowed) {
|
|
||||||
if (typeof extensionAllowed === 'string') {
|
|
||||||
return this.extension.toLowerCase() === extensionAllowed.toLowerCase();
|
|
||||||
} else if (extensionAllowed.length > 0) {
|
|
||||||
return extensionAllowed.find((currentExtension) => this.extension.toLowerCase() === currentExtension.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmitFile(newImageBlob: Blob) {
|
onSubmitFile(newImageBlob: Blob) {
|
||||||
if (this?.nodeEntry?.entry?.id && !this.readOnly) {
|
this.submitFile.emit(newImageBlob);
|
||||||
const newImageFile: File = new File([newImageBlob], this?.nodeEntry?.entry?.name, { type: this?.nodeEntry?.entry?.content?.mimeType });
|
|
||||||
const newFile = new FileModel(
|
|
||||||
newImageFile,
|
|
||||||
{
|
|
||||||
majorVersion: false,
|
|
||||||
newVersion: true,
|
|
||||||
parentId: this?.nodeEntry?.entry?.parentId,
|
|
||||||
nodeType: this?.nodeEntry?.entry?.content?.mimeType
|
|
||||||
},
|
|
||||||
this?.nodeEntry?.entry?.id
|
|
||||||
);
|
|
||||||
this.uploadService.addToQueue(...[newFile]);
|
|
||||||
this.uploadService.uploadFilesInTheQueue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsupportedFile() {
|
ngOnDestroy() {
|
||||||
this.viewerType = 'unknown';
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeOverlayManager() {
|
|
||||||
this.dialog.afterOpened.pipe(
|
|
||||||
skipWhile(() => !this.overlayMode),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
).subscribe(() => this.shouldCloseViewer = false);
|
|
||||||
|
|
||||||
this.dialog.afterAllClosed.pipe(
|
|
||||||
skipWhile(() => !this.overlayMode),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
).subscribe(() => this.shouldCloseViewer = true);
|
|
||||||
|
|
||||||
this.keyDown$.pipe(
|
|
||||||
skipWhile(() => !this.overlayMode),
|
|
||||||
filter((e: KeyboardEvent) => e.keyCode === 27),
|
|
||||||
takeUntil(this.onDestroy$)
|
|
||||||
).subscribe((event: KeyboardEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (this.shouldCloseViewer) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateCacheBusterNumber() {
|
|
||||||
this.cacheBusterNumber = Date.now();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import { Location } from '@angular/common';
|
|||||||
import { SpyLocation } from '@angular/common/testing';
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ViewerComponent } from '../components/viewer.component';
|
import { ViewerRenderComponent } from '../components/viewer-render.component';
|
||||||
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
||||||
import { setupTestBed } from '../../testing/setup-test-bed';
|
import { setupTestBed } from '../../testing/setup-test-bed';
|
||||||
import { CoreTestingModule } from '../../testing/core.testing.module';
|
import { CoreTestingModule } from '../../testing/core.testing.module';
|
||||||
@ -43,7 +43,7 @@ describe('ExtensionViewerDirective', () => {
|
|||||||
{ provide: Location, useClass: SpyLocation },
|
{ provide: Location, useClass: SpyLocation },
|
||||||
ViewerExtensionDirective,
|
ViewerExtensionDirective,
|
||||||
{provide: ElementRef, useClass: MockElementRef},
|
{provide: ElementRef, useClass: MockElementRef},
|
||||||
ViewerComponent,
|
ViewerRenderComponent,
|
||||||
{ provide: ChangeDetectorRef, useValue: { detectChanges: () => {} } }
|
{ provide: ChangeDetectorRef, useValue: { detectChanges: () => {} } }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AfterContentInit, ContentChild, Directive, Input, TemplateRef, OnDestroy } from '@angular/core';
|
import { AfterContentInit, ContentChild, Directive, Input, TemplateRef, OnDestroy } from '@angular/core';
|
||||||
import { ViewerComponent } from '../components/viewer.component';
|
import { ViewerRenderComponent } from '../components/viewer-render.component';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export class ViewerExtensionDirective implements AfterContentInit, OnDestroy {
|
|||||||
|
|
||||||
private onDestroy$ = new Subject<boolean>();
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
constructor(private viewerComponent: ViewerComponent) {
|
constructor(private viewerComponent: ViewerRenderComponent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit() {
|
ngAfterContentInit() {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './services/view-util.service';
|
export * from './services/view-util.service';
|
||||||
export * from './components/viewer.component';
|
|
||||||
export * from './components/img-viewer.component';
|
export * from './components/img-viewer.component';
|
||||||
export * from './components/media-player.component';
|
export * from './components/media-player.component';
|
||||||
export * from './components/pdf-viewer-password-dialog';
|
export * from './components/pdf-viewer-password-dialog';
|
||||||
@ -31,6 +30,8 @@ export * from './components/viewer-open-with.component';
|
|||||||
export * from './components/viewer-sidebar.component';
|
export * from './components/viewer-sidebar.component';
|
||||||
export * from './components/viewer-toolbar.component';
|
export * from './components/viewer-toolbar.component';
|
||||||
export * from './components/viewer-toolbar-actions.component';
|
export * from './components/viewer-toolbar-actions.component';
|
||||||
|
export * from './components/viewer-toolbar-custom-actions.component';
|
||||||
|
export * from './components/viewer-render.component';
|
||||||
export * from './components/viewer.component';
|
export * from './components/viewer.component';
|
||||||
|
|
||||||
export * from './directives/viewer-extension.directive';
|
export * from './directives/viewer-extension.directive';
|
||||||
|
@ -16,158 +16,87 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ContentApi, RenditionEntry, RenditionPaging, RenditionsApi, VersionsApi } from '@alfresco/js-api';
|
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
|
||||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
|
||||||
import { LogService } from '../../common/services/log.service';
|
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
import { Track } from '../models/viewer.model';
|
|
||||||
import { TranslationService } from '../../translation/translation.service';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ViewUtilService {
|
export class ViewUtilService {
|
||||||
static TARGET = '_new';
|
|
||||||
|
|
||||||
/**
|
// Extensions that are supported by the Viewer without conversion
|
||||||
* Content groups based on categorization of files that can be viewed in the web browser. This
|
private extensions = {
|
||||||
* implementation or grouping is tied to the definition the ng component: ViewerComponent
|
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm', 'svg'],
|
||||||
*/
|
media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'],
|
||||||
static ContentGroup = {
|
text: ['txt', 'xml', 'html', 'json', 'ts', 'css', 'md'],
|
||||||
IMAGE: 'image',
|
pdf: ['pdf']
|
||||||
MEDIA: 'media',
|
|
||||||
PDF: 'pdf',
|
|
||||||
TEXT: 'text'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Mime types that are supported by the Viewer without conversion
|
||||||
* The name of the rendition with the media subtitles in the supported format
|
|
||||||
*/
|
|
||||||
static SUBTITLES_RENDITION_NAME = 'webvtt';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on ViewerComponent Implementation, this value is used to determine how many times we try
|
|
||||||
* to get the rendition of a file for preview, or printing.
|
|
||||||
*/
|
|
||||||
maxRetries = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mime-type grouping based on the ViewerComponent.
|
|
||||||
*/
|
|
||||||
private mimeTypes = {
|
private mimeTypes = {
|
||||||
text: ['text/plain', 'text/csv', 'text/xml', 'text/html', 'application/x-javascript'],
|
text: ['text/plain', 'text/csv', 'text/xml', 'text/html', 'application/x-javascript'],
|
||||||
pdf: ['application/pdf'],
|
pdf: ['application/pdf'],
|
||||||
image: ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/svg+xml'],
|
image: ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/svg+xml'],
|
||||||
media: ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/ogg', 'audio/wav']
|
media: ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/mp3', 'audio/ogg', 'audio/wav']
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timeout used for setInterval.
|
* Returns a list of the active Viewer content extensions.
|
||||||
*/
|
*/
|
||||||
TRY_TIMEOUT: number = 10000;
|
get viewerExtensions(): ViewerExtensionRef[] {
|
||||||
|
return this.extensionService.getViewerExtensions();
|
||||||
/**
|
|
||||||
* Subscribers needed for ViewerComponent to update the viewerType and urlFileContent.
|
|
||||||
*/
|
|
||||||
viewerTypeChange: Subject<string> = new Subject<string>();
|
|
||||||
urlFileContentChange: Subject<string> = new Subject<string>();
|
|
||||||
|
|
||||||
private _renditionsApi: RenditionsApi;
|
|
||||||
get renditionsApi(): RenditionsApi {
|
|
||||||
this._renditionsApi = this._renditionsApi ?? new RenditionsApi(this.apiService.getInstance());
|
|
||||||
return this._renditionsApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _contentApi: ContentApi;
|
|
||||||
get contentApi(): ContentApi {
|
|
||||||
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
|
|
||||||
return this._contentApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _versionsApi: VersionsApi;
|
|
||||||
get versionsApi(): VersionsApi {
|
|
||||||
this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance());
|
|
||||||
return this._versionsApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService,
|
|
||||||
private logService: LogService,
|
|
||||||
private translateService: TranslationService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method takes a url to trigger the print dialog against, and the type of artifact that it
|
* Provides a list of file extensions supported by external plugins.
|
||||||
* is.
|
|
||||||
* This URL should be one that can be rendered in the browser, for example PDF, Image, or Text
|
|
||||||
*/
|
*/
|
||||||
printFile(url: string, type: string): void {
|
get externalExtensions(): string[] {
|
||||||
const pwa = window.open(url, ViewUtilService.TARGET);
|
return this.viewerExtensions.map(ext => ext.fileExtension);
|
||||||
if (pwa) {
|
}
|
||||||
// Because of the way chrome focus and close image window vs. pdf preview window
|
|
||||||
if (type === ViewUtilService.ContentGroup.IMAGE) {
|
|
||||||
pwa.onfocus = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
pwa.close();
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pwa.onload = () => {
|
constructor(private extensionService: AppExtensionService) {
|
||||||
pwa.print();
|
}
|
||||||
};
|
|
||||||
|
/**
|
||||||
|
* get File name from url
|
||||||
|
*
|
||||||
|
* @param url - url file
|
||||||
|
*/
|
||||||
|
getFilenameFromUrl(url: string): string {
|
||||||
|
const anchor = url.indexOf('#');
|
||||||
|
const query = url.indexOf('?');
|
||||||
|
const end = Math.min(
|
||||||
|
anchor > 0 ? anchor : url.length,
|
||||||
|
query > 0 ? query : url.length);
|
||||||
|
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file extension from the string.
|
||||||
|
* Supports the URL formats like:
|
||||||
|
* http://localhost/test.jpg?cache=1000
|
||||||
|
* http://localhost/test.jpg#cache=1000
|
||||||
|
*
|
||||||
|
* @param fileName - file name
|
||||||
|
*/
|
||||||
|
getFileExtension(fileName: string): string {
|
||||||
|
if (fileName) {
|
||||||
|
const match = fileName.match(/\.([^\./\?\#]+)($|\?|\#)/);
|
||||||
|
return match ? match[1] : null;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getViewerType(extension: string, mimeType: string): string {
|
||||||
* Launch the File Print dialog from anywhere other than the preview service, which resolves the
|
let viewerType = this.getViewerTypeByExtension(extension);
|
||||||
* rendition of the object that can be printed from a web browser.
|
|
||||||
* These are: images, PDF files, or PDF rendition of files.
|
|
||||||
* We also force PDF rendition for TEXT type objects, otherwise the default URL is to download.
|
|
||||||
* TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled)
|
|
||||||
*/
|
|
||||||
printFileGeneric(objectId: string, mimeType: string): void {
|
|
||||||
const nodeId = objectId;
|
|
||||||
const type: string = this.getViewerTypeByMimeType(mimeType);
|
|
||||||
|
|
||||||
this.getRendition(nodeId, ViewUtilService.ContentGroup.PDF)
|
if (viewerType === 'unknown') {
|
||||||
.then((value) => {
|
viewerType = this.getViewerTypeByMimeType(mimeType);
|
||||||
const url: string = this.getRenditionUrl(nodeId, type, (!!value));
|
|
||||||
const printType = (type === ViewUtilService.ContentGroup.PDF
|
|
||||||
|| type === ViewUtilService.ContentGroup.TEXT)
|
|
||||||
? ViewUtilService.ContentGroup.PDF : type;
|
|
||||||
this.printFile(url, printType);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.logService.error('Error with Printing');
|
|
||||||
this.logService.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getRenditionUrl(nodeId: string, type: string, renditionExists: boolean): string {
|
|
||||||
return (renditionExists && type !== ViewUtilService.ContentGroup.IMAGE) ?
|
|
||||||
this.contentApi.getRenditionUrl(nodeId, ViewUtilService.ContentGroup.PDF) :
|
|
||||||
this.contentApi.getContentUrl(nodeId, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async waitRendition(nodeId: string, renditionId: string, retries: number): Promise<RenditionEntry> {
|
|
||||||
const rendition = await this.renditionsApi.getRendition(nodeId, renditionId);
|
|
||||||
|
|
||||||
if (this.maxRetries < retries) {
|
|
||||||
const status = rendition.entry.status.toString();
|
|
||||||
|
|
||||||
if (status === 'CREATED') {
|
|
||||||
return rendition;
|
|
||||||
} else {
|
|
||||||
retries += 1;
|
|
||||||
await this.wait(1000);
|
|
||||||
return this.waitRendition(nodeId, renditionId, retries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return viewerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewerTypeByMimeType(mimeType: string): string {
|
getViewerTypeByMimeType(mimeType: string) {
|
||||||
if (mimeType) {
|
if (mimeType) {
|
||||||
mimeType = mimeType.toLowerCase();
|
mimeType = mimeType.toLowerCase();
|
||||||
|
|
||||||
@ -181,164 +110,51 @@ export class ViewUtilService {
|
|||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(ms: number): Promise<any> {
|
private getViewerTypeByExtension(extension: string): string {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
if (extension) {
|
||||||
}
|
extension = extension.toLowerCase();
|
||||||
|
|
||||||
async getRendition(nodeId: string, renditionId: string): Promise<RenditionEntry> {
|
|
||||||
const renditionPaging: RenditionPaging = await this.renditionsApi.listRenditions(nodeId);
|
|
||||||
let rendition: RenditionEntry = renditionPaging.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId);
|
|
||||||
|
|
||||||
if (rendition) {
|
|
||||||
const status = rendition.entry.status.toString();
|
|
||||||
|
|
||||||
if (status === 'NOT_CREATED') {
|
|
||||||
try {
|
|
||||||
await this.renditionsApi.createRendition(nodeId, { id: renditionId });
|
|
||||||
rendition = await this.waitRendition(nodeId, renditionId, 0);
|
|
||||||
} catch (err) {
|
|
||||||
this.logService.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Promise<RenditionEntry>((resolve) => resolve(rendition));
|
|
||||||
}
|
|
||||||
|
|
||||||
async displayNodeRendition(nodeId: string, versionId?: string) {
|
|
||||||
try {
|
|
||||||
const rendition = versionId ? await this.resolveNodeRendition(nodeId, 'pdf', versionId) :
|
|
||||||
await this.resolveNodeRendition(nodeId, 'pdf');
|
|
||||||
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.contentApi.getVersionRenditionUrl(nodeId, versionId, renditionId) :
|
|
||||||
this.contentApi.getRenditionUrl(nodeId, renditionId);
|
|
||||||
this.urlFileContentChange.next(urlFileContent);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.logService.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async resolveNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise<RenditionEntry> {
|
|
||||||
renditionId = renditionId.toLowerCase();
|
|
||||||
|
|
||||||
const supportedRendition: RenditionPaging = versionId ? await this.versionsApi.listVersionRenditions(nodeId, versionId) :
|
|
||||||
await this.renditionsApi.listRenditions(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) {
|
if (this.isExternalViewer()) {
|
||||||
const status: string = rendition.entry.status.toString();
|
return 'external';
|
||||||
|
|
||||||
if (status === 'NOT_CREATED') {
|
|
||||||
try {
|
|
||||||
if (versionId) {
|
|
||||||
await this.versionsApi.createVersionRendition(nodeId, versionId, { id: renditionId }).then(() => {
|
|
||||||
this.viewerTypeChange.next('in_creation');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await this.renditionsApi.createRendition(nodeId, { id: renditionId }).then(() => {
|
|
||||||
this.viewerTypeChange.next('in_creation');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
rendition = versionId ? await this.waitNodeRendition(nodeId, renditionId, versionId) : await this.waitNodeRendition(nodeId, renditionId);
|
|
||||||
} catch (e) {
|
|
||||||
this.viewerTypeChange.next('error_in_creation');
|
|
||||||
rendition = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
this.logService.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rendition;
|
if (this.isCustomViewerExtension(extension)) {
|
||||||
}
|
return 'custom';
|
||||||
|
|
||||||
private async waitNodeRendition(nodeId: string, renditionId: string, versionId?: string): Promise<RenditionEntry> {
|
|
||||||
let currentRetry: number = 0;
|
|
||||||
return new Promise<RenditionEntry>((resolve, reject) => {
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
currentRetry++;
|
|
||||||
if (this.maxRetries >= currentRetry) {
|
|
||||||
if (versionId) {
|
|
||||||
this.versionsApi.getVersionRendition(nodeId, versionId, renditionId).then((rendition: RenditionEntry) => {
|
|
||||||
const status: string = rendition.entry.status.toString();
|
|
||||||
|
|
||||||
if (status === 'CREATED') {
|
|
||||||
this.handleNodeRendition(nodeId, renditionId, versionId);
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return resolve(rendition);
|
|
||||||
}
|
|
||||||
}, () => reject());
|
|
||||||
} else {
|
|
||||||
this.renditionsApi.getRendition(nodeId, renditionId).then((rendition: RenditionEntry) => {
|
|
||||||
const status: string = rendition.entry.status.toString();
|
|
||||||
|
|
||||||
if (status === 'CREATED') {
|
|
||||||
this.handleNodeRendition(nodeId, renditionId);
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return resolve(rendition);
|
|
||||||
}
|
|
||||||
}, () => reject());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return reject();
|
|
||||||
}
|
|
||||||
}, this.TRY_TIMEOUT);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleNodeRendition(nodeId: string, renditionId: string, versionId?: string) {
|
|
||||||
if (renditionId === 'pdf') {
|
|
||||||
this.viewerTypeChange.next('pdf');
|
|
||||||
} else if (renditionId === 'imgpreview') {
|
|
||||||
this.viewerTypeChange.next('image');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlFileContent = versionId ? this.contentApi.getVersionRenditionUrl(nodeId, versionId, renditionId) :
|
if (this.extensions.image.indexOf(extension) >= 0) {
|
||||||
this.contentApi.getRenditionUrl(nodeId, renditionId);
|
return 'image';
|
||||||
this.urlFileContentChange.next(urlFileContent);
|
}
|
||||||
|
|
||||||
|
if (this.extensions.media.indexOf(extension) >= 0) {
|
||||||
|
return 'media';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.extensions.text.indexOf(extension) >= 0) {
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.extensions.pdf.indexOf(extension) >= 0) {
|
||||||
|
return 'pdf';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateMediaTracks(nodeId: string): Promise<Track[]> {
|
private isExternalViewer(): boolean {
|
||||||
return this.isRenditionAvailable(nodeId, ViewUtilService.SUBTITLES_RENDITION_NAME)
|
return !!this.viewerExtensions.find(ext => ext.fileExtension === '*');
|
||||||
.then((value) => {
|
|
||||||
const tracks = [];
|
|
||||||
if (value) {
|
|
||||||
tracks.push({
|
|
||||||
kind: 'subtitles',
|
|
||||||
src: this.contentApi.getRenditionUrl(nodeId, ViewUtilService.SUBTITLES_RENDITION_NAME),
|
|
||||||
label: this.translateService.instant('ADF_VIEWER.SUBTITLES')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return tracks;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.logService.error('Error while retrieving ' + ViewUtilService.SUBTITLES_RENDITION_NAME + ' rendition');
|
|
||||||
this.logService.error(err);
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async isRenditionAvailable(nodeId: string, renditionId: string): Promise<boolean> {
|
isCustomViewerExtension(extension: string): boolean {
|
||||||
const renditionPaging: RenditionPaging = await this.renditionsApi.listRenditions(nodeId);
|
const extensions = this.externalExtensions || [];
|
||||||
const rendition: RenditionEntry = renditionPaging.list.entries.find((renditionEntry: RenditionEntry) => renditionEntry.entry.id.toLowerCase() === renditionId);
|
|
||||||
|
|
||||||
return rendition?.entry?.status?.toString() === 'CREATED' || false;
|
if (extension && extensions.length > 0) {
|
||||||
|
extension = extension.toLowerCase();
|
||||||
|
return extensions.flat().indexOf(extension) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,13 @@ import { ViewerMoreActionsComponent } from './components/viewer-more-actions.com
|
|||||||
import { ViewerOpenWithComponent } from './components/viewer-open-with.component';
|
import { ViewerOpenWithComponent } from './components/viewer-open-with.component';
|
||||||
import { ViewerSidebarComponent } from './components/viewer-sidebar.component';
|
import { ViewerSidebarComponent } from './components/viewer-sidebar.component';
|
||||||
import { ViewerToolbarComponent } from './components/viewer-toolbar.component';
|
import { ViewerToolbarComponent } from './components/viewer-toolbar.component';
|
||||||
import { ViewerComponent } from './components/viewer.component';
|
import { ViewerRenderComponent } from './components/viewer-render.component';
|
||||||
import { ViewerExtensionDirective } from './directives/viewer-extension.directive';
|
import { ViewerExtensionDirective } from './directives/viewer-extension.directive';
|
||||||
import { ViewerToolbarActionsComponent } from './components/viewer-toolbar-actions.component';
|
import { ViewerToolbarActionsComponent } from './components/viewer-toolbar-actions.component';
|
||||||
import { DirectiveModule } from '../directives/directive.module';
|
import { DirectiveModule } from '../directives/directive.module';
|
||||||
import { A11yModule } from '@angular/cdk/a11y';
|
import { A11yModule } from '@angular/cdk/a11y';
|
||||||
|
import { ViewerComponent } from './components/viewer.component';
|
||||||
|
import { ViewerToolbarCustomActionsComponent } from './components/viewer-toolbar-custom-actions.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -60,7 +62,7 @@ import { A11yModule } from '@angular/cdk/a11y';
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
PdfPasswordDialogComponent,
|
PdfPasswordDialogComponent,
|
||||||
ViewerComponent,
|
ViewerRenderComponent,
|
||||||
ImgViewerComponent,
|
ImgViewerComponent,
|
||||||
TxtViewerComponent,
|
TxtViewerComponent,
|
||||||
MediaPlayerComponent,
|
MediaPlayerComponent,
|
||||||
@ -73,10 +75,12 @@ import { A11yModule } from '@angular/cdk/a11y';
|
|||||||
ViewerSidebarComponent,
|
ViewerSidebarComponent,
|
||||||
ViewerOpenWithComponent,
|
ViewerOpenWithComponent,
|
||||||
ViewerMoreActionsComponent,
|
ViewerMoreActionsComponent,
|
||||||
ViewerToolbarActionsComponent
|
ViewerToolbarActionsComponent,
|
||||||
|
ViewerComponent,
|
||||||
|
ViewerToolbarCustomActionsComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ViewerComponent,
|
ViewerRenderComponent,
|
||||||
ImgViewerComponent,
|
ImgViewerComponent,
|
||||||
TxtViewerComponent,
|
TxtViewerComponent,
|
||||||
MediaPlayerComponent,
|
MediaPlayerComponent,
|
||||||
@ -90,7 +94,9 @@ import { A11yModule } from '@angular/cdk/a11y';
|
|||||||
ViewerSidebarComponent,
|
ViewerSidebarComponent,
|
||||||
ViewerOpenWithComponent,
|
ViewerOpenWithComponent,
|
||||||
ViewerMoreActionsComponent,
|
ViewerMoreActionsComponent,
|
||||||
ViewerToolbarActionsComponent
|
ViewerToolbarActionsComponent,
|
||||||
|
ViewerComponent,
|
||||||
|
ViewerToolbarCustomActionsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ViewerModule {
|
export class ViewerModule {
|
||||||
|
@ -27,7 +27,6 @@ import {
|
|||||||
OnChanges
|
OnChanges
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ExtensionService } from '../../services/extension.service';
|
import { ExtensionService } from '../../services/extension.service';
|
||||||
import { Node } from '@alfresco/js-api';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-preview-extension',
|
selector: 'adf-preview-extension',
|
||||||
@ -51,10 +50,6 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
extension: string;
|
extension: string;
|
||||||
|
|
||||||
/** Node containing the content to display. */
|
|
||||||
@Input()
|
|
||||||
node: Node;
|
|
||||||
|
|
||||||
private componentRef: ComponentRef<any>;
|
private componentRef: ComponentRef<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -95,7 +90,6 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
if (this.componentRef && this.componentRef.instance) {
|
if (this.componentRef && this.componentRef.instance) {
|
||||||
const instance = this.componentRef.instance;
|
const instance = this.componentRef.instance;
|
||||||
|
|
||||||
instance.node = this.node;
|
|
||||||
instance.url = this.url;
|
instance.url = this.url;
|
||||||
instance.extension = this.extension;
|
instance.extension = this.extension;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { FormRenderingService } from '@alfresco/adf-core';
|
import { FormFieldTypes, FormRenderingService } from '@alfresco/adf-core';
|
||||||
import { AttachFileCloudWidgetComponent } from './widgets/attach-file/attach-file-cloud-widget.component';
|
import { AttachFileCloudWidgetComponent } from './widgets/attach-file/attach-file-cloud-widget.component';
|
||||||
import { DropdownCloudWidgetComponent } from './widgets/dropdown/dropdown-cloud.widget';
|
import { DropdownCloudWidgetComponent } from './widgets/dropdown/dropdown-cloud.widget';
|
||||||
import { DateCloudWidgetComponent } from './widgets/date/date-cloud.widget';
|
import { DateCloudWidgetComponent } from './widgets/date/date-cloud.widget';
|
||||||
@ -24,6 +24,7 @@ import { PeopleCloudWidgetComponent } from './widgets/people/people-cloud.widget
|
|||||||
import { GroupCloudWidgetComponent } from './widgets/group/group-cloud.widget';
|
import { GroupCloudWidgetComponent } from './widgets/group/group-cloud.widget';
|
||||||
import { PropertiesViewerWidgetComponent } from './widgets/properties-viewer/properties-viewer.widget';
|
import { PropertiesViewerWidgetComponent } from './widgets/properties-viewer/properties-viewer.widget';
|
||||||
import { RadioButtonsCloudWidgetComponent } from './widgets/radio-buttons/radio-buttons-cloud.widget';
|
import { RadioButtonsCloudWidgetComponent } from './widgets/radio-buttons/radio-buttons-cloud.widget';
|
||||||
|
import { FileViewerWidgetComponent } from './widgets/file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -33,13 +34,14 @@ export class CloudFormRenderingService extends FormRenderingService {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.register({
|
this.register({
|
||||||
upload: () => AttachFileCloudWidgetComponent,
|
[FormFieldTypes.UPLOAD]: () => AttachFileCloudWidgetComponent,
|
||||||
dropdown: () => DropdownCloudWidgetComponent,
|
[FormFieldTypes.DROPDOWN]: () => DropdownCloudWidgetComponent,
|
||||||
date: () => DateCloudWidgetComponent,
|
[FormFieldTypes.DATE]: () => DateCloudWidgetComponent,
|
||||||
people: () => PeopleCloudWidgetComponent,
|
[FormFieldTypes.PEOPLE]: () => PeopleCloudWidgetComponent,
|
||||||
'functional-group': () => GroupCloudWidgetComponent,
|
[FormFieldTypes.FUNCTIONAL_GROUP]: () => GroupCloudWidgetComponent,
|
||||||
'properties-viewer': () => PropertiesViewerWidgetComponent,
|
[FormFieldTypes.PROPERTIES_VIEWER]: () => PropertiesViewerWidgetComponent,
|
||||||
'radio-buttons': () => RadioButtonsCloudWidgetComponent
|
[FormFieldTypes.RADIO_BUTTONS]: () => RadioButtonsCloudWidgetComponent,
|
||||||
|
[FormFieldTypes.ALFRESCO_FILE_VIEWER]: () => FileViewerWidgetComponent
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<div class="adf-file-viewer-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
|
||||||
|
[class.adf-readonly]="field.readOnly">
|
||||||
|
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk"
|
||||||
|
*ngIf="isRequired()">*</span></label>
|
||||||
|
<adf-alfresco-viewer [overlayMode]="false" [nodeId]="field.value" [showViewer]="field.value" [allowGoBack]="false"></adf-alfresco-viewer>
|
||||||
|
<error-widget [error]="field.validationSummary"></error-widget>
|
||||||
|
</div>
|
@ -15,12 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FormModel } from '../core/form.model';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { FormFieldModel } from '../core/form-field.model';
|
|
||||||
import { FormService } from '../../../services/form.service';
|
|
||||||
import { FileViewerWidgetComponent } from './file-viewer.widget';
|
import { FileViewerWidgetComponent } from './file-viewer.widget';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { FormModel, FormService, FormFieldModel } from '@alfresco/adf-core';
|
||||||
|
|
||||||
describe('FileViewerWidgetComponent', () => {
|
describe('FileViewerWidgetComponent', () => {
|
||||||
const fakeForm = new FormModel();
|
const fakeForm = new FormModel();
|
@ -16,8 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { FormService } from '../../../services/form.service';
|
import { WidgetComponent, FormService } from '@alfresco/adf-core';
|
||||||
import { WidgetComponent } from '../widget.component';
|
|
||||||
|
|
||||||
/* eslint-disable @angular-eslint/component-selector */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
@ -24,7 +24,7 @@ import { MaterialModule } from '../material.module';
|
|||||||
import { FormCloudComponent } from './components/form-cloud.component';
|
import { FormCloudComponent } from './components/form-cloud.component';
|
||||||
import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
|
import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
|
||||||
import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outcomes.component';
|
import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outcomes.component';
|
||||||
import { ContentMetadataModule, ContentNodeSelectorModule, UploadModule } from '@alfresco/adf-content-services';
|
import { ContentMetadataModule, ContentModule, ContentNodeSelectorModule, UploadModule } from '@alfresco/adf-content-services';
|
||||||
|
|
||||||
import { DateCloudWidgetComponent } from './components/widgets/date/date-cloud.widget';
|
import { DateCloudWidgetComponent } from './components/widgets/date/date-cloud.widget';
|
||||||
import { DropdownCloudWidgetComponent } from './components/widgets/dropdown/dropdown-cloud.widget';
|
import { DropdownCloudWidgetComponent } from './components/widgets/dropdown/dropdown-cloud.widget';
|
||||||
@ -39,6 +39,7 @@ import { PropertiesViewerWidgetComponent } from './components/widgets/properties
|
|||||||
import { PropertiesViewerWrapperComponent } from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
|
import { PropertiesViewerWrapperComponent } from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
|
||||||
import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
||||||
import { FilePropertiesTableCloudComponent } from './components/widgets/attach-file/file-properties-table-cloud.component';
|
import { FilePropertiesTableCloudComponent } from './components/widgets/attach-file/file-properties-table-cloud.component';
|
||||||
|
import { FileViewerWidgetComponent } from './components/widgets/file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -52,7 +53,8 @@ import { FilePropertiesTableCloudComponent } from './components/widgets/attach-f
|
|||||||
PeopleCloudModule,
|
PeopleCloudModule,
|
||||||
GroupCloudModule,
|
GroupCloudModule,
|
||||||
ContentMetadataModule,
|
ContentMetadataModule,
|
||||||
UploadModule
|
UploadModule,
|
||||||
|
ContentModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FormCloudComponent,
|
FormCloudComponent,
|
||||||
@ -67,7 +69,8 @@ import { FilePropertiesTableCloudComponent } from './components/widgets/attach-f
|
|||||||
GroupCloudWidgetComponent,
|
GroupCloudWidgetComponent,
|
||||||
PropertiesViewerWrapperComponent,
|
PropertiesViewerWrapperComponent,
|
||||||
PropertiesViewerWidgetComponent,
|
PropertiesViewerWidgetComponent,
|
||||||
FilePropertiesTableCloudComponent
|
FilePropertiesTableCloudComponent,
|
||||||
|
FileViewerWidgetComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
FormCloudComponent,
|
FormCloudComponent,
|
||||||
@ -80,7 +83,9 @@ import { FilePropertiesTableCloudComponent } from './components/widgets/attach-f
|
|||||||
DateCloudWidgetComponent,
|
DateCloudWidgetComponent,
|
||||||
PeopleCloudWidgetComponent,
|
PeopleCloudWidgetComponent,
|
||||||
GroupCloudWidgetComponent,
|
GroupCloudWidgetComponent,
|
||||||
PropertiesViewerWidgetComponent
|
PropertiesViewerWidgetComponent,
|
||||||
|
ContentModule,
|
||||||
|
FileViewerWidgetComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FormCloudModule {
|
export class FormCloudModule {
|
||||||
|
@ -30,6 +30,7 @@ export * from './components/widgets/group/group-cloud.widget';
|
|||||||
export * from './components/widgets/people/people-cloud.widget';
|
export * from './components/widgets/people/people-cloud.widget';
|
||||||
export * from './components/widgets/properties-viewer/properties-viewer.widget';
|
export * from './components/widgets/properties-viewer/properties-viewer.widget';
|
||||||
export * from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
export * from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
||||||
|
export * from './components/widgets/file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
export * from './services/content-cloud-node-selector.service';
|
export * from './services/content-cloud-node-selector.service';
|
||||||
export * from './services/form-cloud.service';
|
export * from './services/form-cloud.service';
|
||||||
|
@ -31,11 +31,14 @@ import { RadioButtonsWidgetComponent } from './widgets/radio-buttons/radio-butto
|
|||||||
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
||||||
import { DropdownWidgetComponent } from './widgets/dropdown/dropdown.widget';
|
import { DropdownWidgetComponent } from './widgets/dropdown/dropdown.widget';
|
||||||
import { DynamicTableModule } from './widgets/dynamic-table/dynamic-table.module';
|
import { DynamicTableModule } from './widgets/dynamic-table/dynamic-table.module';
|
||||||
|
import { FileViewerWidgetComponent } from './widgets/file-viewer/file-viewer.widget';
|
||||||
|
import { ContentModule } from '@alfresco/adf-content-services';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
DynamicTableModule,
|
DynamicTableModule,
|
||||||
CoreModule,
|
CoreModule,
|
||||||
|
ContentModule,
|
||||||
MaterialModule
|
MaterialModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -50,7 +53,8 @@ import { DynamicTableModule } from './widgets/dynamic-table/dynamic-table.module
|
|||||||
FormListComponent,
|
FormListComponent,
|
||||||
RadioButtonsWidgetComponent,
|
RadioButtonsWidgetComponent,
|
||||||
DropdownWidgetComponent,
|
DropdownWidgetComponent,
|
||||||
TypeaheadWidgetComponent
|
TypeaheadWidgetComponent,
|
||||||
|
FileViewerWidgetComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
FormComponent,
|
FormComponent,
|
||||||
@ -61,7 +65,8 @@ import { DynamicTableModule } from './widgets/dynamic-table/dynamic-table.module
|
|||||||
RadioButtonsWidgetComponent,
|
RadioButtonsWidgetComponent,
|
||||||
TypeaheadWidgetComponent,
|
TypeaheadWidgetComponent,
|
||||||
DropdownWidgetComponent,
|
DropdownWidgetComponent,
|
||||||
FormListComponent
|
FormListComponent,
|
||||||
|
FileViewerWidgetComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FormModule {
|
export class FormModule {
|
||||||
|
@ -26,6 +26,7 @@ import { RadioButtonsWidgetComponent } from './widgets/radio-buttons/radio-butto
|
|||||||
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
||||||
import { DocumentWidgetComponent } from './widgets/document/document.widget';
|
import { DocumentWidgetComponent } from './widgets/document/document.widget';
|
||||||
import { AttachFileWidgetComponent } from './widgets/content-widget/attach-file-widget.component';
|
import { AttachFileWidgetComponent } from './widgets/content-widget/attach-file-widget.component';
|
||||||
|
import { FileViewerWidgetComponent } from './widgets/file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
describe('ProcessFormRenderingService', () => {
|
describe('ProcessFormRenderingService', () => {
|
||||||
|
|
||||||
@ -100,4 +101,10 @@ describe('ProcessFormRenderingService', () => {
|
|||||||
expect(type).toBe(DynamicTableWidgetComponent);
|
expect(type).toBe(DynamicTableWidgetComponent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should resolve File Viewer widget for file viewer', () => {
|
||||||
|
const resolver = service.getComponentTypeResolver(FormFieldTypes.ALFRESCO_FILE_VIEWER);
|
||||||
|
const type = resolver(null);
|
||||||
|
expect(type).toBe(FileViewerWidgetComponent);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -29,6 +29,7 @@ import { RadioButtonsWidgetComponent } from './widgets/radio-buttons/radio-butto
|
|||||||
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
import { TypeaheadWidgetComponent } from './widgets/typeahead/typeahead.widget';
|
||||||
import { DynamicTableWidgetComponent } from './widgets/dynamic-table/dynamic-table.widget';
|
import { DynamicTableWidgetComponent } from './widgets/dynamic-table/dynamic-table.widget';
|
||||||
import { DropdownWidgetComponent } from './widgets/dropdown/dropdown.widget';
|
import { DropdownWidgetComponent } from './widgets/dropdown/dropdown.widget';
|
||||||
|
import { FileViewerWidgetComponent } from './widgets/file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -46,7 +47,8 @@ export class ProcessFormRenderingService extends FormRenderingService {
|
|||||||
[FormFieldTypes.DOCUMENT]: () => DocumentWidgetComponent,
|
[FormFieldTypes.DOCUMENT]: () => DocumentWidgetComponent,
|
||||||
[FormFieldTypes.PEOPLE]: () => PeopleWidgetComponent,
|
[FormFieldTypes.PEOPLE]: () => PeopleWidgetComponent,
|
||||||
[FormFieldTypes.FUNCTIONAL_GROUP]: () => FunctionalGroupWidgetComponent,
|
[FormFieldTypes.FUNCTIONAL_GROUP]: () => FunctionalGroupWidgetComponent,
|
||||||
[FormFieldTypes.DYNAMIC_TABLE]: () => DynamicTableWidgetComponent
|
[FormFieldTypes.DYNAMIC_TABLE]: () => DynamicTableWidgetComponent,
|
||||||
|
[FormFieldTypes.ALFRESCO_FILE_VIEWER]: () => FileViewerWidgetComponent
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
[class.adf-readonly]="field.readOnly">
|
[class.adf-readonly]="field.readOnly">
|
||||||
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk"
|
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk"
|
||||||
*ngIf="isRequired()">*</span></label>
|
*ngIf="isRequired()">*</span></label>
|
||||||
<adf-viewer [overlayMode]="false" [nodeId]="field.value" [showViewer]="field.value" [allowGoBack]="false"></adf-viewer>
|
<adf-alfresco-viewer [nodeId]="field.value" [showViewer]="field.value" [allowGoBack]="false"></adf-alfresco-viewer>
|
||||||
<error-widget [error]="field.validationSummary"></error-widget>
|
<error-widget [error]="field.validationSummary"></error-widget>
|
||||||
</div>
|
</div>
|
@ -0,0 +1,19 @@
|
|||||||
|
file-viewer-widget {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.adf-file-viewer-widget {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
adf-viewer.adf-viewer {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.adf-viewer-container {
|
||||||
|
.adf-viewer-content > div {
|
||||||
|
height: 90vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FileViewerWidgetComponent } from './file-viewer.widget';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { FormModel, FormService, FormFieldModel } from '@alfresco/adf-core';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
describe('FileViewerWidgetComponent', () => {
|
||||||
|
const fakeForm = new FormModel();
|
||||||
|
let widget: FileViewerWidgetComponent;
|
||||||
|
let formServiceStub: Partial<FormService>;
|
||||||
|
let fixture: ComponentFixture<FileViewerWidgetComponent>;
|
||||||
|
|
||||||
|
const fakePngAnswer: any = {
|
||||||
|
id: '1933',
|
||||||
|
link: false,
|
||||||
|
isExternal: false,
|
||||||
|
relatedContent: false,
|
||||||
|
contentAvailable: true,
|
||||||
|
name: 'a_png_file.png',
|
||||||
|
simpleType: 'image',
|
||||||
|
mimeType: 'image/png',
|
||||||
|
previewStatus: 'queued',
|
||||||
|
thumbnailStatus: 'queued',
|
||||||
|
created: '2022-10-14T17:17:37.099Z',
|
||||||
|
createdBy: { id: 1001, firstName: 'Admin', lastName: 'admin', email: 'admin@example.com' }
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot()
|
||||||
|
],
|
||||||
|
declarations: [ FileViewerWidgetComponent ],
|
||||||
|
providers: [ { provide: FormService, useValue: formServiceStub } ]
|
||||||
|
});
|
||||||
|
|
||||||
|
formServiceStub = TestBed.inject(FormService);
|
||||||
|
fixture = TestBed.createComponent(FileViewerWidgetComponent);
|
||||||
|
widget = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the file id corretly when the field value is an array', (done) => {
|
||||||
|
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: [fakePngAnswer] });
|
||||||
|
widget.field = fakeField;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(widget.field.value).toBe('1933');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the file id corretly when the field value is a string', (done) => {
|
||||||
|
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: 'fakeValue' });
|
||||||
|
widget.field = fakeField;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(widget.field.value).toBe('fakeValue');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,54 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FormService, WidgetComponent } from '@alfresco/adf-core';
|
||||||
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
|
||||||
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'file-viewer-widget',
|
||||||
|
templateUrl: './file-viewer.widget.html',
|
||||||
|
styleUrls: ['./file-viewer.widget.scss'],
|
||||||
|
host: {
|
||||||
|
'(click)': 'event($event)',
|
||||||
|
'(blur)': 'event($event)',
|
||||||
|
'(change)': 'event($event)',
|
||||||
|
'(focus)': 'event($event)',
|
||||||
|
'(focusin)': 'event($event)',
|
||||||
|
'(focusout)': 'event($event)',
|
||||||
|
'(input)': 'event($event)',
|
||||||
|
'(invalid)': 'event($event)',
|
||||||
|
'(select)': 'event($event)'
|
||||||
|
},
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FileViewerWidgetComponent extends WidgetComponent implements OnInit {
|
||||||
|
constructor(formService: FormService) {
|
||||||
|
super(formService);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.field &&
|
||||||
|
this.field.value &&
|
||||||
|
Array.isArray(this.field.value) &&
|
||||||
|
this.field.value.length) {
|
||||||
|
const file = this.field.value[0];
|
||||||
|
this.field.value = file.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ export * from './radio-buttons/radio-buttons.widget';
|
|||||||
export * from './functional-group/functional-group.widget';
|
export * from './functional-group/functional-group.widget';
|
||||||
export * from './typeahead/typeahead.widget';
|
export * from './typeahead/typeahead.widget';
|
||||||
export * from './dropdown/dropdown.widget';
|
export * from './dropdown/dropdown.widget';
|
||||||
|
export * from './file-viewer/file-viewer.widget';
|
||||||
|
|
||||||
export * from './dynamic-table/index';
|
export * from './dynamic-table/index';
|
||||||
|
|
||||||
|
@ -86,10 +86,10 @@ export class ViewerPage {
|
|||||||
pdfPageLoaded = $('[data-page-number="1"][data-loaded="true"], adf-img-viewer, adf-txt-viewer');
|
pdfPageLoaded = $('[data-page-number="1"][data-loaded="true"], adf-img-viewer, adf-txt-viewer');
|
||||||
|
|
||||||
downloadSwitch = $('#adf-switch-download');
|
downloadSwitch = $('#adf-switch-download');
|
||||||
downloadButton = $('#adf-viewer-download');
|
downloadButton = $('#adf-alfresco-viewer-download');
|
||||||
|
|
||||||
printSwitch = $('#adf-switch-print');
|
printSwitch = $('#adf-switch-print');
|
||||||
printButton = $('#adf-viewer-print');
|
printButton = $('#adf-alfresco-viewer-print');
|
||||||
|
|
||||||
allowSidebarSwitch = $('#adf-switch-allowsidebar');
|
allowSidebarSwitch = $('#adf-switch-allowsidebar');
|
||||||
allowLeftSidebarSwitch = $('#adf-switch-allowLeftSidebar');
|
allowLeftSidebarSwitch = $('#adf-switch-allowLeftSidebar');
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"ng": "nx",
|
"ng": "nx",
|
||||||
"00": "echo -------------------------------------------- DOC -----------------------------------------------",
|
"00": "echo -------------------------------------------- DOC -----------------------------------------------",
|
||||||
"build-doc-tools": "tsc -p ./tools/doc/tsconfig.json",
|
"build-doc-tools": "tsc -p ./tools/doc/tsconfig.json",
|
||||||
"docbuild": "node tools/doc/node_modules/typedoc/bin/typedoc --tsconfig lib/tsconfig.doc.json && node tools/doc/buildYamlSourceInfo.js docs/docs.json && node ./tools/doc/docProcessor.js",
|
"markdownlint": "markdownlint ./docs",
|
||||||
|
"docbuild": "(cd ./tools/doc; npm i) && node tools/doc/node_modules/typedoc/bin/typedoc --tsconfig lib/tsconfig.doc.json && node tools/doc/buildYamlSourceInfo.js docs/docs.json && node ./tools/doc/docProcessor.js",
|
||||||
"review-checker": "node ./tools/doc/reviewChecker.js",
|
"review-checker": "node ./tools/doc/reviewChecker.js",
|
||||||
"01": "echo -------------------------------------------- Build Lib -----------------------------------------------",
|
"01": "echo -------------------------------------------- Build Lib -----------------------------------------------",
|
||||||
"affected:libs": "nx affected:libs",
|
"affected:libs": "nx affected:libs",
|
||||||
|
@ -3,10 +3,12 @@ var __extends = (this && this.__extends) || (function () {
|
|||||||
var extendStatics = function (d, b) {
|
var extendStatics = function (d, b) {
|
||||||
extendStatics = Object.setPrototypeOf ||
|
extendStatics = Object.setPrototypeOf ||
|
||||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||||
return extendStatics(d, b);
|
return extendStatics(d, b);
|
||||||
};
|
};
|
||||||
return function (d, b) {
|
return function (d, b) {
|
||||||
|
if (typeof b !== "function" && b !== null)
|
||||||
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||||
extendStatics(d, b);
|
extendStatics(d, b);
|
||||||
function __() { this.constructor = d; }
|
function __() { this.constructor = d; }
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||||
|
@ -13,11 +13,11 @@ var tree = remark()
|
|||||||
.use(frontMatter, ['yaml'])
|
.use(frontMatter, ['yaml'])
|
||||||
.parse(docSrc);
|
.parse(docSrc);
|
||||||
tree = removePosInfo(tree);
|
tree = removePosInfo(tree);
|
||||||
var schema = graphql_1.buildSchema(MQ.schema);
|
var schema = (0, graphql_1.buildSchema)(MQ.schema);
|
||||||
var root = {
|
var root = {
|
||||||
document: function () { return new MQ.Root(tree); }
|
document: function () { return new MQ.Root(tree); }
|
||||||
};
|
};
|
||||||
var query = "\n {\n document {\n metadata(key: \"Status\")\n heading {\n link {\n text {\n value\n }\n }\n }\n paragraph {\n plaintext\n }\n }\n }\n";
|
var query = "\n {\n document {\n metadata(key: \"Status\")\n heading {\n link {\n text {\n value\n }\n }\n }\n paragraph {\n plaintext\n }\n }\n }\n";
|
||||||
graphql_1.graphql(schema, query, root).then(function (response) {
|
(0, graphql_1.graphql)(schema, query, root).then(function (response) {
|
||||||
console.log(JSON.stringify(response));
|
console.log(JSON.stringify(response));
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ var client = new graphql_request_1.GraphQLClient('https://api.github.com/graphql
|
|||||||
});
|
});
|
||||||
var query = "query commitHistory($path: String) {\n repository(name: \"alfresco-ng2-components\", owner: \"alfresco\") {\n ref(qualifiedName: \"development\") {\n target {\n ... on Commit {\n history(first: 15, path: $path) {\n nodes {\n pushedDate\n message\n }\n }\n }\n }\n }\n }\n}";
|
var query = "query commitHistory($path: String) {\n repository(name: \"alfresco-ng2-components\", owner: \"alfresco\") {\n ref(qualifiedName: \"development\") {\n target {\n ... on Commit {\n history(first: 15, path: $path) {\n nodes {\n pushedDate\n message\n }\n }\n }\n }\n }\n }\n}";
|
||||||
var docFiles = getDocFilePaths(docsFolderPath);
|
var docFiles = getDocFilePaths(docsFolderPath);
|
||||||
var docNames = rxjs_1.of(docFiles);
|
var docNames = (0, rxjs_1.of)(docFiles);
|
||||||
console.log("'Name','Review date','Commits since review','Score'");
|
console.log("'Name','Review date','Commits since review','Score'");
|
||||||
docNames.subscribe(function (docs) {
|
docNames.subscribe(function (docs) {
|
||||||
docs.forEach(function (x) {
|
docs.forEach(function (x) {
|
||||||
@ -48,7 +48,7 @@ docNames.subscribe(function (docs) {
|
|||||||
if (numUsefulCommits > 0) {
|
if (numUsefulCommits > 0) {
|
||||||
var dateString = lastReviewDate.format('YYYY-MM-DD');
|
var dateString = lastReviewDate.format('YYYY-MM-DD');
|
||||||
var score = priorityScore(lastReviewDate, numUsefulCommits).toPrecision(3);
|
var score = priorityScore(lastReviewDate, numUsefulCommits).toPrecision(3);
|
||||||
console.log("'" + key + "','" + dateString + "','" + numUsefulCommits + "','" + score + "'");
|
console.log("'".concat(key, "','").concat(dateString, "','").concat(numUsefulCommits, "','").concat(score, "'"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -38,13 +38,13 @@ var PropInfo = /** @class */ (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_this.docText && !_this.isDeprecated) {
|
if (!_this.docText && !_this.isDeprecated) {
|
||||||
_this.errorMessages.push("Warning: Input \"" + sourceData.name + "\" has no doc text.");
|
_this.errorMessages.push("Warning: Input \"".concat(sourceData.name, "\" has no doc text."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dec.name === 'Output') {
|
if (dec.name === 'Output') {
|
||||||
_this.isOutput = true;
|
_this.isOutput = true;
|
||||||
if (!_this.docText && !_this.isDeprecated) {
|
if (!_this.docText && !_this.isDeprecated) {
|
||||||
_this.errorMessages.push("Warning: Output \"" + sourceData.name + "\" has no doc text.");
|
_this.errorMessages.push("Warning: Output \"".concat(sourceData.name, "\" has no doc text."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -77,9 +77,9 @@ var ParamInfo = /** @class */ (function () {
|
|||||||
if (this.isOptional) {
|
if (this.isOptional) {
|
||||||
this.combined += '?';
|
this.combined += '?';
|
||||||
}
|
}
|
||||||
this.combined += ": `" + this.type + "`";
|
this.combined += ": `".concat(this.type, "`");
|
||||||
if (this.defaultValue !== '') {
|
if (this.defaultValue !== '') {
|
||||||
this.combined += " = `" + this.defaultValue + "`";
|
this.combined += " = `".concat(this.defaultValue, "`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ParamInfo;
|
return ParamInfo;
|
||||||
@ -93,7 +93,7 @@ var MethodSigInfo = /** @class */ (function () {
|
|||||||
this.docText = sourceData.summary || '';
|
this.docText = sourceData.summary || '';
|
||||||
this.docText = this.docText.replace(/[\n\r]+/g, ' ').trim();
|
this.docText = this.docText.replace(/[\n\r]+/g, ' ').trim();
|
||||||
if (!this.docText && this.name.indexOf('service') > 0) {
|
if (!this.docText && this.name.indexOf('service') > 0) {
|
||||||
this.errorMessages.push("Warning: method \"" + sourceData.name + "\" has no doc text.");
|
this.errorMessages.push("Warning: method \"".concat(sourceData.name, "\" has no doc text."));
|
||||||
}
|
}
|
||||||
this.returnType = sourceData.syntax['return'].type || '';
|
this.returnType = sourceData.syntax['return'].type || '';
|
||||||
this.returnType = this.returnType.toString().replace(/\s/g, '');
|
this.returnType = this.returnType.toString().replace(/\s/g, '');
|
||||||
@ -103,7 +103,7 @@ var MethodSigInfo = /** @class */ (function () {
|
|||||||
this.returnsSomething = false;
|
this.returnsSomething = false;
|
||||||
}
|
}
|
||||||
if (this.returnsSomething && !this.returnDocText && this.name.indexOf('service') > 0) {
|
if (this.returnsSomething && !this.returnDocText && this.name.indexOf('service') > 0) {
|
||||||
this.errorMessages.push("Warning: Return value of method \"" + sourceData.name + "\" has no doc text.");
|
this.errorMessages.push("Warning: Return value of method \"".concat(sourceData.name, "\" has no doc text."));
|
||||||
}
|
}
|
||||||
this.isDeprecated = false;
|
this.isDeprecated = false;
|
||||||
if (sourceData.tags) {
|
if (sourceData.tags) {
|
||||||
@ -118,7 +118,7 @@ var MethodSigInfo = /** @class */ (function () {
|
|||||||
if (sourceData.syntax.parameters) {
|
if (sourceData.syntax.parameters) {
|
||||||
sourceData.syntax.parameters.forEach(function (rawParam) {
|
sourceData.syntax.parameters.forEach(function (rawParam) {
|
||||||
if (rawParam.name && !rawParam.description && !rawParam.name.startWith('on')) {
|
if (rawParam.name && !rawParam.description && !rawParam.name.startWith('on')) {
|
||||||
_this.errorMessages.push("Warning: parameter \"" + rawParam.name + "\" of method \"" + sourceData.name + "\" has no doc text.");
|
_this.errorMessages.push("Warning: parameter \"".concat(rawParam.name, "\" of method \"").concat(sourceData.name, "\" has no doc text."));
|
||||||
}
|
}
|
||||||
var param = new ParamInfo(rawParam);
|
var param = new ParamInfo(rawParam);
|
||||||
_this.params.push(param);
|
_this.params.push(param);
|
||||||
|
@ -29,13 +29,13 @@ function processDocs(mdCache, aggData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var linkElems = unist_util_select_1.selectAll('link', tree);
|
var linkElems = (0, unist_util_select_1.selectAll)('link', tree);
|
||||||
linkElems.forEach(function (linkElem) {
|
linkElems.forEach(function (linkElem) {
|
||||||
var normUrl = normaliseLinkPath(pathname, linkElem.url);
|
var normUrl = normaliseLinkPath(pathname, linkElem.url);
|
||||||
multiSetAdd(linkRefs, normUrl, pathname);
|
multiSetAdd(linkRefs, normUrl, pathname);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var imageElems = unist_util_select_1.selectAll('image', tree);
|
var imageElems = (0, unist_util_select_1.selectAll)('image', tree);
|
||||||
imageElems.forEach(function (imageElem) {
|
imageElems.forEach(function (imageElem) {
|
||||||
var normUrl = normaliseLinkPath(pathname, imageElem.url);
|
var normUrl = normaliseLinkPath(pathname, imageElem.url);
|
||||||
multiSetAdd(imageRefs, normUrl, pathname);
|
multiSetAdd(imageRefs, normUrl, pathname);
|
||||||
@ -46,11 +46,11 @@ function processDocs(mdCache, aggData) {
|
|||||||
});
|
});
|
||||||
classlessDocs.forEach(function (docPath) {
|
classlessDocs.forEach(function (docPath) {
|
||||||
var relDocPath = docPath.substring(docPath.indexOf('docs'));
|
var relDocPath = docPath.substring(docPath.indexOf('docs'));
|
||||||
console.group("Warning: no source class found for \"" + relDocPath + "\"");
|
console.group("Warning: no source class found for \"".concat(relDocPath, "\""));
|
||||||
if (linkRefs[docPath]) {
|
if (linkRefs[docPath]) {
|
||||||
linkRefs[docPath].forEach(function (linkRef) {
|
linkRefs[docPath].forEach(function (linkRef) {
|
||||||
var relLinkPath = linkRef.substring(linkRef.indexOf('docs'));
|
var relLinkPath = linkRef.substring(linkRef.indexOf('docs'));
|
||||||
console.log("Linked from: \"" + relLinkPath + "\"");
|
console.log("Linked from: \"".concat(relLinkPath, "\""));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
@ -60,7 +60,7 @@ function processDocs(mdCache, aggData) {
|
|||||||
imagePaths.forEach(function (imagePath) {
|
imagePaths.forEach(function (imagePath) {
|
||||||
if (!imageRefs[imagePath]) {
|
if (!imageRefs[imagePath]) {
|
||||||
var relImagePath = imagePath.substring(imagePath.indexOf('docs'));
|
var relImagePath = imagePath.substring(imagePath.indexOf('docs'));
|
||||||
console.log("Warning: no links to image file \"" + relImagePath + "\"");
|
console.log("Warning: no links to image file \"".concat(relImagePath, "\""));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log();
|
console.log();
|
||||||
@ -68,11 +68,11 @@ function processDocs(mdCache, aggData) {
|
|||||||
brokenImUrls.forEach(function (url) {
|
brokenImUrls.forEach(function (url) {
|
||||||
var relUrl = url.substring(url.indexOf('docs'));
|
var relUrl = url.substring(url.indexOf('docs'));
|
||||||
var relDocPath = brokenImageRefs[url].substring(brokenImageRefs[url].indexOf('docs'));
|
var relDocPath = brokenImageRefs[url].substring(brokenImageRefs[url].indexOf('docs'));
|
||||||
console.group("Broken image link \"" + relUrl + "\" found in \"" + relDocPath);
|
console.group("Broken image link \"".concat(relUrl, "\" found in \"").concat(relDocPath));
|
||||||
imagePaths.forEach(function (imPath) {
|
imagePaths.forEach(function (imPath) {
|
||||||
if (lev.get(imPath, url) <= maxImagePathLevDistance) {
|
if (lev.get(imPath, url) <= maxImagePathLevDistance) {
|
||||||
var relImPath = imPath.substring(imPath.indexOf('docs'));
|
var relImPath = imPath.substring(imPath.indexOf('docs'));
|
||||||
console.log("Should it be \"" + relImPath + "\"?");
|
console.log("Should it be \"".concat(relImPath, "\"?"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
|
@ -24,9 +24,9 @@ function processDocs(mdCache, aggData) {
|
|||||||
var indexMD = remark()
|
var indexMD = remark()
|
||||||
.use(frontMatter, ['yaml'])
|
.use(frontMatter, ['yaml'])
|
||||||
.parse(indexFileText);
|
.parse(indexFileText);
|
||||||
var schema = graphql_1.buildSchema(MQ.schema);
|
var schema = (0, graphql_1.buildSchema)(MQ.schema);
|
||||||
libNamesList.forEach(function (libName) {
|
libNamesList.forEach(function (libName) {
|
||||||
graphql_1.graphql(schema, query, docset, null, { 'libName': libName })
|
(0, graphql_1.graphql)(schema, query, docset, null, { 'libName': libName })
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (!response['data']) {
|
if (!response['data']) {
|
||||||
console.log(JSON.stringify(response));
|
console.log(JSON.stringify(response));
|
||||||
|
@ -18,7 +18,7 @@ function processDocs(mdCache, aggData) {
|
|||||||
}
|
}
|
||||||
exports.processDocs = processDocs;
|
exports.processDocs = processDocs;
|
||||||
function fixUrls(tree, docFilePath, linkSet, selector) {
|
function fixUrls(tree, docFilePath, linkSet, selector) {
|
||||||
var linksInDoc = unist_util_select_1.selectAll(selector, tree);
|
var linksInDoc = (0, unist_util_select_1.selectAll)(selector, tree);
|
||||||
var errors = [];
|
var errors = [];
|
||||||
linksInDoc.forEach(function (linkElem) {
|
linksInDoc.forEach(function (linkElem) {
|
||||||
var origFullUrlPath = path.resolve(path.dirname(docFilePath), linkElem.url);
|
var origFullUrlPath = path.resolve(path.dirname(docFilePath), linkElem.url);
|
||||||
@ -34,14 +34,14 @@ function fixUrls(tree, docFilePath, linkSet, selector) {
|
|||||||
!fs.existsSync(origFullUrlPath)) {
|
!fs.existsSync(origFullUrlPath)) {
|
||||||
var newUrl = linkSet.update(origFullUrlPath) || origFullUrlPath;
|
var newUrl = linkSet.update(origFullUrlPath) || origFullUrlPath;
|
||||||
linkElem.url = path.relative(path.dirname(docFilePath), newUrl).replace(/\\/g, '/') + anchor;
|
linkElem.url = path.relative(path.dirname(docFilePath), newUrl).replace(/\\/g, '/') + anchor;
|
||||||
errors.push("Bad link: " + origFullUrlPath + "\nReplacing with " + linkElem.url);
|
errors.push("Bad link: ".concat(origFullUrlPath, "\nReplacing with ").concat(linkElem.url));
|
||||||
} /*else {
|
} /*else {
|
||||||
console.log(`Link OK: ${origFullUrlPath}`);
|
console.log(`Link OK: ${origFullUrlPath}`);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
});
|
});
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
showMessages("File: " + docFilePath + ":", errors);
|
showMessages("File: ".concat(docFilePath, ":"), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function showMessages(groupName, messages) {
|
function showMessages(groupName, messages) {
|
||||||
@ -81,7 +81,7 @@ var LinkSet = /** @class */ (function () {
|
|||||||
return candidates[0];
|
return candidates[0];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Multiple candidates for " + oldUrl);
|
console.log("Multiple candidates for ".concat(oldUrl));
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ function processDocs(mdCache, aggData) {
|
|||||||
var className = ngHelpers.ngNameToClassName(fileBaseName, aggData.config.typeNameExceptions);
|
var className = ngHelpers.ngNameToClassName(fileBaseName, aggData.config.typeNameExceptions);
|
||||||
var classInfo = aggData.classInfo[className];
|
var classInfo = aggData.classInfo[className];
|
||||||
var sourcePath = classInfo ? classInfo.sourcePath : '';
|
var sourcePath = classInfo ? classInfo.sourcePath : '';
|
||||||
var titleHeading = unist_util_select_1.select('heading[depth=1]:first-of-type', tree);
|
var titleHeading = (0, unist_util_select_1.select)('heading[depth=1]:first-of-type', tree);
|
||||||
var relDocPath = pathname.substring(pathname.indexOf('docs'));
|
var relDocPath = pathname.substring(pathname.indexOf('docs'));
|
||||||
var srcUrl = fixRelSrcUrl(relDocPath, sourcePath);
|
var srcUrl = fixRelSrcUrl(relDocPath, sourcePath);
|
||||||
if (titleHeading && titleHeading.children[0] && titleHeading.children[0].type === 'text') {
|
if (titleHeading && titleHeading.children[0] && titleHeading.children[0].type === 'text') {
|
||||||
@ -24,14 +24,14 @@ function processDocs(mdCache, aggData) {
|
|||||||
titleHeading.children[0] = {
|
titleHeading.children[0] = {
|
||||||
type: 'link',
|
type: 'link',
|
||||||
url: srcUrl,
|
url: srcUrl,
|
||||||
title: "Defined in " + path.basename(sourcePath),
|
title: "Defined in ".concat(path.basename(sourcePath)),
|
||||||
children: [titleText]
|
children: [titleText]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ((titleHeading && titleHeading.children[0].type === 'link') && sourcePath) {
|
else if ((titleHeading && titleHeading.children[0].type === 'link') && sourcePath) {
|
||||||
var linkElem = titleHeading.children[0];
|
var linkElem = titleHeading.children[0];
|
||||||
linkElem.url = srcUrl, // `../../${sourcePath}`;
|
linkElem.url = srcUrl, // `../../${sourcePath}`;
|
||||||
linkElem.title = "Defined in " + path.basename(sourcePath);
|
linkElem.title = "Defined in ".concat(path.basename(sourcePath));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ function showErrors(filename, errorMessages) {
|
|||||||
console.log('');
|
console.log('');
|
||||||
}
|
}
|
||||||
function updateFile(tree, pathname, aggData, errorMessages) {
|
function updateFile(tree, pathname, aggData, errorMessages) {
|
||||||
var className = ngHelpers_1.ngNameToClassName(path.basename(pathname, '.md'), nameExceptions);
|
var className = (0, ngHelpers_1.ngNameToClassName)(path.basename(pathname, '.md'), nameExceptions);
|
||||||
var classTypeMatch = className.match(/component|directive|service/i);
|
var classTypeMatch = className.match(/component|directive|service/i);
|
||||||
var compData = aggData.classInfo[className];
|
var compData = aggData.classInfo[className];
|
||||||
if (classTypeMatch && compData) {
|
if (classTypeMatch && compData) {
|
||||||
@ -168,7 +168,7 @@ function updatePropDocsFromMD(comp, inputDocs, outputDocs, errorMessages) {
|
|||||||
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
|
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
|
||||||
if (!prop.docText && propMDDoc) {
|
if (!prop.docText && propMDDoc) {
|
||||||
prop.docText = propMDDoc;
|
prop.docText = propMDDoc;
|
||||||
errorMessages.push("Warning: empty JSDocs for property \"" + prop.name + "\" may need sync with the .md file.");
|
errorMessages.push("Warning: empty JSDocs for property \"".concat(prop.name, "\" may need sync with the .md file."));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -178,12 +178,12 @@ function updateMethodDocsFromMD(comp, methodDocs, errorMessages) {
|
|||||||
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
|
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
|
||||||
if (!meth.docText && currMethMD && currMethMD.docText) {
|
if (!meth.docText && currMethMD && currMethMD.docText) {
|
||||||
meth.docText = currMethMD.docText;
|
meth.docText = currMethMD.docText;
|
||||||
errorMessages.push("Warning: empty JSDocs for method sig \"" + meth.name + "\" may need sync with the .md file.");
|
errorMessages.push("Warning: empty JSDocs for method sig \"".concat(meth.name, "\" may need sync with the .md file."));
|
||||||
}
|
}
|
||||||
meth.params.forEach(function (param) {
|
meth.params.forEach(function (param) {
|
||||||
if (!param.docText && currMethMD && currMethMD.params[param.name]) {
|
if (!param.docText && currMethMD && currMethMD.params[param.name]) {
|
||||||
param.docText = currMethMD.params[param.name];
|
param.docText = currMethMD.params[param.name];
|
||||||
errorMessages.push("Warning: empty JSDocs for parameter \"" + param.name + " (" + meth.name + ")\" may need sync with the .md file.");
|
errorMessages.push("Warning: empty JSDocs for parameter \"".concat(param.name, " (").concat(meth.name, ")\" may need sync with the .md file."));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||||
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
if (ar || !(i in from)) {
|
||||||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||||
r[k] = a[j];
|
ar[i] = from[i];
|
||||||
return r;
|
}
|
||||||
|
}
|
||||||
|
return to.concat(ar || Array.prototype.slice.call(from));
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.processDocs = void 0;
|
exports.processDocs = void 0;
|
||||||
@ -72,7 +74,7 @@ function updateFile(tree, pathname, aggData) {
|
|||||||
var _a;
|
var _a;
|
||||||
if ((child.type === 'text') || (child.type === 'inlineCode')) {
|
if ((child.type === 'text') || (child.type === 'inlineCode')) {
|
||||||
var newNodes = handleLinksInBodyText(aggData, pathname, child.value, child.type === 'inlineCode');
|
var newNodes = handleLinksInBodyText(aggData, pathname, child.value, child.type === 'inlineCode');
|
||||||
(_a = node.children).splice.apply(_a, __spreadArrays([index, 1], newNodes));
|
(_a = node.children).splice.apply(_a, __spreadArray([index, 1], newNodes, false));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
traverseMDTree(child);
|
traverseMDTree(child);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user