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