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