LP#1680110 Webstaff circ.staff_client.receipt.* AOUS support
[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         ]
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         scope.staff = egIDL.toHash(egAuth.user());
62         scope.current_location = 
63             egIDL.toHash(egOrg.get(egAuth.user().ws_ou()));
64
65         return service.fetch_includes(scope);
66     }
67
68     // Retrieve org settings for receipt includes and add them
69     // to the print scope under scope.includes.<name>
70     service.fetch_includes = function(scope) {
71         // org settings for the workstation org are cached
72         // within egOrg.  No need to cache them locally.
73         return egOrg.settings(service.include_settings).then(
74
75             function(settings) {
76                 scope.includes = {};
77                 angular.forEach(settings, function(val, key) {
78                     // strip the settings prefix so you just have
79                     // e.g. scope.includes.alert_text
80                     scope.includes[key.split(/\./).pop()] = val;
81                 });
82             }
83         );
84     }
85
86     service.last_print = {};
87
88     // Template has been fetched (or no template needed) 
89     // Process the template and send the result off to the printer.
90     service.print_content = function(args) {
91         return service.fleshPrintScope(args.scope).then(function() {
92             var promise = egHatch.usePrinting() ?
93                 service.print_via_hatch(args) :
94                 service.print_via_browser(args);
95
96             return promise['finally'](
97                 function() { service.clear_print_content() });
98         });
99     }
100
101     service.print_via_hatch = function(args) {
102         var promise;
103
104         if (args.content_type == 'text/html') {
105             promise = service.ingest_print_content(
106                 args.content_type, args.content, args.scope);
107         } else {
108             // text content requires no compilation for remote printing.
109             promise = $q.when(args.content);
110         }
111
112         return promise.then(function(html) {
113             // For good measure, wrap the compiled HTML in container tags.
114             html = "<html><body>" + html + "</body></html>";
115             service.last_print.content = html;
116             service.last_print.context = args.context || 'default';
117             service.last_print.content_type = args.content_type;
118             service.last_print.show_dialog = args.show_dialog;
119
120             egHatch.setItem('eg.print.last_printed', service.last_print);
121
122             return service._remotePrint();
123         });
124     }
125
126     service._remotePrint = function () {
127         return egHatch.remotePrint(
128             service.last_print.context,
129             service.last_print.content_type,
130             service.last_print.content, 
131             service.last_print.show_dialog
132         );
133     }
134
135     service.print_via_browser = function(args) {
136         var type = args.content_type;
137         var content = args.content;
138         var printScope = args.scope;
139
140         if (type == 'text/csv' || type == 'text/plain') {
141             // preserve newlines, spaces, etc.
142             content = '<pre>' + content + '</pre>';
143         }
144
145         // Fetch the print CSS required for in-browser printing.
146         return $http.get(egEnv.basePath + 'css/print.css')
147         .then(function(response) {
148
149             // Add the bare CSS to the content
150             return '<style type="text/css" media="print">' +
151                   response.data +
152                   '</style>' +
153                   content;
154
155         }).then(function(content) {
156             service.last_print.content = content;
157             service.last_print.content_type = type;
158             service.last_print.printScope = printScope
159
160             egHatch.setItem('eg.print.last_printed', service.last_print);
161
162             // Ingest the content into the page DOM.
163             return service.ingest_print_content(
164                 service.last_print.content_type,
165                 service.last_print.content,
166                 service.last_print.printScope
167             );
168
169         }).then(function() { 
170             $window.print();
171         });
172     }
173
174     service.reprintLast = function () {
175         var deferred = $q.defer();
176         var promise = deferred.promise;
177         promise.finally( function() { service.clear_print_content() });
178
179         egHatch.getItem(
180             'eg.print.last_printed'
181         ).then(function (last) {
182             if (last && last.content) {
183                 service.last_print = last;
184
185                 if (egHatch.usePrinting()) {
186                     promise.then(function () {
187                         egHatch._remotePrint()
188                     });
189                 } else {
190                     promise.then(function () {
191                         service.ingest_print_content(
192                             service.last_print.content_type,
193                             service.last_print.content,
194                             service.last_print.printScope
195                         ).then(function() { $window.print() });
196                     });
197                 }
198                 return deferred.resolve();
199             } else {
200                 return deferred.reject();
201             }
202         });
203     }
204
205     // loads an HTML print template by name from the server
206     // If no template is available in local/hatch storage, 
207     // fetch the template as an HTML file from the server.
208     service.getPrintTemplate = function(name) {
209         var deferred = $q.defer();
210
211         egHatch.getItem('eg.print.template.' + name)
212         .then(function(html) {
213
214             if (html) {
215                 // we have a locally stored template
216                 deferred.resolve(html);
217                 return;
218             }
219
220             var path = service.template_base_path + name;
221             console.debug('fetching template ' + path);
222
223             $http.get(path)
224             .success(function(data) { deferred.resolve(data) })
225             .error(function() {
226                 console.error('unable to locate print template: ' + name);
227                 deferred.reject();
228             });
229         });
230
231         return deferred.promise;
232     }
233
234     service.storePrintTemplate = function(name, html) {
235         return egHatch.setItem('eg.print.template.' + name, html);
236     }
237
238     service.getPrintTemplateContext = function(name) {
239         var deferred = $q.defer();
240
241         egHatch.getItem('eg.print.template_context.' + name)
242         .then(
243             function(context) { deferred.resolve(context); },
244             function()        { deferred.resolve('default'); }
245         );
246
247         return deferred.promise;
248     }
249     service.storePrintTemplateContext = function(name, context) {
250         return egHatch.setItem('eg.print.template_context.' + name, context);
251     }
252
253     return service;
254 }])
255
256
257 /**
258  * Container for inserting print data into the browser page.
259  * On insert, $window.print() is called to print the data.
260  * The div housing eg-print-container must apply the correct
261  * print media CSS to ensure this content (and not the rest
262  * of the page) is printed.
263  *
264  * NOTE: There should only ever be 1 egPrintContainer instance per page.
265  * egPrintContainer attaches functions to the egPrint service with
266  * closures around the egPrintContainer instance's $scope (including its
267  * DOM element). Having multiple egPrintContainers could result in chaos.
268  */
269
270 .directive('egPrintContainer', ['$compile', function($compile) {
271     return {
272         restrict : 'AE',
273         scope : {}, // isolate our scope
274         link : function(scope, element, attrs) {
275             scope.elm = element;
276         },
277         controller : 
278                    ['$scope','$q','$window','$timeout','egHatch','egPrint','egEnv',
279             function($scope , $q , $window , $timeout , egHatch , egPrint , egEnv) {
280
281                 egPrint.clear_print_content = function() {
282                     $scope.elm.html('');
283                     $compile($scope.elm.contents())($scope.$new(true));
284                 }
285
286                 // Insert the printable content into the DOM.
287                 // For remote printing, this lets us exract the compiled HTML
288                 // from the DOM.
289                 // For local printing, this lets us print directly from the
290                 // DOM with print CSS.
291                 // Returns a promise reolved with the compiled HTML as a string.
292                 egPrint.ingest_print_content = function(type, content, printScope) {
293                     $scope.elm.html(content);
294
295                     var sub_scope = $scope.$new(true);
296                     angular.forEach(printScope, function(val, key) {
297                         sub_scope[key] = val;
298                     })
299
300                     var resp = $compile($scope.elm.contents())(sub_scope);
301
302
303                     var deferred = $q.defer();
304                     $timeout(function(){
305                         // give the $digest a chance to complete then resolve
306                         // with the compiled HTML from our print container
307                         deferred.resolve($scope.elm.html());
308                     });
309
310                     return deferred.promise;
311                 }
312             }
313         ]
314     }
315 }])
316