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 2372-2377 sub _ZOOM_event_loop { Link Here
2372
    }
2373
    }
2373
}
2374
}
2374
2375
2376
=head2 GetExternalSearchTargets
2377
2378
Returns the list of Z39.50 servers that are marked for search in the OPAC using
2379
Pazpar2.
2380
2381
=cut
2382
2383
sub GetExternalSearchTargets {
2384
    my ( $branchcode ) = @_;
2385
2386
    if ( $branchcode ) {
2387
        return C4::Context->dbh->selectall_arrayref( q{
2388
            SELECT * FROM external_targets et
2389
            LEFT JOIN external_target_restrictions etr
2390
                ON (etr.target_id = et.target_id and etr.branchcode = ?)
2391
            WHERE etr.target_id IS NULL
2392
            ORDER BY et.name
2393
        }, { Slice => {} }, $branchcode );
2394
    } else {
2395
        return C4::Context->dbh->selectall_arrayref( q{
2396
            SELECT * FROM external_targets et
2397
            LEFT JOIN external_target_restrictions etr USING (target_id)
2398
            GROUP by et.target_id
2399
            HAVING branchcode IS NULL
2400
            ORDER BY et.name
2401
        }, { Slice => {} } );
2402
    }
2403
}
2404
2375
2405
2376
END { }    # module clean-up code here (global destructor)
2406
END { }    # module clean-up code here (global destructor)
2377
2407
(-)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 7909-7914 if ( CheckVersion($DBversion) ) { Link Here
7909
    });
7909
    });
7910
7910
7911
    print "Upgrade to $DBversion done (Bug 10863 - Add permissions force_checkout and manage_restrictions)\n";
7911
    print "Upgrade to $DBversion done (Bug 10863 - Add permissions force_checkout and manage_restrictions)\n";
7912
     SetVersion($DBversion);
7913
}
7914
7915
$DBversion = "3.15.00.XXX";
7916
if(CheckVersion($DBversion)) {
7917
    $dbh->do(
7918
"INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('OPACSearchExternalTargets','0','Whether to search external targets in the OPAC','','YesNo')"
7919
    );
7920
    $dbh->do( q{
7921
CREATE TABLE `external_targets` (
7922
  `target_id` int(11) NOT NULL AUTO_INCREMENT,
7923
  `host` varchar(128) NOT NULL,
7924
  `port` int(11) NOT NULL,
7925
  `db` varchar(64) NOT NULL,
7926
  `userid` varchar(64) DEFAULT '',
7927
  `password` varchar(64) DEFAULT '',
7928
  `name` varchar(64) NOT NULL,
7929
  `syntax` varchar(64) NOT NULL,
7930
  `encoding` varchar(16) DEFAULT 'MARC-8',
7931
  PRIMARY KEY (`target_id`)
7932
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
7933
    } );
7934
    $dbh->do( q{
7935
CREATE TABLE `external_target_restrictions` (
7936
  `branchcode` varchar(10) NOT NULL,
7937
  `target_id` int(11) NOT NULL,
7938
  KEY `branchcode` (`branchcode`),
7939
  KEY `target_id` (`target_id`),
7940
  CONSTRAINT `external_target_restrictions_ibfk_1` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE,
7941
  CONSTRAINT `external_target_restrictions_ibfk_2` FOREIGN KEY (`target_id`) REFERENCES `external_targets` (`target_id`) ON DELETE CASCADE
7942
) ENGINE=InnoDB DEFAULT CHARSET=utf8
7943
    } );
7944
    print "Upgrade to $DBversion done (Bug 10486 - Allow external Z39.50 targets to be searched from the OPAC)\n";
7912
    SetVersion($DBversion);
7945
    SetVersion($DBversion);
7913
}
7946
}
7914
7947
(-)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 424-429 OPAC: Link Here
424
                  yes: Allow
424
                  yes: Allow
425
                  no: Do not allow
425
                  no: Do not allow
426
            - users to add a note when placing a hold.
426
            - users to add a note when placing a hold.
427
        -
428
            - pref: OPACSearchExternalTargets
429
              default: 0
430
              choices:
431
                  yes: Search
432
                  no: "Don't search"
433
            - external targets from the OPAC.
427
434
428
    Policy:
435
    Policy:
429
        -
436
        -
(-)a/koha-tmpl/opac-tmpl/ccsr/en/css/opac.css (+16 lines)
Lines 1609-1614 strong em, em strong { Link Here
1609
     vertical-align: top;
1609
     vertical-align: top;
1610
   width: 10px;
1610
   width: 10px;
1611
}
1611
}
1612
.sourcecol {
1613
	vertical-align: top;
1614
	width: 100px;
1615
}
1612
#container {
1616
#container {
1613
    color : #000;
1617
    color : #000;
1614
}
1618
}
Lines 2876-2878 a.reviewlink,a.reviewlink:visited { Link Here
2876
    display: block;
2880
    display: block;
2877
    overflow: auto;
2881
    overflow: auto;
2878
}
2882
}
2883
2884
#modal-overlay {
2885
    background-color: white;
2886
    border-radius: 5px;
2887
    max-width: 75%;
2888
    padding: 15px;
2889
}
2890
2891
#overdrive-results, #pazpar2-results {
2892
    font-weight: bold;
2893
    padding-left: 1em;
2894
}
(-)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 (+1110 lines)
Line 0 Link Here
1
/*
2
 * $Id: 3a9980787bb5d0fe966140b243a4a5eb6768913e $
3
** pz2.js - pazpar2's javascript client library.
4
*/
5
6
//since explorer is flawed
7
if (!window['Node']) {
8
    window.Node = new Object();
9
    Node.ELEMENT_NODE = 1;
10
    Node.ATTRIBUTE_NODE = 2;
11
    Node.TEXT_NODE = 3;
12
    Node.CDATA_SECTION_NODE = 4;
13
    Node.ENTITY_REFERENCE_NODE = 5;
14
    Node.ENTITY_NODE = 6;
15
    Node.PROCESSING_INSTRUCTION_NODE = 7;
16
    Node.COMMENT_NODE = 8;
17
    Node.DOCUMENT_NODE = 9;
18
    Node.DOCUMENT_TYPE_NODE = 10;
19
    Node.DOCUMENT_FRAGMENT_NODE = 11;
20
    Node.NOTATION_NODE = 12;
21
}
22
23
// prevent execution of more than once
24
if(typeof window.pz2 == "undefined") {
25
window.undefined = window.undefined;
26
27
var pz2 = function ( paramArray )
28
{
29
30
    // at least one callback required
31
    if ( !paramArray )
32
        throw new Error("Pz2.js: Array with parameters has to be supplied.");
33
34
    //supported pazpar2's protocol version
35
    this.suppProtoVer = '1';
36
    if (typeof paramArray.pazpar2path != "undefined")
37
        this.pz2String = paramArray.pazpar2path;
38
    else
39
        this.pz2String = "/pazpar2/search.pz2";
40
    this.useSessions = true;
41
42
    this.stylesheet = paramArray.detailstylesheet || null;
43
    //load stylesheet if required in async mode
44
    if( this.stylesheet ) {
45
        var context = this;
46
        var request = new pzHttpRequest( this.stylesheet );
47
        request.get( {}, function ( doc ) { context.xslDoc = doc; } );
48
    }
49
50
    this.errorHandler = paramArray.errorhandler || null;
51
    this.showResponseType = paramArray.showResponseType || "xml";
52
53
    // function callbacks
54
    this.initCallback = paramArray.oninit || null;
55
    this.statCallback = paramArray.onstat || null;
56
    this.showCallback = paramArray.onshow || null;
57
    this.termlistCallback = paramArray.onterm || null;
58
    this.recordCallback = paramArray.onrecord || null;
59
    this.bytargetCallback = paramArray.onbytarget || null;
60
    this.resetCallback = paramArray.onreset || null;
61
62
    // termlist keys
63
    this.termKeys = paramArray.termlist || "subject";
64
65
    // some configurational stuff
66
    this.keepAlive = 50000;
67
68
    if ( paramArray.keepAlive < this.keepAlive )
69
        this.keepAlive = paramArray.keepAlive;
70
71
    this.sessionID = paramArray.sessionId || null;
72
    this.serviceId = paramArray.serviceId || null;
73
    this.initStatusOK = false;
74
    this.pingStatusOK = false;
75
    this.searchStatusOK = false;
76
77
    // for sorting
78
    this.currentSort = "relevance";
79
80
    // where are we?
81
    this.currentStart = 0;
82
    // currentNum can be overwritten in show
83
    this.currentNum = 20;
84
85
    // last full record retrieved
86
    this.currRecID = null;
87
88
    // current query
89
    this.currQuery = null;
90
91
    //current raw record offset
92
    this.currRecOffset = null;
93
94
    //timers
95
    this.pingTimer = null;
96
    this.statTime = paramArray.stattime || 1000;
97
    this.statTimer = null;
98
    this.termTime = paramArray.termtime || 1000;
99
    this.termTimer = null;
100
    this.showTime = paramArray.showtime || 1000;
101
    this.showTimer = null;
102
    this.showFastCount = 4;
103
    this.bytargetTime = paramArray.bytargettime || 1000;
104
    this.bytargetTimer = null;
105
    this.recordTime = paramArray.recordtime || 500;
106
    this.recordTimer = null;
107
108
    // counters for each command and applied delay
109
    this.dumpFactor = 500;
110
    this.showCounter = 0;
111
    this.termCounter = 0;
112
    this.statCounter = 0;
113
    this.bytargetCounter = 0;
114
    this.recordCounter = 0;
115
116
    // active clients, updated by stat and show
117
    // might be an issue since bytarget will poll accordingly
118
    this.activeClients = 1;
119
120
    // if in proxy mode no need to init
121
    if (paramArray.usesessions != undefined) {
122
         this.useSessions = paramArray.usesessions;
123
        this.initStatusOK = true;
124
    }
125
    // else, auto init session or wait for a user init?
126
    if (this.useSessions && paramArray.autoInit !== false) {
127
        this.init(this.sessionID, this.serviceId);
128
    }
129
    // Version parameter
130
    this.version = paramArray.version || null;
131
};
132
133
pz2.prototype =
134
{
135
    //error handler for async error throws
136
   throwError: function (errMsg, errCode)
137
   {
138
        var err = new Error(errMsg);
139
        if (errCode) err.code = errCode;
140
141
        if (this.errorHandler) {
142
            this.errorHandler(err);
143
        }
144
        else {
145
            throw err;
146
        }
147
   },
148
149
    // stop activity by clearing tiemouts
150
   stop: function ()
151
   {
152
       clearTimeout(this.statTimer);
153
       clearTimeout(this.showTimer);
154
       clearTimeout(this.termTimer);
155
       clearTimeout(this.bytargetTimer);
156
    },
157
158
    // reset status variables
159
    reset: function ()
160
    {
161
        if ( this.useSessions ) {
162
            this.sessionID = null;
163
            this.initStatusOK = false;
164
            this.pingStatusOK = false;
165
            clearTimeout(this.pingTimer);
166
        }
167
        this.searchStatusOK = false;
168
        this.stop();
169
170
        if ( this.resetCallback )
171
                this.resetCallback();
172
    },
173
174
    init: function (sessionId, serviceId)
175
    {
176
        this.reset();
177
178
        // session id as a param
179
        if (sessionId && this.useSessions ) {
180
            this.initStatusOK = true;
181
            this.sessionID = sessionId;
182
            this.ping();
183
        // old school direct pazpar2 init
184
        } else if (this.useSessions) {
185
            var context = this;
186
            var request = new pzHttpRequest(this.pz2String, this.errorHandler);
187
            var opts = {'command' : 'init'};
188
            if (serviceId) opts.service = serviceId;
189
            request.safeGet(
190
                opts,
191
                function(data) {
192
                    if ( data.getElementsByTagName("status")[0]
193
                            .childNodes[0].nodeValue == "OK" ) {
194
                        if ( data.getElementsByTagName("protocol")[0]
195
                                .childNodes[0].nodeValue
196
                            != context.suppProtoVer )
197
                            throw new Error(
198
                                "Server's protocol not supported by the client"
199
                            );
200
                        context.initStatusOK = true;
201
                        context.sessionID =
202
                            data.getElementsByTagName("session")[0]
203
                                .childNodes[0].nodeValue;
204
                        if (data.getElementsByTagName("keepAlive").length > 0) {
205
                            context.keepAlive = data.getElementsByTagName("keepAlive")[0].childNodes[0].nodeValue;
206
                        }
207
                        context.pingTimer =
208
                            setTimeout(
209
                                function () {
210
                                    context.ping();
211
                                },
212
                                context.keepAlive
213
                            );
214
                        if ( context.initCallback )
215
                            context.initCallback();
216
                    }
217
                    else
218
                        context.throwError('Init failed. Malformed WS resonse.',
219
                                            110);
220
                }
221
            );
222
        // when through proxy no need to init
223
        } else {
224
            this.initStatusOK = true;
225
	}
226
    },
227
    // no need to ping explicitly
228
    ping: function ()
229
    {
230
        // pinging only makes sense when using pazpar2 directly
231
        if( !this.initStatusOK || !this.useSessions )
232
            throw new Error(
233
            'Pz2.js: Ping not allowed (proxy mode) or session not initialized.'
234
            );
235
        var context = this;
236
237
        clearTimeout(context.pingTimer);
238
239
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
240
        request.safeGet(
241
            { "command": "ping", "session": this.sessionID, "windowid" : window.name },
242
            function(data) {
243
                if ( data.getElementsByTagName("status")[0]
244
                        .childNodes[0].nodeValue == "OK" ) {
245
                    context.pingStatusOK = true;
246
                    context.pingTimer =
247
                        setTimeout(
248
                            function () {
249
                                context.ping();
250
                            },
251
                            context.keepAlive
252
                        );
253
                }
254
                else
255
                    context.throwError('Ping failed. Malformed WS resonse.',
256
                                        111);
257
            }
258
        );
259
    },
260
    search: function (query, num, sort, filter, showfrom, addParamsArr)
261
    {
262
        clearTimeout(this.statTimer);
263
        clearTimeout(this.showTimer);
264
        clearTimeout(this.termTimer);
265
        clearTimeout(this.bytargetTimer);
266
267
        this.showCounter = 0;
268
        this.termCounter = 0;
269
        this.bytargetCounter = 0;
270
        this.statCounter = 0;
271
        this.activeClients = 1;
272
273
        // no proxy mode
274
        if( !this.initStatusOK )
275
            throw new Error('Pz2.js: session not initialized.');
276
277
        if( query !== undefined )
278
            this.currQuery = query;
279
        else
280
            throw new Error("Pz2.js: no query supplied to the search command.");
281
282
        if ( showfrom !== undefined )
283
            var start = showfrom;
284
        else
285
            var start = 0;
286
287
	var searchParams = {
288
          "command": "search",
289
          "query": this.currQuery,
290
          "session": this.sessionID,
291
          "windowid" : window.name
292
        };
293
294
        if( sort !== undefined ) {
295
            this.currentSort = sort;
296
	    searchParams["sort"] = sort;
297
	}
298
        if (filter !== undefined)
299
	        searchParams["filter"] = filter;
300
301
        // copy additional parmeters, do not overwrite
302
        if (addParamsArr != undefined) {
303
            for (var prop in addParamsArr) {
304
                if (!searchParams.hasOwnProperty(prop))
305
                    searchParams[prop] = addParamsArr[prop];
306
            }
307
        }
308
309
        var context = this;
310
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
311
        request.safeGet(
312
            searchParams,
313
            function(data) {
314
                if ( data.getElementsByTagName("status")[0]
315
                        .childNodes[0].nodeValue == "OK" ) {
316
                    context.searchStatusOK = true;
317
                    //piggyback search
318
                    context.show(start, num, sort);
319
                    if (context.statCallback)
320
                        context.stat();
321
                    if (context.termlistCallback)
322
                        context.termlist();
323
                    if (context.bytargetCallback)
324
                        context.bytarget();
325
                }
326
                else
327
                    context.throwError('Search failed. Malformed WS resonse.',
328
                                        112);
329
            }
330
        );
331
    },
332
    stat: function()
333
    {
334
        if( !this.initStatusOK )
335
            throw new Error('Pz2.js: session not initialized.');
336
337
        // if called explicitly takes precedence
338
        clearTimeout(this.statTimer);
339
340
        var context = this;
341
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
342
        request.safeGet(
343
            { "command": "stat", "session": this.sessionID, "windowid" : window.name },
344
            function(data) {
345
                if ( data.getElementsByTagName("stat") ) {
346
                    var activeClients =
347
                        Number( data.getElementsByTagName("activeclients")[0]
348
                                    .childNodes[0].nodeValue );
349
                    context.activeClients = activeClients;
350
351
		    var stat = Element_parseChildNodes(data.documentElement);
352
353
                    context.statCounter++;
354
		    var delay = context.statTime
355
                        + context.statCounter * context.dumpFactor;
356
357
                    if ( activeClients > 0 )
358
                        context.statTimer =
359
                            setTimeout(
360
                                function () {
361
                                    context.stat();
362
                                },
363
                                delay
364
                            );
365
                    context.statCallback(stat);
366
                }
367
                else
368
                    context.throwError('Stat failed. Malformed WS resonse.',
369
                                        113);
370
            }
371
        );
372
    },
373
    show: function(start, num, sort, query_state)
374
    {
375
        if( !this.searchStatusOK && this.useSessions )
376
            throw new Error(
377
                'Pz2.js: show command has to be preceded with a search command.'
378
            );
379
380
        // if called explicitly takes precedence
381
        clearTimeout(this.showTimer);
382
383
        if( sort !== undefined )
384
            this.currentSort = sort;
385
        if( start !== undefined )
386
            this.currentStart = Number( start );
387
        if( num !== undefined )
388
            this.currentNum = Number( num );
389
390
        var context = this;
391
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
392
	var requestParameters =
393
          {
394
              "command": "show",
395
              "session": this.sessionID,
396
              "start": this.currentStart,
397
              "num": this.currentNum,
398
              "sort": this.currentSort,
399
              "block": 1,
400
              "type": this.showResponseType,
401
              "windowid" : window.name
402
          };
403
        if (query_state)
404
          requestParameters["query-state"] = query_state;
405
	if (this.version && this.version > 0)
406
	    requestParameters["version"] = this.version;
407
        request.safeGet(
408
	  requestParameters,
409
          function(data, type) {
410
            var show = null;
411
            var activeClients = 0;
412
            if (type === "json") {
413
              show = {};
414
              activeClients = Number(data.activeclients[0]);
415
              show.activeclients = activeClients;
416
              show.merged = Number(data.merged[0]);
417
              show.total = Number(data.total[0]);
418
              show.start = Number(data.start[0]);
419
              show.num = Number(data.num[0]);
420
              show.hits = data.hit;
421
            } else if (data.getElementsByTagName("status")[0]
422
                  .childNodes[0].nodeValue == "OK") {
423
                // first parse the status data send along with records
424
                // this is strictly bound to the format
425
                activeClients =
426
                  Number(data.getElementsByTagName("activeclients")[0]
427
                      .childNodes[0].nodeValue);
428
                show = {
429
                  "activeclients": activeClients,
430
                  "merged":
431
                    Number( data.getElementsByTagName("merged")[0]
432
                        .childNodes[0].nodeValue ),
433
                  "total":
434
                    Number( data.getElementsByTagName("total")[0]
435
                        .childNodes[0].nodeValue ),
436
                  "start":
437
                    Number( data.getElementsByTagName("start")[0]
438
                        .childNodes[0].nodeValue ),
439
                  "num":
440
                    Number( data.getElementsByTagName("num")[0]
441
                        .childNodes[0].nodeValue ),
442
                  "hits": []
443
                };
444
                // parse all the first-level nodes for all <hit> tags
445
                var hits = data.getElementsByTagName("hit");
446
                for (i = 0; i < hits.length; i++)
447
                  show.hits[i] = Element_parseChildNodes(hits[i]);
448
            } else {
449
              context.throwError('Show failed. Malformed WS resonse.',
450
                  114);
451
            };
452
453
	    var approxNode = data.getElementsByTagName("approximation");
454
	    if (approxNode && approxNode[0] && approxNode[0].childNodes[0] && approxNode[0].childNodes[0].nodeValue)
455
		show['approximation'] =
456
		  Number( approxNode[0].childNodes[0].nodeValue);
457
458
459
	      data.getElementsByTagName("")
460
            context.activeClients = activeClients;
461
            context.showCounter++;
462
            var delay = context.showTime;
463
            if (context.showCounter > context.showFastCount)
464
              delay += context.showCounter * context.dumpFactor;
465
            if ( activeClients > 0 )
466
              context.showTimer = setTimeout(
467
                function () {
468
                  context.show();
469
                },
470
                delay);
471
            context.showCallback(show);
472
          }
473
        );
474
    },
475
    record: function(id, offset, syntax, handler)
476
    {
477
        // we may call record with no previous search if in proxy mode
478
        if(!this.searchStatusOK && this.useSessions)
479
           throw new Error(
480
            'Pz2.js: record command has to be preceded with a search command.'
481
            );
482
483
        if( id !== undefined )
484
            this.currRecID = id;
485
486
	var recordParams = {
487
            "command": "record",
488
            "session": this.sessionID,
489
            "id": this.currRecID,
490
            "windowid" : window.name
491
        };
492
493
	this.currRecOffset = null;
494
        if (offset != undefined) {
495
	    recordParams["offset"] = offset;
496
            this.currRecOffset = offset;
497
        }
498
499
        if (syntax != undefined)
500
            recordParams['syntax'] = syntax;
501
502
        //overwrite default callback id needed
503
        var callback = this.recordCallback;
504
        var args = undefined;
505
        if (handler != undefined) {
506
            callback = handler['callback'];
507
            args = handler['args'];
508
        }
509
510
        var context = this;
511
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
512
513
        request.safeGet(
514
	    recordParams,
515
            function(data) {
516
                var recordNode;
517
                var record;
518
                //raw record
519
                if (context.currRecOffset !== null) {
520
                    record = new Array();
521
                    record['xmlDoc'] = data;
522
                    record['offset'] = context.currRecOffset;
523
                    callback(record, args);
524
                //pz2 record
525
                } else if ( recordNode =
526
                    data.getElementsByTagName("record")[0] ) {
527
                    // if stylesheet was fetched do not parse the response
528
                    if ( context.xslDoc ) {
529
                        record = new Array();
530
                        record['xmlDoc'] = data;
531
                        record['xslDoc'] = context.xslDoc;
532
                        record['recid'] =
533
                            recordNode.getElementsByTagName("recid")[0]
534
                                .firstChild.nodeValue;
535
                    //parse record
536
                    } else {
537
                        record = Element_parseChildNodes(recordNode);
538
                    }
539
		    var activeClients =
540
		       Number( data.getElementsByTagName("activeclients")[0]
541
				.childNodes[0].nodeValue );
542
		    context.activeClients = activeClients;
543
                    context.recordCounter++;
544
                    var delay = context.recordTime + context.recordCounter * context.dumpFactor;
545
                    if ( activeClients > 0 )
546
                        context.recordTimer =
547
                           setTimeout (
548
                               function() {
549
                                  context.record(id, offset, syntax, handler);
550
                                  },
551
                                  delay
552
                               );
553
                    callback(record, args);
554
                }
555
                else
556
                    context.throwError('Record failed. Malformed WS resonse.',
557
                                        115);
558
            }
559
        );
560
    },
561
562
    termlist: function()
563
    {
564
        if( !this.searchStatusOK && this.useSessions )
565
            throw new Error(
566
            'Pz2.js: termlist command has to be preceded with a search command.'
567
            );
568
569
        // if called explicitly takes precedence
570
        clearTimeout(this.termTimer);
571
572
        var context = this;
573
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
574
        request.safeGet(
575
            {
576
                "command": "termlist",
577
                "session": this.sessionID,
578
                "name": this.termKeys,
579
                "windowid" : window.name,
580
		"version" : this.version
581
582
            },
583
            function(data) {
584
                if ( data.getElementsByTagName("termlist") ) {
585
                    var activeClients =
586
                        Number( data.getElementsByTagName("activeclients")[0]
587
                                    .childNodes[0].nodeValue );
588
                    context.activeClients = activeClients;
589
                    var termList = { "activeclients":  activeClients };
590
                    var termLists = data.getElementsByTagName("list");
591
                    //for each termlist
592
                    for (i = 0; i < termLists.length; i++) {
593
			var listName = termLists[i].getAttribute('name');
594
                        termList[listName] = new Array();
595
                        var terms = termLists[i].getElementsByTagName('term');
596
                        //for each term in the list
597
                        for (j = 0; j < terms.length; j++) {
598
                            var term = {
599
                                "name":
600
                                    (terms[j].getElementsByTagName("name")[0]
601
                                        .childNodes.length
602
                                    ? terms[j].getElementsByTagName("name")[0]
603
                                        .childNodes[0].nodeValue
604
                                    : 'ERROR'),
605
                                "freq":
606
                                    terms[j]
607
                                    .getElementsByTagName("frequency")[0]
608
                                    .childNodes[0].nodeValue || 'ERROR'
609
                            };
610
611
			    // Only for xtargets: id, records, filtered
612
                            var termIdNode =
613
                                terms[j].getElementsByTagName("id");
614
                            if(terms[j].getElementsByTagName("id").length)
615
                                term["id"] =
616
                                    termIdNode[0].childNodes[0].nodeValue;
617
                            termList[listName][j] = term;
618
619
			    var recordsNode  = terms[j].getElementsByTagName("records");
620
			    if (recordsNode && recordsNode.length)
621
				term["records"] = recordsNode[0].childNodes[0].nodeValue;
622
623
			    var filteredNode  = terms[j].getElementsByTagName("filtered");
624
			    if (filteredNode && filteredNode.length)
625
				term["filtered"] = filteredNode[0].childNodes[0].nodeValue;
626
627
                        }
628
                    }
629
630
                    context.termCounter++;
631
                    var delay = context.termTime
632
                        + context.termCounter * context.dumpFactor;
633
                    if ( activeClients > 0 )
634
                        context.termTimer =
635
                            setTimeout(
636
                                function () {
637
                                    context.termlist();
638
                                },
639
                                delay
640
                            );
641
642
                   context.termlistCallback(termList);
643
                }
644
                else
645
                    context.throwError('Termlist failed. Malformed WS resonse.',
646
                                        116);
647
            }
648
        );
649
650
    },
651
    bytarget: function()
652
    {
653
        if( !this.initStatusOK && this.useSessions )
654
            throw new Error(
655
            'Pz2.js: bytarget command has to be preceded with a search command.'
656
            );
657
658
        // no need to continue
659
        if( !this.searchStatusOK )
660
            return;
661
662
        // if called explicitly takes precedence
663
        clearTimeout(this.bytargetTimer);
664
665
        var context = this;
666
        var request = new pzHttpRequest(this.pz2String, this.errorHandler);
667
        request.safeGet(
668
            {
669
		"command": "bytarget",
670
		"session": this.sessionID,
671
		"block": 1,
672
		"windowid" : window.name,
673
		"version" : this.version
674
	    },
675
            function(data) {
676
                if ( data.getElementsByTagName("status")[0]
677
                        .childNodes[0].nodeValue == "OK" ) {
678
                    var targetNodes = data.getElementsByTagName("target");
679
                    var bytarget = new Array();
680
                    for ( i = 0; i < targetNodes.length; i++) {
681
                        bytarget[i] = new Array();
682
                        for( j = 0; j < targetNodes[i].childNodes.length; j++ ) {
683
                            if ( targetNodes[i].childNodes[j].nodeType
684
                                == Node.ELEMENT_NODE ) {
685
                                var nodeName =
686
                                    targetNodes[i].childNodes[j].nodeName;
687
				if (targetNodes[i].childNodes[j].firstChild != null)
688
				{
689
                                    var nodeText = targetNodes[i].childNodes[j]
690
					.firstChild.nodeValue;
691
                                    bytarget[i][nodeName] = nodeText;
692
				}
693
				else {
694
				    bytarget[i][nodeName] = "";
695
				}
696
697
698
                            }
699
                        }
700
                        if (bytarget[i]["state"]=="Client_Disconnected") {
701
                          bytarget[i]["hits"] = "Error";
702
                        } else if (bytarget[i]["state"]=="Client_Error") {
703
                          bytarget[i]["hits"] = "Error";
704
                        } else if (bytarget[i]["state"]=="Client_Working") {
705
                          bytarget[i]["hits"] = "...";
706
                        }
707
                        if (bytarget[i].diagnostic == "1") {
708
                          bytarget[i].diagnostic = "Permanent system error";
709
                        } else if (bytarget[i].diagnostic == "2") {
710
                          bytarget[i].diagnostic = "Temporary system error";
711
                        }
712
                        var targetsSuggestions = targetNodes[i].getElementsByTagName("suggestions");
713
                        if (targetsSuggestions != undefined && targetsSuggestions.length>0) {
714
                          var suggestions = targetsSuggestions[0];
715
                          bytarget[i]["suggestions"] = Element_parseChildNodes(suggestions);
716
                        }
717
                    }
718
719
                    context.bytargetCounter++;
720
                    var delay = context.bytargetTime
721
                        + context.bytargetCounter * context.dumpFactor;
722
                    if ( context.activeClients > 0 )
723
                        context.bytargetTimer =
724
                            setTimeout(
725
                                function () {
726
                                    context.bytarget();
727
                                },
728
                                delay
729
                            );
730
731
                    context.bytargetCallback(bytarget);
732
                }
733
                else
734
                    context.throwError('Bytarget failed. Malformed WS resonse.',
735
                                        117);
736
            }
737
        );
738
    },
739
740
    // just for testing, probably shouldn't be here
741
    showNext: function(page)
742
    {
743
        var step = page || 1;
744
        this.show( ( step * this.currentNum ) + this.currentStart );
745
    },
746
747
    showPrev: function(page)
748
    {
749
        if (this.currentStart == 0 )
750
            return false;
751
        var step = page || 1;
752
        var newStart = this.currentStart - (step * this.currentNum );
753
        this.show( newStart > 0 ? newStart : 0 );
754
    },
755
756
    showPage: function(pageNum)
757
    {
758
        //var page = pageNum || 1;
759
        this.show(pageNum * this.currentNum);
760
    }
761
};
762
763
/*
764
********************************************************************************
765
** AJAX HELPER CLASS ***********************************************************
766
********************************************************************************
767
*/
768
var pzHttpRequest = function ( url, errorHandler ) {
769
        this.maxUrlLength = 2048;
770
        this.request = null;
771
        this.url = url;
772
        this.errorHandler = errorHandler || null;
773
        this.async = true;
774
        this.requestHeaders = {};
775
776
        if ( window.XMLHttpRequest ) {
777
            this.request = new XMLHttpRequest();
778
        } else if ( window.ActiveXObject ) {
779
            try {
780
                this.request = new ActiveXObject( 'Msxml2.XMLHTTP' );
781
            } catch (err) {
782
                this.request = new ActiveXObject( 'Microsoft.XMLHTTP' );
783
            }
784
        }
785
};
786
787
788
pzHttpRequest.prototype =
789
{
790
    safeGet: function ( params, callback )
791
    {
792
        var encodedParams =  this.encodeParams(params);
793
        var url = this._urlAppendParams(encodedParams);
794
        if (url.length >= this.maxUrlLength) {
795
            this.requestHeaders["Content-Type"]
796
                = "application/x-www-form-urlencoded";
797
            this._send( 'POST', this.url, encodedParams, callback );
798
        } else {
799
            this._send( 'GET', url, '', callback );
800
        }
801
    },
802
803
    get: function ( params, callback )
804
    {
805
        this._send( 'GET', this._urlAppendParams(this.encodeParams(params)),
806
            '', callback );
807
    },
808
809
    post: function ( params, data, callback )
810
    {
811
        this._send( 'POST', this._urlAppendParams(this.encodeParams(params)),
812
            data, callback );
813
    },
814
815
    load: function ()
816
    {
817
        this.async = false;
818
        this.request.open( 'GET', this.url, this.async );
819
        this.request.send('');
820
        if ( this.request.status == 200 )
821
            return this.request.responseXML;
822
    },
823
824
    encodeParams: function (params)
825
    {
826
        var sep = "";
827
        var encoded = "";
828
        for (var key in params) {
829
            if (params[key] != null) {
830
                encoded += sep + key + '=' + encodeURIComponent(params[key]);
831
                sep = '&';
832
            }
833
        }
834
        return encoded;
835
    },
836
837
    _send: function ( type, url, data, callback)
838
    {
839
        var context = this;
840
        this.callback = callback;
841
        this.async = true;
842
        this.request.open( type, url, this.async );
843
        for (var key in this.requestHeaders)
844
            this.request.setRequestHeader(key, this.requestHeaders[key]);
845
        this.request.onreadystatechange = function () {
846
            context._handleResponse(url); /// url used ONLY for error reporting
847
        }
848
        this.request.send(data);
849
    },
850
851
    _urlAppendParams: function (encodedParams)
852
    {
853
        if (encodedParams)
854
            return this.url + "?" + encodedParams;
855
        else
856
            return this.url;
857
    },
858
859
    _handleResponse: function (savedUrlForErrorReporting)
860
    {
861
        if ( this.request.readyState == 4 ) {
862
            // pick up appplication errors first
863
            var errNode = null;
864
            if (this.request.responseXML &&
865
                (errNode = this.request.responseXML.documentElement)
866
                && errNode.nodeName == 'error') {
867
                var errMsg = errNode.getAttribute("msg");
868
                var errCode = errNode.getAttribute("code");
869
                var errAddInfo = '';
870
                if (errNode.childNodes.length)
871
                    errAddInfo = ': ' + errNode.childNodes[0].nodeValue;
872
873
                var err = new Error(errMsg + errAddInfo);
874
                err.code = errCode;
875
876
                if (this.errorHandler) {
877
                    this.errorHandler(err);
878
                }
879
                else {
880
                    throw err;
881
                }
882
            } else if (this.request.status == 200 &&
883
                       this.request.responseXML == null) {
884
              if (this.request.responseText != null) {
885
                //assume JSON
886
887
		var json = null;
888
		var text = this.request.responseText;
889
		if (typeof window.JSON == "undefined")
890
		    json = eval("(" + text + ")");
891
		else {
892
		    try	{
893
			json = JSON.parse(text);
894
		    }
895
		    catch (e) {
896
			// Safari: eval will fail as well. Considering trying JSON2 (non-native implementation) instead
897
			/* DEBUG only works in mk2-mobile
898
			if (document.getElementById("log"))
899
			    document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
900
			*/
901
			try {
902
			    json = eval("(" + text + ")");
903
			}
904
			catch (e) {
905
			    /* DEBUG only works in mk2-mobile
906
			    if (document.getElementById("log"))
907
				document.getElementById("log").innerHTML = "" + e + " " + length + ": " + text;
908
			    */
909
			}
910
		    }
911
		}
912
		this.callback(json, "json");
913
              } else {
914
                var err = new Error("XML response is empty but no error " +
915
                                    "for " + savedUrlForErrorReporting);
916
                err.code = -1;
917
                if (this.errorHandler) {
918
                    this.errorHandler(err);
919
                } else {
920
                    throw err;
921
                }
922
              }
923
            } else if (this.request.status == 200) {
924
                this.callback(this.request.responseXML);
925
            } else {
926
                var err = new Error("HTTP response not OK: "
927
                            + this.request.status + " - "
928
                            + this.request.statusText );
929
                err.code = '00' + this.request.status;
930
                if (this.errorHandler) {
931
                    this.errorHandler(err);
932
                }
933
                else {
934
                    throw err;
935
                }
936
            }
937
        }
938
    }
939
};
940
941
/*
942
********************************************************************************
943
** XML HELPER FUNCTIONS ********************************************************
944
********************************************************************************
945
*/
946
947
// DOMDocument
948
949
if ( window.ActiveXObject) {
950
    var DOMDoc = document;
951
} else {
952
    var DOMDoc = Document.prototype;
953
}
954
955
DOMDoc.newXmlDoc = function ( root )
956
{
957
    var doc;
958
959
    if (document.implementation && document.implementation.createDocument) {
960
        doc = document.implementation.createDocument('', root, null);
961
    } else if ( window.ActiveXObject ) {
962
        doc = new ActiveXObject("MSXML2.DOMDocument");
963
        doc.loadXML('<' + root + '/>');
964
    } else {
965
        throw new Error ('No XML support in this browser');
966
    }
967
968
    return doc;
969
}
970
971
972
DOMDoc.parseXmlFromString = function ( xmlString )
973
{
974
    var doc;
975
976
    if ( window.DOMParser ) {
977
        var parser = new DOMParser();
978
        doc = parser.parseFromString( xmlString, "text/xml");
979
    } else if ( window.ActiveXObject ) {
980
        doc = new ActiveXObject("MSXML2.DOMDocument");
981
        doc.loadXML( xmlString );
982
    } else {
983
        throw new Error ("No XML parsing support in this browser.");
984
    }
985
986
    return doc;
987
}
988
989
DOMDoc.transformToDoc = function (xmlDoc, xslDoc)
990
{
991
    if ( window.XSLTProcessor ) {
992
        var proc = new XSLTProcessor();
993
        proc.importStylesheet( xslDoc );
994
        return proc.transformToDocument(xmlDoc);
995
    } else if ( window.ActiveXObject ) {
996
        return document.parseXmlFromString(xmlDoc.transformNode(xslDoc));
997
    } else {
998
        alert( 'Unable to perform XSLT transformation in this browser' );
999
    }
1000
}
1001
1002
// DOMElement
1003
1004
Element_removeFromDoc = function (DOM_Element)
1005
{
1006
    DOM_Element.parentNode.removeChild(DOM_Element);
1007
}
1008
1009
Element_emptyChildren = function (DOM_Element)
1010
{
1011
    while( DOM_Element.firstChild ) {
1012
        DOM_Element.removeChild( DOM_Element.firstChild )
1013
    }
1014
}
1015
1016
Element_appendTransformResult = function ( DOM_Element, xmlDoc, xslDoc )
1017
{
1018
    if ( window.XSLTProcessor ) {
1019
        var proc = new XSLTProcessor();
1020
        proc.importStylesheet( xslDoc );
1021
        var docFrag = false;
1022
        docFrag = proc.transformToFragment( xmlDoc, DOM_Element.ownerDocument );
1023
        DOM_Element.appendChild(docFrag);
1024
    } else if ( window.ActiveXObject ) {
1025
        DOM_Element.innerHTML = xmlDoc.transformNode( xslDoc );
1026
    } else {
1027
        alert( 'Unable to perform XSLT transformation in this browser' );
1028
    }
1029
}
1030
1031
Element_appendTextNode = function (DOM_Element, tagName, textContent )
1032
{
1033
    var node = DOM_Element.ownerDocument.createElement(tagName);
1034
    var text = DOM_Element.ownerDocument.createTextNode(textContent);
1035
1036
    DOM_Element.appendChild(node);
1037
    node.appendChild(text);
1038
1039
    return node;
1040
}
1041
1042
Element_setTextContent = function ( DOM_Element, textContent )
1043
{
1044
    if (typeof DOM_Element.textContent !== "undefined") {
1045
        DOM_Element.textContent = textContent;
1046
    } else if (typeof DOM_Element.innerText !== "undefined" ) {
1047
        DOM_Element.innerText = textContent;
1048
    } else {
1049
        throw new Error("Cannot set text content of the node, no such method.");
1050
    }
1051
}
1052
1053
Element_getTextContent = function (DOM_Element)
1054
{
1055
    if ( typeof DOM_Element.textContent != 'undefined' ) {
1056
        return DOM_Element.textContent;
1057
    } else if (typeof DOM_Element.text != 'undefined') {
1058
        return DOM_Element.text;
1059
    } else {
1060
        throw new Error("Cannot get text content of the node, no such method.");
1061
    }
1062
}
1063
1064
Element_parseChildNodes = function (node)
1065
{
1066
    var parsed = {};
1067
    var hasChildElems = false;
1068
    var textContent = '';
1069
1070
    if (node.hasChildNodes()) {
1071
        var children = node.childNodes;
1072
        for (var i = 0; i < children.length; i++) {
1073
            var child = children[i];
1074
            switch (child.nodeType) {
1075
              case Node.ELEMENT_NODE:
1076
                hasChildElems = true;
1077
                var nodeName = child.nodeName;
1078
                if (!(nodeName in parsed))
1079
                    parsed[nodeName] = [];
1080
                parsed[nodeName].push(Element_parseChildNodes(child));
1081
                break;
1082
              case Node.TEXT_NODE:
1083
                textContent += child.nodeValue;
1084
                break;
1085
              case Node.CDATA_SECTION_NODE:
1086
                textContent += child.nodeValue;
1087
                break;
1088
            }
1089
        }
1090
    }
1091
1092
    var attrs = node.attributes;
1093
    for (var i = 0; i < attrs.length; i++) {
1094
        hasChildElems = true;
1095
        var attrName = '@' + attrs[i].nodeName;
1096
        var attrValue = attrs[i].nodeValue;
1097
        parsed[attrName] = attrValue;
1098
    }
1099
1100
    // if no nested elements/attrs set value to text
1101
    if (hasChildElems)
1102
      parsed['#text'] = textContent;
1103
    else
1104
      parsed = textContent;
1105
1106
    return parsed;
1107
}
1108
1109
/* do not remove trailing bracket */
1110
}
(-)a/koha-tmpl/opac-tmpl/prog/en/css/opac.css (-1 / +26 lines)
Lines 1690-1695 strong em, em strong { Link Here
1690
	vertical-align: top;
1690
	vertical-align: top;
1691
	width: 10px;
1691
	width: 10px;
1692
}
1692
}
1693
.sourcecol {
1694
	vertical-align: top;
1695
	width: 100px;
1696
}
1693
#container {
1697
#container {
1694
	color : #000;
1698
	color : #000;
1695
}
1699
}
Lines 3017-3022 float:left; Link Here
3017
padding: 0.1em 0;
3021
padding: 0.1em 0;
3018
}
3022
}
3019
3023
3024
.notesrow label {
3025
    font-weight: bold;
3026
}
3027
.notesrow span {
3028
    display: block;
3029
}
3030
.notesrow textarea {
3031
    width: 100%;
3032
}
3033
3020
.thumbnail-shelfbrowser span {
3034
.thumbnail-shelfbrowser span {
3021
    margin: 0px auto;
3035
    margin: 0px auto;
3022
}
3036
}
Lines 3041-3047 padding: 0.1em 0; Link Here
3041
    background: #EEEEEE none;
3055
    background: #EEEEEE none;
3042
}
3056
}
3043
3057
3044
#overdrive-results {
3058
#overdrive-results, #pazpar2-results {
3045
    font-weight: bold;
3059
    font-weight: bold;
3046
    padding-left: 1em;
3060
    padding-left: 1em;
3047
}
3061
}
Lines 3085-3087 padding: 0.1em 0; Link Here
3085
    width: 35%;
3099
    width: 35%;
3086
    font-size: 111%;
3100
    font-size: 111%;
3087
}
3101
}
3102
3103
.thumbnail-shelfbrowser span {
3104
    margin: 0px auto;
3105
}
3106
3107
#modal-overlay {
3108
    background-color: white;
3109
    border-radius: 5px;
3110
    max-width: 75%;
3111
    padding: 15px;
3112
}
(-)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" /></span>' );
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 ' ) + data.total + _( ' results in ' ) + $( '#targets-facet input:checked' ).length + _( ' external targets.' ) + '</strong>' );
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 ( 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 (+3 lines)
Lines 12-17 Link Here
12
<xsl:output method = "html" indent="yes" omit-xml-declaration = "yes" encoding="UTF-8"/>
12
<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"/>
13
<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)"/>
14
<xsl:key name="item-by-status-and-branch" match="items:item" use="concat(items:status, ' ', items:homebranch)"/>
15
<xsl:param name="showAvailability" select="true()"/>
15
16
16
<xsl:template match="/">
17
<xsl:template match="/">
17
  <xsl:apply-templates/>
18
  <xsl:apply-templates/>
Lines 99-104 Link Here
99
100
100
  <xsl:call-template name="tag_215" />
101
  <xsl:call-template name="tag_215" />
101
102
103
  <xsl:if test="$showAvailability">
102
  <span class="results_summary availability">
104
  <span class="results_summary availability">
103
    <span class="label">Availability: </span>
105
    <span class="label">Availability: </span>
104
    <xsl:choose>
106
    <xsl:choose>
Lines 247-252 Link Here
247
      </span>
249
      </span>
248
    </xsl:if>
250
    </xsl:if>
249
  </span>
251
  </span>
252
  </xsl:if>
250
253
251
</xsl:template>
254
</xsl:template>
252
255
(-)a/opac/opac-external-search.pl (+63 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2009 BibLibre SARL
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
use strict;
21
use warnings;
22
23
use CGI;
24
25
use C4::Auth qw(:DEFAULT get_session);
26
use C4::Output;
27
use C4::Search qw( GetExternalSearchTargets );
28
use C4::XSLT qw( XSLTGetFilename );
29
30
my $cgi = new CGI;
31
32
# Getting the template and auth
33
my ($template, $loggedinuser, $cookie)
34
= get_template_and_user({template_name => "opac-external-search.tmpl",
35
                                query => $cgi,
36
                                type => "opac",
37
                                authnotrequired => 1,
38
                                flagsrequired => {borrowers => 1},
39
                                debug => 1,
40
                                });
41
42
$template->{VARS}->{q} = $cgi->param('q');
43
$template->{VARS}->{limit} = C4::Context->preference('OPACnumSearchResults') || 20;
44
$template->{VARS}->{OPACnumSearchResults} = C4::Context->preference('OPACnumSearchResults') || 20;
45
$template->{VARS}->{external_search_targets} = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
46
47
my @xsltResultStylesheets;
48
my @xsltDetailStylesheets;
49
50
foreach my $syntax ( qw( MARC21 UNIMARC NORMARC ) ) {
51
    if ( XSLTGetFilename( $syntax, 'OPACXSLTResultsDisplay' ) =~ m,/opac-tmpl/.*|^https:?.*, ) {
52
        push @xsltResultStylesheets, { syntax => $syntax, url => $& };
53
    }
54
55
    if ( XSLTGetFilename( $syntax, 'OPACXSLTDetailsDisplay' ) =~ m,/opac-tmpl/.*|^https:?.*, ) {
56
        push @xsltDetailStylesheets, { syntax => $syntax, url => $& };
57
    }
58
}
59
60
$template->{VARS}->{xslt_result_stylesheets} = \@xsltResultStylesheets;
61
$template->{VARS}->{xslt_detail_stylesheets} = \@xsltDetailStylesheets;
62
63
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 (+99 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 ByWater
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
=head1 NAME
21
22
svc/pazpar2_init: Initialize a Pazpar2 session
23
24
=head1 SYNOPSIS
25
26
svc/pazpar2_init?targets=3,5,9 -> ../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.
32
33
=cut
34
35
use strict;
36
use warnings;
37
38
use CGI qw(-oldstyle_urls);
39
use HTTP::Request::Common;
40
use JSON;
41
use URI;
42
use XML::Simple;
43
44
use C4::Context;
45
use C4::Search;
46
use C4::Output;
47
48
my $dbh = C4::Context->dbh;
49
my $query = new CGI;
50
51
my %init_opts;
52
53
my $targets = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
54
55
foreach my $target ( @$targets ) {
56
    my $target_url = $target->{'host'} . ':' . $target->{'port'} . '/' . $target->{'db'};
57
    $init_opts{ 'pz:name[' . $target_url . ']' } = $target->{'name'};
58
    $init_opts{ 'pz:queryencoding[' . $target_url . ']' } = $target->{'encoding'};
59
    $init_opts{ 'pz:xslt[' . $target_url . ']' } = lc( $target->{'syntax'} ) . '-work-groups.xsl';
60
    $init_opts{ 'pz:requestsyntax[' . $target_url . ']' } = $target->{'syntax'};
61
    $init_opts{ 'pz:nativesyntax[' . $target_url . ']' } = 'iso2709';
62
63
    if ( $target->{'userid'} ) {
64
        if ( $target->{'password'} ) {
65
            $init_opts{ 'pz:authentication[' . $target_url . ']' } = $target->{'userid'} . '/' . $target->{'password'};
66
        } else {
67
            $init_opts{ 'pz:authentication[' . $target_url . ']' } = $target->{'userid'};
68
        }
69
    }
70
}
71
72
my $uri = 'http://' . C4::Context->preference( 'OPACBaseURL' ) . "/pazpar2/search.pz2";
73
74
my $request = HTTP::Request::Common::POST( $uri, [ command => 'init', %init_opts ] );
75
76
my $ua = LWP::UserAgent->new( "Koha " . C4::Context->KOHAVERSION );
77
78
my $response = $ua->request( $request ) ;
79
if ( !$response->is_success ) {
80
    print $query->header(
81
        -status => '500 Internal Server Error'
82
    );
83
84
    warn "Pazpar2 init failed: " . $response->message;
85
    my $content = to_json({
86
        error => 'Could not connect to Pazpar2',
87
    });
88
    output_with_http_headers $query, undef, $content, 'json', '500 Internal Server Error';
89
90
    exit;
91
} else {
92
    my $xs = XML::Simple->new;
93
    my $data = $xs->XMLin( $response->content );
94
95
    my $content = to_json({
96
        sessionID => $data->{'session'}
97
    });
98
    output_with_http_headers $query, undef, $content, 'json', '200 OK';
99
}
(-)a/rewrite-config.PL (-5 / +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];
166
- 

Return to bug 10486