]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/AddedContent/ContentCafe.pm
LP2045292 Color contrast for AngularJS patron bills
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / AddedContent / ContentCafe.pm
1 package OpenILS::WWW::AddedContent::ContentCafe;
2 use strict; use warnings;
3 use OpenSRF::Utils::Logger qw/$logger/;
4 use OpenSRF::Utils::SettingsParser;
5 use OpenSRF::Utils::JSON;
6 use OpenSRF::EX qw/:try/;
7 use OpenILS::WWW::AddedContent;
8 use XML::LibXML;
9 use MIME::Base64;
10 use DateTime;
11
12 # Hack country_data to allow B&T fake ISBNs LP#1559281
13 $Business::ISBN::country_data{630} = ['B&T' => ['0000000' => '9999999']];
14 $Business::ISBN::country_data{631} = ['B&T' => ['0000000' => '9999999']];
15
16 my $AC = 'OpenILS::WWW::AddedContent';
17
18 my $post_url = 'http://contentcafe2.btol.com/ContentCafe/ContentCafe.asmx/XmlPost';
19
20 sub new {
21     my( $class, $args ) = @_;
22     $class = ref $class || $class;
23     return bless($args, $class);
24 }
25
26 sub userid {
27     my $self = shift;
28     return $self->{ContentCafe}->{userid};
29 }
30
31 sub password {
32     my $self = shift;
33     return $self->{ContentCafe}->{password};
34 }
35
36 sub identifier_order {
37     my $self = shift;
38     if ($self->{ContentCafe}->{identifier_order}) {
39         my $order = [ split(',',$self->{ContentCafe}->{identifier_order}) ];
40         return $order;
41     }
42     return ['isbn','upc'];
43 }
44
45 sub expects_keyhash {
46     # we expect a keyhash as opposed to a simple scalar containing an ISBN
47     return 1;
48 }
49
50 # --------------------------------------------------------------------------
51
52 # This function fetches everything and returns:
53 #     0 if we had no usable keys
54 #     0 if we had a lookup failure
55 #     A hash of format_type => result if you called that directly
56 sub fetch_all {
57     my( $self, $keyhash ) = @_;
58     my $doc = $self->fetch_xmldoc([
59         'TocDetail', # toc_*
60         'BiographyDetail', #anotes_*
61         'ExcerptDetail', #excerpt_*
62         'ReviewDetail', #reviews_*
63         'AnnotationDetail', #summary_*
64         {name => 'JacketDetail', attributes => [['Type','S'],['Encoding','HEX']]}, # jacket_small
65         {name => 'JacketDetail', attributes => [['Type','M'],['Encoding','HEX']]}, # jacket_medium
66         {name => 'JacketDetail', attributes => [['Type','L'],['Encoding','HEX']]}, # jacket_large
67     ], $keyhash);
68     return 0 unless defined $doc;
69     my $resulthash = {
70         toc_html        => $self->parse_toc_html($doc),
71         toc_json        => $self->send_json($doc, 'TocItems'),
72         toc_xml         => $self->send_xml($doc, 'TocItems'),
73         anotes_html     => $self->parse_anotes_html($doc),
74         anotes_json     => $self->send_json($doc, 'BiographyItems'),
75         anotes_xml      => $self->send_xml($doc, 'BiographyItems'),
76         excerpt_html    => $self->parse_excerpt_html($doc),
77         excerpt_json    => $self->send_json($doc, 'ExcerptItems'),
78         excerpt_xml     => $self->send_xml($doc, 'ExcerptItems'),
79         reviews_html    => $self->parse_reviews_html($doc),
80         reviews_json    => $self->send_json($doc, 'ReviewItems'),
81         reviews_xml     => $self->send_xml($doc, 'ReviewItems'),
82         summary_html    => $self->parse_summary_html($doc),
83         summary_json    => $self->send_json($doc, 'AnnotationItems'),
84         summary_xml     => $self->send_xml($doc, 'AnnotationItems'),
85         jacket_small    => $self->parse_jacket($doc, 'S'),
86         jacket_medium   => $self->parse_jacket($doc, 'M'),
87         jacket_large    => $self->parse_jacket($doc, 'L')
88     };
89     return $resulthash;
90 }
91
92 # --------------------------------------------------------------------------
93 sub jacket_small {
94     my( $self, $keyhash ) = @_;
95     return $self->send_jacket( $keyhash, 'S' );
96 }
97
98 sub jacket_medium {
99     my( $self, $keyhash ) = @_;
100     return $self->send_jacket( $keyhash, 'M' );
101 }
102
103 sub jacket_large {
104     my( $self, $keyhash ) = @_;
105     return $self->send_jacket( $keyhash, 'L' );
106 }
107
108 # --------------------------------------------------------------------------
109
110 sub toc_html {
111     my( $self, $keyhash ) = @_;
112     my $doc = $self->fetch_xmldoc('TocDetail', $keyhash);
113     return 0 unless defined $doc;
114     return $self->parse_toc_html($doc);
115 }
116
117 sub parse_toc_html {
118     my( $self, $doc ) = @_;
119     my $html = '';
120     my @nodes = $doc->findnodes('//cc:TocItems[*]');
121     return 0 if (scalar(@nodes) < 1);
122     @nodes = $nodes[0]->findnodes('.//cc:Toc');
123     return 0 if (scalar(@nodes) < 1);
124     foreach my $node ( @nodes ) {
125         $html .= $node->textContent . '</P></P>';
126     }
127     return $self->send_html($html);
128 }
129
130 sub toc_xml {
131     my( $self, $keyhash ) = @_;
132     return $self->send_xml(
133         $self->fetch_xmldoc('TocDetail', $keyhash),
134         'TocItems');
135 }
136
137 sub toc_json {
138     my( $self, $keyhash ) = @_;
139     return $self->send_json(
140         $self->fetch_xmldoc('TocDetail', $keyhash),
141         'TocItems');
142 }
143
144 # --------------------------------------------------------------------------
145
146 sub anotes_html {
147     my( $self, $keyhash ) = @_;
148     my $doc = $self->fetch_xmldoc('BiographyDetail', $keyhash);
149     return 0 unless defined $doc;
150     return $self->parse_anotes_html($doc);
151 }
152
153 sub parse_anotes_html {
154     my( $self, $doc ) = @_;
155     my $html = '';
156     my @nodes = $doc->findnodes('//cc:BiographyItems[*]');
157     return 0 if (scalar(@nodes) < 1);
158     @nodes = $nodes[0]->findnodes('.//cc:Biography');
159     return 0 if (scalar(@nodes) < 1);
160     foreach my $node ( @nodes ) {
161         $html .= '<P class="biography">' . $node->textContent . '</P>';
162     }
163     return $self->send_html($html);
164 }
165
166 sub anotes_xml {
167     my( $self, $keyhash ) = @_;
168     return $self->send_xml(
169         $self->fetch_xmldoc('BiographyDetail', $keyhash),
170         'BiographyItems');
171 }
172
173 sub anotes_json {
174     my( $self, $keyhash ) = @_;
175     return $self->send_json(
176         $self->fetch_xmldoc('BiographyDetail', $keyhash),
177         'BiographyItems');
178 }
179
180
181 # --------------------------------------------------------------------------
182
183 sub excerpt_html {
184     my( $self, $keyhash ) = @_;
185     my $doc = $self->fetch_xmldoc('ExcerptDetail', $keyhash);
186     return 0 unless defined $doc;
187     return $self->parse_excerpt_html($doc);
188 }
189
190 sub parse_excerpt_html {
191     my( $self, $doc ) = @_;
192     my $html = '';
193     my @nodes = $doc->findnodes('//cc:ExcerptItems[*]');
194     return 0 if (scalar(@nodes) < 1);
195     @nodes = $nodes[0]->findnodes('.//cc:Excerpt');
196     return 0 if (scalar(@nodes) < 1);
197     foreach my $node ( @nodes ) {
198         $html .= $node->textContent;
199     }
200     return $self->send_html($html);
201 }
202
203 sub excerpt_xml {
204     my( $self, $keyhash ) = @_;
205     return $self->send_xml(
206         $self->fetch_xmldoc('ExcerptDetail', $keyhash),
207         'ExcerptItems');
208 }
209
210 sub excerpt_json {
211     my( $self, $keyhash ) = @_;
212     return $self->send_json(
213         $self->fetch_xmldoc('ExcerptDetail', $keyhash),
214         'ExcerptItems');
215 }
216
217 # --------------------------------------------------------------------------
218
219 sub reviews_html {
220     my( $self, $keyhash ) = @_;
221     my $doc = $self->fetch_xmldoc('ReviewDetail', $keyhash);
222     return 0 unless defined $doc;
223     return $self->parse_reviews_html($doc);
224 }
225
226 sub parse_reviews_html {
227     my( $self, $doc ) = @_;
228     my $html = '<ul>';
229     my @nodes = $doc->findnodes('//cc:ReviewItems[*]');
230     return 0 if (scalar(@nodes) < 1);
231     @nodes = $nodes[0]->findnodes('.//cc:ReviewItem');
232     return 0 if (scalar(@nodes) < 1);
233     foreach my $node ( @nodes ) {
234         my @s_nodes = $node->findnodes('./cc:Supplier');
235         my @p_nodes = $node->findnodes('./cc:Publication');
236         my @i_nodes = $node->findnodes('./cc:Issue');
237         my @r_nodes = $node->findnodes('./cc:Review');
238         $html .= '<li><b>' . (scalar(@p_nodes) ? $p_nodes[0]->textContent : '') . '</b>';
239         if (scalar(@i_nodes) && scalar(@p_nodes)) { $html .= ' : '; }
240         $html .= (scalar(@i_nodes) ? $i_nodes[0]->textContent : '') . '<br/>';
241         $html .= (scalar(@r_nodes) ? $r_nodes[0]->textContent : '') . '</li>';
242     }
243     $html .= '</ul>';
244     return $self->send_html($html);
245 }
246
247 sub reviews_xml {
248     my( $self, $keyhash ) = @_;
249     return $self->send_xml(
250         $self->fetch_xmldoc('ReviewDetail', $keyhash),
251         'ReviewItems');
252 }
253
254 sub reviews_json {
255     my( $self, $keyhash ) = @_;
256     return $self->send_json(
257         $self->fetch_xmldoc('ReviewDetail', $keyhash),
258         'ReviewItems');
259 }
260
261 # --------------------------------------------------------------------------
262
263 sub summary_html {
264     my( $self, $keyhash ) = @_;
265     my $doc = $self->fetch_xmldoc('AnnotationDetail', $keyhash);
266     return 0 unless defined $doc;
267     return $self->parse_summary_html($doc);
268 }
269
270 sub parse_summary_html {
271     my( $self, $doc ) = @_;
272     my $html = '<ul>';
273     my @nodes = $doc->findnodes('//cc:AnnotationItems[*]');
274     return 0 if (scalar(@nodes) < 1);
275     @nodes = $nodes[0]->findnodes('.//cc:AnnotationItem');
276     return 0 if (scalar(@nodes) < 1);
277     foreach my $node ( @nodes ) {
278         my @s_nodes = $node->findnodes('./cc:Supplier');
279         my @a_nodes = $node->findnodes('./cc:Annotation');
280         $html .= '<li><b>' . (scalar(@s_nodes) ? $s_nodes[0]->textContent : '') . '</b><br/>';
281         $html .= (scalar(@a_nodes) ? $a_nodes[0]->textContent : '') . '</li>';
282     }
283     $html .= '</ul>';
284     return $self->send_html($html);
285 }
286
287 sub summary_xml {
288     my( $self, $keyhash ) = @_;
289     return $self->send_xml(
290         $self->fetch_xmldoc('AnnotationDetail', $keyhash),
291         'AnnotationItems');
292 }
293
294 sub summary_json {
295     my( $self, $keyhash ) = @_;
296     return $self->send_json(
297         $self->fetch_xmldoc('AnnotationDetail', $keyhash),
298         'AnnotationItems');
299 }
300
301 # --------------------------------------------------------------------------
302
303 sub build_keylist {
304     my ( $self, $keyhash ) = @_;
305     my $keys = []; # Start with an empty array
306     foreach my $identifier (@{$self->identifier_order}) {
307         foreach my $key (@{$keyhash->{$identifier}}) {
308             push @{$keys}, $key;
309         }
310     }
311     return $keys;
312 }
313
314 sub send_json {
315     my( $self, $doc, $contentNode ) = @_;
316     return 0 unless defined $doc;
317     my @nodes = $doc->findnodes('//cc:' . $contentNode . '[*]');
318     return 0 if (scalar(@nodes) < 1);
319     my $perl = OpenSRF::Utils::SettingsParser::XML2perl($nodes[0]);
320     my $json = OpenSRF::Utils::JSON->perl2JSON($perl);
321     return { content_type => 'text/plain', content => $json };
322 }
323
324 sub send_xml {
325     my( $self, $doc, $contentNode ) = @_;
326     return 0 unless defined $doc;
327     my @nodes = $doc->findnodes('//cc:' . $contentNode . '[*]');
328     return 0 if (scalar(@nodes) < 1);
329     my $newdoc = XML::LibXML::Document->new( '1.0', 'utf-8' );
330     my $clonenode = $nodes[0]->cloneNode(1);
331     $newdoc->adoptNode($clonenode);
332     $newdoc->setDocumentElement($clonenode);
333     return { content_type => 'application/xml', content => $newdoc->toString() };
334 }
335
336 sub send_html {
337     my( $self, $content ) = @_;
338
339     # Hide anything that might contain a link since it will be broken
340     my $HTML = <<"    HTML";
341         <div>
342             <style type='text/css'>
343                 div.ac input, div.ac a[href],div.ac img, div.ac button { display: none; visibility: hidden }
344             </style>
345             <div class='ac'>
346                 $content
347             </div>
348         </div>
349     HTML
350
351     return { content_type => 'text/html', content => $HTML };
352 }
353
354 sub send_jacket {
355     my( $self, $keyhash, $size ) = @_;
356
357     my $doc = $self->fetch_xmldoc({name => 'JacketDetail', attributes => [['Type',$size],['Encoding','HEX']]}, $keyhash);
358     return 0 unless defined $doc;
359
360     return $self->parse_jacket($doc, $size);
361 }
362
363 sub parse_jacket {
364     my( $self, $doc, $size ) = @_;
365     my @nodes = $doc->findnodes("//cc:JacketItem[cc:Type/\@Code = '$size']");
366     return 0 if (scalar(@nodes) < 1);
367
368     my $jacketItem = shift(@nodes); # We only care about the first jacket
369     my @formatNodes = $jacketItem->findnodes('./cc:Format');
370     my $format = $formatNodes[0]->textContent;
371     my @jacketNodes = $jacketItem->findnodes('./cc:Jacket');
372     my $imageData = pack('H*',$jacketNodes[0]->textContent);
373
374     return {
375         content_type => 'image/' . lc($format),
376         content => $imageData,
377         binary => 1 
378     };
379 }
380
381 # returns an XML document ready for parsing if $keyhash contained usable keys
382 # otherwise returns undef
383 sub fetch_xmldoc {
384     my( $self, $contentTypes, $keyhash ) = @_;
385
386     my $keys = $self->build_keylist($keyhash);
387     return undef unless @{$keys};
388
389     my $content = $self->fetch_response($contentTypes, $keys)->content;
390     my $doc = XML::LibXML->new->parse_string($content);
391     $doc->documentElement->setNamespace('http://ContentCafe2.btol.com', 'cc');
392     return $doc;
393 }
394
395 # returns the HTTP response object from the URL fetch
396 sub fetch_response {
397     my( $self, $contentTypes, $keys ) = @_;
398
399     if (ref($contentTypes) ne 'ARRAY') {
400         $contentTypes = [ $contentTypes ];
401     }
402
403     my $xmlRequest = XML::LibXML::Document->new( '1.0', 'utf-8' );
404     my $root = $xmlRequest->createElementNS('http://ContentCafe2.btol.com','ContentCafe');
405     $root->addChild($xmlRequest->createAttribute('DateTime', DateTime->now()->datetime));
406     $xmlRequest->setDocumentElement($root);
407     my $requestItems = $xmlRequest->createElement('RequestItems');
408     $requestItems->addChild($xmlRequest->createAttribute('UserID', $self->userid));
409     $requestItems->addChild($xmlRequest->createAttribute('Password', $self->password));
410     $root->addChild($requestItems);
411
412     foreach my $key (@{$keys}) {
413         my $requestItem = $xmlRequest->createElement('RequestItem');
414         my $keyNode = $xmlRequest->createElement('Key');
415         $keyNode->addChild($xmlRequest->createTextNode($key));
416         $requestItem->addChild($keyNode);
417
418         foreach my $contentType (@{$contentTypes}) {
419             my $contentNode = $xmlRequest->createElement('Content');
420             if (ref($contentType) eq 'HASH') {
421                 $contentNode->addChild($xmlRequest->createTextNode($contentType->{name}));
422                 foreach my $contentAttribute (@{$contentType->{attributes}}) {
423                     $contentNode->addChild($xmlRequest->createAttribute($contentAttribute->[0], $contentAttribute->[1]));
424                 }
425             } else {
426                 $contentNode->addChild($xmlRequest->createTextNode($contentType));
427             }
428             $requestItem->addChild($contentNode);
429         }
430
431         $requestItems->addChild($requestItem);
432     }
433     my $response = $AC->post_url($post_url, $xmlRequest->toString);
434     return $response;
435 }
436
437 1;