1 package OpenILS::WWW::FlatFielder;
7 use Apache2::Const -compile => qw(
8 OK HTTP_NOT_ACCEPTABLE HTTP_PAYMENT_REQUIRED HTTP_INTERNAL_SERVER_ERROR :log
13 use CGI qw(:all -utf8);
15 use OpenSRF::Utils::JSON;
16 use OpenSRF::AppSession;
17 use OpenSRF::Utils::SettingsClient;
19 use OpenILS::Application::AppUtils;
20 my $U = 'OpenILS::Application::AppUtils';
23 my $_parser = new XML::LibXML;
24 my $_xslt = new XML::LibXSLT;
26 # BEGIN package globals
28 # We'll probably never need this fanciness for autosuggest, but
29 # you can add handlers for different requested content-types here, and
30 # you can weight them to control what matches requests for things like
35 my ($r, $args, $xslt) = @_;
36 $args->{'stylesheet'} =
37 OpenSRF::Utils::SettingsClient->new->config_value(dirs => 'xsl') . '/' . $xslt;
38 print data_to_xml($args);
39 return Apache2::Const::OK;
42 my $_output_handler_dispatch = {
47 $r->headers_out->set("Content-Disposition" => "attachment; filename=FlatSearch.csv");
48 # Anecdotally, IE 8 needs name= here to provoke downloads, where
49 # other browswers respect filename= in Content-Disposition. Also,
50 # we might want to make the filename choosable by CGI param later?
51 # Or vary it by timestamp?
52 $r->content_type('text/csv; name=FlatSearch.csv; charset=utf-8');
53 print_data_as_csv($args, \*STDOUT);
54 return Apache2::Const::OK;
60 $_[0]->content_type("text/html; charset=utf-8");
61 print html_ish_output( @_, 'FlatFielder2HTML.xsl' );
62 return Apache2::Const::OK;
65 "application/xml" => {
69 $r->content_type("application/xml; charset=utf-8");
70 print data_to_xml($args);
71 return Apache2::Const::OK;
74 "application/json" => {
78 $r->content_type("application/json; charset=utf-8");
79 print data_to_json($args);
80 return Apache2::Const::OK;
85 my @_output_handler_types = sort {
86 $_output_handler_dispatch->{$a}->{prio} <=>
87 $_output_handler_dispatch->{$b}->{prio}
88 } keys %$_output_handler_dispatch;
94 <FlatSearch hint='foo' identifier='bar' label='Foo Bar' FS_key='ad1awe43a3a2a3ra32a23ra32ra23rar23a23r'>
96 <column name='fiz'>YAY!</column>
97 <column name='faz'>boooo</column>
100 <column name='fiz'>WHEEE!</column>
101 <column name='faz'>noooo</column>
110 my $dom = new XML::LibXML::Document("1.0", "UTF-8");
111 my $fs = $dom->createElement("FlatSearch");
112 $fs->setAttribute("hint", $args->{hint}) if $args->{hint};
113 $fs->setAttribute("identifier", $args->{id_field}) if $args->{id_field};
114 $fs->setAttribute("label", $args->{label_field}) if $args->{label_field};
115 $fs->setAttribute("FS_key", $args->{key}) if $args->{key};
116 $dom->setDocumentElement($fs);
119 for my $i (@{$$args{data}}) {
120 my $item = $dom->createElement("row");
121 $item->setAttribute('ordinal', $rownum);
123 for my $k (keys %$i) {
124 my $val = $dom->createElement('column');
125 $val->setAttribute('name', $k);
126 $val->appendText($i->{$k});
127 $item->addChild($val);
129 $fs->addChild($item);
132 # XML::LibXML::Document::toString() returns an encoded byte string, which
133 # is why we don't need to binmode STDOUT, ':utf8'.
135 return $_xslt->parse_stylesheet(
136 $_parser->parse_file( $$args{stylesheet} )
139 )->toString if ($$args{stylesheet}); # configured transform, early return
141 return $dom->toString();
144 sub print_data_as_csv {
145 my ($args, $fh) = @_;
147 my @keys = sort keys %{ $$args{data}[0] };
150 my $csv = new Text::CSV({ always_quote => 1, eol => "\r\n" });
152 $csv->print($fh, \@keys);
154 for my $row (@{$$args{data}}) {
155 $csv->print($fh, [map { $row->{$_} } @keys]);
162 # Turns out we don't want the data structure you'd use to initialize an
163 # itemfilereadstore or similar. We just want rows.
165 # return OpenSRF::Utils::JSON->perl2JSON({
166 # ($$args{hint} ? (hint => $$args{hint}) : ()),
167 # ($$args{id_field} ? (identifier => $$args{id_field}) : ()),
168 # ($$args{label_field} ? (label => $$args{label_field}) : ()),
169 # ($$args{key} ? (FS_key => $$args{key}) : ()),
170 # items => $$args{data}
172 return OpenSRF::Utils::JSON->perl2JSON($args->{data});
175 # Given data and the Apache request object, this sub picks a sub from a
176 # dispatch table based on the list of content-type encodings that the client
177 # has indicated it will accept, and calls that sub, which will deliver
178 # a response of appropriately encoded data.
182 my @types = split /,/, $r->headers_in->{Accept};
184 if ($$args{format}) {
185 unshift @types, $$args{format};
188 foreach my $media_range (@types) {
189 $media_range =~ s/;.+$//; # keep type, subtype. lose parameters.
192 Text::Glob::match_glob($media_range, $_)
193 } @_output_handler_types;
196 return $_output_handler_dispatch->{$match}{code}->($r, $args);
200 return Apache2::Const::HTTP_NOT_ACCEPTABLE;
208 $args{format} = $cgi->param('format');
209 $args{auth} = $cgi->param('ses');
210 $args{hint} = $cgi->param('hint');
211 $args{map} = OpenSRF::Utils::JSON->JSON2perl($cgi->param('map'));
212 $args{where} = OpenSRF::Utils::JSON->JSON2perl($cgi->param('where'));
213 $args{slo} = OpenSRF::Utils::JSON->JSON2perl($cgi->param('slo'));
214 $args{key} = $cgi->param('key');
215 $args{id_field} = $cgi->param('identifier');
216 $args{label_field} = $cgi->param('label');
218 my $fielder = OpenSRF::AppSession->create('open-ils.fielder');
220 $args{data} = $fielder->request(
221 'open-ils.fielder.flattened_search.atomic',
222 @args{qw/auth hint map where slo/}
225 $args{data} = $fielder->request(
226 'open-ils.fielder.flattened_search.execute.atomic',
227 @args{qw/auth key where slo/}
230 if (ref $args{data} and $args{data}[0] and
231 $U->event_equals($args{data}[0], 'CACHE_MISS')) {
233 # You have to pay the cache! I kill me.
234 return Apache2::Const::HTTP_PAYMENT_REQUIRED;
238 return output_handler( $r, \%args );