blib/lib/SeeAlso/Source/BeaconAggregator/Publisher.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 7 | 9 | 77.7 |
branch | n/a | ||
condition | n/a | ||
subroutine | 3 | 3 | 100.0 |
pod | n/a | ||
total | 10 | 12 | 83.3 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package SeeAlso::Source::BeaconAggregator::Publisher; | ||||||
2 | 3 | 3 | 24788 | use strict; | |||
3 | 3 | ||||||
3 | 76 | ||||||
3 | 3 | 3 | 11 | use warnings; | |||
3 | 2 | ||||||
3 | 150 | ||||||
4 | |||||||
5 | our $VERSION = "0.2_90"; | ||||||
6 | |||||||
7 | =head1 NAME | ||||||
8 | |||||||
9 | SeeAlso::Source::BeaconAggregator::Publisher - additional methods for SeeAlso::Source::BeaconAggregator | ||||||
10 | |||||||
11 | =head1 SYNOPSIS | ||||||
12 | |||||||
13 | $source = SeeAlso::Source::BeaconAggregator::Publisher->new(...); | ||||||
14 | |||||||
15 | =head1 DESCRIPTION | ||||||
16 | |||||||
17 | This package provides the functionallity to export a BEACON file from the | ||||||
18 | data connected with an SeeAlso::Source::BeaconAggregator instance and | ||||||
19 | also the additional formats "redirect" and "sources" which universally | ||||||
20 | can be used as callbacks for SeeAlso::Server (replacing the default | ||||||
21 | "seealso" method yielding JSON data). | ||||||
22 | |||||||
23 | =cut | ||||||
24 | |||||||
25 | our %Defaults = ( | ||||||
26 | # "REVISIT" => 86400, # one day | ||||||
27 | "REVISIT" => undef, # no default (leave empty unless otherwise set) | ||||||
28 | "uAformatname" => "sources", | ||||||
29 | "beaconformatname" => "beacon", | ||||||
30 | "FORMAT" => "BEACON", | ||||||
31 | "VERSION" => "0.1", # no other exist | ||||||
32 | ); | ||||||
33 | |||||||
34 | 3 | 3 | 666 | use SeeAlso::Source::BeaconAggregator; | |||
0 | |||||||
0 | |||||||
35 | use Carp; | ||||||
36 | |||||||
37 | =head2 new ( ... ) | ||||||
38 | |||||||
39 | Creates an SeeAlso::Source::BeaconAggregator object with additional methods from | ||||||
40 | this package enabled | ||||||
41 | |||||||
42 | =cut | ||||||
43 | |||||||
44 | sub new { # directly create BeaconAggregator instance with extended features... | ||||||
45 | my $class = shift @_; | ||||||
46 | push(@SeeAlso::Source::BeaconAggregator::ISA, $class); | ||||||
47 | return SeeAlso::Source::BeaconAggregator->new(@_); | ||||||
48 | } | ||||||
49 | |||||||
50 | |||||||
51 | =head2 activate () | ||||||
52 | |||||||
53 | Makes SeeAlso::Source::BeaconAggregator objects member of this class, | ||||||
54 | globally enabling the additional methods | ||||||
55 | |||||||
56 | Usage: | ||||||
57 | |||||||
58 | $db = SeeAlso::Source::BeaconAggregator::Maintenance->new(...); | ||||||
59 | ... | ||||||
60 | do stuff | ||||||
61 | ... | ||||||
62 | require SeeAlso::Source::BeaconAggregator::Publisher | ||||||
63 | or die "could not require Publisher extension"; | ||||||
64 | SeeAlso::Source::BeaconAggregator::Publisher->activate(); # "recast" all objects | ||||||
65 | ... | ||||||
66 | do more stuff | ||||||
67 | |||||||
68 | =cut | ||||||
69 | sub activate { # enrich SeeAlso::Source and derived classes with our methods | ||||||
70 | my $class = shift @_; | ||||||
71 | push(@SeeAlso::Source::BeaconAggregator::ISA, $class); | ||||||
72 | return 1; | ||||||
73 | } | ||||||
74 | |||||||
75 | |||||||
76 | ### Produktion der Beacon-Datei | ||||||
77 | |||||||
78 | =head2 beacon ( [dumpmeta arguments] ) | ||||||
79 | |||||||
80 | produces a BEACON file (however, $cgibase is mandatory) | ||||||
81 | |||||||
82 | =head2 dumpmeta ( [$cgibase, [$uAformatname, [$headersonly]]] [, $preset]) | ||||||
83 | |||||||
84 | produces only the meta fields of a BEACON file | ||||||
85 | |||||||
86 | Meta fields are generated from the $preset Hashref, falling back to | ||||||
87 | values stored in the database, falling back to reasonable default | ||||||
88 | values. | ||||||
89 | |||||||
90 | Arguments: | ||||||
91 | |||||||
92 | =over 8 | ||||||
93 | |||||||
94 | =item $cgibase | ||||||
95 | |||||||
96 | URL of the SeeAlso service the BEACON file is provided for | ||||||
97 | |||||||
98 | =item $uAformatname | ||||||
99 | |||||||
100 | unAPI format name to be used as target (Default: "sources") | ||||||
101 | |||||||
102 | =item $headersonly | ||||||
103 | |||||||
104 | currently unused | ||||||
105 | |||||||
106 | =item $preset | ||||||
107 | |||||||
108 | Hashref of Beacon header fields overriding the contents of the database | ||||||
109 | |||||||
110 | =back | ||||||
111 | |||||||
112 | Regular Usage: | ||||||
113 | |||||||
114 | $db = SeeAlso::Source::BeaconAggregator::Publisher->new(...); | ||||||
115 | binmode(STDOUT, ":utf8"); | ||||||
116 | my $cgibase = "http://address/of/service"; | ||||||
117 | my ( $error, $headerref) = $db->beacon($cgibase, @ARGV, {'FORMAT' => 'PND-BEACON'}); | ||||||
118 | |||||||
119 | |||||||
120 | CGI Usage: | ||||||
121 | |||||||
122 | $format = $CGI->param('format') || ""; | ||||||
123 | if ( $format eq "beacon" ) { # bypass SeeAlso::Server->query() b/c performance / interim storage | ||||||
124 | insert access restrictions here... | ||||||
125 | do_beacon($source, $CGI); | ||||||
126 | } | ||||||
127 | ... | ||||||
128 | |||||||
129 | sub do_beacon { | ||||||
130 | my ($self, $cgi) = @_; # Of type SeeAlso::Source::BeaconAggregator | ||||||
131 | unless ( $self->can("beacon") ) { | ||||||
132 | croak "On the fly generation of beacon Files not supported by this service";} | ||||||
133 | my $cgibase = $cgi->url(-path_info=>1); | ||||||
134 | |||||||
135 | print $cgi->header( -status => 200, | ||||||
136 | -expires => '+1d', | ||||||
137 | -type => 'text/plain', | ||||||
138 | -charset => 'utf-8', | ||||||
139 | ); | ||||||
140 | return $self->beacon($cgibase, "sources", {}); # prints directly to stdout..., returns $error, $headerref | ||||||
141 | } | ||||||
142 | |||||||
143 | =cut | ||||||
144 | |||||||
145 | sub beacon { | ||||||
146 | my ($self) = shift @_ or croak("beacon is a method!"); # Of type SeeAlso::Source::BeaconAggregator | ||||||
147 | my ($error, $headerref) = $self->dumpmeta(@_); | ||||||
148 | croak("Error generating Header, will not proceed") if $error; | ||||||
149 | |||||||
150 | print @$headerref; | ||||||
151 | |||||||
152 | my $c = (defined $self->{identifierClass}) ? $self->{identifierClass} : $self->autoIdentifier(); | ||||||
153 | |||||||
154 | my ($sth, $sthexpl) = $self->stmtHdl(<<"XxX"); | ||||||
155 | SELECT hash, COUNT(DISTINCT seqno) FROM beacons GROUP BY hash ORDER BY hash; | ||||||
156 | XxX | ||||||
157 | $self->stmtExplain($sthexpl) if $ENV{'DBI_PROFILE'}; | ||||||
158 | $sth->execute() or croak("Could not execute >".$sth->{Statement}."<: ".$sth->errstr); | ||||||
159 | my $rows = 0; | ||||||
160 | while ( my $row = $sth->fetchrow_arrayref ) { | ||||||
161 | $rows++; | ||||||
162 | my $expanded = $row->[0]; | ||||||
163 | if ( defined $c ) { | ||||||
164 | # compat: hash might not take an argument, must resort to value, has to be cleared before... | ||||||
165 | $c->value(""); | ||||||
166 | my $did = $c->hash($row->[0]) || $c->value($row->[0]); | ||||||
167 | $expanded = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
168 | # illegal identifier b/c different classes for loading and exporting? | ||||||
169 | next unless defined $expanded && ($expanded ne ""); | ||||||
170 | } | ||||||
171 | print $expanded.(($row->[1] > 1) ? "|".$row->[1] : "")."\n"; | ||||||
172 | } | ||||||
173 | |||||||
174 | return $rows, $headerref; | ||||||
175 | } | ||||||
176 | |||||||
177 | sub dumpmeta { # cgibase unAPIformatname headers_only {preset} | ||||||
178 | my ($self) = shift @_ or croak("dumpmeta is a method!"); # Of type SeeAlso::Source::BeaconAggregator | ||||||
179 | my ($error, @result) = (0, ()); | ||||||
180 | |||||||
181 | my $cgibase = shift @_ if @_ && !ref($_[0]); | ||||||
182 | my $uAformatname = shift @_ if @_ && !ref($_[0]); | ||||||
183 | $uAformatname ||= $Defaults{'uAformatname'}; | ||||||
184 | my $headersonly = shift @_ if @_ && !ref($_[0]); | ||||||
185 | my $preset = (@_ && ref($_[0])) ? (shift @_) : {}; | ||||||
186 | |||||||
187 | my ($metasth, $metasthexpl) = $self->stmtHdl(<<"XxX"); | ||||||
188 | SELECT key, val FROM osd; | ||||||
189 | XxX | ||||||
190 | $self->stmtExplain($metasthexpl) if $ENV{'DBI_PROFILE'}; | ||||||
191 | $metasth->execute() or croak("Could not execute >".$metasth->{Statement}."<: ".$metasth->errstr); | ||||||
192 | |||||||
193 | my (%osd, %beaconmeta); | ||||||
194 | while ( my $aryref = $metasth->fetchrow_arrayref ) { | ||||||
195 | my ($key, $val) = @$aryref; | ||||||
196 | next unless $val; | ||||||
197 | if ($key =~ s/^bc// ) { # BeaconMeta Fields | ||||||
198 | $beaconmeta{$key} = $val} | ||||||
199 | elsif ( exists $osd{$key} ) { | ||||||
200 | if ( ref($osd{$key}) ) { | ||||||
201 | push(@{$osd{$key}}, $val)} | ||||||
202 | else { | ||||||
203 | $osd{$key} = [$osd{$key}, $val]}; | ||||||
204 | } | ||||||
205 | else { | ||||||
206 | $osd{$key} = $val}; | ||||||
207 | }; | ||||||
208 | my @osdexamples; | ||||||
209 | if ( $osd{'Examples'} && ref($osd{'Examples'}) ) { | ||||||
210 | foreach my $expl ( @{$osd{'Examples'}} ) { | ||||||
211 | $expl =~ s/\s*\|.*$//; | ||||||
212 | push(@osdexamples, $expl); | ||||||
213 | } | ||||||
214 | } | ||||||
215 | elsif ( my $expl = $osd{'Examples'} ) { | ||||||
216 | $expl =~ s/\s*\|.*$//; | ||||||
217 | push(@osdexamples, $expl); | ||||||
218 | }; | ||||||
219 | |||||||
220 | foreach ( grep /^[A-Z]+$/, keys %$preset ) { | ||||||
221 | $beaconmeta{$_} = $preset->{$_}} | ||||||
222 | # Mandatory fields | ||||||
223 | push(@result, "#FORMAT: ".($beaconmeta{'FORMAT'} || $Defaults{'FORMAT'})."\n"); | ||||||
224 | push(@result, "#VERSION: ".($beaconmeta{'VERSION'} || $Defaults{'VERSION'})."\n"); | ||||||
225 | if ( $beaconmeta{'TARGET'} ) { | ||||||
226 | $beaconmeta{'TARGET'} =~ s/^\{BASE\}/$cgibase/; | ||||||
227 | push(@result, "#TARGET: $beaconmeta{'TARGET'}\n"); | ||||||
228 | } | ||||||
229 | elsif ( $cgibase ) { | ||||||
230 | push(@result, "#TARGET: $cgibase?format=$uAformatname&id={ID}\n")} | ||||||
231 | else { | ||||||
232 | carp "Don't know how to construct the mandatory #TARGET field!"; | ||||||
233 | $error ++; | ||||||
234 | } | ||||||
235 | |||||||
236 | my $timestamp = $preset->{'TIMESTAMP'} || $osd{DateModified} || $^T; | ||||||
237 | push(@result, "#TIMESTAMP: ".SeeAlso::Source::BeaconAggregator::tToISO($timestamp)."\n") if $timestamp > 0; | ||||||
238 | my $revisit = ($beaconmeta{'REVISIT'} || $Defaults{'REVISIT'}) || ""; | ||||||
239 | $revisit =~ tr/ //d; | ||||||
240 | $revisit =~ s/(\d+)mo\w*/($1*30)."d"/ei; | ||||||
241 | $revisit =~ s/(\d+)M\w*/($1*30)."d"/e; | ||||||
242 | $revisit =~ s/(\d+)w\w*/($1*7)."d"/ei; | ||||||
243 | $revisit =~ s/(\d+)d\w*/($1*24)."h"/ei; | ||||||
244 | $revisit =~ s/(\d+)h\w*/($1*60)."m"/ei; | ||||||
245 | $revisit =~ s/(\d+)m\w*/($1*60)."s"/ei; | ||||||
246 | $revisit =~ s/(\d+)s\w*/$1/i; | ||||||
247 | push(@result, "#REVISIT: ".SeeAlso::Source::BeaconAggregator::tToISO($timestamp + $revisit)."\n") if $revisit && ($revisit =~ /^[+-]?\d+$/) && ($revisit > 0);; | ||||||
248 | |||||||
249 | # $beaconmeta{'UPDATE'} ||= "daily"; | ||||||
250 | $beaconmeta{'FEED'} ||= "$cgibase?format=".$Defaults{'beaconformatname'} if $cgibase; | ||||||
251 | $beaconmeta{'EXAMPLES'} ||= join("|", @osdexamples); | ||||||
252 | $beaconmeta{'CONTACT'} ||= $self->{Contact} || $osd{'Contact'}; | ||||||
253 | $beaconmeta{'DESCRIPTION'} ||= $self->{Description} || $osd{'Description'}; | ||||||
254 | $beaconmeta{'NAME'} ||= $self->{ShortName} || $osd{'ShortName'}; | ||||||
255 | foreach ( grep !/^(FORMAT|REVISIT|TARGET|TIMESTAMP|VERSION)$/, SeeAlso::Source::BeaconAggregator->beaconfields() ) { | ||||||
256 | next unless my $val = $beaconmeta{$_}; | ||||||
257 | next if $val =~ /^-/; | ||||||
258 | $val =~ s/\s+/ /g; $val =~ s/^\s+//; $val =~ s/\s+$//; | ||||||
259 | push(@result, "#$_: $val\n"); | ||||||
260 | } | ||||||
261 | |||||||
262 | # extract admin info of last transaction (i.e. last possible modification of underlying data) | ||||||
263 | # alternatively: SELECT seqno, utime FROM repos WHERE seqno=(SELECT MAX(seqno) FROM repos); | ||||||
264 | my ($laststh, $laststhexpl) = $self->stmtHdl(<<"XxX"); | ||||||
265 | SELECT MAX(seqno), MAX(mtime) FROM repos; | ||||||
266 | XxX | ||||||
267 | $self->stmtExplain($laststhexpl) if $ENV{'DBI_PROFILE'}; | ||||||
268 | $laststh->execute() or croak("Could not execute >".$laststh->{Statement}."<: ".$laststh->errstr); | ||||||
269 | if ( my $aryref = $laststh->fetchrow_arrayref ) { | ||||||
270 | my ($sq, $ut) = @$aryref; | ||||||
271 | push(@result, "#X-REVISION: $sq [".SeeAlso::Source::BeaconAggregator::tToISO($ut)."]\n") if $sq; | ||||||
272 | }; | ||||||
273 | my $admref = $self->admhash(); | ||||||
274 | if ( my $cu = $admref->{'gcountu'} ) { | ||||||
275 | my $type = $admref->{'IDENTIFIER_CLASS'} || ""; | ||||||
276 | push(@result, "#X-EXTENT: $cu unique identifiers".($type ? " of type $type" : "")."\n"); | ||||||
277 | }; | ||||||
278 | |||||||
279 | |||||||
280 | ## PND-BEACON | ||||||
281 | # CONTACT => ['VARCHAR(63)'], | ||||||
282 | # INSTITUTION => ['VARCHAR(255)'], | ||||||
283 | # ISIL => ['VARCHAR(63)'], | ||||||
284 | # DESCRIPTION => ['VARCHAR(255)'], | ||||||
285 | ## BEACON | ||||||
286 | # MESSAGE => ['VARCHAR(255)'], # enthaelt {hits} | ||||||
287 | # ONEMESSAGE => ['VARCHAR(255)'], | ||||||
288 | # SOMEMESSAGE => ['VARCHAR(255)'], | ||||||
289 | # PREFIX => ['VARCHAR(255)'], | ||||||
290 | ## WInofficial | ||||||
291 | # NAME => ['VARCHAR(255)'], | ||||||
292 | |||||||
293 | return $error, \@result; | ||||||
294 | } | ||||||
295 | |||||||
296 | |||||||
297 | =head2 redirect ( $server, $format, $extra, $query ) | ||||||
298 | |||||||
299 | Produces an HTTP redirect page, HTML content contains very terse details in case | ||||||
300 | of multiple results. | ||||||
301 | |||||||
302 | This subroutine may be used as callback method in SeeAlso::Server | ||||||
303 | |||||||
304 | Usage is a bit cludgy due to author's lack of understanding of SeeAlso::Server | ||||||
305 | |||||||
306 | $source = SeeAlso::Sources::BeaconAggregator::Publisher->new(...); | ||||||
307 | $CGI = CGI->new(); | ||||||
308 | |||||||
309 | $formats = { | ||||||
310 | ... | ||||||
311 | redirect => { | ||||||
312 | type => "text/html", | ||||||
313 | docs => "http://www.endofthe.net/", | ||||||
314 | # method => \&SeeAlso::Source::BeaconAggregator::Publisher::redirect, | ||||||
315 | #redirect_300 => 'sources', | ||||||
316 | } | ||||||
317 | }; | ||||||
318 | |||||||
319 | $server = SeeAlso::Server->new ( | ||||||
320 | 'cgi' => $CGI, | ||||||
321 | 'formats' => $formats, | ||||||
322 | ... | ||||||
323 | ); | ||||||
324 | |||||||
325 | # Closure as fix: Server.pm does not expose self, $source and the CGI object to the format methods | ||||||
326 | my $oref = \&SeeAlso::Source::BeaconAggregator::Publisher::redirect; | ||||||
327 | $server->{'formats'}->{'redirect'}->{method} | ||||||
328 | = sub {return &$oref($source, $server, $method, $formats->{$method}, @_)}; | ||||||
329 | |||||||
330 | my $result = $server->query($source); | ||||||
331 | |||||||
332 | Arguments: | ||||||
333 | |||||||
334 | =over 8 | ||||||
335 | |||||||
336 | =item $server | ||||||
337 | |||||||
338 | SeeAlso::Server object. Must contain a CGI object | ||||||
339 | |||||||
340 | =item $format | ||||||
341 | |||||||
342 | Name of a format registered with the $server object () | ||||||
343 | |||||||
344 | =item $extra | ||||||
345 | |||||||
346 | Hashref with the following configuration directives | ||||||
347 | |||||||
348 | redirect_300 => CGI 'format' parameter to be used in HTML content (eg. format=sources) | ||||||
349 | |||||||
350 | force_single => Only regard the first hit (thus always redirect) | ||||||
351 | |||||||
352 | =item $query | ||||||
353 | |||||||
354 | Identifier to be queried | ||||||
355 | |||||||
356 | =back | ||||||
357 | |||||||
358 | =cut | ||||||
359 | |||||||
360 | sub redirect { # Liste der Beacon-Header fuer Treffer oder einfaches redirect | ||||||
361 | my ($self, $server, $format, $extra, $query) = @_; | ||||||
362 | my $formatprops = $server->{'formats'}->{$format} || {}; | ||||||
363 | my $cgi = $server->{'cgi'} or croak("I rely on a prepared CGI.pm object"); | ||||||
364 | |||||||
365 | my %headerdefaults = ( -type => ($formatprops->{'type'} || 'text/html'), | ||||||
366 | # ($formatprops->{'charset'} ? (-charset => $formatprops->{'charset'}) : ()), | ||||||
367 | -charset => ($formatprops->{'charset'} || 'UTF-8'), | ||||||
368 | -expires => ($server->{'expires'} || '+1h'), | ||||||
369 | ); | ||||||
370 | |||||||
371 | my ($hash, $pretty, $canon) = $self->prepare_query($query); | ||||||
372 | unless ( $hash ) { | ||||||
373 | print $cgi->header(-status => "400 Bad Request (Identifier '$query' not valid)", | ||||||
374 | -expires => "+1y", | ||||||
375 | -type => 'text/html', | ||||||
376 | ), | ||||||
377 | $cgi->start_html (-dtd => "-//W3C//DTD HTML 3.2 Final//EN", | ||||||
378 | -title => "No valid identifier", | ||||||
379 | ), | ||||||
380 | $cgi->p("Malformed identifier '$query'"), | ||||||
381 | $cgi->end_html; | ||||||
382 | return ""; | ||||||
383 | }; | ||||||
384 | |||||||
385 | my $clusterid; | ||||||
386 | if ( $self->{cluster} ) { | ||||||
387 | my ($clusterh, $clusterexpl) = $self->stmtHdl("SELECT beacons.altid FROM cluster.beacons WHERE beacons.hash=? OR beacons.altid=? LIMIT 1;"); | ||||||
388 | $self->stmtExplain($clusterexpl, $hash, $hash) if $ENV{'DBI_PROFILE'}; | ||||||
389 | $clusterh->execute($hash, $hash); | ||||||
390 | while ( my $onerow = $clusterh->fetchrow_arrayref() ) { | ||||||
391 | $clusterid = $onerow->[0];} | ||||||
392 | } | ||||||
393 | |||||||
394 | my $clause = $extra->{force_single} ? "LIMIT 1" : "ORDER BY repos.sort, repos.alias"; | ||||||
395 | my ( $tfield,$afield, $gfield, $mfield,$nfield,$ifield) = map{ scalar $self->beaconfields($_) } | ||||||
396 | qw(TARGET ALTTARGET IMGTARGET MESSAGE NAME INSTITUTION); | ||||||
397 | # above 5 6 7 8 9 10 | ||||||
398 | # below 0 1 2 3 4 | ||||||
399 | # 11 | ||||||
400 | my ($sth, $sthexpl); | ||||||
401 | if ( $clusterid ) { # query IN cluster | ||||||
402 | ($sth, $sthexpl) = $self->stmtHdl(<<"XxX"); | ||||||
403 | SELECT beacons.hash, beacons.altid, beacons.hits, beacons.info, beacons.link, | ||||||
404 | repos.$tfield, repos.$afield, repos.$gfield, repos.$mfield, repos.$nfield, repos.$ifield, | ||||||
405 | repos.alias | ||||||
406 | FROM beacons NATURAL LEFT JOIN repos | ||||||
407 | WHERE ( (beacons.hash=?) | ||||||
408 | OR (beacons.hash IN (SELECT cluster.beacons.hash FROM cluster.beacons WHERE cluster.beacons.altid=?)) ) | ||||||
409 | $clause; | ||||||
410 | XxX | ||||||
411 | $self->stmtExplain($sthexpl, $clusterid, $clusterid) if $ENV{'DBI_PROFILE'}; | ||||||
412 | $sth->execute($clusterid, $clusterid) or croak("Could not execute >".$sth->{Statement}."<: ".$sth->errstr); | ||||||
413 | } | ||||||
414 | else { | ||||||
415 | ($sth, $sthexpl) = $self->stmtHdl(<<"XxX"); | ||||||
416 | SELECT beacons.hash, beacons.altid, beacons.hits, beacons.info, beacons.link, | ||||||
417 | repos.$tfield, repos.$afield, repos.$gfield, repos.$mfield, repos.$nfield, repos.$ifield, | ||||||
418 | repos.alias | ||||||
419 | FROM beacons NATURAL LEFT JOIN repos | ||||||
420 | WHERE beacons.hash=? | ||||||
421 | $clause; | ||||||
422 | XxX | ||||||
423 | $self->stmtExplain($sthexpl, $hash) if $ENV{'DBI_PROFILE'}; | ||||||
424 | $sth->execute($hash) or croak("Could not execute >".$sth->{Statement}."<: ".$sth->errstr); | ||||||
425 | } | ||||||
426 | |||||||
427 | my $c = $self->{identifierClass} || undef; | ||||||
428 | my @rawres; | ||||||
429 | my %didalready; | ||||||
430 | while ( my $onerow = $sth->fetchrow_arrayref ) { | ||||||
431 | next if $onerow->[11] && exists $self->{'aliasfilter'}->{$onerow->[11]}; | ||||||
432 | my $uri = $onerow->[4]; # Evtl. Expliziter Link | ||||||
433 | my $guri = ""; | ||||||
434 | |||||||
435 | my $h = $onerow->[0]; | ||||||
436 | my $p; | ||||||
437 | if ( $h eq $hash ) { | ||||||
438 | $p = $pretty} | ||||||
439 | elsif ( $clusterid && ref($c) ) { | ||||||
440 | $c->value(""); | ||||||
441 | my $did = $c->hash($h) || $c->value($h) || $h; | ||||||
442 | $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
443 | }; | ||||||
444 | $p = ($clusterid ? $h : $pretty) unless defined $p; | ||||||
445 | |||||||
446 | if ( $onerow->[1] ) { # Konkordanzformat | ||||||
447 | $uri ||= sprintf($onerow->[6] || $onerow->[5], $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($onerow->[1])); | ||||||
448 | $guri = sprintf($onerow->[7], $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($onerow->[1])) if $onerow->[7]; | ||||||
449 | } | ||||||
450 | elsif ( $onerow->[5] ) { # normales Beacon-Format | ||||||
451 | $uri ||= sprintf($onerow->[5], $p); | ||||||
452 | $guri = sprintf($onerow->[7], $p) if $onerow->[7]; | ||||||
453 | }; | ||||||
454 | next unless $uri; | ||||||
455 | |||||||
456 | # #NAME #INSTITUTION _alias | ||||||
457 | my $label; | ||||||
458 | if ( $label = $onerow->[8] ) { #MESSAGE | ||||||
459 | $label = sprintf($label, $onerow->[2] || "...")} | ||||||
460 | elsif ( $label = $onerow->[9] || $onerow->[10] || $onerow->[11] || "???" ) { | ||||||
461 | $label .= " (".$onerow->[1].")" if $onerow->[1]} | ||||||
462 | |||||||
463 | push(@rawres, [$uri, $guri, $label, $onerow->[11], $onerow->[3]]) unless $didalready{join("\x7f", $label, $uri)}++;; | ||||||
464 | }; | ||||||
465 | my $hits = scalar @rawres; | ||||||
466 | |||||||
467 | if ( ! $hits ) { | ||||||
468 | print $cgi->header(-status => "404 Not Found (identifier '$canon')", | ||||||
469 | %headerdefaults), | ||||||
470 | $cgi->start_html (-dtd => "-//W3C//DTD HTML 3.2 Final//EN", | ||||||
471 | -title => "No References for $pretty", | ||||||
472 | ), | ||||||
473 | $cgi->p("No References found for ", $cgi->a({href=>"$canon"}, $pretty)), | ||||||
474 | $cgi->end_html; | ||||||
475 | return ""; | ||||||
476 | } | ||||||
477 | elsif ( $hits == 1 ) { | ||||||
478 | return $cgi->redirect(-status => "302 Found (Redirecting for identifier '$canon')", | ||||||
479 | -uri => $rawres[0]->[0], | ||||||
480 | %headerdefaults); | ||||||
481 | } | ||||||
482 | |||||||
483 | my $sources = new CGI($cgi); | ||||||
484 | $sources->param(-name => 'id', -value=>"$canon"); | ||||||
485 | unless ( $canon =~ /:\/\// ) { | ||||||
486 | my ($osd, $beaconmeta) = $self->get_meta; | ||||||
487 | my $prefix = $beaconmeta->{'PREFIX'} || ""; | ||||||
488 | $canon = "$prefix$pretty" if $prefix; | ||||||
489 | }; | ||||||
490 | if ( my $multired = $extra->{redirect_300} ) { | ||||||
491 | $sources->param(-name => 'format', -value=>$multired); | ||||||
492 | print $cgi->redirect(-status => "300 Multiple Choices for identifier '$canon'", | ||||||
493 | -uri => $sources->url(-path_info=>1, -query=>1), | ||||||
494 | %headerdefaults); | ||||||
495 | } | ||||||
496 | else { | ||||||
497 | print $cgi->header(-status => "300 Multiple Choices for identifier '$canon'", | ||||||
498 | # -nph => 1, # for older CGI/mod_perl??? | ||||||
499 | %headerdefaults); | ||||||
500 | # mod_perl overrides the header and adds a custom document at the end of everything | ||||||
501 | # therefore we force the header out (a simple print "" does not suffice) and then can | ||||||
502 | # safely reset the status to OK via CGI.pm leaking the Apache2::Request object | ||||||
503 | if ( my $r = $sources->r ) { | ||||||
504 | local($|) = 1; | ||||||
505 | print "\n"; | ||||||
506 | $r->status(200); | ||||||
507 | }; | ||||||
508 | }; | ||||||
509 | my @result; | ||||||
510 | push(@result, $cgi->start_html ( -title => "$hits References for $pretty", | ||||||
511 | -dtd => "-//W3C//DTD HTML 3.2 Final//EN"), | ||||||
512 | $cgi->h1("$hits References for ", $cgi->a({href=>"$canon"}, $pretty)), | ||||||
513 | '
|
||||||
514 | |||||||
515 | my $rowcnt = 0; | ||||||
516 | foreach ( @rawres ) { # uri, guri, label, alias, info | ||||||
517 | if ( $_->[1] ) { | ||||||
518 | my $tooltip = $_->[4] ? ($_->[4]." [".$_->[2]."]") : $_->[2]; | ||||||
519 | my $img = $cgi->a({href=>$_->[0], title=>$tooltip}, $cgi->img({src=>$_->[1], alt=>$_->[4]||$_->[2], style=>"width: 5em; border: 0pt;"})); | ||||||
520 | push(@result, $cgi->li({id=>"$_->[3]".++$rowcnt}, $img, $cgi->a({href=>$_->[0]}, $_->[2]), ($_->[4] ? " [".$_->[4]."]" : ""))); | ||||||
521 | } | ||||||
522 | else { | ||||||
523 | push(@result, $cgi->li({id=>"$_->[3]".++$rowcnt}, $cgi->a({href=>$_->[0]}, $_->[2]), $_->[4] ? " [".$_->[4]."]" : ""))}; | ||||||
524 | }; | ||||||
525 | |||||||
526 | push(@result, ''); | ||||||
527 | |||||||
528 | if ( $server->{'formats'}->{'sources'} ) { | ||||||
529 | $sources->param(-name => 'format', -value=>"sources"); | ||||||
530 | push(@result, $cgi->p("[", $cgi->a({href=>($sources->url(-path_info=>1, -query=>1))}, "Details"), "]")); | ||||||
531 | }; | ||||||
532 | |||||||
533 | my($tu, $ts, $tcu, $tcs) = times(); | ||||||
534 | push(@result, sprintf("", $tu, $ts, $tu+$ts), $cgi->end_html()); | ||||||
535 | return join("\n", @result); | ||||||
536 | } | ||||||
537 | |||||||
538 | =head2 sources ( $server, $format, $extra, $query ) | ||||||
539 | |||||||
540 | Produces an HTML page with details to the queried identifier (description of sources) | ||||||
541 | |||||||
542 | This subroutine may be used as callback method in SeeAlso::Server (cf. description | ||||||
543 | of redirect above | ||||||
544 | |||||||
545 | =over 8 | ||||||
546 | |||||||
547 | =item $server | ||||||
548 | |||||||
549 | SeeAlso::Server object | ||||||
550 | |||||||
551 | |||||||
552 | =item $format | ||||||
553 | |||||||
554 | Format selected for $server | ||||||
555 | |||||||
556 | |||||||
557 | =item $extra | ||||||
558 | |||||||
559 | Hashref with the following configuration directives | ||||||
560 | |||||||
561 | css => URL of css file to be referenced | ||||||
562 | |||||||
563 | =item $query | ||||||
564 | |||||||
565 | Identifier to be queried | ||||||
566 | |||||||
567 | =back | ||||||
568 | |||||||
569 | =cut | ||||||
570 | |||||||
571 | sub sources { # Liste der Beacon-Header fuer Treffer | ||||||
572 | # We escape all characters except US-ASCII, because older CGI.pm's set an xml declaration | ||||||
573 | # which somehow interferes with IE8's adherence to the character set... | ||||||
574 | my ($self, $server, $format, $extra, $query) = @_; | ||||||
575 | my $formatprops = $server->{'formats'}->{$format} || {}; | ||||||
576 | my $cgi = $server->{'cgi'} || CGI->new(); | ||||||
577 | |||||||
578 | my ($hash, $pretty, $canon) = $self->prepare_query($query); | ||||||
579 | unless ( $hash ) { | ||||||
580 | print $cgi->header(-status => "400 Bad Request (Identifier '$query' not valid)", | ||||||
581 | -expires => "+1y", | ||||||
582 | -type => 'text/html', | ||||||
583 | ), | ||||||
584 | $cgi->start_html (-dtd => "-//W3C//DTD HTML 3.2 Final//EN", | ||||||
585 | -title => "No valid identifier", | ||||||
586 | ), | ||||||
587 | $cgi->p("Malformed identifier '$query'"), | ||||||
588 | $cgi->end_html; | ||||||
589 | return ""; | ||||||
590 | }; | ||||||
591 | |||||||
592 | my ($clusterid, %idlist); | ||||||
593 | my $c = $self->{identifierClass} || undef; | ||||||
594 | if ( $self->{cluster} ) { | ||||||
595 | my ($clusterh, $clusterexpl) = $self->stmtHdl("SELECT beacons.hash, beacons.altid FROM cluster.beacons WHERE beacons.hash=? OR beacons.altid=? LIMIT 1;"); | ||||||
596 | $self->stmtExplain($clusterexpl, $hash, $hash) if $ENV{'DBI_PROFILE'}; | ||||||
597 | $clusterh->execute($hash, $hash) or croak("Could not execute >".$clusterh->{Statement}."<: ".$clusterh->errstr); | ||||||
598 | while ( my $onerow = $clusterh->fetchrow_arrayref() ) { | ||||||
599 | $clusterid = $onerow->[1]; | ||||||
600 | my $h = $onerow->[0]; | ||||||
601 | if ( $c ) { | ||||||
602 | $c->value(""); | ||||||
603 | my $did = $c->hash($h) || $c->value($h); | ||||||
604 | my $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
605 | $idlist{$p} = ""; | ||||||
606 | } | ||||||
607 | else { | ||||||
608 | $idlist{$h} = ""; | ||||||
609 | } | ||||||
610 | }; | ||||||
611 | $idlist{$pretty} = "queriedid"; | ||||||
612 | if ( $clusterid ) { | ||||||
613 | if ( $clusterid eq $hash ) { | ||||||
614 | $idlist{$pretty} .= " preferredid"} | ||||||
615 | elsif ( $c ) { | ||||||
616 | $c->value(""); | ||||||
617 | my $did = $c->hash($clusterid) || $c->value($clusterid); | ||||||
618 | my $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
619 | $idlist{$p} = "variantid preferredid"; | ||||||
620 | } | ||||||
621 | else { | ||||||
622 | $idlist{$clusterid} = "variantid preferredid"; | ||||||
623 | }; | ||||||
624 | my ($varianth, $variantexpl) = $self->stmtHdl("SELECT beacons.hash FROM cluster.beacons WHERE beacons.altid=?;"); | ||||||
625 | $self->stmtExplain($variantexpl, $clusterid) if $ENV{'DBI_PROFILE'}; | ||||||
626 | $varianth->execute($clusterid) or croak("Could not execute >".$varianth->{Statement}."<: ".$varianth->errstr); | ||||||
627 | while ( my $onerow = $varianth->fetchrow_arrayref() ) { | ||||||
628 | my $v = $onerow->[0]; | ||||||
629 | if ( $c ) { | ||||||
630 | $c->value(""); | ||||||
631 | my $did = $c->hash($v) || $c->value($v); | ||||||
632 | my $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
633 | (exists $idlist{$p}) || ($idlist{$p} = "variantid"); | ||||||
634 | } | ||||||
635 | else { | ||||||
636 | (exists $idlist{$v}) || ($idlist{$v} = "variantid"); | ||||||
637 | } | ||||||
638 | } | ||||||
639 | } | ||||||
640 | } | ||||||
641 | |||||||
642 | my ($countsth, $countexpl); | ||||||
643 | if ( $clusterid ) { | ||||||
644 | ($countsth, $countexpl) = $self->stmtHdl(<<"XxX"); | ||||||
645 | SELECT COUNT(DISTINCT seqno) FROM beacons | ||||||
646 | WHERE ( (hash=?) OR (hash IN (SELECT beacons.hash FROM cluster.beacons WHERE cluster.beacons.altid=?)) ); | ||||||
647 | XxX | ||||||
648 | $self->stmtExplain($countexpl, $clusterid, $clusterid) if $ENV{'DBI_PROFILE'}; | ||||||
649 | $countsth->execute($clusterid, $clusterid) or croak("Could not execute >".$countsth->{Statement}."<: ".$countsth->errstr); | ||||||
650 | } | ||||||
651 | else { | ||||||
652 | ($countsth, $countexpl) = $self->stmtHdl(<<"XxX"); | ||||||
653 | SELECT COUNT(DISTINCT seqno) FROM beacons WHERE hash=?; | ||||||
654 | XxX | ||||||
655 | $self->stmtExplain($countexpl, $hash) if $ENV{'DBI_PROFILE'}; | ||||||
656 | $countsth->execute($hash) or croak("Could not execute >".$countsth->{Statement}."<: ".$countsth->errstr); | ||||||
657 | }; | ||||||
658 | my $hitsref = $countsth->fetchrow_arrayref; | ||||||
659 | my $hits = $hitsref->[0] || 0; | ||||||
660 | |||||||
661 | my ($osd, $beaconmeta) = $self->get_meta; | ||||||
662 | my $prefix = $beaconmeta->{'PREFIX'} || ""; | ||||||
663 | (my $servicename = $beaconmeta->{'NAME'} || $osd->{'ShortName'} || "") =~ s/([<>&"]|[^\x00-\x7f])/''.ord($1).';'/ge; | ||||||
664 | |||||||
665 | my $target = $cgi->url(-path=>1); | ||||||
666 | |||||||
667 | my @result; | ||||||
668 | push(@result, $cgi->start_html( | ||||||
669 | -encoding => "UTF-8", | ||||||
670 | -title => "$servicename referring ".$query->as_string(), | ||||||
671 | -meta => {'robots'=>'noindex'}, | ||||||
672 | ($extra->{'css'} ? (-style => {'src'=>$extra->{'css'}}) : ()), | ||||||
673 | -head => [$cgi->Link({-rel=>'unapi-server', | ||||||
674 | -type=>'application/xml', | ||||||
675 | title=>'unAPI', | ||||||
676 | -href=>$target}), | ||||||
677 | $cgi->Link({-rel=>'start', | ||||||
678 | -href=>$target}), | ||||||
679 | ], | ||||||
680 | )); | ||||||
681 | |||||||
682 | push(@result, ''); | ||||||
683 | push(@result, ''); | ||||||
684 | |||||||
685 | push(@result, $cgi->h1("$hits References for ".$cgi->abbr({class=>"unapi-id", title=>"$canon"}, $query))); | ||||||
686 | |||||||
687 | push(@result, ' '); |
||||||
688 | push(@result, $cgi->p($cgi->span("Identifier:"), $cgi->a({href=>"$prefix$pretty"}, "$prefix$pretty"))) if $prefix; | ||||||
689 | # delete $idlist{$pretty} if $prefix; | ||||||
690 | push(@result, $cgi->p($cgi->span("Variant Identifiers:"), map {$cgi->span({class=>($idlist{$_} || "variantid")}, $_)} sort keys %idlist)) if %idlist; | ||||||
691 | push(@result, ''); | ||||||
692 | |||||||
693 | my ($srcsth, $srcexpl); | ||||||
694 | if ( $clusterid ) { | ||||||
695 | ($srcsth, $srcexpl) = $self->stmtHdl(<<"XxX"); | ||||||
696 | SELECT beacons.*, repos.* | ||||||
697 | FROM beacons NATURAL LEFT JOIN repos | ||||||
698 | WHERE ( (beacons.hash=?) | ||||||
699 | OR (beacons.hash IN (SELECT beacons.hash FROM cluster.beacons WHERE cluster.beacons.altid=?)) ) | ||||||
700 | ORDER BY repos.sort, repos.alias; | ||||||
701 | XxX | ||||||
702 | $self->stmtExplain($srcexpl, $clusterid, $clusterid) if $ENV{'DBI_PROFILE'}; | ||||||
703 | $srcsth->execute($clusterid, $clusterid) or croak("Could not execute >".$srcsth->{Statement}."<: ".$srcsth->errstr); | ||||||
704 | } | ||||||
705 | else { | ||||||
706 | ($srcsth, $srcexpl) = $self->stmtHdl(<<"XxX"); | ||||||
707 | SELECT beacons.*, repos.* | ||||||
708 | FROM beacons NATURAL LEFT JOIN repos | ||||||
709 | WHERE beacons.hash=? | ||||||
710 | ORDER BY repos.sort, repos.alias; | ||||||
711 | XxX | ||||||
712 | $self->stmtExplain($srcexpl, $hash) if $ENV{'DBI_PROFILE'}; | ||||||
713 | $srcsth->execute($hash) or croak("Could not execute >".$srcsth->{Statement}."<: ".$srcsth->errstr); | ||||||
714 | } | ||||||
715 | |||||||
716 | my $rows = 0; | ||||||
717 | push(@result, ' '); |
||||||
718 | my ($lastseq, @groups) = (0, ()); | ||||||
719 | while ( my $onerow = $srcsth->fetchrow_hashref ) { | ||||||
720 | $rows ++; | ||||||
721 | if ( $lastseq and $onerow->{'seqno'} == $lastseq ) { | ||||||
722 | my %vary; | ||||||
723 | foreach my $key ( grep /^(hash|altid|hits|info|link)$/, keys %$onerow ) { | ||||||
724 | my $pval = $onerow->{$key}; | ||||||
725 | next unless defined $pval; | ||||||
726 | $pval =~ s/([<>&"]|[^\x00-\x7f])/''.ord($1).';'/ge if $key eq "link"; | ||||||
727 | $vary{$key} = $pval; | ||||||
728 | } | ||||||
729 | push(@{$groups[$#groups]}, \%vary); | ||||||
730 | } | ||||||
731 | else { | ||||||
732 | my (%vary, %repos, %meta); | ||||||
733 | while ( my($key, $val) = each %$onerow ) { | ||||||
734 | my $pval = $val; | ||||||
735 | unless ( $key =~ /altid|feed|target|uri|link/i ) { | ||||||
736 | $pval =~ s/([<>&"]|[^\x00-\x7f])/''.ord($1).';'/ge if defined $pval}; | ||||||
737 | if ( $key =~ /time|revisit/i ) { | ||||||
738 | next unless $val; | ||||||
739 | $pval = HTTP::Date::time2str($val); | ||||||
740 | }; | ||||||
741 | if ( $key =~ /^bc(\w+)$/ ) { | ||||||
742 | $repos{$1} = $pval if $pval} | ||||||
743 | elsif ( $key =~ /^(hash|altid|hits|info|link)$/ ) { | ||||||
744 | $vary{$key} = $pval} | ||||||
745 | else { | ||||||
746 | $meta{"_$key"} = $pval if $pval} | ||||||
747 | }; | ||||||
748 | push(@groups, [\%repos, \%meta, \%vary]); | ||||||
749 | }; | ||||||
750 | $lastseq = $onerow->{'seqno'}; | ||||||
751 | }; | ||||||
752 | # Grouping done, now display... | ||||||
753 | |||||||
754 | my %didalreadysee; | ||||||
755 | foreach my $groupref ( @groups ) { | ||||||
756 | my ($repos, $meta, @vary) = @$groupref; | ||||||
757 | |||||||
758 | my $aos = $meta->{'_alias'} || $meta->{'_seqno'}; | ||||||
759 | |||||||
760 | my $multi = (scalar @vary > 1) ? "multi" : "single"; | ||||||
761 | push(@result, qq! !); |
||||||
762 | push(@result, $cgi->h3({class=>"aggregator", onClick=>"toggle('ag$aos')"}, "Administrative Details")); | ||||||
763 | |||||||
764 | push(@result, $cgi->h3({class=>"beacon", onClick=>"toggle('bc$aos')"}, "Repository Details")); | ||||||
765 | |||||||
766 | if ( $multi eq "single" ) { | ||||||
767 | push(@result, $cgi->h3({class=>"hit", onClick=>"toggle('ht$aos')"}, "Result Details")); | ||||||
768 | |||||||
769 | my $vary = $vary[0]; | ||||||
770 | |||||||
771 | my $hits = $vary->{'hits'}; | ||||||
772 | my $description = $hits; | ||||||
773 | |||||||
774 | my $h = $vary->{'hash'}; | ||||||
775 | my $variantid = ($h eq $hash) ? "" : "variantid"; | ||||||
776 | my $p; | ||||||
777 | if ( $h eq $hash ) { | ||||||
778 | $p = $pretty} | ||||||
779 | elsif ( $clusterid && ref($c) ) { | ||||||
780 | $c->value(""); | ||||||
781 | my $did = $c->hash($h) || $c->value($h) || $h; | ||||||
782 | $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
783 | }; | ||||||
784 | $p = ($clusterid ? $h : $pretty) unless defined $p; | ||||||
785 | |||||||
786 | my $uri = "???"; | ||||||
787 | if ( $uri = $vary->{'link'} ) { # o.k. | ||||||
788 | } | ||||||
789 | elsif ( $repos->{'ALTTARGET'} && $vary->{'altid'} ) { | ||||||
790 | $uri = sprintf($repos->{'ALTTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($vary->{'altid'}))} | ||||||
791 | elsif ( $repos->{'TARGET'} ) { | ||||||
792 | $uri = sprintf($repos->{'TARGET'}, $p)} | ||||||
793 | elsif ( $repos->{'ALTTARGET'} ) { | ||||||
794 | $uri = sprintf($repos->{'ALTTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($p))}; | ||||||
795 | |||||||
796 | my $redundant = ($didalreadysee{$uri}++) ? "subsequent" : ""; | ||||||
797 | |||||||
798 | my $guri = ""; | ||||||
799 | if ( $repos->{'IMGTARGET'} ) { | ||||||
800 | $guri = sprintf($repos->{'IMGTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($vary->{'altid'}))} | ||||||
801 | |||||||
802 | my @labels = grep /\S/, $repos->{'NAME'} || "", $repos->{'DESCRIPTION'} || "", $repos->{'INSTITUTION'} || ""; | ||||||
803 | my $rlabel; | ||||||
804 | if ( $hits == 1 ) { | ||||||
805 | $rlabel = $repos->{'ONEMESSAGE'} if $repos->{'ONEMESSAGE'}} | ||||||
806 | elsif ( $hits == 0 ) { | ||||||
807 | $rlabel = $repos->{'SOMEMESSAGE'} if $repos->{'SOMEMESSAGE'}}; | ||||||
808 | unless ( $rlabel ) { | ||||||
809 | $rlabel = $repos->{'MESSAGE'} || shift @labels || "???"}; | ||||||
810 | my $label = sprintf($rlabel, $hits); | ||||||
811 | |||||||
812 | my $ttip = pop @labels || ""; | ||||||
813 | $ttip =~ s/(\d+);/chr($1)/ge; | ||||||
814 | |||||||
815 | push(@result, $cgi->a({style=>"float: right; clear: right;", href=>$uri}, $cgi->img({alt=>$vary->{'info'}||$label,src=>$guri}))) if $guri; | ||||||
816 | |||||||
817 | push(@result, $cgi->h2({class=>"label $redundant $variantid ident_$p", id=>"head$aos"}, $cgi->a({href=>$uri, title=>$ttip}, $label))); | ||||||
818 | |||||||
819 | push(@result, qq! !); |
||||||
820 | push(@result, $cgi->span($vary->{'info'})) if $vary->{'info'}; | ||||||
821 | push(@result, $cgi->span("($hits Treffer)")) if $hits && ($rlabel !~ /%s/); | ||||||
822 | push(@result, ''); | ||||||
823 | |||||||
824 | push(@result, qq! | ||||||
825 | push(@result, $cgi->p({class=>"ht_uri"}, $cgi->span("Target URL:"), $cgi->a({href=>$uri}, CGI::escapeHTML($uri)))); | ||||||
826 | push(@result, $cgi->p({class=>"ht_guri"}, $cgi->span("Preview URL:"), $cgi->a({href=>$guri}, $guri))) if $guri; | ||||||
827 | push(@result, $cgi->p({class=>"ht_hits"}, $cgi->span("Hits:"), $hits)) if $hits; | ||||||
828 | push(@result, $cgi->p({class=>"ht_info"}, $cgi->span("Additional Info:"), $vary->{'info'})) if $vary->{'info'}; | ||||||
829 | push(@result, $cgi->p({class=>"ht_idnote"}, $cgi->span("Variant Identifier:"), $p)) if $variantid; | ||||||
830 | push(@result, ''); | ||||||
831 | } | ||||||
832 | else { | ||||||
833 | push(@result, $cgi->h3({class=>"hit", onClick=>"mtoggle('res$aos', 'hit')"}, "Result Details")); | ||||||
834 | my $hits = scalar @vary; | ||||||
835 | |||||||
836 | my @labels = grep /\S/, $repos->{'NAME'} || "", $repos->{'DESCRIPTION'} || "", $repos->{'INSTITUTION'} || ""; | ||||||
837 | my $rlabel = $repos->{'MESSAGE'} || shift @labels || "???"; | ||||||
838 | my $ttip = pop @labels || ""; | ||||||
839 | $ttip =~ s/(\d+);/chr($1)/ge; | ||||||
840 | |||||||
841 | # my $rlabel = $repos->{'SOMEMESSAGE'} || $repos->{'MESSAGE'} || $repos->{'DESCRIPTION'} || $repos->{'NAME'} || $repos->{'INSTITUTION'} || "???"; | ||||||
842 | |||||||
843 | # my $ttip = $repos->{'MESSAGE'} ? $repos->{'DESCRIPTION'} || $repos->{'NAME'} || $repos->{'INSTITUTION'} || "" | ||||||
844 | |||||||
845 | # : $repos->{'INSTITUTION'} || $repos->{'NAME'} || ""; | ||||||
846 | |||||||
847 | # $ttip = "" if $ttip eq $rlabel; | ||||||
848 | $ttip =~ s/(\d+);/chr($1)/ge; | ||||||
849 | |||||||
850 | my $label = sprintf($rlabel, $hits); | ||||||
851 | push(@result, $cgi->h2({class=>"label", id=>"head$aos"}, $label)); | ||||||
852 | |||||||
853 | push(@result, qq!
|
||||||
854 | my $cnt = 0; | ||||||
855 | foreach my $vary ( @vary ) { | ||||||
856 | $cnt ++; | ||||||
857 | |||||||
858 | my $h = $vary->{'hash'}; | ||||||
859 | my $variantid = ($h eq $hash) ? "" : "variantid"; | ||||||
860 | my $p; | ||||||
861 | if ( $h eq $hash ) { | ||||||
862 | $p = $pretty} | ||||||
863 | elsif ( $clusterid && ref($c) ) { | ||||||
864 | $c->value(""); | ||||||
865 | my $did = $c->hash($h) || $c->value($h) || $h; | ||||||
866 | $p = $c->can("pretty") ? $c->pretty() : $c->value(); | ||||||
867 | }; | ||||||
868 | $p = ($clusterid ? $h : $pretty) unless defined $p; | ||||||
869 | |||||||
870 | my $uri = "???"; | ||||||
871 | if ( $uri = $vary->{'link'} ) { # o.k. | ||||||
872 | } | ||||||
873 | elsif ( $repos->{'ALTTARGET'} && $vary->{'altid'} ) { | ||||||
874 | $uri = sprintf($repos->{'ALTTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($vary->{'altid'}))} | ||||||
875 | elsif ( $repos->{'TARGET'} ) { | ||||||
876 | $uri = sprintf($repos->{'TARGET'}, $p)} | ||||||
877 | elsif ( $repos->{'ALTTARGET'} ) { | ||||||
878 | $uri = sprintf($repos->{'ALTTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($p))}; | ||||||
879 | |||||||
880 | my $redundant = ($didalreadysee{$uri}++) ? "subsequent" : ""; | ||||||
881 | |||||||
882 | my $guri = ""; | ||||||
883 | if ( $repos->{'IMGTARGET'} ) { | ||||||
884 | $guri = sprintf($repos->{'IMGTARGET'}, $p, SeeAlso::Source::BeaconAggregator::urlpseudoescape($vary->{'altid'}))} | ||||||
885 | |||||||
886 | my $hits = $vary->{hits} if $vary->{hits} and $vary->{hits} != 1; | ||||||
887 | |||||||
888 | push(@result, qq! |
||||||
889 | push(@result, $cgi->div({style=>"float: right;"}, $cgi->a({href=>$uri}, $cgi->img({src=>$guri})))) if $guri; | ||||||
890 | push(@result, $cgi->a({href=>$uri}, $cgi->span($vary->{'info'} || "[$cnt.]"))); | ||||||
891 | push(@result, $cgi->span("($hits Treffer)")) if $hits; | ||||||
892 | push(@result, ''); | ||||||
893 | |||||||
894 | push(@result, qq! | ||||||
895 | push(@result, $cgi->p({class=>"ht_uri"}, $cgi->span("Target URL:"), $cgi->a({href=>$uri}, $uri))); | ||||||
896 | push(@result, $cgi->p({class=>"ht_guri"}, $cgi->span("Preview URL:"), $cgi->a({href=>$guri}, $guri))) if $guri; | ||||||
897 | push(@result, $cgi->p({class=>"ht_hits"}, $cgi->span("Hits:"), $vary->{hits})) if $vary->{hits}; | ||||||
898 | push(@result, $cgi->p({class=>"ht_info"}, $cgi->span("Additional Info:"), $vary->{'info'})) if $vary->{'info'}; | ||||||
899 | push(@result, $cgi->p({class=>"ht_idnote"}, $cgi->span("Variant Identifier:"), $p)) if $variantid; | ||||||
900 | |||||||
901 | push(@result, ''); | ||||||
902 | push(@result, ''); | ||||||
903 | }; | ||||||
904 | push(@result, qq!!); | ||||||
905 | } | ||||||
906 | |||||||
907 | push(@result, qq! | ||||||
908 | foreach ( sort keys %$repos ) { | ||||||
909 | next if /(MESSAGE|TARGET)$/; | ||||||
910 | next unless $repos->{$_}; | ||||||
911 | $repos->{$_} =~ s!([a-z]+://\S+)!$cgi->a({href=>"$1", target=>"_blank"}, "$1")!ge; # URL | ||||||
912 | $repos->{$_} =~ s!(?:\<\s*)?(\w[\w.-]*)\@((?:\w[\w-]*\.)+\w+)(?:\s*\>)?!<$1 (at) $2>!g; # Mail Addr | ||||||
913 | $repos->{$_} =~ s/\s*\|\s*/ | /g; # Examples | ||||||
914 | next if /^(FORMAT|PREFIX|REVISIT|VERSION)$/; # Postpone to "administrative Details" | ||||||
915 | push(@result, $cgi->p({class=>"bc_$_"}, $cgi->span("#$_:"), $repos->{$_})); | ||||||
916 | }; | ||||||
917 | push(@result, $cgi->p({class=>"ag_mtime"}, $cgi->span("Modified:"), $meta->{'_mtime'})) if $meta->{'_mtime'}; | ||||||
918 | push(@result, ''); | ||||||
919 | |||||||
920 | push(@result, qq! | ||||||
921 | foreach ( sort keys %$repos ) { | ||||||
922 | next unless /^(FORMAT|PREFIX|REVISIT|VERSION)$/; | ||||||
923 | next unless $repos->{$_}; | ||||||
924 | push(@result, $cgi->p({class=>"bc_$_"}, $cgi->span("#$_:"), $repos->{$_})); | ||||||
925 | }; | ||||||
926 | push(@result, $cgi->p({class=>"ag_ftime"}, $cgi->span("Loaded:"), $meta->{'_ftime'})) if $meta->{'_ftime'}; | ||||||
927 | push(@result, $cgi->p({class=>"ag_fstat"}, $cgi->span("Load status:"), $meta->{'_fstat'})) if $meta->{'_fstat'}; | ||||||
928 | push(@result, $cgi->p({class=>"ag_utime"}, $cgi->span("Update attempt:"), $meta->{'_utime'})) if $meta->{'_utime'}; | ||||||
929 | push(@result, $cgi->p({class=>"ag_ustat"}, $cgi->span("Update status:"), $meta->{'_ustat'})) if $meta->{'_ustat'}; | ||||||
930 | push(@result, $cgi->p({class=>"ag_counti"}, $cgi->span("Identifiers:"), $meta->{'_counti'})) if $meta->{'_counti'}; | ||||||
931 | push(@result, $cgi->p({class=>"ag_countu"}, $cgi->span("Distinct Ids:"), $meta->{'_countu'})) if $meta->{'_countu'}; | ||||||
932 | push(@result, $cgi->p({class=>"ag_sort"}, $cgi->span("Sort key:"), $meta->{'_sort'})) if $meta->{'_sort'}; | ||||||
933 | push(@result, $cgi->p({class=>"ag_admin"}, $cgi->span("Remark:"), $meta->{'_admin'})) if $meta->{'_admin'}; | ||||||
934 | push(@result, ''); | ||||||
935 | |||||||
936 | push(@result, ''); | ||||||
937 | |||||||
938 | push(@result, ''); | ||||||
939 | }; | ||||||
940 | push(@result, ''); | ||||||
941 | |||||||
942 | push(@result, ' '); |
||||||
943 | # $cgi->span("provided by:"), | ||||||
944 | push(@result, $cgi->p({class=>"mt_NAME"}, $cgi->a({href=>$target}, $servicename))); | ||||||
945 | # $cgi->span("Service description:"), | ||||||
946 | (my $descr = $beaconmeta->{'DESCRIPTION'} || $osd->{'Description'} || "") =~ s/([<>&"]|[^\x00-\x7f])/''.ord($1).';'/ge; | ||||||
947 | push(@result, $cgi->p({class=>"mt_DESCRIPTION"}, $descr)); | ||||||
948 | push(@result, ''); | ||||||
949 | |||||||
950 | my($tu, $ts, $tcu, $tcs) = times(); | ||||||
951 | push(@result, sprintf("", $tu, $ts, $tu+$ts), $cgi->end_html()); | ||||||
952 | return join("\n", @result); | ||||||
953 | } | ||||||
954 | |||||||
955 | |||||||
956 | =head2 get_meta () | ||||||
957 | |||||||
958 | Returns a pair of hash references: | ||||||
959 | |||||||
960 | =over 8 | ||||||
961 | |||||||
962 | =item 1 | ||||||
963 | |||||||
964 | OSD fields | ||||||
965 | |||||||
966 | =item 2 | ||||||
967 | |||||||
968 | Beacon header fields | ||||||
969 | |||||||
970 | =back | ||||||
971 | |||||||
972 | =cut | ||||||
973 | |||||||
974 | sub get_meta { | ||||||
975 | my ($self) = @_; | ||||||
976 | |||||||
977 | my ($metasth, $metaexpl) = $self->stmtHdl(<<"XxX"); | ||||||
978 | SELECT key, val FROM osd; | ||||||
979 | XxX | ||||||
980 | $self->stmtExplain($metaexpl) if $ENV{'DBI_PROFILE'}; | ||||||
981 | $metasth->execute() or croak("Could not execute >".$metasth->{Statement}."<: ".$metasth->errstr); | ||||||
982 | my (%osd, %beaconmeta); | ||||||
983 | while ( my $aryref = $metasth->fetchrow_arrayref ) { | ||||||
984 | my ($key, $val) = @$aryref; | ||||||
985 | next unless $val; | ||||||
986 | if ($key =~ s/^bc// ) { # BeaconMeta Fields | ||||||
987 | $beaconmeta{$key} = $val} | ||||||
988 | elsif ( exists $osd{$key} ) { | ||||||
989 | if ( ref($osd{$key}) ) { | ||||||
990 | push(@{$osd{$key}}, $val)} | ||||||
991 | else { | ||||||
992 | $osd{$key} = [$osd{$key}, $val]}; | ||||||
993 | } | ||||||
994 | else { | ||||||
995 | $osd{$key} = $val}; | ||||||
996 | }; | ||||||
997 | return (\%osd, \%beaconmeta); | ||||||
998 | } | ||||||
999 | |||||||
1000 | =head1 AUTHOR | ||||||
1001 | |||||||
1002 | Thomas Berger | ||||||
1003 | CPAN ID: THB | ||||||
1004 | gymel.com | ||||||
1005 | THB@cpan.org | ||||||
1006 | |||||||
1007 | =head1 COPYRIGHT | ||||||
1008 | |||||||
1009 | This program is free software; you can redistribute | ||||||
1010 | it and/or modify it under the same terms as Perl itself. | ||||||
1011 | |||||||
1012 | The full text of the license can be found in the | ||||||
1013 | LICENSE file included with this module. | ||||||
1014 | |||||||
1015 | |||||||
1016 | =cut | ||||||
1017 | |||||||
1018 | 1; | ||||||
1019 | # The preceding line will help the module return a true value | ||||||
1020 |