Revert "LP#1332651 do not strip internal spaces in barcode"
[working/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         this.isPrinting = true;
78
79         this.applyTemplate(printReq).then(() => {
80             // Give templates a chance to render before printing
81             setTimeout(() => {
82                 this.dispatchPrint(printReq).then(_ => this.reset());
83             });
84         });
85     }
86
87     applyTemplate(printReq: PrintRequest): Promise<any> {
88
89         if (printReq.template) {
90             // Local Angular template.
91             this.template = printReq.template;
92             this.context = {$implicit: printReq.contextData};
93             return Promise.resolve();
94         }
95
96         let promise;
97
98         // Precompiled text
99         if (printReq.text) {
100             promise = Promise.resolve();
101
102         } else if (printReq.templateName || printReq.templateId) {
103             // Server-compiled template
104
105             promise = this.printer.compileRemoteTemplate(printReq).then(
106                 response => {
107                     printReq.text = response.content;
108                     printReq.contentType = response.contentType;
109                 },
110                 err => {
111
112                     if (err && err.notFound) {
113
114                         this.strings.interpolate(
115                             'eg.print.template.not_found',
116                             {name: printReq.templateName}
117                         ).then(msg => this.toast.danger(msg));
118
119                     } else {
120
121                         console.error('Print generation failed', printReq);
122
123                         this.strings.interpolate(
124                             'eg.print.template.error',
125                             {name: printReq.templateName, id: printReq.templateId}
126                         ).then(msg => this.toast.danger(msg));
127                     }
128
129                     return Promise.reject(new Error(
130                         'Error compiling server-hosted print template'));
131                 }
132             );
133
134         } else {
135             console.error('Cannot find template', printReq);
136             return Promise.reject(new Error('Cannot find print template'));
137         }
138
139         return promise.then(() => {
140
141             return this.checkHatchEnabled().then(enabled => {
142
143                 // Insert HTML into the browser DOM for in-browser printing.
144                 if (printReq.text && !enabled) {
145
146                     if (printReq.contentType === 'text/plain') {
147                     // Wrap text/plain content in pre's to prevent
148                     // unintended html formatting.
149                         printReq.text = `<pre>${printReq.text}</pre>`;
150                     }
151
152                     this.htmlContainer.innerHTML = printReq.text;
153                 }
154             });
155         });
156     }
157
158     // Clear the print data
159     reset() {
160         this.isPrinting = false;
161         this.template = null;
162         this.context = null;
163         this.htmlContainer.innerHTML = '';
164
165         if (this.printQueue.length) {
166             this.handlePrintRequest(this.printQueue.pop());
167         }
168     }
169
170     dispatchPrint(printReq: PrintRequest): Promise<any> {
171
172         if (!printReq.text) {
173
174             // Extract the print container div from our component markup.
175             const container =
176                 this.elm.nativeElement.querySelector('#eg-print-container');
177
178             // Sometimes the results come from an externally-parsed HTML
179             // template, other times they come from an in-page template.
180             printReq.text = container.innerHTML;
181         }
182
183         // Retain a copy of each printed document in localStorage
184         // so it may be reprinted.
185         this.store.setLocalItem('eg.print.last_printed', {
186             content: printReq.text,
187             context: printReq.printContext,
188             content_type: printReq.contentType,
189             show_dialog: printReq.showDialog
190         });
191
192         return this.checkHatchEnabled().then(enabled => {
193             if (enabled) {
194                 this.printViaHatch(printReq);
195             } else {
196                 // Here the needed HTML is already in the page.
197                 window.print();
198             }
199         });
200     }
201
202     printViaHatch(printReq: PrintRequest) {
203         if (!printReq.contentType) {
204             printReq.contentType = 'text/html';
205         }
206
207         // Send a full HTML document to Hatch
208         let html = printReq.text;
209         if (printReq.contentType === 'text/html') {
210             html = `<html><body>${printReq.text}</body></html>`;
211         }
212
213         this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
214         .then(config => {
215
216             let msg: HatchMessage;
217
218             if (config && config.printer === HATCH_FILE_WRITER_PRINTER) {
219
220                 const text = printReq.contentType === 'text/plain' ?
221                     html : this.h2txt.htmlToTxt(html);
222
223                 msg = new HatchMessage({
224                     action: 'set',
225                     key: `receipt.${printReq.printContext}.txt`,
226                     content: text,
227                     bare: true
228                 });
229
230             } else {
231
232                 msg = new HatchMessage({
233                     action: 'print',
234                     content: html,
235                     settings: config || {},
236                     contentType: 'text/html',
237                     showDialog: printReq.showDialog
238                 });
239             }
240
241             this.hatch.sendRequest(msg).then(
242                 ok  => console.debug('Print request succeeded'),
243                 err => console.warn('Print request failed', err)
244             );
245         });
246     }
247 }
248