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
  &SimpleSearch
74
  &SimpleSearch
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 2358-2363 sub _ZOOM_event_loop { Link Here
2358
    }
2359
    }
2359
}
2360
}
2360
2361
2362
=head2 GetExternalSearchTargets
2363
2364
Returns the list of Z39.50 servers that are marked for search in the OPAC using
2365
Pazpar2.
2366
2367
=cut
2368
2369
sub GetExternalSearchTargets {
2370
    my ( $branchcode ) = @_;
2371
2372
    if ( $branchcode ) {
2373
        return C4::Context->dbh->selectall_arrayref( q{
2374
            SELECT * FROM external_targets et
2375
            LEFT JOIN external_target_restrictions etr
2376
                ON (etr.target_id = et.target_id and etr.branchcode = ?)
2377
            WHERE etr.target_id IS NULL
2378
            ORDER BY et.name
2379
        }, { Slice => {} }, $branchcode );
2380
    } else {
2381
        return C4::Context->dbh->selectall_arrayref( q{
2382
            SELECT * FROM external_targets et
2383
            LEFT JOIN external_target_restrictions etr USING (target_id)
2384
            GROUP by et.target_id
2385
            HAVING branchcode IS NULL
2386
            ORDER BY et.name
2387
        }, { Slice => {} } );
2388
    }
2389
}
2390
2361
2391
2362
END { }    # module clean-up code here (global destructor)
2392
END { }    # module clean-up code here (global destructor)
2363
2393
(-)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 534-540 if ($config{'INSTALL_ZEBRA'} eq "yes") { Link Here
534
    );
534
    );
535
    if ($config{'INSTALL_PAZPAR2'} eq 'yes') {
535
    if ($config{'INSTALL_PAZPAR2'} eq 'yes') {
536
        push @{ $pl_files->{'rewrite-config.PL'} }, (
536
        push @{ $pl_files->{'rewrite-config.PL'} }, (
537
            'blib/PAZPAR2_CONF_DIR/koha-biblios.xml',
537
            'blib/PAZPAR2_CONF_DIR/generic-settings.xml',
538
            'blib/PAZPAR2_CONF_DIR/pazpar2.xml'
538
            'blib/PAZPAR2_CONF_DIR/pazpar2.xml'
539
        );
539
        );
540
    }
540
    }
(-)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 (+11 lines)
Lines 106-111 Link Here
106
     RewriteRule ^/isbn/([^\/]*)/?$ /search?q=isbn:$1 [PT]
106
     RewriteRule ^/isbn/([^\/]*)/?$ /search?q=isbn:$1 [PT]
107
     RewriteRule ^/issn/([^\/]*)/?$ /search?q=issn:$1 [PT]
107
     RewriteRule ^/issn/([^\/]*)/?$ /search?q=issn:$1 [PT]
108
   </IfModule>
108
   </IfModule>
109
110
   __PAZPAR2_TOGGLE_HTTPD_PRE__
111
     <Proxy *>
112
         AddDefaultCharset off
113
         Order deny,allow
114
         Allow from all
115
     </Proxy>
116
117
     ProxyRequests off
118
     ProxyPass /pazpar2/search.pz2 http://__PAZPAR2_HOST__:__PAZPAR2_PORT__/search.pz2
119
   __PAZPAR2_TOGGLE_HTTPD_POST__
109
</VirtualHost>
120
</VirtualHost>
110
121
111
## Intranet
122
## Intranet
(-)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/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css (-11 / +15 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;
Lines 939-954 div.sysprefs div.hint { Link Here
939
	margin : .7em;
953
	margin : .7em;
940
}
954
}
941
955
942
tr.highlight td {
943
	background-color : #F6F6F6;
944
	border-color : #BCBCBC;
945
}
946
947
tr.highlight th[scope=row] {
948
	background-color : #DDDDDD;
949
	border-color : #BCBCBC;
950
}
951
952
#circ_circulation_issue label {
956
#circ_circulation_issue label {
953
	font-size: 105%;
957
	font-size: 105%;
954
	font-weight : bold;
958
	font-weight : bold;
Lines 2570-2573 fieldset.rows table.mceListBox { Link Here
2570
    font-size: 140%;
2574
    font-size: 140%;
2571
    font-family : monospace;
2575
    font-family : monospace;
2572
    padding : .3em;
2576
    padding : .3em;
2573
}
2577
}
(-)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/opac-tmpl/ccsr/en/css/opac.css (+16 lines)
Lines 1605-1610 strong em, em strong { Link Here
1605
     vertical-align: top;
1605
     vertical-align: top;
1606
   width: 10px;
1606
   width: 10px;
1607
}
1607
}
1608
.sourcecol {
1609
	vertical-align: top;
1610
	width: 100px;
1611
}
1608
#container {
1612
#container {
1609
    color : #000;
1613
    color : #000;
1610
}
1614
}
Lines 2819-2821 a.reviewlink,a.reviewlink:visited { Link Here
2819
.subjectSearch ul {
2823
.subjectSearch ul {
2820
    padding-left: 0px;
2824
    padding-left: 0px;
2821
}
2825
}
2826
2827
#modal-overlay {
2828
    background-color: white;
2829
    border-radius: 5px;
2830
    max-width: 75%;
2831
    padding: 15px;
2832
}
2833
2834
#overdrive-results, #pazpar2-results {
2835
    font-weight: bold;
2836
    padding-left: 1em;
2837
}
(-)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 / +17 lines)
Lines 1628-1633 strong em, em strong { Link Here
1628
	vertical-align: top;
1628
	vertical-align: top;
1629
	width: 10px;
1629
	width: 10px;
1630
}
1630
}
1631
.sourcecol {
1632
	vertical-align: top;
1633
	width: 100px;
1634
}
1631
#container {
1635
#container {
1632
	color : #000;
1636
	color : #000;
1633
}
1637
}
Lines 2955-2960 a.reviewlink,a.reviewlink:visited { Link Here
2955
float:left;
2959
float:left;
2956
padding: 0.1em 0;
2960
padding: 0.1em 0;
2957
}
2961
}
2962
2958
.notesrow label {
2963
.notesrow label {
2959
    font-weight: bold;
2964
    font-weight: bold;
2960
}
2965
}
Lines 2969-2975 padding: 0.1em 0; Link Here
2969
    margin: 0px auto;
2974
    margin: 0px auto;
2970
}
2975
}
2971
2976
2972
#overdrive-results {
2977
#overdrive-results, #pazpar2-results {
2973
    font-weight: bold;
2978
    font-weight: bold;
2974
    padding-left: 1em;
2979
    padding-left: 1em;
2975
}
2980
}
Lines 2977-2979 padding: 0.1em 0; Link Here
2977
.throbber {
2982
.throbber {
2978
    vertical-align: middle;
2983
    vertical-align: middle;
2979
}
2984
}
2985
2986
.thumbnail-shelfbrowser span {
2987
    margin: 0px auto;
2988
}
2989
2990
#modal-overlay {
2991
    background-color: white;
2992
    border-radius: 5px;
2993
    max-width: 75%;
2994
    padding: 15px;
2995
}
(-)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 13-19 Link Here
13
[% IF ( OpacStarRatings == 'all' ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.rating.js"></script>
13
[% IF ( OpacStarRatings == 'all' ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.rating.js"></script>
14
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />[% END %]
14
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />[% END %]
15
15
16
<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script>
16
[% IF ( OverDriveEnabled ) %]<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script>[% END %]
17
[% IF ( 1 ) %]
18
<script type="text/javascript" src="/opac-tmpl/lib/pz2.js"></script>
19
<script type="text/javascript" src="[% themelang %]/js/externalsearch.js"></script>
20
[% END %]
17
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
21
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
18
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
22
[% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
19
[% END %]<script type="text/javascript">
23
[% END %]<script type="text/javascript">
Lines 262-285 $(document).ready(function(){ Link Here
262
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
266
        $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;});
263
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
267
        $("#highlight_toggle_off").show().click(function() {highlightOff();});
264
    [% END %]
268
    [% END %]
269
    function StartExternalSearch(name, text) {
270
        $( '#breadcrumbs p' )
271
            .eq( 0 )
272
            .append( ' <span id="' + name + '-results">' + text + '... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
273
    }
274
    function FailExternalSearch( name, text ) {
275
        $( '#' + name + '-results' ).html( text );
276
    }
277
    function FinishExternalSearch( name, text, numItems, url ) {
278
        if ( numItems ) {
279
            $( '#' + name + '-results' ).html( text.replace( '__LINK__', '<a href="' + url + '">'  + numItems + ' results</a>' ) );
280
        } else {
281
            $( '#' + name + '-results' ).remove();
282
        }
283
    }
284
265
    [% IF ( OverDriveEnabled ) %]
285
    [% IF ( OverDriveEnabled ) %]
266
        var $overdrive_results = $( '<span id="overdrive-results">Searching OverDrive... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
286
        StartExternalSearch( 'overdrive', _( 'Searching OverDrive' ) );
267
        $( '#breadcrumbs p' ).eq(0)
268
            .append( ' ' )
269
            .append( $overdrive_results );
270
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
287
        KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) {
271
            if ( data.error ) {
288
            if ( data.error ) {
272
                $overdrive_results.html( 'Error searching OverDrive collection' );
289
                FailExternalSearch( 'overdrive', _( 'Error searching OverDrive collection' ) );
273
                return;
290
                return;
274
            }
291
            }
275
292
276
            if ( data.totalItems ) {
293
            FinishExternalSearch( 'overdrive', 'Found __LINK__ in OverDrive collection', data.totalItems, '/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) );
277
                $overdrive_results.html( 'Found <a href="/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) + '">' + data.totalItems + ' results</a> in OverDrive collection' );
294
        } );
278
            } else {
295
    [% END %]
279
                $overdrive_results.remove();
296
297
    [% IF ( 1 ) %]
298
        KOHA.ExternalSearch.targets = {
299
            [% FOREACH target IN external_search_targets %]
300
                '[% target.host %]:[% target.port %]/[% target.db %]': {
301
                    id: '[% target.target_id %]',
302
                    name: '[% target.name %]',
303
                    syntax: '[% target.syntax %]',
304
                },
305
            [% END %]
306
        };
307
        var num_targets = 0; $.each( KOHA.ExternalSearch.targets, function() { num_targets++ } );
308
        var first_succeeded;
309
310
        StartExternalSearch( 'pazpar2', _( 'Searching external targets' ) );
311
        KOHA.ExternalSearch.Search( querystring, 1, function( data ) {
312
            if ( data.error ) {
313
                if ( !first_succeeded ) FailExternalSearch( 'pazpar2', _( 'Error searching external targets' ) );
314
                return;
280
            }
315
            }
316
317
            first_succeeded = true;
318
            FinishExternalSearch( 'pazpar2', _( 'Found __LINK__ in ' ) + num_targets + _( ' external targets' ), data.total, '/cgi-bin/koha/opac-external-search.pl?q=' + escape( querystring ) );
281
        } );
319
        } );
282
    [% END %]
320
    [% END %]
321
    
283
[% END %]
322
[% END %]
284
323
285
[% IF ( TagsInputEnabled && loggedinusername ) %]
324
[% 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 1143-1148 Link Here
1143
                       <xsl:text>). </xsl:text>                   </span>
1146
                       <xsl:text>). </xsl:text>                   </span>
1144
                   </xsl:if>
1147
                   </xsl:if>
1145
               </span>
1148
               </span>
1149
               </xsl:if>
1146
    <xsl:choose>
1150
    <xsl:choose>
1147
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1151
        <xsl:when test="($OPACItemLocation='location' or $OPACItemLocation='ccode') and (count(key('item-by-status', 'available'))!=0 or count(key('item-by-status', 'reference'))!=0)">
1148
            <span class="results_summary" id="location">
1152
            <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 95-100 Link Here
95
96
96
  <xsl:call-template name="tag_215" />
97
  <xsl:call-template name="tag_215" />
97
98
99
  <xsl:if test="$showAvailability">
98
  <span class="results_summary">
100
  <span class="results_summary">
99
    <span class="label">Availability: </span>
101
    <span class="label">Availability: </span>
100
    <xsl:choose>
102
    <xsl:choose>
Lines 238-243 Link Here
238
      </span>
240
      </span>
239
    </xsl:if>
241
    </xsl:if>
240
  </span>
242
  </span>
243
  </xsl:if>
241
244
242
</xsl:template>
245
</xsl:template>
243
246
(-)a/opac/opac-external-search.pl (+65 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, 'OPACXSLTDetailDisplay' ) =~ 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;
64
65
(-)a/opac/opac-search.pl (-4 / +7 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 900-909 $template->{VARS}->{IDreamBooksReviews} = C4::Context->preference('IDreamBooksRe Link Here
900
$template->{VARS}->{IDreamBooksReadometer} = C4::Context->preference('IDreamBooksReadometer');
903
$template->{VARS}->{IDreamBooksReadometer} = C4::Context->preference('IDreamBooksReadometer');
901
$template->{VARS}->{IDreamBooksResults} = C4::Context->preference('IDreamBooksResults');
904
$template->{VARS}->{IDreamBooksResults} = C4::Context->preference('IDreamBooksResults');
902
905
903
if ($offset == 0 && IsOverDriveEnabled()) {
906
$template->{VARS}->{external_search_targets} = GetExternalSearchTargets( C4::Context->userenv ? C4::Context->userenv->{branch} : '' );
904
    $template->param(OverDriveEnabled => 1);
907
905
    $template->param(OverDriveLibraryID => C4::Context->preference('OverDriveLibraryID'));
908
$template->{VARS}->{OverDriveLibraryID} = C4::Context->preference('OverDriveLibraryID');
906
}
909
$template->{VARS}->{OverDriveEnabled} = ($offset == 0 && IsOverDriveEnabled());
907
910
908
    $template->param( borrowernumber    => $borrowernumber);
911
    $template->param( borrowernumber    => $borrowernumber);
909
output_with_http_headers $cgi, $cookie, $template->output, $content_type;
912
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 136-143 $prefix = $ENV{'INSTALL_BASE'} || "/usr"; Link Here
136
  "__INSTALL_ZEBRA__" => 'yes',
136
  "__INSTALL_ZEBRA__" => 'yes',
137
  "__INSTALL_SRU__" => 'yes',
137
  "__INSTALL_SRU__" => 'yes',
138
  "__INSTALL_PAZPAR2__" => 'no',
138
  "__INSTALL_PAZPAR2__" => 'no',
139
  "__PAZPAR2_TOGGLE_XML_PRE__" => '<!--',
139
  "__PAZPAR2_TOGGLE_HTTPD_PRE__" => '<IfDefine PAZPAR2_IS_DISABLED>',
140
  "__PAZPAR2_TOGGLE_XML_POST__" => '-->',
140
  "__PAZPAR2_TOGGLE_HTTPD_POST__" => '</IfDefine>',
141
  "__AUTH_INDEX_MODE__" => 'grs1',
141
  "__AUTH_INDEX_MODE__" => 'grs1',
142
  "__BIB_INDEX_MODE__" => 'grs1',
142
  "__BIB_INDEX_MODE__" => 'grs1',
143
  "__RUN_DATABASE_TESTS__" => 'no',
143
  "__RUN_DATABASE_TESTS__" => 'no',
Lines 157-164 foreach $key (keys %configuration) { Link Here
157
# munge commenting out the PazPar2 mergeserver
157
# munge commenting out the PazPar2 mergeserver
158
# entry in koha-conf.xml if necessary
158
# entry in koha-conf.xml if necessary
159
if ($configuration{'__INSTALL_PAZPAR2__'} eq 'yes') {
159
if ($configuration{'__INSTALL_PAZPAR2__'} eq 'yes') {
160
    $configuration{'__PAZPAR2_TOGGLE_XML_PRE__'} = '';
160
    $configuration{'__PAZPAR2_TOGGLE_HTTPD_PRE__'} = '';
161
    $configuration{'__PAZPAR2_TOGGLE_XML_POST__'} = '';
161
    $configuration{'__PAZPAR2_TOGGLE_HTTPD_POST__'} = '';
162
}
162
}
163
163
164
$fname = $ARGV[0];
164
$fname = $ARGV[0];
165
- 

Return to bug 10486