]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/services/print.js
LP1615805 No inputs after submit in patron search (AngularJS)
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / print.js
1 /**
2  * egPrint : manage print templates, process templates, print content
3  *
4  */
5 angular.module('egCoreMod')
6
7 .factory('egPrint',
8        ['$q','$window','$timeout','$http','egHatch','egAuth','egIDL','egOrg','egEnv',
9 function($q , $window , $timeout , $http , egHatch , egAuth , egIDL , egOrg , egEnv) {
10
11     var service = {
12         include_settings : [
13             'circ.staff_client.receipt.alert_text',
14             'circ.staff_client.receipt.event_text',
15             'circ.staff_client.receipt.footer_text',
16             'circ.staff_client.receipt.header_text',
17             'circ.staff_client.receipt.notice_text',
18             'lib.info_url',
19             'lib.my_account_url'
20         ]
21     };
22
23     service.template_base_path = 'share/print_templates/t_';
24
25     /*
26      * context  : 'default', 'receipt','label', etc. 
27      * scope    : data loaded into the template environment
28      * template : template name (e.g. 'checkout', 'transit_slip'
29      * content  : content to print.  If 'template' is set, content is
30      *            derived from the template.
31      * content_type : 'text/html', 'text/plain', 'text/csv'
32      * show_dialog  : boolean, if true, print dialog is shown.  This setting
33      *                only affects remote printers, since browser printers
34      *                do not allow such control
35      */
36     service.print = function(args) {
37         if (!args) return $q.when();
38
39         if (args.template) {
40             // fetch the template, then proceed to printing
41
42             return service.getPrintTemplate(args.template)
43             .then(function(content) {
44                 args.content = content;
45                 if (!args.content_type) args.content_type = 'text/html';
46                 service.getPrintTemplateContext(args.template)
47                 .then(function(context) {
48                     args.context = context;
49                     return service.print_content(args);
50                 });
51             });
52
53         } 
54
55         return service.print_content(args);
56     }
57
58     // add commonly used attributes to the print scope
59     service.fleshPrintScope = function(scope) {
60         if (!scope) scope = {};
61         scope.today = new Date().toISOString();
62
63         if (!lf.isOffline) {
64             scope.staff = egIDL.toHash(egAuth.user());
65             scope.current_location = 
66                 egIDL.toHash(egOrg.get(egAuth.user().ws_ou()));
67         }
68
69         return service.fetch_includes(scope);
70     }
71
72     // Retrieve org settings for receipt includes and add them
73     // to the print scope under scope.includes.<name>
74     service.fetch_includes = function(scope) {
75         // org settings for the workstation org are cached
76         // within egOrg.  No need to cache them locally.
77         return egOrg.settings(service.include_settings).then(
78
79             function(settings) {
80                 scope.includes = {};
81                 angular.forEach(settings, function(val, key) {
82                     // strip the settings prefix so you just have
83                     // e.g. scope.includes.alert_text
84                     scope.includes[key.split(/\./).pop()] = val;
85                 });
86             }
87         );
88     }
89
90     service.last_print = {};
91
92     // Template has been fetched (or no template needed) 
93     // Process the template and send the result off to the printer.
94     service.print_content = function(args) {
95
96         if (args.context === 'no-print') {
97             console.debug('Skipping print request with No-Print context');
98             return $q.when();
99         }
100
101         return service.fleshPrintScope(args.scope)
102         .then(function() { return egHatch.usePrinting(); })
103         .then(function(useHatch) {
104             if (!useHatch) { return false; }
105             return egHatch.getPrintConfig(args.context || 'default') 
106             .then(function(config) {
107                 // Avoid using Hatch if the print context calls
108                 // for native browser printing.
109                 return config.printer != 'hatch_browser_printing';
110             });
111         })
112         .then(function(useHatch) {
113             var promise = useHatch ?
114                 service.print_via_hatch(args) :
115                 service.print_via_browser(args);
116
117             return promise['finally'](
118                 function() { service.clear_print_content() });
119         });
120     }
121
122     service.print_via_hatch = function(args) {
123         var promise;
124
125         if (args.content_type == 'text/html') {
126             promise = service.ingest_print_content(
127                 args.content_type, args.content, args.scope
128             ).then(function(html) {
129                 // For good measure, wrap the compiled HTML in container tags.
130                 return "<html><body>" + html + "</body></html>";
131             });
132         } else {
133             // text content requires no compilation for remote printing.
134             promise = $q.when(args.content);
135         }
136
137         return promise.then(function(content) {
138             service.last_print.content = content;
139             service.last_print.context = args.context || 'default';
140             service.last_print.content_type = args.content_type;
141             service.last_print.show_dialog = args.show_dialog;
142
143             egHatch.setLocalItem('eg.print.last_printed', service.last_print);
144
145             return service._remotePrint();
146         });
147     }
148
149     service._remotePrint = function () {
150         return egHatch.remotePrint(
151             service.last_print.context,
152             service.last_print.content_type,
153             service.last_print.content, 
154             service.last_print.show_dialog
155         );
156     }
157
158     service.print_via_browser = function(args) {
159         var type = args.content_type;
160         var content = args.content;
161         var printScope = args.scope;
162
163         if (type == 'text/csv' || type == 'text/plain') {
164             // preserve newlines, spaces, etc.
165             content = '<pre>' + content + '</pre>';
166         }
167
168         // Fetch the print CSS required for in-browser printing.
169         return $http.get(egEnv.basePath + 'css/print.css')
170         .then(function(response) {
171
172             // Add the bare CSS to the content
173             return '<style type="text/css" media="print">' +
174                   response.data +
175                   '</style>' +
176                   content;
177
178         }).then(function(content) {
179
180             // Ingest the content into the page DOM.
181             return service.ingest_print_content(type, content, printScope);
182
183         }).then(function(html) { 
184
185             // Note browser ignores print context
186             service.last_print.content = html;
187             service.last_print.content_type = type;
188             egHatch.setLocalItem('eg.print.last_printed', service.last_print);
189
190             $window.print();
191         });
192     }
193
194     service.reprintLast = function () {
195         var last = egHatch.getLocalItem('eg.print.last_printed');
196         if (!last || !last.content) { return $q.reject(); }
197
198         service.last_print = last;
199
200         return egHatch.usePrinting().then(function(useHatch) {
201
202             if (useHatch) {
203                 return service._remotePrint();
204             } else {
205                 return service.ingest_print_content(
206                     null, null, null, service.last_print.content)
207                 .then(function() { $window.print(); });
208             }
209
210         }).finally(function() { service.clear_print_content(); });
211     }
212
213     // loads an HTML print template by name from the server
214     // If no template is available in local/hatch storage, 
215     // fetch the template as an HTML file from the server.
216     service.getPrintTemplate = function(name) {
217         var deferred = $q.defer();
218
219         egHatch.getItem('eg.print.template.' + name)
220         .then(function(html) {
221
222             if (html) {
223                 // we have a locally stored template
224                 deferred.resolve(html);
225                 return;
226             }
227
228             var path = service.template_base_path + name;
229             console.debug('fetching template ' + path);
230
231             $http.get(path).then(
232                 function(data) { deferred.resolve(data.data) },
233                 function() {
234                     console.error('unable to locate print template: ' + name);
235                     deferred.reject();
236                 }
237             );
238         });
239
240         return deferred.promise;
241     }
242
243     service.storePrintTemplate = function(name, html) {
244         return egHatch.setItem('eg.print.template.' + name, html);
245     }
246
247     service.removePrintTemplate = function(name) {
248         return egHatch.removeItem('eg.print.template.' + name);
249     }
250
251     service.getPrintTemplateContext = function(name) {
252         var deferred = $q.defer();
253
254         egHatch.getItem('eg.print.template_context.' + name)
255         .then(
256             function(context) { deferred.resolve(context); },
257             function()        { deferred.resolve('default'); }
258         );
259
260         return deferred.promise;
261     }
262     service.storePrintTemplateContext = function(name, context) {
263         return egHatch.setItem('eg.print.template_context.' + name, context);
264     }
265     service.removePrintTemplateContext = function(name) {
266         return egHatch.removeItem('eg.print.template_context.' + name);
267     }
268
269     return service;
270 }])
271
272
273 /**
274  * Container for inserting print data into the browser page.
275  * On insert, $window.print() is called to print the data.
276  * The div housing eg-print-container must apply the correct
277  * print media CSS to ensure this content (and not the rest
278  * of the page) is printed.
279  *
280  * NOTE: There should only ever be 1 egPrintContainer instance per page.
281  * egPrintContainer attaches functions to the egPrint service with
282  * closures around the egPrintContainer instance's $scope (including its
283  * DOM element). Having multiple egPrintContainers could result in chaos.
284  */
285
286 .directive('egPrintContainer', ['$compile', function($compile) {
287     return {
288         restrict : 'AE',
289         scope : {}, // isolate our scope
290         link : function(scope, element, attrs) {
291             scope.elm = element;
292         },
293         controller : 
294                    ['$scope','$q','$window','$timeout','egHatch','egPrint','egEnv',
295             function($scope , $q , $window , $timeout , egHatch , egPrint , egEnv) {
296
297                 egPrint.clear_print_content = function() {
298                     $scope.elm.html('');
299                     $compile($scope.elm.contents())($scope.$new(true));
300                 }
301
302                 // Insert the printable content into the DOM.
303                 // For remote printing, this lets us exract the compiled HTML
304                 // from the DOM.
305                 // For local printing, this lets us print directly from the
306                 // DOM with print CSS.
307                 // Returns a promise reolved with the compiled HTML as a string.
308                 //
309                 // If a pre-compiled HTML string is provided, it's inserted
310                 // as-is into the DOM for browser printing without any 
311                 // additional interpolation.  This is useful for reprinting,
312                 // previously compiled content.
313                 egPrint.ingest_print_content = 
314                     function(type, content, printScope, compiledHtml) {
315
316                     if (compiledHtml) {
317                         $scope.elm.html(compiledHtml);
318                         return $q.when(compiledHtml);
319                     }
320                         
321                     $scope.elm.html(content);
322
323                     var sub_scope = $scope.$new(true);
324                     angular.forEach(printScope, function(val, key) {
325                         sub_scope[key] = val;
326                     })
327
328                     var resp = $compile($scope.elm.contents())(sub_scope);
329
330
331                     var deferred = $q.defer();
332                     $timeout(function(){
333                         // give the $digest a chance to complete then resolve
334                         // with the compiled HTML from our print container
335                         deferred.resolve($scope.elm.html());
336                     });
337
338                     return deferred.promise;
339                 }
340             }
341         ]
342     }
343 }])
344