]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/print/print.component.ts
LP2061136 - Stamping 1405 DB upgrade script
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / print / print.component.ts
1 import {Component, OnInit, TemplateRef, ElementRef, Renderer2} from '@angular/core';
2 import {PrintService, PrintRequest} from './print.service';
3 import {StoreService} from '@eg/core/store.service';
4 import {ServerStoreService} from '@eg/core/server-store.service';
5 import {HatchService, HatchMessage} from '@eg/core/hatch.service';
6 import {ToastService} from '@eg/share/toast/toast.service';
7 import {StringService} from '@eg/share/string/string.service';
8 import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service';
9
10 const HATCH_FILE_WRITER_PRINTER = 'hatch_file_writer';
11 const HATCH_BROWSER_PRINTING_PRINTER = 'hatch_browser_printing';
12
13 @Component({
14     selector: 'eg-print',
15     templateUrl: './print.component.html'
16 })
17
18 export class PrintComponent implements OnInit {
19
20     // Template that requires local processing
21     template: TemplateRef<any>;
22
23     // Context data used for processing the template.
24     context: any;
25
26     // Insertion point for externally-compiled templates
27     htmlContainer: Element;
28
29     isPrinting: boolean;
30
31     printQueue: PrintRequest[];
32
33     // True if Hatch printing is enabled and we're able to talk to Hatch.
34     useHatchPrinting: boolean = null;
35
36     constructor(
37         private renderer: Renderer2,
38         private elm: ElementRef,
39         private store: StoreService,
40         private serverStore: ServerStoreService,
41         private h2txt: HtmlToTxtService,
42         private hatch: HatchService,
43         private toast: ToastService,
44         private strings: StringService,
45         private printer: PrintService) {
46         this.isPrinting = false;
47         this.printQueue = [];
48     }
49
50     ngOnInit() {
51         this.printer.onPrintRequest$.subscribe(
52             printReq => this.handlePrintRequest(printReq));
53
54         this.htmlContainer =
55             this.renderer.selectRootElement('#eg-print-html-container');
56     }
57
58
59     // Returns promise of true if Hatch should be used for printing.
60     // To avoid race conditions, always check this inline before
61     // relaying print requests.
62     checkHatchEnabled(): Promise<boolean> {
63         if (this.useHatchPrinting !== null) {
64             return Promise.resolve(this.useHatchPrinting);
65         }
66
67         return this.serverStore.getItem('eg.hatch.enable.printing')
68             .then(use => this.useHatchPrinting = (use && this.hatch.connect()));
69     }
70
71     // Resolves to true if a) Hatch is usable and b) the requested print
72     // context is not using the 'native brower printing' printer.
73     checkHatchEnabledForRequest(printReq: PrintRequest): Promise<boolean> {
74         return this.checkHatchEnabled().then(enabled => {
75             if (!enabled) { return false; }
76
77             return this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
78                 .then(config => {
79                     return (
80                         !config ||
81                     config.printer !== HATCH_BROWSER_PRINTING_PRINTER
82                     );
83                 });
84         });
85     }
86
87     handlePrintRequest(printReq: PrintRequest) {
88
89         if (this.isPrinting) {
90             // Avoid print collisions by queuing requests as needed.
91             this.printQueue.push(printReq);
92             return;
93         }
94
95         // Load the configured print context.
96         let promise = Promise.resolve();
97         if (printReq.templateName) {
98             promise = this.serverStore.getItem(
99                 'eg.print.template_context.' + printReq.templateName)
100                 .then(setting => {
101                     if (setting) {
102                         printReq.printContext = setting;
103                     }
104                 });
105         }
106
107         this.isPrinting = true;
108         promise.then(_ => {
109
110             if (printReq.printContext === 'no-print') {
111                 console.debug('Skipping print request with No-Print context');
112                 this.reset();
113                 return;
114             }
115
116             this.applyTemplate(printReq).then(() => {
117                 // Give templates a chance to render before printing
118                 setTimeout(() => {
119                     this.dispatchPrint(printReq).then(__ => {
120                         this.reset();
121                         this.printer.printJobQueued$.emit(printReq);
122                     });
123                 });
124             });
125         });
126     }
127
128     applyTemplate(printReq: PrintRequest): Promise<any> {
129
130         if (printReq.template) {
131             // Local Angular template.
132             this.template = printReq.template;
133             this.context = {$implicit: printReq.contextData};
134             return Promise.resolve();
135         }
136
137         let promise;
138
139         // Precompiled text
140         if (printReq.text) {
141             promise = Promise.resolve();
142
143         } else if (printReq.templateName || printReq.templateId) {
144             // Server-compiled template
145
146             promise = this.printer.compileRemoteTemplate(printReq).then(
147                 response => {
148                     printReq.text = response.content;
149                     printReq.contentType = response.contentType;
150                 },
151                 err => {
152
153                     if (err && err.notFound) {
154
155                         this.strings.interpolate(
156                             'eg.print.template.not_found',
157                             {name: printReq.templateName}
158                         ).then(msg => this.toast.danger(msg));
159
160                     } else {
161
162                         console.error('Print generation failed', printReq);
163
164                         this.strings.interpolate(
165                             'eg.print.template.error',
166                             {name: printReq.templateName, id: printReq.templateId}
167                         ).then(msg => this.toast.danger(msg));
168                     }
169
170                     return Promise.reject(new Error(
171                         'Error compiling server-hosted print template'));
172                 }
173             );
174
175         } else {
176             console.error('Cannot find template', printReq);
177             return Promise.reject(new Error('Cannot find print template'));
178         }
179
180         return promise.then(() => {
181
182             return this.checkHatchEnabledForRequest(printReq).then(enabled => {
183
184                 // Insert HTML into the browser DOM for in-browser printing.
185                 if (printReq.text && !enabled) {
186
187                     if (printReq.contentType === 'text/plain') {
188                     // Wrap text/plain content in pre's to prevent
189                     // unintended html formatting.
190                         printReq.text = `<pre>${printReq.text}</pre>`;
191                     }
192
193                     this.htmlContainer.innerHTML = printReq.text;
194                 }
195             });
196         });
197     }
198
199     // Clear the print data
200     reset() {
201         this.isPrinting = false;
202         this.template = null;
203         this.context = null;
204         this.htmlContainer.innerHTML = '';
205
206         if (this.printQueue.length) {
207             this.handlePrintRequest(this.printQueue.pop());
208         }
209     }
210
211     dispatchPrint(printReq: PrintRequest): Promise<any> {
212
213         if (!printReq.text) {
214
215             // Extract the print container div from our component markup.
216             const container =
217                 this.elm.nativeElement.querySelector('#eg-print-container');
218
219             // Sometimes the results come from an externally-parsed HTML
220             // template, other times they come from an in-page template.
221             printReq.text = container.innerHTML;
222         }
223
224         // Retain a copy of each printed document in localStorage
225         // so it may be reprinted.
226         this.store.setLocalItem('eg.print.last_printed', {
227             content: printReq.text,
228             context: printReq.printContext,
229             content_type: printReq.contentType,
230             show_dialog: printReq.showDialog
231         });
232
233         return this.checkHatchEnabledForRequest(printReq).then(enabled => {
234             if (enabled) {
235                 this.printViaHatch(printReq);
236             } else {
237                 // Here the needed HTML is already in the page.
238                 window.print();
239             }
240         });
241     }
242
243     printViaHatch(printReq: PrintRequest) {
244         if (!printReq.contentType) {
245             printReq.contentType = 'text/html';
246         }
247
248         // Send a full HTML document to Hatch
249         let html = printReq.text;
250         if (printReq.contentType === 'text/html') {
251             html = `<html><body>${printReq.text}</body></html>`;
252         }
253
254         this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
255             .then(config => {
256
257                 let msg: HatchMessage;
258
259                 if (config && config.printer === HATCH_FILE_WRITER_PRINTER) {
260
261                     const text = printReq.contentType === 'text/plain' ?
262                         html : this.h2txt.htmlToTxt(html);
263
264                     msg = new HatchMessage({
265                         action: 'set',
266                         key: `receipt.${printReq.printContext}.txt`,
267                         content: text,
268                         bare: true
269                     });
270
271                 } else {
272
273                     msg = new HatchMessage({
274                         action: 'print',
275                         content: html,
276                         settings: config || {},
277                         contentType: 'text/html',
278                         showDialog: printReq.showDialog
279                     });
280                 }
281
282                 this.hatch.sendRequest(msg).then(
283                     ok  => console.debug('Print request succeeded'),
284                     err => console.warn('Print request failed', err)
285                 );
286             });
287     }
288 }
289