[ACA-1542] Print file (#518)

* Add Services and Behaviors to support print file dialog, which opens a browser renderable version of the file with the print dialog; after which, closes the window when the print dialog is closed.
This commit is contained in:
alex
2018-07-20 15:21:37 -04:00
committed by Denys Vuika
parent d9e59cfd73
commit 5596738703
6 changed files with 199 additions and 3 deletions

View File

@@ -37,7 +37,8 @@
"tooltips",
"unindent",
"exif",
"cardview"
"cardview",
"webm"
],
"dictionaries": [
"html",

View File

@@ -84,6 +84,7 @@ import { NodePermissionsDialogComponent } from './dialogs/node-permissions/node-
import { NodePermissionsDirective } from './directives/node-permissions.directive';
import { PermissionsManagerComponent } from './components/permission-manager/permissions-manager.component';
import { AppRouteReuseStrategy } from './app.routes.strategy';
import { ViewUtilService} from './services/view-util.service';
import { ExtensionService } from './extensions/extension.service';
export function setupExtensionServiceFactory(service: ExtensionService): Function {
@@ -169,7 +170,8 @@ export function setupExtensionServiceFactory(service: ExtensionService): Functio
useFactory: setupExtensionServiceFactory,
deps: [ExtensionService],
multi: true
}
},
ViewUtilService
],
entryComponents: [
LibraryDialogComponent,

View File

@@ -4,9 +4,11 @@
[fileNodeId]="nodeId"
[allowNavigate]="navigateMultiple"
[allowSidebar]="true"
[allowPrint] ="true"
[canNavigateBefore]="previousNodeId"
[canNavigateNext]="nextNodeId"
[overlayMode]="true"
(print) = "printFile($event)"
(showViewerChange)="onVisibilityChanged($event)"
(navigateBefore)="onNavigateBefore()"
(navigateNext)="onNavigateNext()">

View File

@@ -33,6 +33,7 @@ import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { ContentManagementService } from '../../services/content-management.service';
import { ViewUtilService } from '../../services/view-util.service';
import { ContentActionRef } from '../../extensions/action.extensions';
@Component({
selector: 'app-preview',
@@ -59,6 +60,7 @@ export class PreviewComponent extends PageComponent implements OnInit {
private preferences: UserPreferencesService,
private route: ActivatedRoute,
private router: Router,
private viewUtils: ViewUtilService,
store: Store<AppStore>,
extensions: ExtensionService,
content: ContentManagementService) {
@@ -341,6 +343,10 @@ export class PreviewComponent extends PageComponent implements OnInit {
this.onVisibilityChanged(false);
}
printFile(event: any) {
this.viewUtils.printFileGeneric(this.nodeId, this.node.content.mimeType);
}
private getNavigationCommands(url: string): any[] {
const urlTree: UrlTree = this.router.parseUrl(url);
const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];

View File

@@ -0,0 +1,183 @@
import {Injectable} from '@angular/core';
import {AlfrescoApiService, LogService} from '@alfresco/adf-core';
import {RenditionEntry} from 'alfresco-js-api';
import {ContentApiService} from './content-api.service';
@Injectable()
export class ViewUtilService {
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: ViewerComponent
*/
public static ContentGroup = {
IMAGE: 'image',
MEDIA: 'media',
PDF: 'pdf',
TEXT: 'text'
};
/**
* 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.
* @type {number}
*/
maxRetries = 5;
/**
* Mime-type grouping based on the ViewerComponent.
*/
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/ogg', 'audio/wav']
};
constructor(private apiService: AlfrescoApiService,
private contentApi: ContentApiService,
private logService: LogService) {
}
/**
* 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
* @param {string} url
* @param {string} type
*/
public printFile(url: string, type: string) {
const pwa = window.open(url, ViewUtilService.TARGET);
// 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 = () => {
pwa.print();
};
} else {
pwa.onload = () => {
pwa.print();
pwa.onfocus = () => {
setTimeout( () => {
pwa.close();
}, 10);
};
};
}
}
/**
* 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)
* @param {string} objectId
* @param {string} objectType
*/
public printFileGeneric(objectId: string, mimeType: string) {
const nodeId = objectId;
const type: string = this.getViewerTypeByMimeType(mimeType);
this.getRendition(nodeId, ViewUtilService.ContentGroup.PDF)
.then(value => {
const url: string = this.getRenditionUrl(nodeId, type, (value ? true : false));
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);
});
}
public getRenditionUrl(nodeId: string, type: string, renditionExists: boolean): string {
return (renditionExists && type !== ViewUtilService.ContentGroup.IMAGE) ?
this.apiService.contentApi.getRenditionUrl(nodeId, ViewUtilService.ContentGroup.PDF) :
this.contentApi.getContentUrl(nodeId, false);
}
/**
* From ViewerComponent
* @param {string} nodeId
* @param {string} renditionId
* @param {number} retries
* @returns {Promise<AlfrescoApi.RenditionEntry>}
*/
private async waitRendition(nodeId: string, renditionId: string, retries: number): Promise<RenditionEntry> {
const rendition = await this.apiService.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 await this.waitRendition(nodeId, renditionId, retries);
}
}
}
/**
* From ViewerComponent
* @param {string} mimeType
* @returns {string}
*/
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';
}
/**
* From ViewerComponent
* @param {number} ms
* @returns {Promise<any>}
*/
public wait(ms: number): Promise<any> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* From ViewerComponent
* @param {string} nodeId
* @returns {string}
*/
public async getRendition(nodeId: string, renditionId: string): Promise<RenditionEntry> {
const supported = await this.apiService.renditionsApi.getRenditions(nodeId);
let rendition = supported.list.entries.find(obj => obj.entry.id.toLowerCase() === renditionId);
if (rendition) {
const status = rendition.entry.status.toString();
if (status === 'NOT_CREATED') {
try {
await this.apiService.renditionsApi.createRendition(nodeId, {id: renditionId});
rendition = await this.waitRendition(nodeId, renditionId, 0);
} catch (err) {
this.logService.error(err);
}
}
}
return new Promise(resolve => resolve(rendition));
}
}

View File

@@ -60,6 +60,7 @@ import { NodeActionsService } from '../services/node-actions.service';
import { NodePermissionService } from '../services/node-permission.service';
import { ContentApiService } from '../services/content-api.service';
import { ExtensionService } from '../extensions/extension.service';
import {ViewUtilService} from '../services/view-util.service';
@NgModule({
imports: [
@@ -112,7 +113,8 @@ import { ExtensionService } from '../extensions/extension.service';
NodeActionsService,
NodePermissionService,
ContentApiService,
ExtensionService
ExtensionService,
ViewUtilService
]
})
export class AppTestingModule {}