View | Details | Raw Unified | Return to bug 10486
Collapse All | Expand All

(-)a/C4/Search.pm (+30 lines)
Lines 72-77 This module provides searching functions for Koha's bibliographic databases Link Here
72
  &GetDistinctValues
72
  &GetDistinctValues
73
  &enabled_staff_search_views
73
  &enabled_staff_search_views
74
  &PurgeSearchHistory
74
  &PurgeSearchHistory
75
  &GetExternalSearchTargets
75
);
76
);
76
77
77
# make all your functions, whether exported or not;
78
# make all your functions, whether exported or not;
Lines 2379-2384 sub _ZOOM_event_loop { Link Here
2379
    }
2380
    }
2380
}
2381
}
2381
2382
2383
=head2 GetExternalSearchTargets
2384
2385
Returns the list of Z39.50 servers that are marked for search in the OPAC using
2386
Pazpar2.
2387
2388
=cut
2389
2390
sub GetExternalSearchTargets {
2391
    my ( $branchcode ) = @_;
2392
2393
    if ( $branchcode ) {
2394
        return C4::Context->dbh->selectall_arrayref( q{
2395
            SELECT * FROM external_targets et
2396
            LEFT JOIN external_target_restrictions etr
2397
                ON (etr.target_id = et.target_id and etr.branchcode = ?)
2398
            WHERE etr.target_id IS NULL
2399
            ORDER BY et.name
2400
        }, { Slice => {} }, $branchcode );
2401
    } else {
2402
        return C4::Context->dbh->selectall_arrayref( q{
2403
            SELECT * FROM external_targets et
2404
            LEFT JOIN external_target_restrictions etr USING (target_id)
2405
            GROUP by et.target_id
2406
            HAVING branchcode IS NULL
2407
            ORDER BY et.name
2408
        }, { Slice => {} } );
2409
    }
2410
}
2411
2382
2412
2383
END { }    # module clean-up code here (global destructor)
2413
END { }    # module clean-up code here (global destructor)
2384
2414
(-)a/C4/XSLT.pm (-7 / +17 lines)
Lines 42-47 BEGIN { Link Here
42
    $VERSION = 3.07.00.049;
42
    $VERSION = 3.07.00.049;
43
    @ISA = qw(Exporter);
43
    @ISA = qw(Exporter);
44
    @EXPORT = qw(
44
    @EXPORT = qw(
45
        &XSLTGetFilename
45
        &XSLTParse4Display
46
        &XSLTParse4Display
46
        &GetURI
47
        &GetURI
47
    );
48
    );
Lines 156-164 sub _get_best_default_xslt_filename { Link Here
156
    return $xslfilename;
157
    return $xslfilename;
157
}
158
}
158
159
159
sub XSLTParse4Display {
160
sub XSLTGetFilename {
160
    my ( $biblionumber, $orig_record, $xslsyspref, $fixamps, $hidden_items ) = @_;
161
    my ( $marcflavour, $xslsyspref ) = @_;
161
    my $xslfilename = C4::Context->preference($xslsyspref);
162
163
    my $xslfilename = $marcflavour eq C4::Context->preference('marcflavour') ? C4::Context->preference($xslsyspref) : 'default';
162
    if ( $xslfilename =~ /^\s*"?default"?\s*$/i ) {
164
    if ( $xslfilename =~ /^\s*"?default"?\s*$/i ) {
163
        my $htdocs;
165
        my $htdocs;
164
        my $theme;
166
        my $theme;
Lines 167-188 sub XSLTParse4Display { Link Here
167
        if ($xslsyspref eq "XSLTDetailsDisplay") {
169
        if ($xslsyspref eq "XSLTDetailsDisplay") {
168
            $htdocs  = C4::Context->config('intrahtdocs');
170
            $htdocs  = C4::Context->config('intrahtdocs');
169
            $theme   = C4::Context->preference("template");
171
            $theme   = C4::Context->preference("template");
170
            $xslfile = C4::Context->preference('marcflavour') .
172
            $xslfile = $marcflavour .
171
                       "slim2intranetDetail.xsl";
173
                       "slim2intranetDetail.xsl";
172
        } elsif ($xslsyspref eq "XSLTResultsDisplay") {
174
        } elsif ($xslsyspref eq "XSLTResultsDisplay") {
173
            $htdocs  = C4::Context->config('intrahtdocs');
175
            $htdocs  = C4::Context->config('intrahtdocs');
174
            $theme   = C4::Context->preference("template");
176
            $theme   = C4::Context->preference("template");
175
            $xslfile = C4::Context->preference('marcflavour') .
177
            $xslfile = $marcflavour .
176
                        "slim2intranetResults.xsl";
178
                        "slim2intranetResults.xsl";
177
        } elsif ($xslsyspref eq "OPACXSLTDetailsDisplay") {
179
        } elsif ($xslsyspref eq "OPACXSLTDetailsDisplay") {
178
            $htdocs  = C4::Context->config('opachtdocs');
180
            $htdocs  = C4::Context->config('opachtdocs');
179
            $theme   = C4::Context->preference("opacthemes");
181
            $theme   = C4::Context->preference("opacthemes");
180
            $xslfile = C4::Context->preference('marcflavour') .
182
            $xslfile = $marcflavour .
181
                       "slim2OPACDetail.xsl";
183
                       "slim2OPACDetail.xsl";
182
        } elsif ($xslsyspref eq "OPACXSLTResultsDisplay") {
184
        } elsif ($xslsyspref eq "OPACXSLTResultsDisplay") {
183
            $htdocs  = C4::Context->config('opachtdocs');
185
            $htdocs  = C4::Context->config('opachtdocs');
184
            $theme   = C4::Context->preference("opacthemes");
186
            $theme   = C4::Context->preference("opacthemes");
185
            $xslfile = C4::Context->preference('marcflavour') .
187
            $xslfile = $marcflavour .
186
                       "slim2OPACResults.xsl";
188
                       "slim2OPACResults.xsl";
187
        }
189
        }
188
        $xslfilename = _get_best_default_xslt_filename($htdocs, $theme, $lang, $xslfile);
190
        $xslfilename = _get_best_default_xslt_filename($htdocs, $theme, $lang, $xslfile);
Lines 193-198 sub XSLTParse4Display { Link Here
193
        $xslfilename =~ s/\{langcode\}/$lang/;
195
        $xslfilename =~ s/\{langcode\}/$lang/;
194
    }
196
    }
195
197
198
    return $xslfilename;
199
}
200
201
sub XSLTParse4Display {
202
    my ( $biblionumber, $orig_record, $xslsyspref, $fixamps, $hidden_items ) = @_;
203
204
    my $xslfilename = XSLTGetFilename( C4::Context->preference( 'marcflavour' ), $xslsyspref );
205
196
    # grab the XML, run it through our stylesheet, push it out to the browser
206
    # grab the XML, run it through our stylesheet, push it out to the browser
197
    my $record = transformMARCXML4XSLT($biblionumber, $orig_record);
207
    my $record = transformMARCXML4XSLT($biblionumber, $orig_record);
198
    #return $record->as_formatted();
208
    #return $record->as_formatted();
(-)a/Makefile.PL (-1 / +1 lines)
Lines 536-542 if ($config{'INSTALL_ZEBRA'} eq "yes") { Link Here
536
    );
536
    );
537
    if ($config{'INSTALL_PAZPAR2'} eq 'yes') {
537
    if ($config{'INSTALL_PAZPAR2'} eq 'yes') {
538
        push @{ $pl_files->{'rewrite-config.PL'} }, (
538
        push @{ $pl_files->{'rewrite-config.PL'} }, (
539
            'blib/PAZPAR2_CONF_DIR/koha-biblios.xml',
539
            'blib/PAZPAR2_CONF_DIR/generic-settings.xml',
540
            'blib/PAZPAR2_CONF_DIR/pazpar2.xml'
540
            'blib/PAZPAR2_CONF_DIR/pazpar2.xml'
541
        );
541
        );
542
    }
542
    }
(-)a/admin/external_targets.pl (+125 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# Copyright 2013 Jesse Weaver
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 2 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
#
20
21
use Modern::Perl '2010';
22
23
use CGI;
24
25
use C4::Auth;
26
use C4::Context;
27
use C4::Branch;
28
use C4::Output;
29
use URI::Escape;
30
31
our $dbh = C4::Context->dbh;
32
our $input = new CGI;
33
34
our ( $template, $loggedinuser, $cookie ) = get_template_and_user( {
35
    template_name => "admin/external-targets.tt",
36
    query => $input,
37
    type => "intranet",
38
    authnotrequired => 0,
39
    flagsrequired => {parameters => 'parameters_remaining_permissions'},
40
    debug => 1,
41
} );
42
43
our $op = $input->param( 'op' ) // 'show';
44
$template->{VARS}->{op} = $op;
45
46
given ( $op ) {
47
    when ( 'show' ) { show_external_targets(); }
48
    when ( 'add' ) { show_edit_form(); }
49
    when ( 'edit' ) { show_edit_form(); }
50
    when ( 'save' ) { save_target(); }
51
    when ( 'delete' ) { delete_target(); }
52
}
53
54
output_html_with_http_headers $input, $cookie, $template->output;
55
56
sub show_external_targets {
57
    $template->{VARS}->{saved_id} = $input->param( 'saved_id' );
58
    $template->{VARS}->{deleted_name} = $input->param( 'deleted_name' );
59
    $template->{VARS}->{targets} = $dbh->selectall_arrayref( q{
60
        SELECT *
61
        FROM external_targets
62
    }, { Slice => {} } );
63
}
64
65
sub show_edit_form {
66
    $template->{VARS}->{branches} = GetBranchesLoop( undef, 0 );
67
    $template->{VARS}->{syntaxes} = [ 'MARC21', 'UNIMARC', 'NORMARC' ];
68
    $template->{VARS}->{encodings} = { 'utf8' => 'UTF-8', 'marc8' => 'MARC-8' };
69
70
    my $target_id;
71
    if ( $target_id = $input->param( 'target_id' ) ) {
72
        $template->{VARS}->{target} = $dbh->selectrow_hashref( q{ SELECT * FROM external_targets WHERE target_id = ? }, {}, $target_id );
73
74
        my $available_branches = $dbh->selectall_hashref( q{ SELECT * FROM external_target_restrictions WHERE target_id = ? }, 'branchcode', {}, $target_id );
75
76
        foreach my $branch ( @{ $template->{VARS}->{branches} } ) {
77
            $branch->{selected} = 1 if ( $available_branches->{$branch->{branchcode}} );
78
        }
79
    }
80
}
81
82
sub save_target {
83
    my $target_id;
84
    if ( $target_id = $input->param( 'target_id' ) ) {
85
        $dbh->do( q{
86
            UPDATE external_targets
87
            SET name = ?, host = ?, port = ?, db = ?, userid = ?, password = ?, syntax = ?, encoding = ?
88
            WHERE target_id = ?
89
        }, {}, map { $input->param( $_ ) // '' } qw( name host port db userid password syntax encoding target_id ) );
90
    } else {
91
        $dbh->do( q{
92
            INSERT
93
            INTO external_targets(name, host, port, db, userid, password, syntax, encoding)
94
            VALUES(?, ?, ?, ?, ?, ?, ?, ?)
95
        }, {}, map { $input->param( $_ ) // '' } qw( name host port db userid password syntax encoding ) );
96
        $target_id = $dbh->last_insert_id( undef, undef, undef, undef );
97
    }
98
99
    $dbh->do( q{
100
        DELETE
101
        FROM external_target_restrictions
102
        WHERE target_id = ?
103
    }, {}, $target_id );
104
105
    foreach my $branchcode ( $input->param( 'branch' ) ) {
106
        $dbh->do( q{
107
            INSERT
108
            INTO external_target_restrictions(branchcode, target_id)
109
            VALUES(?, ?)
110
        }, {}, $branchcode, $target_id );
111
    }
112
113
    print $input->redirect( '/cgi-bin/koha/admin/external_targets.pl?saved_id=' . $target_id );
114
    exit;
115
}
116
117
sub delete_target {
118
    my ($target_id, $target);
119
120
    return unless ( $target_id = $input->param( 'target_id' ) and $target = $dbh->selectrow_hashref( q{ SELECT * FROM external_targets WHERE target_id = ? }, {}, $target_id ) );
121
122
    $dbh->do( q{ DELETE FROM external_targets WHERE target_id = ? }, {}, $target_id );
123
124
    print $input->redirect( '/cgi-bin/koha/admin/external_targets.pl?deleted_name=' . uri_escape( $target->{'name'} ) );
125
}
(-)a/etc/koha-conf.xml (-13 lines)
Lines 19-37 Link Here
19
<listen id="publicserver" >tcp:@:__ZEBRA_SRU_BIBLIOS_PORT__</listen>
19
<listen id="publicserver" >tcp:@:__ZEBRA_SRU_BIBLIOS_PORT__</listen>
20
-->
20
-->
21
21
22
<!-- Settings for special biblio server instance for PazPar2.
23
     Because PazPar2 only connects to a Z39.50 server using TCP/IP,
24
     it cannot use the Unix-domain socket that biblioserver uses.
25
     Therefore, a custom server is defined. -->
26
__PAZPAR2_TOGGLE_XML_PRE__
27
<listen id="mergeserver">tcp:@:__MERGE_SERVER_PORT__</listen>
28
<server id="mergeserver"  listenref="mergeserver"> 
29
    <directory>__ZEBRA_DATA_DIR__/biblios</directory>
30
    <config>__ZEBRA_CONF_DIR__/__ZEBRA_BIB_CFG__</config>
31
    <cql2rpn>__ZEBRA_CONF_DIR__/pqf.properties</cql2rpn>
32
</server>
33
__PAZPAR2_TOGGLE_XML_POST__
34
35
<!-- BIBLIOGRAPHIC RECORDS -->
22
<!-- BIBLIOGRAPHIC RECORDS -->
36
<server id="biblioserver"  listenref="biblioserver"> 
23
<server id="biblioserver"  listenref="biblioserver"> 
37
    <directory>__ZEBRA_DATA_DIR__/biblios</directory>
24
    <directory>__ZEBRA_DATA_DIR__/biblios</directory>
(-)a/etc/koha-httpd.conf (-2 / +13 lines)
Lines 32-38 Link Here
32
   </Directory>
32
   </Directory>
33
33
34
   # Secure internal stuff
34
   # Secure internal stuff
35
   <DirectoryMatch "__OPAC_WWW_DIR__/.*/(modules|xslt|includes)">
35
   <DirectoryMatch "__OPAC_WWW_DIR__/.*/(modules|includes)">
36
      Order deny,allow
36
      Order deny,allow
37
      Deny from all
37
      Deny from all
38
   </DirectoryMatch>
38
   </DirectoryMatch>
Lines 113-118 Link Here
113
     RewriteRule ^/isbn/([^\/]*)/?$ /search?q=isbn:$1 [PT]
113
     RewriteRule ^/isbn/([^\/]*)/?$ /search?q=isbn:$1 [PT]
114
     RewriteRule ^/issn/([^\/]*)/?$ /search?q=issn:$1 [PT]
114
     RewriteRule ^/issn/([^\/]*)/?$ /search?q=issn:$1 [PT]
115
   </IfModule>
115
   </IfModule>
116
117
   __PAZPAR2_TOGGLE_HTTPD_PRE__
118
     <Proxy *>
119
         AddDefaultCharset off
120
         Order deny,allow
121
         Allow from all
122
     </Proxy>
123
124
     ProxyRequests off
125
     ProxyPass /pazpar2/search.pz2 http://__PAZPAR2_HOST__:__PAZPAR2_PORT__/search.pz2
126
   __PAZPAR2_TOGGLE_HTTPD_POST__
116
</VirtualHost>
127
</VirtualHost>
117
128
118
## Intranet
129
## Intranet
Lines 149-155 Link Here
149
   </Directory>
160
   </Directory>
150
161
151
   # Secure internal stuff
162
   # Secure internal stuff
152
   <DirectoryMatch "__INTRANET_WWW_DIR__/.*/(modules|xslt|includes)">
163
   <DirectoryMatch "__INTRANET_WWW_DIR__/.*/(modules|includes)">
153
      Order deny,allow
164
      Order deny,allow
154
      Deny from all
165
      Deny from all
155
   </DirectoryMatch>
166
   </DirectoryMatch>
(-)a/etc/pazpar2/generic-settings.xml (+21 lines)
Line 0 Link Here
1
<settings target="*">
2
  <!-- This file introduces default settings for pazpar2 -->
3
  <!-- $Id: loc.xml,v 1.2 2007-07-10 13:43:07 adam Exp $ -->
4
5
  <!-- mapping for unqualified search -->
6
  <set name="pz:cclmap:term" value="u=1016 t=l,r s=al"/>
7
8
  <!-- field-specific mappings -->
9
10
  <set name="pz:cclmap:au" value="u=1004 s=al"/>
11
  <set name="pz:cclmap:ti" value="u=4 s=al"/>
12
  <set name="pz:cclmap:su" value="u=21 s=al"/>
13
  <set name="pz:cclmap:isbn" value="u=7"/>
14
  <set name="pz:cclmap:issn" value="u=8"/>
15
  <set name="pz:cclmap:date" value="u=30 r=r"/>
16
17
  <!-- Retrieval settings -->
18
19
  <set name="pz:elements" value="F"/>
20
21
</settings>
(-)a/etc/pazpar2/pazpar2.xml (-1 / +1 lines)
Lines 4-10 Link Here
4
  
4
  
5
  <server>
5
  <server>
6
    <listen port="__PAZPAR2_PORT__"/>
6
    <listen port="__PAZPAR2_PORT__"/>
7
    <settings src="__PAZPAR2_CONF_DIR__/koha-biblios.xml"/>
7
    <settings src="__PAZPAR2_CONF_DIR__/generic-settings.xml"/>
8
8
9
    <relevance>
9
    <relevance>
10
      <icu_chain id="relevance" locale="el">
10
      <icu_chain id="relevance" locale="el">
(-)a/etc/pazpar2/unimarc-work-groups.xsl (+98 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet
3
    version="1.0"
4
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
5
    xmlns:pz="http://www.indexdata.com/pazpar2/1.0"
6
    xmlns:marc="http://www.loc.gov/MARC21/slim">
7
8
 <xsl:output indent="yes" method="xml" version="1.0" encoding="UTF-8"/>
9
10
11
  <xsl:template match="/marc:record">
12
    <pz:record>
13
14
      <xsl:for-each select="marc:controlfield[@tag='001']">
15
        <pz:metadata type="id">
16
          <xsl:value-of select="."/>
17
        </pz:metadata>
18
      </xsl:for-each>
19
20
      <!-- -->
21
      <xsl:for-each select="marc:datafield[@tag='020']">
22
	<xsl:if test="marc:subfield[@code='a'] = 'US'">
23
          <pz:metadata type="lccn">
24
	    <xsl:value-of select="marc:subfield[@code='b']"/>
25
	  </pz:metadata>
26
	</xsl:if>
27
      </xsl:for-each>
28
29
      <xsl:for-each select="marc:datafield[@tag='010']">
30
        <pz:metadata type="isbn">
31
	  <xsl:value-of select="marc:subfield[@code='a']"/>
32
	</pz:metadata>
33
      </xsl:for-each>
34
35
      <xsl:for-each select="marc:datafield[@tag='011']">
36
        <pz:metadata type="issn">
37
	  <xsl:value-of select="marc:subfield[@code='a']"/>
38
	</pz:metadata>
39
      </xsl:for-each>
40
41
42
      <xsl:for-each select="marc:datafield[@tag='200']">
43
        <pz:metadata type="work-title">
44
          <xsl:value-of select="marc:subfield[@code='a']"/>
45
        </pz:metadata>
46
      </xsl:for-each>
47
48
49
      <!-- Date of Pulbication -->
50
      <xsl:for-each select="marc:datafield[@tag='210']">
51
        <pz:metadata type="date">
52
	  <xsl:value-of select="marc:subfield[@code='d']"/>
53
	</pz:metadata>
54
      </xsl:for-each>
55
56
      <!--  Usmarc 650 maps to unimarc 606 and marc21 653 maps to unimarc 610 -->
57
      <xsl:for-each select="marc:datafield[@tag='606' or @tag='610']">
58
	<pz:metadata type="subject">
59
	  <xsl:value-of select="marc:subfield[@code='a']"/>
60
	</pz:metadata>
61
      </xsl:for-each>
62
63
      <xsl:for-each select="marc:datafield[@tag &gt;= 300 and @tag &lt;= 345]
64
                            [@tag != '325']">
65
        <pz:metadata type="description">
66
            <xsl:value-of select="*/text()"/>
67
        </pz:metadata>
68
      </xsl:for-each>
69
70
71
      <!-- Author : primary, alternative and secondary responsibility (equivalent marc21 tags : 100, 700 -->
72
      <xsl:for-each select="marc:datafield[@tag='700' or @tag='701' or @tag='702']">
73
	<pz:metadata type="work-author">
74
	  <xsl:value-of select="marc:subfield[@code='a']"/>
75
          <xsl:text>, </xsl:text>
76
	  <xsl:value-of select="marc:subfield[@code='b']"/>
77
	</pz:metadata>
78
      </xsl:for-each>
79
80
      <!-- Author : marc21 tag 720 maps to unimarc 730
81
      <xsl:for-each select="marc:datafield[@tag='730']">
82
	<pz:metadata type="author">
83
	  <xsl:value-of select="marc:subfield[@code='a']"/>
84
	</pz:metadata>
85
      </xsl:for-each>
86
      -->
87
88
      <!-- -->
89
      <xsl:for-each select="marc:datafield[@tag='856']">
90
	<pz:metadata type="url">
91
	  <xsl:value-of select="marc:subfield[@code='u']"/>
92
	</pz:metadata>
93
      </xsl:for-each>
94
95
    </pz:record>
96
  </xsl:template>
97
98
</xsl:stylesheet>
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 257-262 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
257
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
257
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
258
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
258
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
259
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
259
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
260
('OPACSearchExternalTargets','0',NULL,'Whether to search external targets in the OPAC','YesNo'),
260
('OPACSearchForTitleIn','<li><a  href=\"http://worldcat.org/search?q={TITLE}\" target=\"_blank\">Other Libraries (WorldCat)</a></li>\n<li><a href=\"http://www.scholar.google.com/scholar?q={TITLE}\" target=\"_blank\">Other Databases (Google Scholar)</a></li>\n<li><a href=\"http://www.bookfinder.com/search/?author={AUTHOR}&amp;title={TITLE}&amp;st=xl&amp;ac=qr\" target=\"_blank\">Online Stores (Bookfinder.com)</a></li>\n<li><a href=\"http://openlibrary.org/search/?author=({AUTHOR})&title=({TITLE})\" target=\"_blank\">Open Library (openlibrary.org)</a></li>','70|10','Enter the HTML that will appear in the \'Search for this title in\' box on the detail page in the OPAC.  Enter {TITLE}, {AUTHOR}, or {ISBN} in place of their respective variables in the URL. Leave blank to disable \'More Searches\' menu.','Textarea'),
261
('OPACSearchForTitleIn','<li><a  href=\"http://worldcat.org/search?q={TITLE}\" target=\"_blank\">Other Libraries (WorldCat)</a></li>\n<li><a href=\"http://www.scholar.google.com/scholar?q={TITLE}\" target=\"_blank\">Other Databases (Google Scholar)</a></li>\n<li><a href=\"http://www.bookfinder.com/search/?author={AUTHOR}&amp;title={TITLE}&amp;st=xl&amp;ac=qr\" target=\"_blank\">Online Stores (Bookfinder.com)</a></li>\n<li><a href=\"http://openlibrary.org/search/?author=({AUTHOR})&title=({TITLE})\" target=\"_blank\">Open Library (openlibrary.org)</a></li>','70|10','Enter the HTML that will appear in the \'Search for this title in\' box on the detail page in the OPAC.  Enter {TITLE}, {AUTHOR}, or {ISBN} in place of their respective variables in the URL. Leave blank to disable \'More Searches\' menu.','Textarea'),
261
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
262
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
262
('OpacSeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings (OPAC)','Choice'),
263
('OpacSeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings (OPAC)','Choice'),
(-)a/installer/data/mysql/updatedatabase.pl (+33 lines)
Lines 7953-7958 if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { Link Here
7953
    SetVersion($DBversion);
7953
    SetVersion($DBversion);
7954
}
7954
}
7955
7955
7956
$DBversion = "3.13.00.XXX";
7957
if(CheckVersion($DBversion)) {
7958
    $dbh->do(
7959
"INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('OPACSearchExternalTargets','0','Whether to search external targets in the OPAC','','YesNo')"
7960
    );
7961
    $dbh->do( q{
7962
CREATE TABLE `external_targets` (
7963
  `target_id` int(11) NOT NULL AUTO_INCREMENT,
7964
  `host` varchar(128) NOT NULL,
7965
  `port` int(11) NOT NULL,
7966
  `db` varchar(64) NOT NULL,
7967
  `userid` varchar(64) DEFAULT '',
7968
  `password` varchar(64) DEFAULT '',
7969
  `name` varchar(64) NOT NULL,
7970
  `syntax` varchar(64) NOT NULL,
7971
  `encoding` varchar(16) DEFAULT 'MARC-8',
7972
  PRIMARY KEY (`target_id`)
7973
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
7974
    } );
7975
    $dbh->do( q{
7976
CREATE TABLE `external_target_restrictions` (
7977
  `branchcode` varchar(10) NOT NULL,
7978
  `target_id` int(11) NOT NULL,
7979
  KEY `branchcode` (`branchcode`),
7980
  KEY `target_id` (`target_id`),
7981
  CONSTRAINT `external_target_restrictions_ibfk_1` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE,
7982
  CONSTRAINT `external_target_restrictions_ibfk_2` FOREIGN KEY (`target_id`) REFERENCES `external_targets` (`target_id`) ON DELETE CASCADE
7983
) ENGINE=InnoDB DEFAULT CHARSET=utf8
7984
    } );
7985
    print "Upgrade to $DBversion done (Bug 10486 - Allow external Z39.50 targets to be searched from the OPAC)\n";
7986
    SetVersion($DBversion);
7987
}
7988
7956
=head1 FUNCTIONS
7989
=head1 FUNCTIONS
7957
7990
7958
=head2 TableExists($table)
7991
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css (+14 lines)
Lines 270-275 tr.even td, tr.even.highlight td { Link Here
270
    border-right : 1px solid #BCBCBC;
270
    border-right : 1px solid #BCBCBC;
271
}
271
}
272
272
273
tr.highlight td {
274
	background-color : #F6F6F6;
275
	border-color : #BCBCBC;
276
}
277
278
tr.highlight th[scope=row] {
279
	background-color : #DDDDDD;
280
	border-color : #BCBCBC;
281
}
282
273
td.od {
283
td.od {
274
	color : #cc0000;
284
	color : #cc0000;
275
	font-weight : bold;
285
	font-weight : bold;
Lines 287-292 tr.odd.onissue td { Link Here
287
	background-color: #FFFFE1;
297
	background-color: #FFFFE1;
288
}
298
}
289
299
300
tr.updated td {
301
    background-color: #FFFFBB;
302
}
303
290
tfoot td {
304
tfoot td {
291
	background-color : #f3f3f3;
305
	background-color : #f3f3f3;
292
	font-weight : bold;
306
	font-weight : bold;
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc (+1 lines)
Lines 46-51 Link Here
46
    <li><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></li>
46
    <li><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></li>
47
    <li><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></li>
47
    <li><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></li>
48
    <li><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></li>
48
    <li><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></li>
49
    <li><a href="/cgi-bin/koha/admin/external_targets.pl">External search targets</a></li>
49
</ul>
50
</ul>
50
51
51
<h5>Acquisition parameters</h5>
52
<h5>Acquisition parameters</h5>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt (+2 lines)
Lines 84-89 Link Here
84
      <dt><a href="/cgi-bin/koha/admin/searchengine/solr/indexes.pl">Search engine configuration</a></dt>
84
      <dt><a href="/cgi-bin/koha/admin/searchengine/solr/indexes.pl">Search engine configuration</a></dt>
85
      <dd>Manage indexes, facets, and their mappings to MARC fields and subfields.</dd>
85
      <dd>Manage indexes, facets, and their mappings to MARC fields and subfields.</dd>
86
    [% END %]
86
    [% END %]
87
    <dt><a href="/cgi-bin/koha/admin/external_targets.pl">External search targets</a></dt>
88
    <dd>Define external search targets that can be searched from the OPAC.</dd>
87
</dl>
89
</dl>
88
90
89
<h3>Acquisition parameters</h3>
91
<h3>Acquisition parameters</h3>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/external-targets.tt (+180 lines)
Line 0 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
[% IF op == 'show' %]
3
<title>Koha &rsaquo; Administration &rsaquo; External targets</title>
4
[% ELSIF op == 'add' %]
5
<title>Koha &rsaquo; Administration &rsaquo; External targets &rsaquo; Create an external target</title>
6
[% ELSIF op == 'edit' %]
7
<title>Koha &rsaquo; Administration &rsaquo; External targets &rsaquo; Editing '[% target.name %]'</title>
8
[% END %]
9
[% INCLUDE 'doc-head-close.inc' %]
10
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
11
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
12
[% INCLUDE 'datatables-strings.inc' %]
13
<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
14
<script type="text/javascript">
15
//<![CDATA[
16
 $(document).ready(function() {
17
    [% IF ( targets.size ) %]
18
    var dTable = $("#targets").dataTable( $.extend( true, {}, dataTablesDefaults, {
19
        aoColumnDefs: [
20
            { aTargets: [ 1,2,3,4,5 ], bSortable: false, bSearchable: false },
21
        ],
22
        asStripeClasses: [ '', 'highlight' ],
23
        bPaginate: false,
24
    } ) );
25
26
    [% IF saved_id %]
27
    $( '#targets tr[data-targetid=[% saved_id %]]' ).addClass( 'updated' );
28
    [% END %]
29
30
    $( '#targets .delete' ).click( function() {
31
        return confirm( _("Are you sure you wish to delete this target?") );
32
    } );
33
    [% END %]
34
 });
35
//]]>
36
</script>
37
38
</head>
39
<body id="admin_z3950servers" class="admin">
40
[% INCLUDE 'header.inc' %]
41
[% INCLUDE 'cat-search.inc' %]
42
43
[% IF op == 'show' %]
44
45
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; External targets</div>
46
47
[% ELSIF op == 'add' %]
48
49
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; <a href="/cgi-bin/koha/admin/external_targets.pl">External targets</a> &rsaquo; Create an external target</div>
50
51
[% ELSIF op == 'edit' %]
52
53
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; <a href="/cgi-bin/koha/admin/external_targets.pl">External targets</a> &rsaquo; Editing '[% target.name %]'</div>
54
55
[% END %]
56
57
<div id="doc3" class="yui-t2">
58
<div id="bd">
59
60
<div id="yui-main"><div class="yui-b">
61
62
[% IF op == 'show' %]
63
64
[% IF deleted_name %]
65
<div class="alert">
66
    <p>Deleted target '[% deleted_name %]'</p>
67
</div>
68
[% END %]
69
70
<div id="toolbar" class="btn-toolbar">
71
    <a id="newtarget" class="btn btn-small" href="/cgi-bin/koha/admin/external_targets.pl?op=add"><i class="icon-plus"></i> New external target</a>
72
</div>
73
74
<p>These Z39.50 targets are searched in the OPAC using the "external targets" feature. Note that Pazpar2 must be installed and configured for this to work.</p>
75
76
[% IF targets.size %]
77
<table id="targets">
78
    <thead><tr><th>Name</th><th>Connection</th><th>Login</th><th>Syntax</th><th>Encoding</th><th>&nbsp;</th><th>&nbsp;</th></tr></thead>
79
    <tbody>
80
        [% FOREACH target = targets %]
81
        <tr data-targetid="[% target.target_id %]">
82
            <td>[% target.name %]</td>
83
            <td>[% target.host %]:[% target.port %]/[% target.db %]</td>
84
            <td>[% IF target.userid %][% target.userid %] / [% IF target.password %]********[% ELSE %]<span class="hint">none</span>[% END %][% ELSE %]<span class="hint">none</span>[% END %]</td>
85
            <td>[% target.syntax %]</td>
86
            <td>
87
                [% IF target.encoding == 'marc8' %]
88
                MARC-8
89
                [% ELSIF target.encoding == 'utf8' %]
90
                UTF-8
91
                [% END %]
92
            </td>
93
            <td><a href="/cgi-bin/koha/admin/external_targets.pl?op=edit&amp;target_id=[% target.target_id %]">Edit</a></td>
94
            <td><a class="delete" href="/cgi-bin/koha/admin/external_targets.pl?op=delete&amp;target_id=[% target.target_id %]">Delete</a></td>
95
        </tr>
96
        [% END %]
97
    </tbody>
98
</table>
99
[% ELSE %]
100
<p>No external targets have been defined yet.</p>
101
[% END %]
102
103
[% ELSIF op == 'add' || op == 'edit' %]
104
105
<form action="/cgi-bin/koha/admin/external_targets.pl" method="POST">
106
    [% IF op == 'add' %]
107
    <h1>Create an external target</h1>
108
    [% ELSIF op == 'edit' %]
109
    <h1>Editing '[% target.name %]'</h1>
110
    [% END %]
111
112
    <input type="hidden" name="op" value="save">
113
    <input type="hidden" name="target_id" value="[% target.target_id %]">
114
115
    <fieldset class="rows">
116
        <ol>
117
            <li><label for="name">Name:</label> <input type="text" id="name" name="name" value="[% target.name %]" required></li>
118
            <li><label for="host">Host:</label> <input type="text" id="host" name="host" value="[% target.host %]" required></li>
119
            <li><label for="port">Port:</label> <input type="num" id="port" name="port" value="[% target.port %]" required></li>
120
            <li><label for="db">Database:</label> <input type="text" id="db" name="db" value="[% target.db %]" required></li>
121
            <li><label for="userid">User:</label> <input type="text" id="userid" name="userid" value="[% target.userid %]"></li>
122
            <li><label for="password">Password:</label> <input type="password" id="password" name="password" value="[% target.password %]" autocomplete="off"></li>
123
            <li>
124
                <label for="syntax">Syntax:</label>
125
                <select id="syntax" name="syntax">
126
                    [% FOREACH syntax = syntaxes %]
127
                    [% IF syntax == target.syntax %]
128
                    <option selected>[% syntax %]
129
                    [% ELSE %]
130
                    <option>[% syntax %]</option>
131
                    [% END %]
132
                    [% END %]
133
                </select>
134
            </li>
135
            <li>
136
                <label for="encoding">Encoding:</label>
137
                <select id="encoding" name="encoding">
138
                    [% FOREACH encoding = encodings %]
139
                    [% IF encoding.key == target.encoding %]
140
                    <option value="[% encoding.key %]" selected>[% encoding.value %]
141
                    [% ELSE %]
142
                    <option value="[% encoding.key %]">[% encoding.value %]</option>
143
                    [% END %]
144
                    [% END %]
145
                </select>
146
            </li>
147
            <li>
148
                <label>Restricted libraries:</label>
149
                <fieldset>
150
                    <legend>Not available to patrons from:</legend>
151
                    <p>Only targets with no restrictions will be shown to anonymous (not logged in) users.</p>
152
                    <ol>
153
                        [% FOREACH branch = branches %]
154
                        <li>
155
                            <label for="branch-[% branch.branchcode %]">[% branch.branchname %]</label>
156
                            [% IF branch.selected %]
157
                            <input type="checkbox" id="branch-[% branch.branchcode %]" name="branch" value="[% branch.branchcode %]" checked />
158
                            [% ELSE %]
159
                            <input type="checkbox" id="branch-[% branch.branchcode %]" name="branch" value="[% branch.branchcode %]" />
160
                            [% END %]
161
                        </li>
162
                        [% END %]
163
                    </ol>
164
                </fieldset>
165
            </li>
166
        </ol>
167
    </fieldset>
168
169
    <fieldset class="action"><input type="submit" value="Save"></fieldset>
170
</form>
171
172
[% END %]
173
174
</div></div>
175
<div class="yui-b">
176
[% INCLUDE 'admin-menu.inc' %]
177
</div>
178
179
</div>
180
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (+7 lines)
Lines 430-435 OPAC: Link Here
430
                  yes: Allow
430
                  yes: Allow
431
                  no: Do not allow
431
                  no: Do not allow
432
            - users to add a note when placing a hold.
432
            - users to add a note when placing a hold.
433
        -
434
            - pref: OPACSearchExternalTargets
435
              default: 0
436
              choices:
437
                  yes: Search
438
                  no: "Don't search"
439
            - external targets from the OPAC.
433
440
434
    Policy:
441
    Policy:
435
        -
442
        -
(-)a/koha-tmpl/opac-tmpl/bootstrap/css/opac.css (-1 / +20 lines)
Lines 2142-2148 td img { Link Here
2142
  margin: 0;
2142
  margin: 0;
2143
  padding: 0;
2143
  padding: 0;
2144
}
2144
}
2145
#overdrive-results {
2145
#overdrive-results, #pazpar2-results {
2146
  font-weight: bold;
2146
  font-weight: bold;
2147
  padding-left: 1em;
2147
  padding-left: 1em;
2148
}
2148
}
Lines 2278-2283 a.reviewlink:visited { Link Here
2278
  -moz-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2);
2278
  -moz-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2);
2279
  box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2);
2279
  box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2);
2280
}
2280
}
2281
.sourcecol {
2282
  vertical-align: top;
2283
  width: 100px;
2284
}
2285
.notesrow label {
2286
  font-weight: bold;
2287
}
2288
.notesrow span {
2289
  display: block;
2290
}
2291
.notesrow textarea {
2292
  width: 100%;
2293
}
2294
#modal-overlay {
2295
  background-color: white;
2296
  border-radius: 5px;
2297
  max-width: 75%;
2298
  padding: 15px;
2299
}
2281
@media only screen and (min-width: 0px) and (max-width: 304px) {
2300
@media only screen and (min-width: 0px) and (max-width: 304px) {
2282
  /* Screens bewteen 0 and 304 pixels wide */
2301
  /* Screens bewteen 0 and 304 pixels wide */
2283
  #oh:after {
2302
  #oh:after {
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt (-9 / +50 lines)
Lines 552-558 Link Here
552
[% IF ( OverDriveEnabled ) %]<script type="text/javascript" src="[% interface %]/[% theme %]/js/overdrive.js"></script>[% END %]
552
[% IF ( OverDriveEnabled ) %]<script type="text/javascript" src="[% interface %]/[% theme %]/js/overdrive.js"></script>[% END %]
553
<script type="text/javascript" src="[% interface %]/[% theme %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
553
<script type="text/javascript" src="[% interface %]/[% theme %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
554
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% interface %]/[% theme %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
554
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% interface %]/[% theme %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
555
[% END %]<script type="text/javascript">
555
[% END %]
556
[% IF ( Koha.Preference('OPACSearchExternalTargets') ) %]
557
<script type="text/javascript" src="/opac-tmpl/lib/pz2.js"></script>
558
<script type="text/javascript" src="[% interface %]/[% theme %]/js/externalsearch.js"></script>
559
[% END %]
560
<script type="text/javascript">
556
//<![CDATA[
561
//<![CDATA[
557
[% IF ( Koha.Preference( 'opacuserlogin' ) == 1 ) && ( Koha.Preference( 'RequestOnOpac' ) == 1 ) %]
562
[% IF ( Koha.Preference( 'opacuserlogin' ) == 1 ) && ( Koha.Preference( 'RequestOnOpac' ) == 1 ) %]
558
function holdMultiple() {
563
function holdMultiple() {
Lines 836-856 $(document).ready(function(){ Link Here
836
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
841
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
837
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
842
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
838
    [% END %]
843
    [% END %]
844
845
    function StartExternalSearch(name, text) {
846
        $( '#numresults' )
847
            .eq( 0 )
848
            .append( ' <span id="' + name + '-results">' + text + '... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
849
    }
850
    function FailExternalSearch( name, text ) {
851
        $( '#' + name + '-results' ).html( text );
852
    }
853
    function FinishExternalSearch( name, text, numItems, url ) {
854
        if ( numItems ) {
855
            $( '#' + name + '-results' ).html( text.replace( '__LINK__', '<a href="' + url + '">' + _("__RESULTS__ results").replace( '__RESULTS__', numItems ) + '</a>' ) );
856
        } else {
857
            $( '#' + name + '-results' ).remove();
858
        }
859
    }
860
839
    [% IF ( OverDriveEnabled ) %]
861
    [% IF ( OverDriveEnabled ) %]
840
        var $overdrive_results = $( '<span id="overdrive-results">' + _( 'Searching OverDrive...' ) + ' <img class="throbber" src="[% interface %]/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
862
        StartExternalSearch( 'overdrive', _("Searching OverDrive") );
841
        $( '#numresults' ) .append( ' ' )
842
            .append( $overdrive_results );
843
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
863
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
844
            if ( data.error ) {
864
            if ( data.error ) {
845
                $overdrive_results.html( _( 'Error searching OverDrive collection' ) );
865
                FailExternalSearch( 'overdrive', _("Error searching OverDrive collection") );
846
                return;
866
                return;
847
            }
867
            }
848
868
849
            if ( data.totalItems ) {
869
            FinishExternalSearch( 'overdrive', _("Found __LINK__ in OverDrive collection"), data.totalItems, '/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) );
850
                $overdrive_results.html( _( 'Found' ) + ' <a href="/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) + '">' + data.totalItems + ' ' + _( 'results' ) + '</a> ' + _( 'in OverDrive collection' ) );
870
        } );
851
            } else {
871
    [% END %]
852
                $overdrive_results.remove();
872
873
    [% IF ( OPACSearchExternalTargets ) %]
874
        KOHA.ExternalSearch.targets = {
875
            [% FOREACH target IN external_search_targets %]
876
                '[% target.host %]:[% target.port %]/[% target.db %]': {
877
                    id: '[% target.target_id %]',
878
                    name: '[% target.name %]',
879
                    syntax: '[% target.syntax %]',
880
                },
881
            [% END %]
882
        };
883
        var num_targets = 0; $.each( KOHA.ExternalSearch.targets, function() { num_targets++ } );
884
        var first_succeeded;
885
886
        StartExternalSearch( 'pazpar2', _("Searching external targets") );
887
        KOHA.ExternalSearch.Search( querystring, 1, function( data ) {
888
            if ( data.error ) {
889
                if ( !first_succeeded ) FailExternalSearch( 'pazpar2', _("Error searching external targets") );
890
                return;
853
            }
891
            }
892
893
            first_succeeded = true;
894
            FinishExternalSearch( 'pazpar2', _("Found __LINK__ in __TARGETS__ external targets").replace( '__TARGETS__', num_targets ), data.total, '/cgi-bin/koha/opac-external-search.pl?q=' + escape( querystring ) );
854
        } );
895
        } );
855
    [% END %]
896
    [% END %]
856
[% END %]
897
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACResults.xsl (-1 / +6 lines)
Lines 5-15 Link Here
5
  xmlns:marc="http://www.loc.gov/MARC21/slim"
5
  xmlns:marc="http://www.loc.gov/MARC21/slim"
6
  xmlns:items="http://www.koha-community.org/items"
6
  xmlns:items="http://www.koha-community.org/items"
7
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
7
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
8
  xmlns="http://www.w3.org/1999/xhtml"
8
  exclude-result-prefixes="marc items">
9
  exclude-result-prefixes="marc items">
9
    <xsl:import href="MARC21slimUtils.xsl"/>
10
    <xsl:import href="MARC21slimUtils.xsl"/>
10
    <xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
11
    <xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
11
    <xsl:key name="item-by-status" match="items:item" use="items:status"/>
12
    <xsl:key name="item-by-status" match="items:item" use="items:status"/>
12
    <xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
13
    <xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
14
    <xsl:param name="showAvailability" select="true()"/>
13
15
14
    <xsl:template match="/">
16
    <xsl:template match="/">
15
            <xsl:apply-templates/>
17
            <xsl:apply-templates/>
Lines 1027-1032 Link Here
1027
                            </xsl:for-each>
1029
                            </xsl:for-each>
1028
                            </span>
1030
                            </span>
1029
                        </xsl:if>
1031
                        </xsl:if>
1032
                        <xsl:if test="$showAvailability">
1030
                        <span class="results_summary availability">
1033
                        <span class="results_summary availability">
1031
                        <span class="label">Availability: </span>
1034
                        <span class="label">Availability: </span>
1032
                        <xsl:choose>
1035
                        <xsl:choose>
Lines 1035-1041 Link Here
1035
                            <xsl:when test="string-length($AlternateHoldingsField)=3 and marc:datafield[@tag=$AlternateHoldingsField]">
1038
                            <xsl:when test="string-length($AlternateHoldingsField)=3 and marc:datafield[@tag=$AlternateHoldingsField]">
1036
                            <xsl:variable name="AlternateHoldingsCount" select="count(marc:datafield[@tag=$AlternateHoldingsField])"/>
1039
                            <xsl:variable name="AlternateHoldingsCount" select="count(marc:datafield[@tag=$AlternateHoldingsField])"/>
1037
                            <xsl:for-each select="marc:datafield[@tag=$AlternateHoldingsField][1]">
1040
                            <xsl:for-each select="marc:datafield[@tag=$AlternateHoldingsField][1]">
1038
                                <xsl:call-template select="marc:datafield[@tag=$AlternateHoldingsField]" name="subfieldSelect">
1041
                                <!-- Removed select="marc:datafield[@tag=$AlternateHoldingsField]" due to incompatibility with browser XSLT -->
1042
                                <xsl:call-template name="subfieldSelect">
1039
                                    <xsl:with-param name="codes"><xsl:value-of select="$AlternateHoldingsSubfields"/></xsl:with-param>
1043
                                    <xsl:with-param name="codes"><xsl:value-of select="$AlternateHoldingsSubfields"/></xsl:with-param>
1040
                                    <xsl:with-param name="delimeter"><xsl:value-of select="$AlternateHoldingsSeparator"/></xsl:with-param>
1044
                                    <xsl:with-param name="delimeter"><xsl:value-of select="$AlternateHoldingsSeparator"/></xsl:with-param>
1041
                                </xsl:call-template>
1045
                                </xsl:call-template>
Lines 1143-1148 Link Here
1143
                       <xsl:text>). </xsl:text>                   </span>
1147
                       <xsl:text>). </xsl:text>                   </span>
1144
                   </xsl:if>
1148
                   </xsl:if>
1145
               </span>
1149
               </span>
1150
               </xsl:if>
1146
    <xsl:choose>
1151
    <xsl:choose>
1147
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1152
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1148
            <span class="results_summary" id="location">
1153
            <span class="results_summary" id="location">
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slimUtils.xsl (-1 / +1 lines)
Lines 1-6 Link Here
1
<?xml version='1.0'?>
1
<?xml version='1.0'?>
2
<!DOCTYPE stylesheet [<!ENTITY nbsp "&#160;" >]>
2
<!DOCTYPE stylesheet [<!ENTITY nbsp "&#160;" >]>
3
<xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
<xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
4
	<xsl:template name="datafield">
4
	<xsl:template name="datafield">
5
		<xsl:param name="tag"/>
5
		<xsl:param name="tag"/>
6
		<xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
6
		<xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl (+4 lines)
Lines 6-17 Link Here
6
  xmlns:marc="http://www.loc.gov/MARC21/slim"
6
  xmlns:marc="http://www.loc.gov/MARC21/slim"
7
  xmlns:items="http://www.koha-community.org/items"
7
  xmlns:items="http://www.koha-community.org/items"
8
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
8
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
9
  xmlns="http://www.w3.org/1999/xhtml"
9
  exclude-result-prefixes="marc items">
10
  exclude-result-prefixes="marc items">
10
11
11
<xsl:import href="UNIMARCslimUtils.xsl"/>
12
<xsl:import href="UNIMARCslimUtils.xsl"/>
12
<xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
13
<xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
13
<xsl:key name="item-by-status" match="items:item" use="items:status"/>
14
<xsl:key name="item-by-status" match="items:item" use="items:status"/>
14
<xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
15
<xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
16
<xsl:param name="showAvailability" select="true()"/>
15
17
16
<xsl:template match="/">
18
<xsl:template match="/">
17
  <xsl:apply-templates/>
19
  <xsl:apply-templates/>
Lines 95-100 Link Here
95
97
96
  <xsl:call-template name="tag_215" />
98
  <xsl:call-template name="tag_215" />
97
99
100
  <xsl:if test="$showAvailability">
98
  <span class="results_summary">
101
  <span class="results_summary">
99
    <span class="label">Availability: </span>
102
    <span class="label">Availability: </span>
100
    <xsl:choose>
103
    <xsl:choose>
Lines 238-243 Link Here
238
      </span>
241
      </span>
239
    </xsl:if>
242
    </xsl:if>
240
  </span>
243
  </span>
244
  </xsl:if>
241
245
242
</xsl:template>
246
</xsl:template>
243
247
(-)a/koha-tmpl/opac-tmpl/ccsr/en/css/opac.css (+16 lines)
Lines 1596-1601 strong em, em strong { Link Here
1596
     vertical-align: top;
1596
     vertical-align: top;
1597
   width: 10px;
1597
   width: 10px;
1598
}
1598
}
1599
.sourcecol {
1600
	vertical-align: top;
1601
	width: 100px;
1602
}
1599
#container {
1603
#container {
1600
    color : #000;
1604
    color : #000;
1601
}
1605
}
Lines 2865-2867 a.reviewlink,a.reviewlink:visited { Link Here
2865
    display: block;
2869
    display: block;
2866
    overflow: auto;
2870
    overflow: auto;
2867
}
2871
}
2872
2873
#modal-overlay {
2874
    background-color: white;
2875
    border-radius: 5px;
2876
    max-width: 75%;
2877
    padding: 15px;
2878
}
2879
2880
#overdrive-results, #pazpar2-results {
2881
    font-weight: bold;
2882
    padding-left: 1em;
2883
}
(-)a/koha-tmpl/opac-tmpl/ccsr/en/js/externalsearch.js (+106 lines)
Line 0 Link Here
1
if ( typeof KOHA == "undefined" || !KOHA ) {
2
    var KOHA = {};
3
}
4
5
KOHA.XSLTGet = ( function() {
6
    // Horrible browser hack, but required due to the following hard-to-detect and long-unfixed bug:
7
    // https://bugs.webkit.org/show_bug.cgi?id=60276
8
    var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
9
    var isSafari = /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
10
11
    if ( !isChrome && !isSafari ) return $.get;
12
13
    return function( url ) {
14
        var result = new jQuery.Deferred();
15
        var basepath = url.match( /(.*\/)*/ )[0];
16
17
        $.get( url ).done( function( xslDoc ) {
18
            var xslImports = xslDoc.getElementsByTagNameNS( 'http://www.w3.org/1999/XSL/Transform', 'import' );
19
            var importsRemaining = xslImports.length;
20
21
            if ( importsRemaining == 0 ) {
22
                result.resolve( xslDoc );
23
                return;
24
            }
25
26
            $.each( xslImports, function( i, importElem ) {
27
                var path = $( importElem ).attr( 'href' );
28
                if ( !/^(\/|https?:)/.test( path ) ) path = basepath + path;
29
30
                KOHA.XSLTGet( path ).done( function( subDoc ) {
31
                    importsRemaining--;
32
                    $( importElem ).replaceWith( subDoc.documentElement.childNodes );
33
34
                    if ( importsRemaining == 0 ) result.resolve( xslDoc );
35
                } ).fail( function() {
36
                    importsRemaining = -1;
37
38
                    result.reject();
39
                } );
40
            } );
41
        } ).fail( function() {
42
            result.reject();
43
        } );
44
45
        return result;
46
    };
47
} )();
48
49
KOHA.TransformToFragment = function( xmlDoc, xslDoc ) {
50
    if ( window.XSLTProcessor ) {
51
        var proc = new XSLTProcessor();
52
        proc.importStylesheet( xslDoc );
53
        proc.setParameter( null, 'showAvailability', false );
54
        return (new XMLSerializer).serializeToString( proc.transformToFragment( xmlDoc, document ) );
55
    } else if ( window.ActiveXObject ) {
56
        var xslt = new ActiveXObject( "Msxml2.XSLTemplate" );
57
        xslt.stylesheet = xslDoc;
58
        var xslProc = xslt.createProcessor();
59
        xslProc.input = xmlDoc;
60
        xslProc.addParameter( 'showAvailability', false );
61
        xslProc.transform();
62
        return xslProc.output;
63
    }
64
};
65
66
KOHA.ExternalSearch = ( function() {
67
    return {
68
        targets: {},
69
        Search: function( q, limit, callback ) {
70
            var targetIDs = [];
71
            var includedTargets = [];
72
73
            $.each( KOHA.ExternalSearch.targets, function ( url, info ) {
74
                if ( !info.disabled ) {
75
                    includedTargets.push( url );
76
                    targetIDs.push( info.id );
77
                }
78
            } );
79
80
            if ( KOHA.ExternalSearch._pz !== undefined ) {
81
                afterinit( KOHA.ExternalSearch._pz );
82
            } else {
83
                $.get( '/cgi-bin/koha/svc/pazpar2_init', { targets: targetIDs.join(',') }, function( data ) {
84
                    KOHA.ExternalSearch._pz = new pz2({
85
                        sessionId: data.sessionID,
86
                        onshow: callback,
87
                        errorhandler: function ( error ) { callback( { error: error } ) },
88
                    } );
89
                    afterinit( KOHA.ExternalSearch._pz );
90
                } );
91
            }
92
93
            function afterinit( pz ) {
94
                pz.search( q, limit, 'relevance:0', 'pz:id=' + includedTargets.join( '|' ) );
95
            }
96
        },
97
        Fetch: function( offset, callback ) {
98
            var pz = KOHA.ExternalSearch._pz;
99
            pz.showCallback = callback;
100
            pz.show( offset );
101
        },
102
        GetDetailedRecord: function( recid, callback ) {
103
            KOHA.ExternalSearch._pz.record( recid, 0, undefined, { callback: callback } );
104
        },
105
    };
106
} )();
(-)a/koha-tmpl/opac-tmpl/ccsr/en/lib/jquery/plugins/jquery.lightbox_me.js (+254 lines)
Line 0 Link Here
1
/*
2
* $ lightbox_me
3
* By: Buck Wilson
4
* Version : 2.3
5
*
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
9
*
10
*     http://www.apache.org/licenses/LICENSE-2.0
11
*
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
17
*/
18
19
20
(function($) {
21
22
    $.fn.lightbox_me = function(options) {
23
24
        return this.each(function() {
25
26
            var
27
                opts = $.extend({}, $.fn.lightbox_me.defaults, options),
28
                $overlay = $(),
29
                $self = $(this),
30
                $iframe = $('<iframe id="foo" style="z-index: ' + (opts.zIndex + 1) + ';border: none; margin: 0; padding: 0; position: absolute; width: 100%; height: 100%; top: 0; left: 0; filter: mask();"/>'),
31
                ie6 = ($.browser.msie && $.browser.version < 7);
32
33
            if (opts.showOverlay) {
34
                //check if there's an existing overlay, if so, make subequent ones clear
35
               var $currentOverlays = $(".js_lb_overlay:visible");
36
                if ($currentOverlays.length > 0){
37
                    $overlay = $('<div class="lb_overlay_clear js_lb_overlay"/>');
38
                } else {
39
                    $overlay = $('<div class="' + opts.classPrefix + '_overlay js_lb_overlay"/>');
40
                }
41
            }
42
43
            /*----------------------------------------------------
44
               DOM Building
45
            ---------------------------------------------------- */
46
            if (ie6) {
47
                var src = /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank';
48
                $iframe.attr('src', src);
49
                $('body').append($iframe);
50
            } // iframe shim for ie6, to hide select elements
51
            $('body').append($self.hide()).append($overlay);
52
53
54
            /*----------------------------------------------------
55
               Overlay CSS stuffs
56
            ---------------------------------------------------- */
57
58
            // set css of the overlay
59
            if (opts.showOverlay) {
60
                setOverlayHeight(); // pulled this into a function because it is called on window resize.
61
                $overlay.css({ position: 'absolute', width: '100%', top: 0, left: 0, right: 0, bottom: 0, zIndex: (opts.zIndex + 2), display: 'none' });
62
				if (!$overlay.hasClass('lb_overlay_clear')){
63
			$overlay.css(opts.overlayCSS);
64
                }
65
            }
66
67
            /*----------------------------------------------------
68
               Animate it in.
69
            ---------------------------------------------------- */
70
               //
71
            if (opts.showOverlay) {
72
                $overlay.fadeIn(opts.overlaySpeed, function() {
73
                    setSelfPosition();
74
                    $self[opts.appearEffect](opts.lightboxSpeed, function() { setOverlayHeight(); setSelfPosition(); opts.onLoad()});
75
                });
76
            } else {
77
                setSelfPosition();
78
                $self[opts.appearEffect](opts.lightboxSpeed, function() { opts.onLoad()});
79
            }
80
81
            /*----------------------------------------------------
82
               Hide parent if parent specified (parentLightbox should be jquery reference to any parent lightbox)
83
            ---------------------------------------------------- */
84
            if (opts.parentLightbox) {
85
                opts.parentLightbox.fadeOut(200);
86
            }
87
88
89
            /*----------------------------------------------------
90
               Bind Events
91
            ---------------------------------------------------- */
92
93
            $(window).resize(setOverlayHeight)
94
                     .resize(setSelfPosition)
95
                     .scroll(setSelfPosition);
96
97
            $(window).bind('keyup.lightbox_me', observeKeyPress);
98
99
            if (opts.closeClick) {
100
                $overlay.click(function(e) { closeLightbox(); e.preventDefault; });
101
            }
102
            $self.delegate(opts.closeSelector, "click", function(e) {
103
                closeLightbox(); e.preventDefault();
104
            });
105
            $self.bind('close', closeLightbox);
106
            $self.bind('reposition', setSelfPosition);
107
108
109
110
            /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
111
              -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
112
113
114
            /*----------------------------------------------------
115
               Private Functions
116
            ---------------------------------------------------- */
117
118
            /* Remove or hide all elements */
119
            function closeLightbox() {
120
                var s = $self[0].style;
121
                if (opts.destroyOnClose) {
122
                    $self.add($overlay).remove();
123
                } else {
124
                    $self.add($overlay).hide();
125
                }
126
127
                //show the hidden parent lightbox
128
                if (opts.parentLightbox) {
129
                    opts.parentLightbox.fadeIn(200);
130
                }
131
132
                $iframe.remove();
133
134
				// clean up events.
135
                $self.undelegate(opts.closeSelector, "click");
136
137
                $(window).unbind('reposition', setOverlayHeight);
138
                $(window).unbind('reposition', setSelfPosition);
139
                $(window).unbind('scroll', setSelfPosition);
140
                $(window).unbind('keyup.lightbox_me');
141
                if (ie6)
142
                    s.removeExpression('top');
143
                opts.onClose();
144
            }
145
146
147
            /* Function to bind to the window to observe the escape/enter key press */
148
            function observeKeyPress(e) {
149
                if((e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) && opts.closeEsc) closeLightbox();
150
            }
151
152
153
            /* Set the height of the overlay
154
                    : if the document height is taller than the window, then set the overlay height to the document height.
155
                    : otherwise, just set overlay height: 100%
156
            */
157
            function setOverlayHeight() {
158
                if ($(window).height() < $(document).height()) {
159
                    $overlay.css({height: $(document).height() + 'px'});
160
                     $iframe.css({height: $(document).height() + 'px'});
161
                } else {
162
                    $overlay.css({height: '100%'});
163
                    if (ie6) {
164
                        $('html,body').css('height','100%');
165
                        $iframe.css('height', '100%');
166
                    } // ie6 hack for height: 100%; TODO: handle this in IE7
167
                }
168
            }
169
170
171
            /* Set the position of the modal'd window ($self)
172
                    : if $self is taller than the window, then make it absolutely positioned
173
                    : otherwise fixed
174
            */
175
            function setSelfPosition() {
176
                var s = $self[0].style;
177
178
                // reset CSS so width is re-calculated for margin-left CSS
179
                $self.css({left: '50%', marginLeft: ($self.outerWidth() / 2) * -1,  zIndex: (opts.zIndex + 3) });
180
181
182
                /* we have to get a little fancy when dealing with height, because lightbox_me
183
                    is just so fancy.
184
                 */
185
186
                // if the height of $self is bigger than the window and self isn't already position absolute
187
                if (($self.height() + 80  >= $(window).height()) && ($self.css('position') != 'absolute' || ie6)) {
188
189
                    // we are going to make it positioned where the user can see it, but they can still scroll
190
                    // so the top offset is based on the user's scroll position.
191
                    var topOffset = $(document).scrollTop() + 40;
192
                    $self.css({position: 'absolute', top: topOffset + 'px', marginTop: 0})
193
                    if (ie6) {
194
                        s.removeExpression('top');
195
                    }
196
                } else if ($self.height()+ 80  < $(window).height()) {
197
                    //if the height is less than the window height, then we're gonna make this thing position: fixed.
198
                    // in ie6 we're gonna fake it.
199
                    if (ie6) {
200
                        s.position = 'absolute';
201
                        if (opts.centered) {
202
                            s.setExpression('top', '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"')
203
                            s.marginTop = 0;
204
                        } else {
205
                            var top = (opts.modalCSS && opts.modalCSS.top) ? parseInt(opts.modalCSS.top) : 0;
206
                            s.setExpression('top', '((blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"')
207
                        }
208
                    } else {
209
                        if (opts.centered) {
210
                            $self.css({ position: 'fixed', top: '50%', marginTop: ($self.outerHeight() / 2) * -1})
211
                        } else {
212
                            $self.css({ position: 'fixed'}).css(opts.modalCSS);
213
                        }
214
215
                    }
216
                }
217
            }
218
219
        });
220
221
222
223
    };
224
225
    $.fn.lightbox_me.defaults = {
226
227
        // animation
228
        appearEffect: "fadeIn",
229
        appearEase: "",
230
        overlaySpeed: 250,
231
        lightboxSpeed: 300,
232
233
        // close
234
        closeSelector: ".close",
235
        closeClick: true,
236
        closeEsc: true,
237
238
        // behavior
239
        destroyOnClose: false,
240
        showOverlay: true,
241
        parentLightbox: false,
242
243
        // callbacks
244
        onLoad: function() {},
245
        onClose: function() {},
246
247
        // style
248
        classPrefix: 'lb',
249
        zIndex: 999,
250
        centered: false,
251
        modalCSS: {top: '40px'},
252
        overlayCSS: {background: 'black', opacity: .3}
253
    }
254
})(jQuery);
(-)a/koha-tmpl/opac-tmpl/lib/pz2.js (+1124 lines)
Line 0 Link Here
1
/*
2
 * pz2.js - pazpar2's javascript client library.
3
 * Copyright (C) 2006-2013 Index Data.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
*/
19
20
//since explorer is flawed
21
if (!window['Node']) {
22
    window.Node = new Object();
23
    Node.ELEMENT_NODE = 1;
24
    Node.ATTRIBUTE_NODE = 2;
25
    Node.TEXT_NODE = 3;
26
    Node.CDATA_SECTION_NODE = 4;
27
    Node.ENTITY_REFERENCE_NODE = 5;
28
    Node.ENTITY_NODE = 6;
29
    Node.PROCESSING_INSTRUCTION_NODE = 7;
30
    Node.COMMENT_NODE = 8;
31
    Node.DOCUMENT_NODE = 9;
32
    Node.DOCUMENT_TYPE_NODE = 10;
33
    Node.DOCUMENT_FRAGMENT_NODE = 11;
34
    Node.NOTATION_NODE = 12;
35
}
36
37
// prevent execution of more than once
38
if(typeof window.pz2 == "undefined") {
39
window.undefined = window.undefined;
40
41
var pz2 = function ( paramArray )
42
{
43
44
    // at least one callback required
45
    if ( !paramArray )
46
        throw new Error("Pz2.js: Array with parameters has to be supplied.");
47
48
    //supported pazpar2's protocol version
49
    this.suppProtoVer = '1';
50
    if (typeof paramArray.pazpar2path != "undefined")
51
        this.pz2String = paramArray.pazpar2path;
52
    else
53
        this.pz2String = "/pazpar2/search.pz2";
54
    this.useSessions = true;
55
56
    this.stylesheet = paramArray.detailstylesheet || null;
57
    //load stylesheet if required in async mode
58
    if( this.stylesheet ) {
59
        var context = this;
60
        var request = new pzHttpRequest( this.stylesheet );
61
        request.get( {}, function ( doc ) { context.xslDoc = doc; } );
62
    }
63
64
    this.errorHandler = paramArray.errorhandler || null;
65
    this.showResponseType = paramArray.showResponseType || "xml";
66
67
    // function callbacks
68
    this.initCallback = paramArray.oninit || null;
69
    this.statCallback = paramArray.onstat || null;
70
    this.showCallback = paramArray.onshow || null;
71
    this.termlistCallback = paramArray.onterm || null;
72
    this.recordCallback = paramArray.onrecord || null;
73
    this.bytargetCallback = paramArray.onbytarget || null;
74
    this.resetCallback = paramArray.onreset || null;
75
76
    // termlist keys
77
    this.termKeys = paramArray.termlist || "subject";
78
79
    // some configurational stuff
80
    this.keepAlive = 50000;
81
82
    if ( paramArray.keepAlive < this.keepAlive )
83
        this.keepAlive = paramArray.keepAlive;
84
85
    this.sessionID = paramArray.sessionId || null;
86
    this.serviceId = paramArray.serviceId || null;
87
    this.initStatusOK = false;
88
    this.pingStatusOK = false;
89
    this.searchStatusOK = false;
90
91
    // for sorting
92
    this.currentSort = "relevance";
93
94
    // where are we?
95
    this.currentStart = 0;
96
    // currentNum can be overwritten in show
97
    this.currentNum = 20;
98
99
    // last full record retrieved
100
    this.currRecID = null;
101
102
    // current query
103
    this.currQuery = null;
104
105
    //current raw record offset
106
    this.currRecOffset = null;
107
108
    //timers
109
    this.pingTimer = null;
110
    this.statTime = paramArray.stattime || 1000;
111
    this.statTimer = null;
112
    this.termTime = paramArray.termtime || 1000;
113
    this.termTimer = null;
114
    this.showTime = paramArray.showtime || 1000;
115
    this.showTimer = null;
116
    this.showFastCount = 4;
117
    this.bytargetTime = paramArray.bytargettime || 1000;
118
    this.bytargetTimer = null;
119
    this.recordTime = paramArray.recordtime || 500;
120
    this.recordTimer = null;
121
122
    // counters for each command and applied delay
123
    this.dumpFactor = 500;
124
    this.showCounter = 0;
125
    this.termCounter = 0;
126
    this.statCounter = 0;
127
    this.bytargetCounter = 0;
128
    this.recordCounter = 0;
129
130
    // active clients, updated by stat and show
131
    // might be an issue since bytarget will poll accordingly
132
    this.activeClients = 1;
133
134
    // if in proxy mode no need to init
135
    if (paramArray.usesessions != undefined) {
136
         this.useSessions = paramArray.usesessions;
137
        this.initStatusOK = true;
138
    }
139
    // else, auto init session or wait for a user init?
140
    if (this.useSessions && paramArray.autoInit !== false) {
141
        this.init(this.sessionID, this.serviceId);
142
    }
143
    // Version parameter
144
    this.version = paramArray.version || null;
145
};
146
147
pz2.prototype =
148
{
149
    //error handler for async error throws
150
   throwError: function (errMsg, errCode)
151
   {
152
        var err = new Error(errMsg);
153
        if (errCode) err.code = errCode;
154
155
        if (this.errorHandler) {
156
            this.errorHandler(err);
157
        }
158
        else {
159
            throw err;
160
        }
161
   },
162
163
    // stop activity by clearing tiemouts
164
   stop: function ()
165
   {
166
       clearTimeout(this.statTimer);
167
       clearTimeout(this.showTimer);
168
       clearTimeout(this.termTimer);
169
       clearTimeout(this.bytargetTimer);
170
    },
171
172
    // reset status variables
173
    reset: function ()
174
    {
175
        if ( this.useSessions ) {
176
            this.sessionID = null;
177
            this.initStatusOK = false;
178
            this.pingStatusOK = false;
179
            clearTimeout(this.pingTimer);
180
        }
181
        this.searchStatusOK = false;
182
        this.stop();
183
184
        if ( this.resetCallback )
185
                this.resetCallback();
186
    },
187
188
    init: function (sessionId, serviceId)
189
    {
190
        this.reset();
191
192
        // session id as a param
193
        if (sessionId && this.useSessions ) {
194
            this.initStatusOK = true;
195
            this.sessionID = sessionId;
196
            this.ping();
197
        // old school direct pazpar2 init
198
        } else if (this.useSessions) {
199
            var context = this;
200
            var request = new pzHttpRequest(this.pz2String, this.errorHandler);
201
            var opts = {'command' : 'init'};
202
            if (serviceId) opts.service = serviceId;
203
            request.safeGet(
204
                opts,
205
                function(data) {
206
                    if ( data.getElementsByTagName("status")[0]
207
                            .childNodes[0].nodeValue == "OK" ) {
208
                        if ( data.getElementsByTagName("protocol")[0]
209
                                .childNodes[0].nodeValue
210
                            != context.suppProtoVer )
211
                            throw new Error(
212
                                "Server's protocol not supported by the client"
213
                            );
214
                        context.initStatusOK = true;
215
                        context.sessionID =
216
                            data.getElementsByTagName("session")[0]
217
                                .childNodes[0].nodeValue;
218
                        if (data.getElementsByTagName("keepAlive").length > 0) {
219
                            context.keepAlive = data.getElementsByTagName("keepAlive")[0].childNodes[0].nodeValue;
220
                        }
221
                        context.pingTimer =
222
                            setTimeout(
223
                                function () {
224
                                    context.ping();
225
                                },
226
                                context.keepAlive
227
                            );
228
                        if ( context.initCallback )
229
                            context.initCallback();
230
                    }
231
                    else
232
                        context.throwError('Init failed. Malformed WS resonse.',
233
                                            110);
234
                }
235
            );
236
        // when through proxy no need to init
237
        } else {
238
            this.initStatusOK = true;
239
	}
240
    },
241
    // no need to ping explicitly
242
    ping: function ()
243
    {
244
        // pinging only makes sense when using pazpar2 directly
245
        if( !this.initStatusOK || !this.useSessions )
246
            throw new Error(
247
            'Pz2.js: Ping not allowed (proxy mode) or session not initialized.'
248
            );
249
        var context = this;
250
251
        clearTimeout(context.pingTimer);
252
253
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
254
        request.safeGet(
255
            { "command": "ping", "session": this.sessionID, "windowid" : window.name },
256
            function(data) {
257
                if ( data.getElementsByTagName("status")[0]
258
                        .childNodes[0].nodeValue == "OK" ) {
259
                    context.pingStatusOK = true;
260
                    context.pingTimer =
261
                        setTimeout(
262
                            function () {
263
                                context.ping();
264
                            },
265
                            context.keepAlive
266
                        );
267
                }
268
                else
269
                    context.throwError('Ping failed. Malformed WS resonse.',
270
                                        111);
271
            }
272
        );
273
    },
274
    search: function (query, num, sort, filter, showfrom, addParamsArr)
275
    {
276
        clearTimeout(this.statTimer);
277
        clearTimeout(this.showTimer);
278
        clearTimeout(this.termTimer);
279
        clearTimeout(this.bytargetTimer);
280
281
        this.showCounter = 0;
282
        this.termCounter = 0;
283
        this.bytargetCounter = 0;
284
        this.statCounter = 0;
285
        this.activeClients = 1;
286
287
        // no proxy mode
288
        if( !this.initStatusOK )
289
            throw new Error('Pz2.js: session not initialized.');
290
291
        if( query !== undefined )
292
            this.currQuery = query;
293
        else
294
            throw new Error("Pz2.js: no query supplied to the search command.");
295
296
        if ( showfrom !== undefined )
297
            var start = showfrom;
298
        else
299
            var start = 0;
300
301
	var searchParams = {
302
          "command": "search",
303
          "query": this.currQuery,
304
          "session": this.sessionID,
305
          "windowid" : window.name
306
        };
307
308
        if( sort !== undefined ) {
309
            this.currentSort = sort;
310
	    searchParams["sort"] = sort;
311
	}
312
        if (filter !== undefined)
313
	        searchParams["filter"] = filter;
314
315
        // copy additional parmeters, do not overwrite
316
        if (addParamsArr != undefined) {
317
            for (var prop in addParamsArr) {
318
                if (!searchParams.hasOwnProperty(prop))
319
                    searchParams[prop] = addParamsArr[prop];
320
            }
321
        }
322
323
        var context = this;
324
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
325
        request.safeGet(
326
            searchParams,
327
            function(data) {
328
                if ( data.getElementsByTagName("status")[0]
329
                        .childNodes[0].nodeValue == "OK" ) {
330
                    context.searchStatusOK = true;
331
                    //piggyback search
332
                    context.show(start, num, sort);
333
                    if (context.statCallback)
334
                        context.stat();
335
                    if (context.termlistCallback)
336
                        context.termlist();
337
                    if (context.bytargetCallback)
338
                        context.bytarget();
339
                }
340
                else
341
                    context.throwError('Search failed. Malformed WS resonse.',
342
                                        112);
343
            }
344
        );
345
    },
346
    stat: function()
347
    {
348
        if( !this.initStatusOK )
349
            throw new Error('Pz2.js: session not initialized.');
350
351
        // if called explicitly takes precedence
352
        clearTimeout(this.statTimer);
353
354
        var context = this;
355
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
356
        request.safeGet(
357
            { "command": "stat", "session": this.sessionID, "windowid" : window.name },
358
            function(data) {
359
                if ( data.getElementsByTagName("stat") ) {
360
                    var activeClients =
361
                        Number( data.getElementsByTagName("activeclients")[0]
362
                                    .childNodes[0].nodeValue );
363
                    context.activeClients = activeClients;
364
365
		    var stat = Element_parseChildNodes(data.documentElement);
366
367
                    context.statCounter++;
368
		    var delay = context.statTime
369
                        + context.statCounter * context.dumpFactor;
370
371
                    if ( activeClients > 0 )
372
                        context.statTimer =
373
                            setTimeout(
374
                                function () {
375
                                    context.stat();
376
                                },
377
                                delay
378
                            );
379
                    context.statCallback(stat);
380
                }
381
                else
382
                    context.throwError('Stat failed. Malformed WS resonse.',
383
                                        113);
384
            }
385
        );
386
    },
387
    show: function(start, num, sort, query_state)
388
    {
389
        if( !this.searchStatusOK && this.useSessions )
390
            throw new Error(
391
                'Pz2.js: show command has to be preceded with a search command.'
392
            );
393
394
        // if called explicitly takes precedence
395
        clearTimeout(this.showTimer);
396
397
        if( sort !== undefined )
398
            this.currentSort = sort;
399
        if( start !== undefined )
400
            this.currentStart = Number( start );
401
        if( num !== undefined )
402
            this.currentNum = Number( num );
403
404
        var context = this;
405
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
406
	var requestParameters =
407
          {
408
              "command": "show",
409
              "session": this.sessionID,
410
              "start": this.currentStart,
411
              "num": this.currentNum,
412
              "sort": this.currentSort,
413
              "block": 1,
414
              "type": this.showResponseType,
415
              "windowid" : window.name
416
          };
417
        if (query_state)
418
          requestParameters["query-state"] = query_state;
419
	if (this.version && this.version > 0)
420
	    requestParameters["version"] = this.version;
421
        request.safeGet(
422
	  requestParameters,
423
          function(data, type) {
424
            var show = null;
425
            var activeClients = 0;
426
            if (type === "json") {
427
              show = {};
428
              activeClients = Number(data.activeclients[0]);
429
              show.activeclients = activeClients;
430
              show.merged = Number(data.merged[0]);
431
              show.total = Number(data.total[0]);
432
              show.start = Number(data.start[0]);
433
              show.num = Number(data.num[0]);
434
              show.hits = data.hit;
435
            } else if (data.getElementsByTagName("status")[0]
436
                  .childNodes[0].nodeValue == "OK") {
437
                // first parse the status data send along with records
438
                // this is strictly bound to the format
439
                activeClients =
440
                  Number(data.getElementsByTagName("activeclients")[0]
441
                      .childNodes[0].nodeValue);
442
                show = {
443
                  "activeclients": activeClients,
444
                  "merged":
445
                    Number( data.getElementsByTagName("merged")[0]
446
                        .childNodes[0].nodeValue ),
447
                  "total":
448
                    Number( data.getElementsByTagName("total")[0]
449
                        .childNodes[0].nodeValue ),
450
                  "start":
451
                    Number( data.getElementsByTagName("start")[0]
452
                        .childNodes[0].nodeValue ),
453
                  "num":
454
                    Number( data.getElementsByTagName("num")[0]
455
                        .childNodes[0].nodeValue ),
456
                  "hits": []
457
                };
458
                // parse all the first-level nodes for all <hit> tags
459
                var hits = data.getElementsByTagName("hit");
460
                for (i = 0; i < hits.length; i++)
461
                  show.hits[i] = Element_parseChildNodes(hits[i]);
462
            } else {
463
              context.throwError('Show failed. Malformed WS resonse.',
464
                  114);
465
            };
466
467
	    var approxNode = data.getElementsByTagName("approximation");
468
	    if (approxNode && approxNode[0] && approxNode[0].childNodes[0] && approxNode[0].childNodes[0].nodeValue)
469
		show['approximation'] =
470
		  Number( approxNode[0].childNodes[0].nodeValue);
471
472
473
	      data.getElementsByTagName("")
474
            context.activeClients = activeClients;
475
            context.showCounter++;
476
            var delay = context.showTime;
477
            if (context.showCounter > context.showFastCount)
478
              delay += context.showCounter * context.dumpFactor;
479
            if ( activeClients > 0 )
480
              context.showTimer = setTimeout(
481
                function () {
482
                  context.show();
483
                },
484
                delay);
485
            context.showCallback(show);
486
          }
487
        );
488
    },
489
    record: function(id, offset, syntax, handler)
490
    {
491
        // we may call record with no previous search if in proxy mode
492
        if(!this.searchStatusOK && this.useSessions)
493
           throw new Error(
494
            'Pz2.js: record command has to be preceded with a search command.'
495
            );
496
497
        if( id !== undefined )
498
            this.currRecID = id;
499
500
	var recordParams = {
501
            "command": "record",
502
            "session": this.sessionID,
503
            "id": this.currRecID,
504
            "windowid" : window.name
505
        };
506
507
	this.currRecOffset = null;
508
        if (offset != undefined) {
509
	    recordParams["offset"] = offset;
510
            this.currRecOffset = offset;
511
        }
512
513
        if (syntax != undefined)
514
            recordParams['syntax'] = syntax;
515
516
        //overwrite default callback id needed
517
        var callback = this.recordCallback;
518
        var args = undefined;
519
        if (handler != undefined) {
520
            callback = handler['callback'];
521
            args = handler['args'];
522
        }
523
524
        var context = this;
525
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
526
527
        request.safeGet(
528
	    recordParams,
529
            function(data) {
530
                var recordNode;
531
                var record;
532
                //raw record
533
                if (context.currRecOffset !== null) {
534
                    record = new Array();
535
                    record['xmlDoc'] = data;
536
                    record['offset'] = context.currRecOffset;
537
                    callback(record, args);
538
                //pz2 record
539
                } else if ( recordNode =
540
                    data.getElementsByTagName("record")[0] ) {
541
                    // if stylesheet was fetched do not parse the response
542
                    if ( context.xslDoc ) {
543
                        record = new Array();
544
                        record['xmlDoc'] = data;
545
                        record['xslDoc'] = context.xslDoc;
546
                        record['recid'] =
547
                            recordNode.getElementsByTagName("recid")[0]
548
                                .firstChild.nodeValue;
549
                    //parse record
550
                    } else {
551
                        record = Element_parseChildNodes(recordNode);
552
                    }
553
		    var activeClients =
554
		       Number( data.getElementsByTagName("activeclients")[0]
555
				.childNodes[0].nodeValue );
556
		    context.activeClients = activeClients;
557
                    context.recordCounter++;
558
                    var delay = context.recordTime + context.recordCounter * context.dumpFactor;
559
                    if ( activeClients > 0 )
560
                        context.recordTimer =
561
                           setTimeout (
562
                               function() {
563
                                  context.record(id, offset, syntax, handler);
564
                                  },
565
                                  delay
566
                               );
567
                    callback(record, args);
568
                }
569
                else
570
                    context.throwError('Record failed. Malformed WS resonse.',
571
                                        115);
572
            }
573
        );
574
    },
575
576
    termlist: function()
577
    {
578
        if( !this.searchStatusOK && this.useSessions )
579
            throw new Error(
580
            'Pz2.js: termlist command has to be preceded with a search command.'
581
            );
582
583
        // if called explicitly takes precedence
584
        clearTimeout(this.termTimer);
585
586
        var context = this;
587
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
588
        request.safeGet(
589
            {
590
                "command": "termlist",
591
                "session": this.sessionID,
592
                "name": this.termKeys,
593
                "windowid" : window.name,
594
		"version" : this.version
595
596
            },
597
            function(data) {
598
                if ( data.getElementsByTagName("termlist") ) {
599
                    var activeClients =
600
                        Number( data.getElementsByTagName("activeclients")[0]
601
                                    .childNodes[0].nodeValue );
602
                    context.activeClients = activeClients;
603
                    var termList = { "activeclients":  activeClients };
604
                    var termLists = data.getElementsByTagName("list");
605
                    //for each termlist
606
                    for (i = 0; i < termLists.length; i++) {
607
			var listName = termLists[i].getAttribute('name');
608
                        termList[listName] = new Array();
609
                        var terms = termLists[i].getElementsByTagName('term');
610
                        //for each term in the list
611
                        for (j = 0; j < terms.length; j++) {
612
                            var term = {
613
                                "name":
614
                                    (terms[j].getElementsByTagName("name")[0]
615
                                        .childNodes.length
616
                                    ? terms[j].getElementsByTagName("name")[0]
617
                                        .childNodes[0].nodeValue
618
                                    : 'ERROR'),
619
                                "freq":
620
                                    terms[j]
621
                                    .getElementsByTagName("frequency")[0]
622
                                    .childNodes[0].nodeValue || 'ERROR'
623
                            };
624
625
			    // Only for xtargets: id, records, filtered
626
                            var termIdNode =
627
                                terms[j].getElementsByTagName("id");
628
                            if(terms[j].getElementsByTagName("id").length)
629
                                term["id"] =
630
                                    termIdNode[0].childNodes[0].nodeValue;
631
                            termList[listName][j] = term;
632
633
			    var recordsNode  = terms[j].getElementsByTagName("records");
634
			    if (recordsNode && recordsNode.length)
635
				term["records"] = recordsNode[0].childNodes[0].nodeValue;
636
637
			    var filteredNode  = terms[j].getElementsByTagName("filtered");
638
			    if (filteredNode && filteredNode.length)
639
				term["filtered"] = filteredNode[0].childNodes[0].nodeValue;
640
641
                        }
642
                    }
643
644
                    context.termCounter++;
645
                    var delay = context.termTime
646
                        + context.termCounter * context.dumpFactor;
647
                    if ( activeClients > 0 )
648
                        context.termTimer =
649
                            setTimeout(
650
                                function () {
651
                                    context.termlist();
652
                                },
653
                                delay
654
                            );
655
656
                   context.termlistCallback(termList);
657
                }
658
                else
659
                    context.throwError('Termlist failed. Malformed WS resonse.',
660
                                        116);
661
            }
662
        );
663
664
    },
665
    bytarget: function()
666
    {
667
        if( !this.initStatusOK && this.useSessions )
668
            throw new Error(
669
            'Pz2.js: bytarget command has to be preceded with a search command.'
670
            );
671
672
        // no need to continue
673
        if( !this.searchStatusOK )
674
            return;
675
676
        // if called explicitly takes precedence
677
        clearTimeout(this.bytargetTimer);
678
679
        var context = this;
680
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
681
        request.safeGet(
682
            {
683
		"command": "bytarget",
684
		"session": this.sessionID,
685
		"block": 1,
686
		"windowid" : window.name,
687
		"version" : this.version
688
	    },
689
            function(data) {
690
                if ( data.getElementsByTagName("status")[0]
691
                        .childNodes[0].nodeValue == "OK" ) {
692
                    var targetNodes = data.getElementsByTagName("target");
693
                    var bytarget = new Array();
694
                    for ( i = 0; i < targetNodes.length; i++) {
695
                        bytarget[i] = new Array();
696
                        for( j = 0; j < targetNodes[i].childNodes.length; j++ ) {
697
                            if ( targetNodes[i].childNodes[j].nodeType
698
                                == Node.ELEMENT_NODE ) {
699
                                var nodeName =
700
                                    targetNodes[i].childNodes[j].nodeName;
701
				if (targetNodes[i].childNodes[j].firstChild != null)
702
				{
703
                                    var nodeText = targetNodes[i].childNodes[j]
704
					.firstChild.nodeValue;
705
                                    bytarget[i][nodeName] = nodeText;
706
				}
707
				else {
708
				    bytarget[i][nodeName] = "";
709
				}
710
711
712
                            }
713
                        }
714
                        if (bytarget[i]["state"]=="Client_Disconnected") {
715
                          bytarget[i]["hits"] = "Error";
716
                        } else if (bytarget[i]["state"]=="Client_Error") {
717
                          bytarget[i]["hits"] = "Error";
718
                        } else if (bytarget[i]["state"]=="Client_Working") {
719
                          bytarget[i]["hits"] = "...";
720
                        }
721
                        if (bytarget[i].diagnostic == "1") {
722
                          bytarget[i].diagnostic = "Permanent system error";
723
                        } else if (bytarget[i].diagnostic == "2") {
724
                          bytarget[i].diagnostic = "Temporary system error";
725
                        }
726
                        var targetsSuggestions = targetNodes[i].getElementsByTagName("suggestions");
727
                        if (targetsSuggestions != undefined && targetsSuggestions.length>0) {
728
                          var suggestions = targetsSuggestions[0];
729
                          bytarget[i]["suggestions"] = Element_parseChildNodes(suggestions);
730
                        }
731
                    }
732
733
                    context.bytargetCounter++;
734
                    var delay = context.bytargetTime
735
                        + context.bytargetCounter * context.dumpFactor;
736
                    if ( context.activeClients > 0 )
737
                        context.bytargetTimer =
738
                            setTimeout(
739
                                function () {
740
                                    context.bytarget();
741
                                },
742
                                delay
743
                            );
744
745
                    context.bytargetCallback(bytarget);
746
                }
747
                else
748
                    context.throwError('Bytarget failed. Malformed WS resonse.',
749
                                        117);
750
            }
751
        );
752
    },
753
754
    // just for testing, probably shouldn't be here
755
    showNext: function(page)
756
    {
757
        var step = page || 1;
758
        this.show( ( step * this.currentNum ) + this.currentStart );
759
    },
760
761
    showPrev: function(page)
762
    {
763
        if (this.currentStart == 0 )
764
            return false;
765
        var step = page || 1;
766
        var newStart = this.currentStart - (step * this.currentNum );
767
        this.show( newStart > 0 ? newStart : 0 );
768
    },
769
770
    showPage: function(pageNum)
771
    {
772
        //var page = pageNum || 1;
773
        this.show(pageNum * this.currentNum);
774
    }
775
};
776
777
/*
778
********************************************************************************
779
** AJAX HELPER CLASS ***********************************************************
780
********************************************************************************
781
*/
782
var pzHttpRequest = function ( url, errorHandler ) {
783
        this.maxUrlLength = 2048;
784
        this.request = null;
785
        this.url = url;
786
        this.errorHandler = errorHandler || null;
787
        this.async = true;
788
        this.requestHeaders = {};
789
790
        if ( window.XMLHttpRequest ) {
791
            this.request = new XMLHttpRequest();
792
        } else if ( window.ActiveXObject ) {
793
            try {
794
                this.request = new ActiveXObject( 'Msxml2.XMLHTTP' );
795
            } catch (err) {
796
                this.request = new ActiveXObject( 'Microsoft.XMLHTTP' );
797
            }
798
        }
799
};
800
801
802
pzHttpRequest.prototype =
803
{
804
    safeGet: function ( params, callback )
805
    {
806
        var encodedParams =  this.encodeParams(params);
807
        var url = this._urlAppendParams(encodedParams);
808
        if (url.length >= this.maxUrlLength) {
809
            this.requestHeaders["Content-Type"]
810
                = "application/x-www-form-urlencoded";
811
            this._send( 'POST', this.url, encodedParams, callback );
812
        } else {
813
            this._send( 'GET', url, '', callback );
814
        }
815
    },
816
817
    get: function ( params, callback )
818
    {
819
        this._send( 'GET', this._urlAppendParams(this.encodeParams(params)),
820
            '', callback );
821
    },
822
823
    post: function ( params, data, callback )
824
    {
825
        this._send( 'POST', this._urlAppendParams(this.encodeParams(params)),
826
            data, callback );
827
    },
828
829
    load: function ()
830
    {
831
        this.async = false;
832
        this.request.open( 'GET', this.url, this.async );
833
        this.request.send('');
834
        if ( this.request.status == 200 )
835
            return this.request.responseXML;
836
    },
837
838
    encodeParams: function (params)
839
    {
840
        var sep = "";
841
        var encoded = "";
842
        for (var key in params) {
843
            if (params[key] != null) {
844
                encoded += sep + key + '=' + encodeURIComponent(params[key]);
845
                sep = '&';
846
            }
847
        }
848
        return encoded;
849
    },
850
851
    _send: function ( type, url, data, callback)
852
    {
853
        var context = this;
854
        this.callback = callback;
855
        this.async = true;
856
        this.request.open( type, url, this.async );
857
        for (var key in this.requestHeaders)
858
            this.request.setRequestHeader(key, this.requestHeaders[key]);
859
        this.request.onreadystatechange = function () {
860
            context._handleResponse(url); /// url used ONLY for error reporting
861
        }
862
        this.request.send(data);
863
    },
864
865
    _urlAppendParams: function (encodedParams)
866
    {
867
        if (encodedParams)
868
            return this.url + "?" + encodedParams;
869
        else
870
            return this.url;
871
    },
872
873
    _handleResponse: function (savedUrlForErrorReporting)
874
    {
875
        if ( this.request.readyState == 4 ) {
876
            // pick up appplication errors first
877
            var errNode = null;
878
            if (this.request.responseXML &&
879
                (errNode = this.request.responseXML.documentElement)
880
                && errNode.nodeName == 'error') {
881
                var errMsg = errNode.getAttribute("msg");
882
                var errCode = errNode.getAttribute("code");
883
                var errAddInfo = '';
884
                if (errNode.childNodes.length)
885
                    errAddInfo = ': ' + errNode.childNodes[0].nodeValue;
886
887
                var err = new Error(errMsg + errAddInfo);
888
                err.code = errCode;
889
890
                if (this.errorHandler) {
891
                    this.errorHandler(err);
892
                }
893
                else {
894
                    throw err;
895
                }
896
            } else if (this.request.status == 200 &&
897
                       this.request.responseXML == null) {
898
              if (this.request.responseText != null) {
899
                //assume JSON
900
901
		var json = null;
902
		var text = this.request.responseText;
903
		if (typeof window.JSON == "undefined")
904
		    json = eval("(" + text + ")");
905
		else {
906
		    try	{
907
			json = JSON.parse(text);
908
		    }
909
		    catch (e) {
910
			// Safari: eval will fail as well. Considering trying JSON2 (non-native implementation) instead
911
			/* DEBUG only works in mk2-mobile
912
			if (document.getElementById("log"))
913
			    document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
914
			*/
915
			try {
916
			    json = eval("(" + text + ")");
917
			}
918
			catch (e) {
919
			    /* DEBUG only works in mk2-mobile
920
			    if (document.getElementById("log"))
921
				document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
922
			    */
923
			}
924
		    }
925
		}
926
		this.callback(json, "json");
927
              } else {
928
                var err = new Error("XML response is empty but no error " +
929
                                    "for " + savedUrlForErrorReporting);
930
                err.code = -1;
931
                if (this.errorHandler) {
932
                    this.errorHandler(err);
933
                } else {
934
                    throw err;
935
                }
936
              }
937
            } else if (this.request.status == 200) {
938
                this.callback(this.request.responseXML);
939
            } else {
940
                var err = new Error("HTTP response not OK: "
941
                            + this.request.status + " - "
942
                            + this.request.statusText );
943
                err.code = '00' + this.request.status;
944
                if (this.errorHandler) {
945
                    this.errorHandler(err);
946
                }
947
                else {
948
                    throw err;
949
                }
950
            }
951
        }
952
    }
953
};
954
955
/*
956
********************************************************************************
957
** XML HELPER FUNCTIONS ********************************************************
958
********************************************************************************
959
*/
960
961
// DOMDocument
962
963
if ( window.ActiveXObject) {
964
    var DOMDoc = document;
965
} else {
966
    var DOMDoc = Document.prototype;
967
}
968
969
DOMDoc.newXmlDoc = function ( root )
970
{
971
    var doc;
972
973
    if (document.implementation && document.implementation.createDocument) {
974
        doc = document.implementation.createDocument('', root, null);
975
    } else if ( window.ActiveXObject ) {
976
        doc = new ActiveXObject("MSXML2.DOMDocument");
977
        doc.loadXML('<' + root + '/>');
978
    } else {
979
        throw new Error ('No XML support in this browser');
980
    }
981
982
    return doc;
983
}
984
985
986
DOMDoc.parseXmlFromString = function ( xmlString )
987
{
988
    var doc;
989
990
    if ( window.DOMParser ) {
991
        var parser = new DOMParser();
992
        doc = parser.parseFromString( xmlString, "text/xml");
993
    } else if ( window.ActiveXObject ) {
994
        doc = new ActiveXObject("MSXML2.DOMDocument");
995
        doc.loadXML( xmlString );
996
    } else {
997
        throw new Error ("No XML parsing support in this browser.");
998
    }
999
1000
    return doc;
1001
}
1002
1003
DOMDoc.transformToDoc = function (xmlDoc, xslDoc)
1004
{
1005
    if ( window.XSLTProcessor ) {
1006
        var proc = new XSLTProcessor();
1007
        proc.importStylesheet( xslDoc );
1008
        return proc.transformToDocument(xmlDoc);
1009
    } else if ( window.ActiveXObject ) {
1010
        return document.parseXmlFromString(xmlDoc.transformNode(xslDoc));
1011
    } else {
1012
        alert( 'Unable to perform XSLT transformation in this browser' );
1013
    }
1014
}
1015
1016
// DOMElement
1017
1018
Element_removeFromDoc = function (DOM_Element)
1019
{
1020
    DOM_Element.parentNode.removeChild(DOM_Element);
1021
}
1022
1023
Element_emptyChildren = function (DOM_Element)
1024
{
1025
    while( DOM_Element.firstChild ) {
1026
        DOM_Element.removeChild( DOM_Element.firstChild )
1027
    }
1028
}
1029
1030
Element_appendTransformResult = function ( DOM_Element, xmlDoc, xslDoc )
1031
{
1032
    if ( window.XSLTProcessor ) {
1033
        var proc = new XSLTProcessor();
1034
        proc.importStylesheet( xslDoc );
1035
        var docFrag = false;
1036
        docFrag = proc.transformToFragment( xmlDoc, DOM_Element.ownerDocument );
1037
        DOM_Element.appendChild(docFrag);
1038
    } else if ( window.ActiveXObject ) {
1039
        DOM_Element.innerHTML = xmlDoc.transformNode( xslDoc );
1040
    } else {
1041
        alert( 'Unable to perform XSLT transformation in this browser' );
1042
    }
1043
}
1044
1045
Element_appendTextNode = function (DOM_Element, tagName, textContent )
1046
{
1047
    var node = DOM_Element.ownerDocument.createElement(tagName);
1048
    var text = DOM_Element.ownerDocument.createTextNode(textContent);
1049
1050
    DOM_Element.appendChild(node);
1051
    node.appendChild(text);
1052
1053
    return node;
1054
}
1055
1056
Element_setTextContent = function ( DOM_Element, textContent )
1057
{
1058
    if (typeof DOM_Element.textContent !== "undefined") {
1059
        DOM_Element.textContent = textContent;
1060
    } else if (typeof DOM_Element.innerText !== "undefined" ) {
1061
        DOM_Element.innerText = textContent;
1062
    } else {
1063
        throw new Error("Cannot set text content of the node, no such method.");
1064
    }
1065
}
1066
1067
Element_getTextContent = function (DOM_Element)
1068
{
1069
    if ( typeof DOM_Element.textContent != 'undefined' ) {
1070
        return DOM_Element.textContent;
1071
    } else if (typeof DOM_Element.text != 'undefined') {
1072
        return DOM_Element.text;
1073
    } else {
1074
        throw new Error("Cannot get text content of the node, no such method.");
1075
    }
1076
}
1077
1078
Element_parseChildNodes = function (node)
1079
{
1080
    var parsed = {};
1081
    var hasChildElems = false;
1082
    var textContent = '';
1083
1084
    if (node.hasChildNodes()) {
1085
        var children = node.childNodes;
1086
        for (var i = 0; i < children.length; i++) {
1087
            var child = children[i];
1088
            switch (child.nodeType) {
1089
              case Node.ELEMENT_NODE:
1090
                hasChildElems = true;
1091
                var nodeName = child.nodeName;
1092
                if (!(nodeName in parsed))
1093
                    parsed[nodeName] = [];
1094
                parsed[nodeName].push(Element_parseChildNodes(child));
1095
                break;
1096
              case Node.TEXT_NODE:
1097
                textContent += child.nodeValue;
1098
                break;
1099
              case Node.CDATA_SECTION_NODE:
1100
                textContent += child.nodeValue;
1101
                break;
1102
            }
1103
        }
1104
    }
1105
1106
    var attrs = node.attributes;
1107
    for (var i = 0; i < attrs.length; i++) {
1108
        hasChildElems = true;
1109
        var attrName = '@' + attrs[i].nodeName;
1110
        var attrValue = attrs[i].nodeValue;
1111
        parsed[attrName] = attrValue;
1112
    }
1113
1114
    // if no nested elements/attrs set value to text
1115
    if (hasChildElems)
1116
      parsed['#text'] = textContent;
1117
    else
1118
      parsed = textContent;
1119
1120
    return parsed;
1121
}
1122
1123
/* do not remove trailing bracket */
1124
}
(-)a/koha-tmpl/opac-tmpl/prog/en/css/opac.css (-1 / +26 lines)
Lines 1677-1682 strong em, em strong { Link Here
1677
	vertical-align: top;
1677
	vertical-align: top;
1678
	width: 10px;
1678
	width: 10px;
1679
}
1679
}
1680
.sourcecol {
1681
	vertical-align: top;
1682
	width: 100px;
1683
}
1680
#container {
1684
#container {
1681
	color : #000;
1685
	color : #000;
1682
}
1686
}
Lines 3006-3011 float:left; Link Here
3006
padding: 0.1em 0;
3010
padding: 0.1em 0;
3007
}
3011
}
3008
3012
3013
.notesrow label {
3014
    font-weight: bold;
3015
}
3016
.notesrow span {
3017
    display: block;
3018
}
3019
.notesrow textarea {
3020
    width: 100%;
3021
}
3022
3009
.thumbnail-shelfbrowser span {
3023
.thumbnail-shelfbrowser span {
3010
    margin: 0px auto;
3024
    margin: 0px auto;
3011
}
3025
}
Lines 3030-3036 padding: 0.1em 0; Link Here
3030
    background: #EEEEEE none;
3044
    background: #EEEEEE none;
3031
}
3045
}
3032
3046
3033
#overdrive-results {
3047
#overdrive-results, #pazpar2-results {
3034
    font-weight: bold;
3048
    font-weight: bold;
3035
    padding-left: 1em;
3049
    padding-left: 1em;
3036
}
3050
}
Lines 3074-3076 padding: 0.1em 0; Link Here
3074
    width: 35%;
3088
    width: 35%;
3075
    font-size: 111%;
3089
    font-size: 111%;
3076
}
3090
}
3091
3092
.thumbnail-shelfbrowser span {
3093
    margin: 0px auto;
3094
}
3095
3096
#modal-overlay {
3097
    background-color: white;
3098
    border-radius: 5px;
3099
    max-width: 75%;
3100
    padding: 15px;
3101
}
(-)a/koha-tmpl/opac-tmpl/prog/en/js/externalsearch.js (+106 lines)
Line 0 Link Here
1
if ( typeof KOHA == "undefined" || !KOHA ) {
2
    var KOHA = {};
3
}
4
5
KOHA.XSLTGet = ( function() {
6
    // Horrible browser hack, but required due to the following hard-to-detect and long-unfixed bug:
7
    // https://bugs.webkit.org/show_bug.cgi?id=60276
8
    var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
9
    var isSafari = /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
10
11
    if ( !isChrome && !isSafari ) return $.get;
12
13
    return function( url ) {
14
        var result = new jQuery.Deferred();
15
        var basepath = url.match( /(.*\/)*/ )[0];
16
17
        $.get( url ).done( function( xslDoc ) {
18
            var xslImports = xslDoc.getElementsByTagNameNS( 'http://www.w3.org/1999/XSL/Transform', 'import' );
19
            var importsRemaining = xslImports.length;
20
21
            if ( importsRemaining == 0 ) {
22
                result.resolve( xslDoc );
23
                return;
24
            }
25
26
            $.each( xslImports, function( i, importElem ) {
27
                var path = $( importElem ).attr( 'href' );
28
                if ( !/^(\/|https?:)/.test( path ) ) path = basepath + path;
29
30
                KOHA.XSLTGet( path ).done( function( subDoc ) {
31
                    importsRemaining--;
32
                    $( importElem ).replaceWith( subDoc.documentElement.childNodes );
33
34
                    if ( importsRemaining == 0 ) result.resolve( xslDoc );
35
                } ).fail( function() {
36
                    importsRemaining = -1;
37
38
                    result.reject();
39
                } );
40
            } );
41
        } ).fail( function() {
42
            result.reject();
43
        } );
44
45
        return result;
46
    };
47
} )();
48
49
KOHA.TransformToFragment = function( xmlDoc, xslDoc ) {
50
    if ( window.XSLTProcessor ) {
51
        var proc = new XSLTProcessor();
52
        proc.importStylesheet( xslDoc );
53
        proc.setParameter( null, 'showAvailability', false );
54
        return (new XMLSerializer).serializeToString( proc.transformToFragment( xmlDoc, document ) );
55
    } else if ( window.ActiveXObject ) {
56
        var xslt = new ActiveXObject( "Msxml2.XSLTemplate" );
57
        xslt.stylesheet = xslDoc;
58
        var xslProc = xslt.createProcessor();
59
        xslProc.input = xmlDoc;
60
        xslProc.addParameter( 'showAvailability', false );
61
        xslProc.transform();
62
        return xslProc.output;
63
    }
64
};
65
66
KOHA.ExternalSearch = ( function() {
67
    return {
68
        targets: {},
69
        Search: function( q, limit, callback ) {
70
            var targetIDs = [];
71
            var includedTargets = [];
72
73
            $.each( KOHA.ExternalSearch.targets, function ( url, info ) {
74
                if ( !info.disabled ) {
75
                    includedTargets.push( url );
76
                    targetIDs.push( info.id );
77
                }
78
            } );
79
80
            if ( KOHA.ExternalSearch._pz !== undefined ) {
81
                afterinit( KOHA.ExternalSearch._pz );
82
            } else {
83
                $.get( '/cgi-bin/koha/svc/pazpar2_init', { targets: targetIDs.join(',') }, function( data ) {
84
                    KOHA.ExternalSearch._pz = new pz2({
85
                        sessionId: data.sessionID,
86
                        onshow: callback,
87
                        errorhandler: function ( error ) { callback( { error: error } ) },
88
                    } );
89
                    afterinit( KOHA.ExternalSearch._pz );
90
                } );
91
            }
92
93
            function afterinit( pz ) {
94
                pz.search( q, limit, 'relevance:0', 'pz:id=' + includedTargets.join( '|' ) );
95
            }
96
        },
97
        Fetch: function( offset, callback ) {
98
            var pz = KOHA.ExternalSearch._pz;
99
            pz.showCallback = callback;
100
            pz.show( offset );
101
        },
102
        GetDetailedRecord: function( recid, callback ) {
103
            KOHA.ExternalSearch._pz.record( recid, 0, undefined, { callback: callback } );
104
        },
105
    };
106
} )();
(-)a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/plugins/jquery.lightbox_me.js (+254 lines)
Line 0 Link Here
1
/*
2
* $ lightbox_me
3
* By: Buck Wilson
4
* Version : 2.3
5
*
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
9
*
10
*     http://www.apache.org/licenses/LICENSE-2.0
11
*
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
17
*/
18
19
20
(function($) {
21
22
    $.fn.lightbox_me = function(options) {
23
24
        return this.each(function() {
25
26
            var
27
                opts = $.extend({}, $.fn.lightbox_me.defaults, options),
28
                $overlay = $(),
29
                $self = $(this),
30
                $iframe = $('<iframe id="foo" style="z-index: ' + (opts.zIndex + 1) + ';border: none; margin: 0; padding: 0; position: absolute; width: 100%; height: 100%; top: 0; left: 0; filter: mask();"/>'),
31
                ie6 = ($.browser.msie && $.browser.version < 7);
32
33
            if (opts.showOverlay) {
34
                //check if there's an existing overlay, if so, make subequent ones clear
35
               var $currentOverlays = $(".js_lb_overlay:visible");
36
                if ($currentOverlays.length > 0){
37
                    $overlay = $('<div class="lb_overlay_clear js_lb_overlay"/>');
38
                } else {
39
                    $overlay = $('<div class="' + opts.classPrefix + '_overlay js_lb_overlay"/>');
40
                }
41
            }
42
43
            /*----------------------------------------------------
44
               DOM Building
45
            ---------------------------------------------------- */
46
            if (ie6) {
47
                var src = /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank';
48
                $iframe.attr('src', src);
49
                $('body').append($iframe);
50
            } // iframe shim for ie6, to hide select elements
51
            $('body').append($self.hide()).append($overlay);
52
53
54
            /*----------------------------------------------------
55
               Overlay CSS stuffs
56
            ---------------------------------------------------- */
57
58
            // set css of the overlay
59
            if (opts.showOverlay) {
60
                setOverlayHeight(); // pulled this into a function because it is called on window resize.
61
                $overlay.css({ position: 'absolute', width: '100%', top: 0, left: 0, right: 0, bottom: 0, zIndex: (opts.zIndex + 2), display: 'none' });
62
				if (!$overlay.hasClass('lb_overlay_clear')){
63
			$overlay.css(opts.overlayCSS);
64
                }
65
            }
66
67
            /*----------------------------------------------------
68
               Animate it in.
69
            ---------------------------------------------------- */
70
               //
71
            if (opts.showOverlay) {
72
                $overlay.fadeIn(opts.overlaySpeed, function() {
73
                    setSelfPosition();
74
                    $self[opts.appearEffect](opts.lightboxSpeed, function() { setOverlayHeight(); setSelfPosition(); opts.onLoad()});
75
                });
76
            } else {
77
                setSelfPosition();
78
                $self[opts.appearEffect](opts.lightboxSpeed, function() { opts.onLoad()});
79
            }
80
81
            /*----------------------------------------------------
82
               Hide parent if parent specified (parentLightbox should be jquery reference to any parent lightbox)
83
            ---------------------------------------------------- */
84
            if (opts.parentLightbox) {
85
                opts.parentLightbox.fadeOut(200);
86
            }
87
88
89
            /*----------------------------------------------------
90
               Bind Events
91
            ---------------------------------------------------- */
92
93
            $(window).resize(setOverlayHeight)
94
                     .resize(setSelfPosition)
95
                     .scroll(setSelfPosition);
96
97
            $(window).bind('keyup.lightbox_me', observeKeyPress);
98
99
            if (opts.closeClick) {
100
                $overlay.click(function(e) { closeLightbox(); e.preventDefault; });
101
            }
102
            $self.delegate(opts.closeSelector, "click", function(e) {
103
                closeLightbox(); e.preventDefault();
104
            });
105
            $self.bind('close', closeLightbox);
106
            $self.bind('reposition', setSelfPosition);
107
108
109
110
            /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
111
              -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
112
113
114
            /*----------------------------------------------------
115
               Private Functions
116
            ---------------------------------------------------- */
117
118
            /* Remove or hide all elements */
119
            function closeLightbox() {
120
                var s = $self[0].style;
121
                if (opts.destroyOnClose) {
122
                    $self.add($overlay).remove();
123
                } else {
124
                    $self.add($overlay).hide();
125
                }
126
127
                //show the hidden parent lightbox
128
                if (opts.parentLightbox) {
129
                    opts.parentLightbox.fadeIn(200);
130
                }
131
132
                $iframe.remove();
133
134
				// clean up events.
135
                $self.undelegate(opts.closeSelector, "click");
136
137
                $(window).unbind('reposition', setOverlayHeight);
138
                $(window).unbind('reposition', setSelfPosition);
139
                $(window).unbind('scroll', setSelfPosition);
140
                $(window).unbind('keyup.lightbox_me');
141
                if (ie6)
142
                    s.removeExpression('top');
143
                opts.onClose();
144
            }
145
146
147
            /* Function to bind to the window to observe the escape/enter key press */
148
            function observeKeyPress(e) {
149
                if((e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) && opts.closeEsc) closeLightbox();
150
            }
151
152
153
            /* Set the height of the overlay
154
                    : if the document height is taller than the window, then set the overlay height to the document height.
155
                    : otherwise, just set overlay height: 100%
156
            */
157
            function setOverlayHeight() {
158
                if ($(window).height() < $(document).height()) {
159
                    $overlay.css({height: $(document).height() + 'px'});
160
                     $iframe.css({height: $(document).height() + 'px'});
161
                } else {
162
                    $overlay.css({height: '100%'});
163
                    if (ie6) {
164
                        $('html,body').css('height','100%');
165
                        $iframe.css('height', '100%');
166
                    } // ie6 hack for height: 100%; TODO: handle this in IE7
167
                }
168
            }
169
170
171
            /* Set the position of the modal'd window ($self)
172
                    : if $self is taller than the window, then make it absolutely positioned
173
                    : otherwise fixed
174
            */
175
            function setSelfPosition() {
176
                var s = $self[0].style;
177
178
                // reset CSS so width is re-calculated for margin-left CSS
179
                $self.css({left: '50%', marginLeft: ($self.outerWidth() / 2) * -1,  zIndex: (opts.zIndex + 3) });
180
181
182
                /* we have to get a little fancy when dealing with height, because lightbox_me
183
                    is just so fancy.
184
                 */
185
186
                // if the height of $self is bigger than the window and self isn't already position absolute
187
                if (($self.height() + 80  >= $(window).height()) && ($self.css('position') != 'absolute' || ie6)) {
188
189
                    // we are going to make it positioned where the user can see it, but they can still scroll
190
                    // so the top offset is based on the user's scroll position.
191
                    var topOffset = $(document).scrollTop() + 40;
192
                    $self.css({position: 'absolute', top: topOffset + 'px', marginTop: 0})
193
                    if (ie6) {
194
                        s.removeExpression('top');
195
                    }
196
                } else if ($self.height()+ 80  < $(window).height()) {
197
                    //if the height is less than the window height, then we're gonna make this thing position: fixed.
198
                    // in ie6 we're gonna fake it.
199
                    if (ie6) {
200
                        s.position = 'absolute';
201
                        if (opts.centered) {
202
                            s.setExpression('top', '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"')
203
                            s.marginTop = 0;
204
                        } else {
205
                            var top = (opts.modalCSS && opts.modalCSS.top) ? parseInt(opts.modalCSS.top) : 0;
206
                            s.setExpression('top', '((blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"')
207
                        }
208
                    } else {
209
                        if (opts.centered) {
210
                            $self.css({ position: 'fixed', top: '50%', marginTop: ($self.outerHeight() / 2) * -1})
211
                        } else {
212
                            $self.css({ position: 'fixed'}).css(opts.modalCSS);
213
                        }
214
215
                    }
216
                }
217
            }
218
219
        });
220
221
222
223
    };
224
225
    $.fn.lightbox_me.defaults = {
226
227
        // animation
228
        appearEffect: "fadeIn",
229
        appearEase: "",
230
        overlaySpeed: 250,
231
        lightboxSpeed: 300,
232
233
        // close
234
        closeSelector: ".close",
235
        closeClick: true,
236
        closeEsc: true,
237
238
        // behavior
239
        destroyOnClose: false,
240
        showOverlay: true,
241
        parentLightbox: false,
242
243
        // callbacks
244
        onLoad: function() {},
245
        onClose: function() {},
246
247
        // style
248
        classPrefix: 'lb',
249
        zIndex: 999,
250
        centered: false,
251
        modalCSS: {top: '40px'},
252
        overlayCSS: {background: 'black', opacity: .3}
253
    }
254
})(jQuery);
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-external-search.tt (+240 lines)
Line 0 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo; External search for '[% q | html %]'
3
[% INCLUDE 'doc-head-close.inc' %]
4
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.lightbox_me.js"></script>
5
<script type="text/javascript" src="/opac-tmpl/lib/pz2.js"></script>
6
<script type="text/javascript" src="[% themelang %]/js/externalsearch.js"></script>
7
<script type="text/javascript">
8
var querystring = "[% q |replace( "'", "\'" ) |replace( '\n', '\\n' ) |replace( '\r', '\\r' ) |html %]";
9
var results_per_page = [% OPACnumSearchResults %];
10
KOHA.ExternalSearch.targets = {
11
    [% FOREACH target IN external_search_targets %]
12
        '[% target.host %]:[% target.port %]/[% target.db %]': {
13
            name: '[% target.name %]',
14
            syntax: '[% target.syntax %]',
15
        },
16
    [% END %]
17
};
18
19
var xsltResultStylesheets = {
20
    [% FOREACH stylesheet IN xslt_result_stylesheets %]
21
    '[% stylesheet.syntax %]': KOHA.XSLTGet( '[% stylesheet.url %]' ),
22
    [% END %]
23
};
24
25
var xsltDetailStylesheets = {
26
    [% FOREACH stylesheet IN xslt_detail_stylesheets %]
27
    '[% stylesheet.syntax %]': KOHA.XSLTGet( '[% stylesheet.url %]' ),
28
    [% END %]
29
};
30
31
var recordCache = {};
32
var resultRenderCache = {};
33
34
function showResult( syntax, recid ) {
35
    if ( recordCache[ recid ] ) {
36
        done( recordCache[ recid ] );
37
    } else {
38
        KOHA.ExternalSearch.GetDetailedRecord( recid, function( record ) {
39
            done( recordCache[ recid ] = record.xmlDoc );
40
        } );
41
    }
42
43
    function done( record ) {
44
        xsltResultStylesheets[ syntax ].done( function( xslDoc ) {
45
            var fragment = resultRenderCache[ recid ] = KOHA.TransformToFragment( record, xslDoc );
46
            var $tr = $( '#results tr' ).filter( function() { return $( this ).data( 'recid' ) == recid } );
47
            $tr.find( '.info' ).html( fragment );
48
            $tr.find( 'a' ).attr( 'href', '#' ).click( function() {
49
                showDetail( syntax, recid );
50
51
                return false;
52
            } );
53
        } );
54
    }
55
}
56
57
function showDetail( syntax, recid ) {
58
    var record = recordCache[ recid ];
59
    console.info((new XMLSerializer).serializeToString( record ));
60
61
    xsltDetailStylesheets[ syntax ].done( function( xslDoc ) {
62
        var fragment = KOHA.TransformToFragment( record, xslDoc );
63
64
        $( '#modal-overlay' ).html( fragment ).lightbox_me( {
65
            centered: true,
66
        } );
67
    } );
68
}
69
70
function search( offset, reset_search ) {
71
    $( '#pazpar2-status' ).html( _("Searching external targets...") + '<img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" />' );
72
73
    if ( reset_search ) {
74
        KOHA.ExternalSearch.Search( querystring, results_per_page, callback );
75
    } else {
76
        KOHA.ExternalSearch.Fetch( offset, callback );
77
    }
78
79
    function callback( data ) {
80
        if ( data.error ) {
81
            $( '#pazpar2-status' ).html( '<strong class="unavailable">' + _("Error searching external targets.") + '</strong>' );
82
            return;
83
        }
84
85
        if ( !data.total ) {
86
            $( '#pazpar2-status' ).html( '<strong>' + _("No results found in the external targets.") + '</strong>' );
87
            return;
88
        }
89
90
        $( '#results tbody' ).empty();
91
92
        $( '#pazpar2-status' ).html( '<strong>' + _("Found __RESULTS__ results in __TARGETS__ external targets.").replace('__RESULTS__', data.total).replace('__TARGETS__', $( '#targets-facet input:checked' ).length );
93
94
        for ( var i = 0; data.hits[i]; i++ ) {
95
            var hit = data.hits[i];
96
            var results = [];
97
            var recordSyntax = KOHA.ExternalSearch.targets[ hit.location[0]['@id'] ].syntax;
98
99
            results.push( '<tr>' );
100
101
            results.push( '<td class="sourcecol">', hit.location[0]['@name'], '</td>' );
102
103
            results.push( '<td class="info">' );
104
105
            if ( resultRenderCache[ hit.recid[0] ] ) {
106
                results.push( resultRenderCache[ hit.recid[0] ] );
107
            } else {
108
                results.push( hit['md-work-title'] ? hit['md-work-title'][0] : _("Loading...") );
109
                showResult( recordSyntax, hit.recid[0] );
110
            }
111
112
            results.push( '</td>' );
113
114
            results.push( '</tr>' );
115
            var $tr = $( results.join( '' ) );
116
            $tr.data( 'recid', hit.recid[0] );
117
            $( '#results tbody' ).append( $tr );
118
119
            ( function( hit, recordSyntax ) {
120
                $tr.find( 'a' ).attr( 'href', '#' ).click( function() {
121
                    showDetail( recordSyntax, hit.recid[0] );
122
123
                    return false;
124
                } );
125
            } )( hit, recordSyntax );
126
        }
127
128
        $( '#results tr:odd' ).addClass( 'highlight' );
129
130
        var pages = [];
131
        var cur_page = data.start / results_per_page;
132
        var max_page = Math.floor( data.total / results_per_page );
133
134
        if ( cur_page != 0 ) {
135
            pages.push( '<a class="nav" href="#" data-offset="' + (offset - results_per_page) + '">&lt;&lt; ' + _("Previous") + '</a>' );
136
        }
137
138
        for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) {
139
            if ( page == cur_page ) {
140
                pages.push( ' <span class="current">' + ( page + 1 ) + '</span>' );
141
            } else {
142
                pages.push( ' <a class="nav" href="#" data-offset="' + ( page * results_per_page ) + '">' + ( page + 1 ) + '</a>' );
143
            }
144
        }
145
146
        if ( cur_page < max_page ) {
147
            pages.push( ' <a class="nav" href="#" data-offset="' + (offset + results_per_page) + '">' + _("Next") + ' >></a>' );
148
        }
149
150
        if ( pages.length > 1 ) $( '#top-pages, #bottom-pages' ).find( '.pages' ).html( pages.join( '' ) );
151
    }
152
}
153
154
$( document ).ready( function() {
155
    $( '#breadcrumbs p' )
156
        .append( ' ' )
157
        .append( '<span id="pazpar2-status"></span>' );
158
159
    $( document ).on( 'click', 'a.nav', function() {
160
        search( $( this ).data( 'offset' ) );
161
        return false;
162
    });
163
164
    var reSearchTimeout;
165
166
    $( '#targets-facet input' ).each( function() {
167
        $( this ).click( function() {
168
            KOHA.ExternalSearch.targets[ $( this ).data( 'url' ) ].disabled = !this.checked;
169
170
            if ( reSearchTimeout ) clearTimeout( reSearchTimeout );
171
172
            reSearchTimeout = setTimeout( function() {
173
                if ( $( '#targets-facet input:checked' ).length ) search( 0, true );
174
            }, 1000 );
175
        } );
176
177
        KOHA.ExternalSearch.targets[ $( this ).data( 'url' ) ].disabled = !this.checked;
178
    } );
179
180
    search( 0, true );
181
} );
182
</script>
183
<style>
184
.actions a.addtocart {
185
    display: inline;
186
}
187
</style>
188
</head>
189
<body>
190
<div id="doc3" class="yui-t1">
191
192
<div id="bd">
193
    [% INCLUDE 'masthead.inc' %]
194
195
    <h1>External search for '[% q | html %]'</h1>
196
    <div id="breadcrumbs">
197
        <p></p>
198
    </div>
199
200
    <div id="yui-main"><div class="yui-b searchresults">
201
        <div id="top-pages">
202
            <div class="pages">
203
            </div>
204
        </div>
205
        <table id="results">
206
            <tbody>
207
            </tbody>
208
        </table>
209
        <div id="bottom-pages">
210
            <div class="pages">
211
            </div>
212
        </div>
213
    </div></div>
214
215
    <div class="yui-b"><div id="facetcontainer" class="container">
216
    <div id="search-facets">
217
218
        <h4>Refine your search</h4>
219
220
        <ul>
221
            <li id="targets-facet">
222
                Targets
223
                <ul>
224
                    [% FOREACH target = external_search_targets %]
225
                    <li>
226
                        <input data-url="[% target.host %]:[% target.port %]/[% target.db %]" type="checkbox" id="target-[% loop.index %]" checked />
227
                        <label for="target-[% loop.index %]">[% target.name %]
228
                    </li>
229
                    [% END %]
230
                </ul>
231
            </li>
232
        </ul>
233
    </div>
234
    </div></div>
235
</div>
236
237
</div>
238
239
<div id="modal-overlay" style="display: none"></div>
240
[% INCLUDE 'opac-bottom.inc' %]
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt (-10 / +49 lines)
Lines 16-22 Link Here
16
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />
16
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />
17
[% END %]
17
[% END %]
18
18
19
<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script>
19
[% IF ( OverDriveEnabled ) %]<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script>[% END %]
20
[% IF ( Koha.Preference('OPACSearchExternalTargets') ) %]
21
<script type="text/javascript" src="/opac-tmpl/lib/pz2.js"></script>
22
<script type="text/javascript" src="[% themelang %]/js/externalsearch.js"></script>
23
[% END %]
20
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
24
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
21
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
25
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
22
[% END %]<script type="text/javascript">
26
[% END %]<script type="text/javascript">
Lines 266-289 $(document).ready(function(){ Link Here
266
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
270
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
267
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
271
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
268
    [% END %]
272
    [% END %]
273
    function StartExternalSearch(name, text) {
274
        $( '#breadcrumbs p' )
275
            .eq( 0 )
276
            .append( ' <span id="' + name + '-results">' + text + '... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
277
    }
278
    function FailExternalSearch( name, text ) {
279
        $( '#' + name + '-results' ).html( text );
280
    }
281
    function FinishExternalSearch( name, text, numItems, url ) {
282
        if ( numItems ) {
283
            $( '#' + name + '-results' ).html( text.replace( '__LINK__', '<a href="' + url + '">'  + numItems + _(" results") + '</a>' ) );
284
        } else {
285
            $( '#' + name + '-results' ).remove();
286
        }
287
    }
288
269
    [% IF ( OverDriveEnabled ) %]
289
    [% IF ( OverDriveEnabled ) %]
270
        var $overdrive_results = $( '<span id="overdrive-results">' + _( 'Searching OverDrive...' ) + ' <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
290
        StartExternalSearch( 'overdrive', _("Searching OverDrive") );
271
        $( '#breadcrumbs p' ).eq(0)
272
            .append( ' ' )
273
            .append( $overdrive_results );
274
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
291
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
275
            if ( data.error ) {
292
            if ( data.error ) {
276
                $overdrive_results.html( _( 'Error searching OverDrive collection' ) );
293
                FailExternalSearch( 'overdrive', _("Error searching OverDrive collection") );
277
                return;
294
                return;
278
            }
295
            }
279
296
280
            if ( data.totalItems ) {
297
            FinishExternalSearch( 'overdrive', _("Found __LINK__ in OverDrive collection"), data.totalItems, '/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) );
281
                $overdrive_results.html( _( 'Found' ) + ' <a href="/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) + '">' + data.totalItems + ' ' + _( 'results' ) + '</a> ' + _( 'in OverDrive collection' ) );
298
        } );
282
            } else {
299
    [% END %]
283
                $overdrive_results.remove();
300
301
    [% IF ( OPACSearchExternalTargets ) %]
302
        KOHA.ExternalSearch.targets = {
303
            [% FOREACH target IN external_search_targets %]
304
                '[% target.host %]:[% target.port %]/[% target.db %]': {
305
                    id: '[% target.target_id %]',
306
                    name: '[% target.name %]',
307
                    syntax: '[% target.syntax %]',
308
                },
309
            [% END %]
310
        };
311
        var num_targets = 0; $.each( KOHA.ExternalSearch.targets, function() { num_targets++ } );
312
        var first_succeeded;
313
314
        StartExternalSearch( 'pazpar2', _("Searching external targets") );
315
        KOHA.ExternalSearch.Search( querystring, 1, function( data ) {
316
            if ( data.error ) {
317
                if ( !first_succeeded ) FailExternalSearch( 'pazpar2', _("Error searching external targets") );
318
                return;
284
            }
319
            }
320
321
            first_succeeded = true;
322
            FinishExternalSearch( 'pazpar2', _("Found __LINK__ in ") + num_targets + _(" external targets"), data.total, '/cgi-bin/koha/opac-external-search.pl?q=' + escape( querystring ) );
285
        } );
323
        } );
286
    [% END %]
324
    [% END %]
325
287
[% END %]
326
[% END %]
288
327
289
[% IF ( TagsInputEnabled && loggedinusername ) %]
328
[% IF ( TagsInputEnabled && loggedinusername ) %]
(-)a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACResults.xsl (-1 / +5 lines)
Lines 5-15 Link Here
5
  xmlns:marc="http://www.loc.gov/MARC21/slim"
5
  xmlns:marc="http://www.loc.gov/MARC21/slim"
6
  xmlns:items="http://www.koha-community.org/items"
6
  xmlns:items="http://www.koha-community.org/items"
7
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
7
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
8
  xmlns="http://www.w3.org/1999/xhtml"
8
  exclude-result-prefixes="marc items">
9
  exclude-result-prefixes="marc items">
9
    <xsl:import href="MARC21slimUtils.xsl"/>
10
    <xsl:import href="MARC21slimUtils.xsl"/>
10
    <xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
11
    <xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
11
    <xsl:key name="item-by-status" match="items:item" use="items:status"/>
12
    <xsl:key name="item-by-status" match="items:item" use="items:status"/>
12
    <xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
13
    <xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
14
    <xsl:param name="showAvailability" select="true()"/>
13
15
14
    <xsl:template match="/">
16
    <xsl:template match="/">
15
            <xsl:apply-templates/>
17
            <xsl:apply-templates/>
Lines 1027-1032 Link Here
1027
                            </xsl:for-each>
1029
                            </xsl:for-each>
1028
                            </span>
1030
                            </span>
1029
                        </xsl:if>
1031
                        </xsl:if>
1032
                        <xsl:if test="$showAvailability">
1030
                        <span class="results_summary availability">
1033
                        <span class="results_summary availability">
1031
                        <span class="label">Availability: </span>
1034
                        <span class="label">Availability: </span>
1032
                        <xsl:choose>
1035
                        <xsl:choose>
Lines 1035-1041 Link Here
1035
                            <xsl:when test="string-length($AlternateHoldingsField)=3 and marc:datafield[@tag=$AlternateHoldingsField]">
1038
                            <xsl:when test="string-length($AlternateHoldingsField)=3 and marc:datafield[@tag=$AlternateHoldingsField]">
1036
                            <xsl:variable name="AlternateHoldingsCount" select="count(marc:datafield[@tag=$AlternateHoldingsField])"/>
1039
                            <xsl:variable name="AlternateHoldingsCount" select="count(marc:datafield[@tag=$AlternateHoldingsField])"/>
1037
                            <xsl:for-each select="marc:datafield[@tag=$AlternateHoldingsField][1]">
1040
                            <xsl:for-each select="marc:datafield[@tag=$AlternateHoldingsField][1]">
1038
                                <xsl:call-template select="marc:datafield[@tag=$AlternateHoldingsField]" name="subfieldSelect">
1041
                                <xsl:call-template name="subfieldSelect">
1039
                                    <xsl:with-param name="codes"><xsl:value-of select="$AlternateHoldingsSubfields"/></xsl:with-param>
1042
                                    <xsl:with-param name="codes"><xsl:value-of select="$AlternateHoldingsSubfields"/></xsl:with-param>
1040
                                    <xsl:with-param name="delimeter"><xsl:value-of select="$AlternateHoldingsSeparator"/></xsl:with-param>
1043
                                    <xsl:with-param name="delimeter"><xsl:value-of select="$AlternateHoldingsSeparator"/></xsl:with-param>
1041
                                </xsl:call-template>
1044
                                </xsl:call-template>
Lines 1145-1150 Link Here
1145
                       <xsl:text>). </xsl:text>                   </span>
1148
                       <xsl:text>). </xsl:text>                   </span>
1146
                   </xsl:if>
1149
                   </xsl:if>
1147
               </span>
1150
               </span>
1151
               </xsl:if>
1148
    <xsl:choose>
1152
    <xsl:choose>
1149
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1153
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1150
            <span class="results_summary" id="location">
1154
            <span class="results_summary" id="location">
(-)a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slimUtils.xsl (-1 / +1 lines)
Lines 1-6 Link Here
1
<?xml version='1.0'?>
1
<?xml version='1.0'?>
2
<!DOCTYPE stylesheet [<!ENTITY nbsp "&#160;" >]>
2
<!DOCTYPE stylesheet [<!ENTITY nbsp "&#160;" >]>
3
<xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
<xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
4
	<xsl:template name="datafield">
4
	<xsl:template name="datafield">
5
		<xsl:param name="tag"/>
5
		<xsl:param name="tag"/>
6
		<xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
6
		<xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
(-)a/koha-tmpl/opac-tmpl/prog/en/xslt/UNIMARCslim2OPACResults.xsl (+4 lines)
Lines 6-17 Link Here
6
  xmlns:marc="http://www.loc.gov/MARC21/slim"
6
  xmlns:marc="http://www.loc.gov/MARC21/slim"
7
  xmlns:items="http://www.koha-community.org/items"
7
  xmlns:items="http://www.koha-community.org/items"
8
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
8
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
9
  xmlns="http://www.w3.org/1999/xhtml"
9
  exclude-result-prefixes="marc items">
10
  exclude-result-prefixes="marc items">
10
11
11
<xsl:import href="UNIMARCslimUtils.xsl"/>
12
<xsl:import href="UNIMARCslimUtils.xsl"/>
12
<xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
13
<xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
13
<xsl:key name="item-by-status" match="items:item" use="items:status"/>
14
<xsl:key name="item-by-status" match="items:item" use="items:status"/>
14
<xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
15
<xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
16
<xsl:param name="showAvailability" select="true()"/>
15
17
16
<xsl:template match="/">
18
<xsl:template match="/">
17
  <xsl:apply-templates/>
19
  <xsl:apply-templates/>
Lines 99-104 Link Here
99
101
100
  <xsl:call-template name="tag_215" />
102
  <xsl:call-template name="tag_215" />
101
103
104
  <xsl:if test="$showAvailability">
102
  <span class="results_summary availability">
105
  <span class="results_summary availability">
103
    <span class="label">Availability: </span>
106
    <span class="label">Availability: </span>
104
    <xsl:choose>
107
    <xsl:choose>
Lines 247-252 Link Here
247
      </span>
250
      </span>
248
    </xsl:if>
251
    </xsl:if>
249
  </span>
252
  </span>
253
  </xsl:if>
250
254
251
</xsl:template>
255
</xsl:template>
252
256
(-)a/opac/opac-external-search.pl (+62 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 ByWater Solutions
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI;
23
24
use C4::Auth qw(:DEFAULT get_session);
25
use C4::Output;
26
use C4::Search qw( GetExternalSearchTargets );
27
use C4::XSLT qw( XSLTGetFilename );
28
29
my $cgi = new CGI;
30
31
# Getting the template and auth
32
my ($template, $loggedinuser, $cookie)
33
= get_template_and_user({template_name => "opac-external-search.tmpl",
34
                                query => $cgi,
35
                                type => "opac",
36
                                authnotrequired => 1,
37
                                flagsrequired => {borrowers => 1},
38
                                debug => 1,
39
                                });
40
41
$template->{VARS}->{q} = $cgi->param('q');
42
$template->{VARS}->{limit} = C4::Context->preference('OPACnumSearchResults') || 20;
43
$template->{VARS}->{OPACnumSearchResults} = C4::Context->preference('OPACnumSearchResults') || 20;
44
$template->{VARS}->{external_search_targets} = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
45
46
my @xsltResultStylesheets;
47
my @xsltDetailStylesheets;
48
49
foreach my $syntax ( qw( MARC21 UNIMARC NORMARC ) ) {
50
    if ( XSLTGetFilename( $syntax, 'OPACXSLTResultsDisplay' ) =~ m,/opac-tmpl/.*|^https:?.*, ) {
51
        push @xsltResultStylesheets, { syntax => $syntax, url => $& };
52
    }
53
54
    if ( XSLTGetFilename( $syntax, 'OPACXSLTDetailsDisplay' ) =~ m,/opac-tmpl/.*|^https:?.*, ) {
55
        push @xsltDetailStylesheets, { syntax => $syntax, url => $& };
56
    }
57
}
58
59
$template->{VARS}->{xslt_result_stylesheets} = \@xsltResultStylesheets;
60
$template->{VARS}->{xslt_detail_stylesheets} = \@xsltDetailStylesheets;
61
62
output_html_with_http_headers $cgi, $cookie, $template->output;
(-)a/opac/opac-search.pl (-4 / +8 lines)
Lines 74-79 if ( $branch_group_limit ) { Link Here
74
            -name => 'multibranchlimit',
74
            -name => 'multibranchlimit',
75
            -values => substr($branch_group_limit, 17)
75
            -values => substr($branch_group_limit, 17)
76
        );
76
        );
77
    } elsif ( $branch_group_limit eq '@overdrive' ) {
78
        print $cgi->redirect( '/cgi-bin/koha/opac-overdrive-search.pl?q=' . join( '+', $cgi->param( 'q' ) ) );
79
        exit;
77
    } else {
80
    } else {
78
        $cgi->append(
81
        $cgi->append(
79
            -name => 'limit',
82
            -name => 'limit',
Lines 879-888 $template->{VARS}->{IDreamBooksReviews} = C4::Context->preference('IDreamBooksRe Link Here
879
$template->{VARS}->{IDreamBooksReadometer} = C4::Context->preference('IDreamBooksReadometer');
882
$template->{VARS}->{IDreamBooksReadometer} = C4::Context->preference('IDreamBooksReadometer');
880
$template->{VARS}->{IDreamBooksResults} = C4::Context->preference('IDreamBooksResults');
883
$template->{VARS}->{IDreamBooksResults} = C4::Context->preference('IDreamBooksResults');
881
884
882
if ($offset == 0 && IsOverDriveEnabled()) {
885
$template->{VARS}->{OPACSearchExternalTargets} = C4::Context->preference('OPACSearchExternalTargets');
883
    $template->param(OverDriveEnabled => 1);
886
$template->{VARS}->{external_search_targets} = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
884
    $template->param(OverDriveLibraryID => C4::Context->preference('OverDriveLibraryID'));
887
885
}
888
$template->{VARS}->{OverDriveLibraryID} = C4::Context->preference('OverDriveLibraryID');
889
$template->{VARS}->{OverDriveEnabled} = ($offset == 0 && IsOverDriveEnabled());
886
890
887
    $template->param( borrowernumber    => $borrowernumber);
891
    $template->param( borrowernumber    => $borrowernumber);
888
output_with_http_headers $cgi, $cookie, $template->output, $content_type;
892
output_with_http_headers $cgi, $cookie, $template->output, $content_type;
(-)a/opac/svc/pazpar2_init (+100 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 ByWater Solutions
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
=head1 NAME
21
22
svc/pazpar2_init: Initialize a Pazpar2 session
23
24
=head1 SYNOPSIS
25
26
svc/pazpar2_init -> ../pazpar2/search.pz2?command=init&...
27
28
=head1 DESCRIPTION
29
30
This services connects to pazpar2 and authenticates connections on behalf of
31
client-side search code. The proxy is necessary to prevent exposing
32
authentication information.
33
34
=cut
35
36
use strict;
37
use warnings;
38
39
use CGI qw(-oldstyle_urls);
40
use HTTP::Request::Common;
41
use JSON;
42
use URI;
43
use XML::Simple;
44
45
use C4::Context;
46
use C4::Search;
47
use C4::Output;
48
49
my $dbh = C4::Context->dbh;
50
my $query = new CGI;
51
52
my %init_opts;
53
54
my $targets = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
55
56
foreach my $target ( @$targets ) {
57
    my $target_url = $target->{'host'} . ':' . $target->{'port'} . '/' . $target->{'db'};
58
    $init_opts{ 'pz:name[' . $target_url . ']' } = $target->{'name'};
59
    $init_opts{ 'pz:queryencoding[' . $target_url . ']' } = $target->{'encoding'};
60
    $init_opts{ 'pz:xslt[' . $target_url . ']' } = lc( $target->{'syntax'} ) . '-work-groups.xsl';
61
    $init_opts{ 'pz:requestsyntax[' . $target_url . ']' } = $target->{'syntax'};
62
    $init_opts{ 'pz:nativesyntax[' . $target_url . ']' } = 'iso2709';
63
64
    if ( $target->{'userid'} ) {
65
        if ( $target->{'password'} ) {
66
            $init_opts{ 'pz:authentication[' . $target_url . ']' } = $target->{'userid'} . '/' . $target->{'password'};
67
        } else {
68
            $init_opts{ 'pz:authentication[' . $target_url . ']' } = $target->{'userid'};
69
        }
70
    }
71
}
72
73
my $uri = 'http://' . C4::Context->preference( 'OPACBaseURL' ) . "/pazpar2/search.pz2";
74
75
my $request = HTTP::Request::Common::POST( $uri, [ command => 'init', %init_opts ] );
76
77
my $ua = LWP::UserAgent->new( "Koha " . C4::Context->KOHAVERSION );
78
79
my $response = $ua->request( $request ) ;
80
if ( !$response->is_success ) {
81
    print $query->header(
82
        -status => '500 Internal Server Error'
83
    );
84
85
    warn "Pazpar2 init failed: " . $response->message;
86
    my $content = to_json({
87
        error => 'Could not connect to Pazpar2',
88
    });
89
    output_with_http_headers $query, undef, $content, 'json', '500 Internal Server Error';
90
91
    exit;
92
} else {
93
    my $xs = XML::Simple->new;
94
    my $data = $xs->XMLin( $response->content );
95
96
    my $content = to_json({
97
        sessionID => $data->{'session'}
98
    });
99
    output_with_http_headers $query, undef, $content, 'json', '200 OK';
100
}
(-)a/rewrite-config.PL (-4 / +4 lines)
Lines 137-144 $prefix = $ENV{'INSTALL_BASE'} || "/usr"; Link Here
137
  "__INSTALL_ZEBRA__" => 'yes',
137
  "__INSTALL_ZEBRA__" => 'yes',
138
  "__INSTALL_SRU__" => 'yes',
138
  "__INSTALL_SRU__" => 'yes',
139
  "__INSTALL_PAZPAR2__" => 'no',
139
  "__INSTALL_PAZPAR2__" => 'no',
140
  "__PAZPAR2_TOGGLE_XML_PRE__" => '<!--',
140
  "__PAZPAR2_TOGGLE_HTTPD_PRE__" => '<IfDefine PAZPAR2_IS_DISABLED>',
141
  "__PAZPAR2_TOGGLE_XML_POST__" => '-->',
141
  "__PAZPAR2_TOGGLE_HTTPD_POST__" => '</IfDefine>',
142
  "__AUTH_INDEX_MODE__" => 'grs1',
142
  "__AUTH_INDEX_MODE__" => 'grs1',
143
  "__BIB_INDEX_MODE__" => 'grs1',
143
  "__BIB_INDEX_MODE__" => 'grs1',
144
  "__RUN_DATABASE_TESTS__" => 'no',
144
  "__RUN_DATABASE_TESTS__" => 'no',
Lines 158-165 foreach $key (keys %configuration) { Link Here
158
# munge commenting out the PazPar2 mergeserver
158
# munge commenting out the PazPar2 mergeserver
159
# entry in koha-conf.xml if necessary
159
# entry in koha-conf.xml if necessary
160
if ($configuration{'__INSTALL_PAZPAR2__'} eq 'yes') {
160
if ($configuration{'__INSTALL_PAZPAR2__'} eq 'yes') {
161
    $configuration{'__PAZPAR2_TOGGLE_XML_PRE__'} = '';
161
    $configuration{'__PAZPAR2_TOGGLE_HTTPD_PRE__'} = '';
162
    $configuration{'__PAZPAR2_TOGGLE_XML_POST__'} = '';
162
    $configuration{'__PAZPAR2_TOGGLE_HTTPD_POST__'} = '';
163
}
163
}
164
164
165
$fname = $ARGV[0];
165
$fname = $ARGV[0];
(-)a/t/XSLT.t (-16 / +79 lines)
Lines 6-12 Link Here
6
use strict;
6
use strict;
7
use warnings;
7
use warnings;
8
8
9
use Test::More tests => 8;
9
use C4::Context;
10
use C4::Templates;
11
use Test::More tests => 13;
12
use Test::MockModule;
13
use File::Basename qw/dirname/;
10
use File::Temp;
14
use File::Temp;
11
use File::Path qw/make_path/;
15
use File::Path qw/make_path/;
12
16
Lines 14-49 BEGIN { Link Here
14
        use_ok('C4::XSLT');
18
        use_ok('C4::XSLT');
15
}
19
}
16
20
17
my $dir = File::Temp->newdir();
21
my $opacdir = File::Temp->newdir();
22
my $staffdir = File::Temp->newdir();
18
my @themes = ('prog', 'test');
23
my @themes = ('prog', 'test');
19
my @langs = ('en', 'es-ES');
24
my @langs = ('en', 'es-ES');
20
25
26
sub make_test_file {
27
    my ( $filename, $contents ) = @_;
28
29
    make_path(dirname($filename));
30
    open my $fh, '>', $filename or die "Could not create test file: $filename";
31
    print $fh $contents;
32
    close $fh;
33
}
34
21
# create temporary files to be tested later
35
# create temporary files to be tested later
22
foreach my $theme (@themes) {
36
foreach my $theme (@themes) {
23
    foreach my $lang (@langs) {
37
    foreach my $lang (@langs) {
24
        make_path("$dir/$theme/$lang/xslt");
38
        foreach my $dir ($opacdir, $staffdir) {
25
        open my $fh, '>', "$dir/$theme/$lang/xslt/my_file.xslt";
39
            make_test_file( "$dir/$theme/$lang/xslt/my_file.xslt", "Theme $theme, language $lang" );
26
        print $fh "Theme $theme, language $lang";
40
            make_test_file( "$dir/$theme/$lang/xslt/MARC21slim2intranetDetail.xsl", "Theme $theme, language $lang, MARC21slim2intranetDetail" );
27
        close $fh;
41
            make_test_file( "$dir/$theme/$lang/xslt/test_en.xsl", "Theme $theme, language $lang, test_en" );
42
            make_test_file( "$dir/$theme/$lang/xslt/UNIMARCslim2OPACDetail.xsl", "Theme $theme, language $lang, UNIMARCslim2OPACDetail" );
43
            make_test_file( "$dir/$theme/$lang/xslt/nondefault_test.xsl", "Theme $theme, language $lang, nondefault_test" );
44
            make_test_file( "$dir/$theme/$lang/xslt/MARC21slim2OPACResults.xsl", "Theme $theme, language $lang, MARC21slim2OPACResults" );
45
        }
28
    }
46
    }
29
}
47
}
30
48
31
sub find_and_slurp {
49
sub find_and_slurp_default {
32
    my ($dir, $theme, $lang) = @_;
50
    my ($dir, $theme, $lang) = @_;
33
51
34
    my $filename = C4::XSLT::_get_best_default_xslt_filename($dir, $theme, $lang, 'my_file.xslt');
52
    my $filename = C4::XSLT::_get_best_default_xslt_filename($dir, $theme, $lang, 'my_file.xslt');
35
    open my $fh, '<', $filename;
53
    open my $fh, '<', $filename or return "Could not open: $filename";
36
    my $str = <$fh>;
54
    my $str = <$fh>;
37
    close $fh;
55
    close $fh;
38
    return $str;
56
    return $str;
39
}
57
}
40
58
59
sub find_and_slurp {
60
    my ($marcflavour, $xslsyspref) = @_;
61
62
    my $filename = C4::XSLT::XSLTGetFilename( $marcflavour, $xslsyspref );
63
    open my $fh, '<', $filename or return "Could not open: $filename";
64
    my $str = <$fh>;
65
    close $fh;
66
    return $str;
67
}
68
69
my $module_context = new Test::MockModule('C4::Context');
70
$module_context->mock(
71
    'config',
72
    sub {
73
        my ( $self, $var ) = @_;
74
        my %predefs = (
75
            opachtdocs => $opacdir,
76
            intrahtdocs => $staffdir,
77
        );
78
79
        return $predefs{$var} || $module_context->original('config')->(@_);
80
    }
81
);
82
$module_context->mock(
83
    'preference',
84
    sub {
85
        my ( $self, $var ) = @_;
86
        my %predefs = (
87
            template => 'prog',
88
            marcflavour => 'MARC21',
89
            opacthemes => 'test',
90
            XSLTDetailsDisplay => 'default',
91
            XSLTResultsDisplay => "$staffdir/prog/en/xslt/test_en.xsl",
92
            OPACXSLTDetailsDisplay => "$opacdir/test/en/xslt/nondefault_test.xsl",
93
            OPACXSLTResultsDisplay => '"default"',
94
        );
95
96
        return $predefs{$var} || $module_context->original('preference')->(@_);
97
    }
98
);
99
41
# These tests verify that we're finding the right XSLT file when present,
100
# These tests verify that we're finding the right XSLT file when present,
42
# and falling back to the right XSLT file when an exact match is not present.
101
# and falling back to the right XSLT file when an exact match is not present.
43
is(find_and_slurp($dir, 'test', 'en'   ), 'Theme test, language en',    'Found test/en');
102
is(find_and_slurp_default($opacdir, 'test', 'en'   ), 'Theme test, language en',    'Found test/en');
44
is(find_and_slurp($dir, 'test', 'es-ES'), 'Theme test, language es-ES', 'Found test/es-ES');
103
is(find_and_slurp_default($opacdir, 'test', 'es-ES'), 'Theme test, language es-ES', 'Found test/es-ES');
45
is(find_and_slurp($dir, 'prog', 'en',  ), 'Theme prog, language en',    'Found test/en');
104
is(find_and_slurp_default($opacdir, 'prog', 'en',  ), 'Theme prog, language en',    'Found test/en');
46
is(find_and_slurp($dir, 'prog', 'es-ES'), 'Theme prog, language es-ES', 'Found test/es-ES');
105
is(find_and_slurp_default($opacdir, 'prog', 'es-ES'), 'Theme prog, language es-ES', 'Found test/es-ES');
47
is(find_and_slurp($dir, 'test', 'fr-FR'), 'Theme test, language en',    'Fell back to test/en for test/fr-FR');
106
is(find_and_slurp_default($opacdir, 'test', 'fr-FR'), 'Theme test, language en',    'Fell back to test/en for test/fr-FR');
48
is(find_and_slurp($dir, 'nope', 'es-ES'), 'Theme prog, language es-ES', 'Fell back to prog/es-ES for nope/es-ES');
107
is(find_and_slurp_default($opacdir, 'nope', 'es-ES'), 'Theme prog, language es-ES', 'Fell back to prog/es-ES for nope/es-ES');
49
is(find_and_slurp($dir, 'nope', 'fr-FR'), 'Theme prog, language en',    'Fell back to prog/en for nope/fr-FR');
108
is(find_and_slurp_default($opacdir, 'nope', 'fr-FR'), 'Theme prog, language en',    'Fell back to prog/en for nope/fr-FR');
109
is(find_and_slurp('MARC21', 'XSLTDetailsDisplay'), 'Theme prog, language en, MARC21slim2intranetDetail', 'Used default for staff/details');
110
is(find_and_slurp('MARC21', 'XSLTResultsDisplay'), 'Theme prog, language en, test_en', 'Used non-default with langcode for staff/results');
111
is(find_and_slurp('MARC21', 'OPACXSLTDetailsDisplay'), 'Theme test, language en, nondefault_test', 'not-prog: Used non-default for local marcflavour and opac/details');
112
is(find_and_slurp('UNIMARC', 'OPACXSLTDetailsDisplay'), 'Theme test, language en, UNIMARCslim2OPACDetail', 'not-prog: Used default for non-local marcflavour and opac/details');
113
is(find_and_slurp('MARC21', 'OPACXSLTResultsDisplay'), 'Theme test, language en, MARC21slim2OPACResults', 'not-prog: Used "default" for opac/results');
50
- 

Return to bug 10486