| File: | C4/XSLT.pm | 
| Coverage: | 23.3% | 
| line | stmt | bran | cond | sub | time | code | 
|---|---|---|---|---|---|---|
| 1 | package C4::XSLT; | |||||
| 2 | # Copyright (C) 2006 LibLime | |||||
| 3 | # <jmf at liblime dot com> | |||||
| 4 | # Parts Copyright Katrin Fischer 2011 | |||||
| 5 | # Parts Copyright ByWater Solutions 2011 | |||||
| 6 | # | |||||
| 7 | # This file is part of Koha. | |||||
| 8 | # | |||||
| 9 | # Koha is free software; you can redistribute it and/or modify it under the | |||||
| 10 | # terms of the GNU General Public License as published by the Free Software | |||||
| 11 | # Foundation; either version 2 of the License, or (at your option) any later | |||||
| 12 | # version. | |||||
| 13 | # | |||||
| 14 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | |||||
| 15 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||||
| 16 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||||
| 17 | # | |||||
| 18 | # You should have received a copy of the GNU General Public License along | |||||
| 19 | # with Koha; if not, write to the Free Software Foundation, Inc., | |||||
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
| 21 | ||||||
| 22 | 14 14 14 | 437 101 369 | use strict; | |||
| 23 | 14 14 14 | 114 51 743 | use warnings; | |||
| 24 | ||||||
| 25 | 14 14 14 | 361 50 220 | use C4::Context; | |||
| 26 | 14 14 14 | 1210 79 3431 | use C4::Branch; | |||
| 27 | 14 14 14 | 237 51 1109 | use C4::Items; | |||
| 28 | 14 14 14 | 91 42 5702 | use C4::Koha; | |||
| 29 | 14 14 14 | 104 37 7915 | use C4::Biblio; | |||
| 30 | 14 14 14 | 95 26 3002 | use C4::Circulation; | |||
| 31 | 14 14 14 | 109 29 1814 | use C4::Reserves; | |||
| 32 | 14 14 14 | 70 76 1398 | use Encode; | |||
| 33 | 14 14 14 | 130 94 391 | use XML::LibXML; | |||
| 34 | 14 14 14 | 31179 86402 805 | use XML::LibXSLT; | |||
| 35 | ||||||
| 36 | 14 14 14 | 192 110 1619 | use vars qw($VERSION @ISA @EXPORT); | |||
| 37 | ||||||
| 38 | BEGIN { | |||||
| 39 | 14 | 199 | require Exporter; | |||
| 40 | 14 | 104 | $VERSION = 0.03; | |||
| 41 | 14 | 256 | @ISA = qw(Exporter); | |||
| 42 | 14 | 22900 | @EXPORT = qw( | |||
| 43 | &XSLTParse4Display | |||||
| 44 | ); | |||||
| 45 | } | |||||
| 46 | ||||||
| 47 - 57 | =head1 NAME C4::XSLT - Functions for displaying XSLT-generated content =head1 FUNCTIONS =head2 transformMARCXML4XSLT Replaces codes with authorized values in a MARC::Record object =cut | |||||
| 58 | ||||||
| 59 | sub transformMARCXML4XSLT { | |||||
| 60 | 0 | my ($biblionumber, $record) = @_; | ||||
| 61 | 0 | my $frameworkcode = GetFrameworkCode($biblionumber) || ''; | ||||
| 62 | 0 | my $tagslib = &GetMarcStructure(1,$frameworkcode); | ||||
| 63 | 0 | my @fields; | ||||
| 64 | # FIXME: wish there was a better way to handle exceptions | |||||
| 65 | 0 | eval { | ||||
| 66 | 0 | @fields = $record->fields(); | ||||
| 67 | }; | |||||
| 68 | 0 0 0 | if ($@) { warn "PROBLEM WITH RECORD"; next; } | ||||
| 69 | 0 | my $av = getAuthorisedValues4MARCSubfields($frameworkcode); | ||||
| 70 | 0 | foreach my $tag ( keys %$av ) { | ||||
| 71 | 0 | foreach my $field ( $record->field( $tag ) ) { | ||||
| 72 | 0 | if ( $av->{ $tag } ) { | ||||
| 73 | 0 | my @new_subfields = (); | ||||
| 74 | 0 | for my $subfield ( $field->subfields() ) { | ||||
| 75 | 0 | my ( $letter, $value ) = @$subfield; | ||||
| 76 | 0 | $value = GetAuthorisedValueDesc( $tag, $letter, $value, '', $tagslib ) | ||||
| 77 | if $av->{ $tag }->{ $letter }; | |||||
| 78 | 0 | push( @new_subfields, $letter, $value ); | ||||
| 79 | } | |||||
| 80 | 0 | $field ->replace_with( MARC::Field->new( | ||||
| 81 | $tag, | |||||
| 82 | $field->indicator(1), | |||||
| 83 | $field->indicator(2), | |||||
| 84 | @new_subfields | |||||
| 85 | ) ); | |||||
| 86 | } | |||||
| 87 | } | |||||
| 88 | } | |||||
| 89 | 0 | return $record; | ||||
| 90 | } | |||||
| 91 | ||||||
| 92 - 96 | =head2 getAuthorisedValues4MARCSubfields Returns a ref of hash of ref of hash for tag -> letter controled by authorised values =cut | |||||
| 97 | ||||||
| 98 | # Cache for tagfield-tagsubfield to decode per framework. | |||||
| 99 | # Should be preferably be placed in Koha-core... | |||||
| 100 | my %authval_per_framework; | |||||
| 101 | ||||||
| 102 | sub getAuthorisedValues4MARCSubfields { | |||||
| 103 | 0 | my ($frameworkcode) = @_; | ||||
| 104 | 0 | unless ( $authval_per_framework{ $frameworkcode } ) { | ||||
| 105 | 0 | my $dbh = C4::Context->dbh; | ||||
| 106 | 0 | my $sth = $dbh->prepare("SELECT DISTINCT tagfield, tagsubfield | ||||
| 107 | FROM marc_subfield_structure | |||||
| 108 | WHERE authorised_value IS NOT NULL | |||||
| 109 | AND authorised_value!='' | |||||
| 110 | AND frameworkcode=?"); | |||||
| 111 | 0 | $sth->execute( $frameworkcode ); | ||||
| 112 | 0 | my $av = { }; | ||||
| 113 | 0 | while ( my ( $tag, $letter ) = $sth->fetchrow() ) { | ||||
| 114 | 0 | $av->{ $tag }->{ $letter } = 1; | ||||
| 115 | } | |||||
| 116 | 0 | $authval_per_framework{ $frameworkcode } = $av; | ||||
| 117 | } | |||||
| 118 | 0 | return $authval_per_framework{ $frameworkcode }; | ||||
| 119 | } | |||||
| 120 | ||||||
| 121 | my $stylesheet; | |||||
| 122 | ||||||
| 123 | sub XSLTParse4Display { | |||||
| 124 | 0 | my ( $biblionumber, $orig_record, $xsl_suffix, $interface, $fixamps, $hidden_items ) = @_; | ||||
| 125 | 0 | $interface = 'opac' unless $interface; | ||||
| 126 | # grab the XML, run it through our stylesheet, push it out to the browser | |||||
| 127 | 0 | my $record = transformMARCXML4XSLT($biblionumber, $orig_record); | ||||
| 128 | #return $record->as_formatted(); | |||||
| 129 | 0 | my $itemsxml = buildKohaItemsNamespace($biblionumber, $hidden_items); | ||||
| 130 | 0 | my $xmlrecord = $record->as_xml(C4::Context->preference('marcflavour')); | ||||
| 131 | 0 | my $sysxml = "<sysprefs>\n"; | ||||
| 132 | 0 | foreach my $syspref ( qw/ hidelostitems OPACURLOpenInNewWindow | ||||
| 133 | DisplayOPACiconsXSLT URLLinkText viewISBD | |||||
| 134 | OPACBaseURL TraceCompleteSubfields UseICU | |||||
| 135 | UseAuthoritiesForTracings TraceSubjectSubdivisions | |||||
| 136 | Display856uAsImage OPACDisplay856uAsImage | |||||
| 137 | UseControlNumber | |||||
| 138 | AlternateHoldingsField AlternateHoldingsSeparator / ) | |||||
| 139 | { | |||||
| 140 | 0 | my $sp = C4::Context->preference( $syspref ); | ||||
| 141 | 0 | next unless defined($sp); | ||||
| 142 | 0 | $sysxml .= "<syspref name=\"$syspref\">$sp</syspref>\n"; | ||||
| 143 | } | |||||
| 144 | 0 | $sysxml .= "</sysprefs>\n"; | ||||
| 145 | 0 | $xmlrecord =~ s/\<\/record\>/$itemsxml$sysxml\<\/record\>/; | ||||
| 146 | 0 | if ($fixamps) { # We need to correct the ampersand entities that Zebra outputs | ||||
| 147 | 0 | $xmlrecord =~ s/\&amp;/\&/g; | ||||
| 148 | } | |||||
| 149 | 0 | $xmlrecord =~ s/\& /\&\; /; | ||||
| 150 | 0 | $xmlrecord =~ s/\&\;amp\; /\&\; /; | ||||
| 151 | ||||||
| 152 | 0 | my $parser = XML::LibXML->new(); | ||||
| 153 | # don't die when you find &, >, etc | |||||
| 154 | 0 | $parser->recover_silently(0); | ||||
| 155 | 0 | my $source = $parser->parse_string($xmlrecord); | ||||
| 156 | 0 | unless ( $stylesheet ) { | ||||
| 157 | 0 | my $xslt = XML::LibXSLT->new(); | ||||
| 158 | 0 | my $xslfile; | ||||
| 159 | 0 | if ($interface eq 'intranet') { | ||||
| 160 | 0 | $xslfile = C4::Context->config('intrahtdocs') . | ||||
| 161 | '/' . C4::Context->preference("template") . | |||||
| 162 | '/' . C4::Templates::_current_language() . | |||||
| 163 | '/xslt/' . | |||||
| 164 | C4::Context->preference('marcflavour') . | |||||
| 165 | "slim2intranet$xsl_suffix.xsl"; | |||||
| 166 | } else { | |||||
| 167 | 0 | $xslfile = C4::Context->config('opachtdocs') . | ||||
| 168 | '/' . C4::Context->preference("opacthemes") . | |||||
| 169 | '/' . C4::Templates::_current_language() . | |||||
| 170 | '/xslt/' . | |||||
| 171 | C4::Context->preference('marcflavour') . | |||||
| 172 | "slim2OPAC$xsl_suffix.xsl"; | |||||
| 173 | } | |||||
| 174 | 0 | my $style_doc = $parser->parse_file($xslfile); | ||||
| 175 | 0 | $stylesheet = $xslt->parse_stylesheet($style_doc); | ||||
| 176 | } | |||||
| 177 | 0 | my $results = $stylesheet->transform($source); | ||||
| 178 | 0 | my $newxmlrecord = $stylesheet->output_string($results); | ||||
| 179 | 0 | return $newxmlrecord; | ||||
| 180 | } | |||||
| 181 | ||||||
| 182 | sub buildKohaItemsNamespace { | |||||
| 183 | 0 | my ($biblionumber, $hidden_items) = @_; | ||||
| 184 | ||||||
| 185 | 0 | my @items = C4::Items::GetItemsInfo($biblionumber); | ||||
| 186 | 0 | if ($hidden_items && @$hidden_items) { | ||||
| 187 | 0 0 | my %hi = map {$_ => 1} @$hidden_items; | ||||
| 188 | 0 0 | @items = grep { !$hi{$_->{itemnumber}} } @items; | ||||
| 189 | } | |||||
| 190 | 0 | my $branches = GetBranches(); | ||||
| 191 | 0 | my $itemtypes = GetItemTypes(); | ||||
| 192 | 0 | my $xml = ''; | ||||
| 193 | 0 | for my $item (@items) { | ||||
| 194 | 0 | my $status; | ||||
| 195 | ||||||
| 196 | 0 | my ( $transfertwhen, $transfertfrom, $transfertto ) = C4::Circulation::GetTransfers($item->{itemnumber}); | ||||
| 197 | ||||||
| 198 | 0 | my ( $reservestatus, $reserveitem, undef ) = C4::Reserves::CheckReserves($item->{itemnumber}); | ||||
| 199 | ||||||
| 200 | 0 | if ( $itemtypes->{ $item->{itype} }->{notforloan} || $item->{notforloan} || $item->{onloan} || $item->{wthdrawn} || $item->{itemlost} || $item->{damaged} || | ||||
| 201 | (defined $transfertwhen && $transfertwhen ne '') || $item->{itemnotforloan} || (defined $reservestatus && $reservestatus eq "Waiting") ){ | |||||
| 202 | 0 | if ( $item->{notforloan} < 0) { | ||||
| 203 | 0 | $status = "On order"; | ||||
| 204 | } | |||||
| 205 | 0 | if ( $item->{itemnotforloan} > 0 || $item->{notforloan} > 0 || $itemtypes->{ $item->{itype} }->{notforloan} == 1 ) { | ||||
| 206 | 0 | $status = "reference"; | ||||
| 207 | } | |||||
| 208 | 0 | if ($item->{onloan}) { | ||||
| 209 | 0 | $status = "Checked out"; | ||||
| 210 | } | |||||
| 211 | 0 | if ( $item->{wthdrawn}) { | ||||
| 212 | 0 | $status = "Withdrawn"; | ||||
| 213 | } | |||||
| 214 | 0 | if ($item->{itemlost}) { | ||||
| 215 | 0 | $status = "Lost"; | ||||
| 216 | } | |||||
| 217 | 0 | if ($item->{damaged}) { | ||||
| 218 | 0 | $status = "Damaged"; | ||||
| 219 | } | |||||
| 220 | 0 | if (defined $transfertwhen && $transfertwhen ne '') { | ||||
| 221 | 0 | $status = 'In transit'; | ||||
| 222 | } | |||||
| 223 | 0 | if (defined $reservestatus && $reservestatus eq "Waiting") { | ||||
| 224 | 0 | $status = 'Waiting'; | ||||
| 225 | } | |||||
| 226 | } else { | |||||
| 227 | 0 | $status = "available"; | ||||
| 228 | } | |||||
| 229 | 0 | my $homebranch = $item->{homebranch}? xml_escape($branches->{$item->{homebranch}}->{'branchname'}):''; | ||||
| 230 | 0 | my $itemcallnumber = xml_escape($item->{itemcallnumber}); | ||||
| 231 | 0 | $xml.= "<item><homebranch>$homebranch</homebranch>". | ||||
| 232 | "<status>$status</status>". | |||||
| 233 | "<itemcallnumber>".$itemcallnumber."</itemcallnumber>" | |||||
| 234 | . "</item>"; | |||||
| 235 | ||||||
| 236 | } | |||||
| 237 | 0 | $xml = "<items xmlns=\"http://www.koha-community.org/items\">".$xml."</items>"; | ||||
| 238 | 0 | return $xml; | ||||
| 239 | } | |||||
| 240 | ||||||
| 241 | ||||||
| 242 | ||||||
| 243 | 1; | |||||