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