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

(-)a/C4/Breeding.pm (-1 / +104 lines)
Lines 30-35 use C4::AuthoritiesMarc; #GuessAuthTypeCode, FindDuplicateAuthority Link Here
30
use C4::Languages;
30
use C4::Languages;
31
use Koha::Database;
31
use Koha::Database;
32
use Koha::XSLT_Handler;
32
use Koha::XSLT_Handler;
33
use Time::HiRes qw( clock_gettime CLOCK_MONOTONIC );
33
34
34
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
35
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
35
36
Lines 38-44 BEGIN { Link Here
38
    $VERSION = 3.07.00.049;
39
    $VERSION = 3.07.00.049;
39
	require Exporter;
40
	require Exporter;
40
	@ISA = qw(Exporter);
41
	@ISA = qw(Exporter);
41
    @EXPORT = qw(&BreedingSearch &Z3950Search &Z3950SearchAuth);
42
    @EXPORT = qw(&BreedingSearch &Z3950Search &Z3950SearchAuth &RunZ3950);
42
}
43
}
43
44
44
=head1 NAME
45
=head1 NAME
Lines 326-333 sub _add_rowdata { Link Here
326
    my ($row, $record)=@_;
327
    my ($row, $record)=@_;
327
    my %fetch= (
328
    my %fetch= (
328
        title => 'biblio.title',
329
        title => 'biblio.title',
330
        seriestitle => 'biblio.seriestitle',
329
        author => 'biblio.author',
331
        author => 'biblio.author',
330
        isbn =>'biblioitems.isbn',
332
        isbn =>'biblioitems.isbn',
333
        issn =>'biblioitems.issn',
331
        lccn =>'biblioitems.lccn', #LC control number (not call number)
334
        lccn =>'biblioitems.lccn', #LC control number (not call number)
332
        edition =>'biblioitems.editionstatement',
335
        edition =>'biblioitems.editionstatement',
333
        date => 'biblio.copyrightdate', #MARC21
336
        date => 'biblio.copyrightdate', #MARC21
Lines 711-716 sub Z3950SearchAuth { Link Here
711
    );
714
    );
712
}
715
}
713
716
717
sub RunZ3950 {
718
    my ( $server_ids, $query, $options ) = @_;
719
720
    $options = {
721
        offset => 0,
722
        fetch => 20,
723
        on_error => sub {},
724
        on_hit => sub {},
725
        %{ $options || {} }
726
    };
727
728
    my $schema = Koha::Database->new->schema;
729
    my $stats = {
730
        num_fetched => {
731
            map { $_ => 0 } @$server_ids
732
        },
733
        num_hits => {
734
            map { $_ => 0 } @$server_ids
735
        },
736
        total_fetched => 0,
737
        total_hits => 0,
738
    };
739
    my $start = clock_gettime( CLOCK_MONOTONIC );
740
    my @servers;
741
742
    foreach my $server ( $schema->resultset('Z3950server')->search( { id => $server_ids } )->all ) {
743
        my $zoptions = ZOOM::Options->new();
744
        $zoptions->option( 'async', 1 );
745
        $zoptions->option( 'elementSetName', 'F' );
746
        $zoptions->option( 'databaseName',   $server->db );
747
        $zoptions->option( 'user', $server->userid ) if $server->userid;
748
        $zoptions->option( 'password', $server->password ) if $server->password;
749
        $zoptions->option( 'preferredRecordSyntax', $server->syntax );
750
        $zoptions->option( 'timeout', $server->timeout ) if $server->timeout;
751
752
        my $connection = ZOOM::Connection->create($zoptions);
753
        $connection->connect( $server->host, $server->port );
754
755
        push @servers, {
756
            connection => $connection,
757
            id => $server->id,
758
            host => $server->host,
759
            name => $server->name,
760
            encoding => ( $server->encoding ? $server->encoding : "iso-5426" ),
761
            results => $connection->search_pqf( $query ), # Starts the search
762
        };
763
    }
764
765
    my $servers_left = scalar @servers;
766
    my $total_raw_size = 0;
767
768
    while ( $servers_left ) {
769
        my $i;
770
771
        # Read pending events from servers until one finishes
772
        while ( ( $i = ZOOM::event( [ map { $_->{connection} } @servers ] ) ) != 0 ) {
773
            last if $servers[ $i - 1 ]->{connection}->last_event() == ZOOM::Event::ZEND;
774
        }
775
776
        $servers_left--;
777
        my $server = $servers[ --$i ];
778
        my $exception = $server->{connection}->exception(); #ignores errmsg, addinfo, diagset
779
780
        if ($exception) {
781
            $options->{on_error}->( $server, $exception );
782
        } else {
783
            my $num_results = $stats->{num_hits}->{ $server->{id} } = $server->{results}->size;
784
            my $num_fetched = $stats->{num_fetched}->{ $server->{id} } = ( $options->{offset} + $options->{fetch} ) < $num_results ? $options->{fetch} : $num_results;
785
786
            $stats->{total_hits} += $num_results;
787
            $stats->{total_fetched} += $num_fetched;
788
789
            next if ( !$num_results );
790
791
            my $hits = $server->{results}->records( $options->{offset}, $num_fetched, 1 );
792
793
            if ( !@$hits ) {
794
                $options->{on_error}->( $server, $server->{connection}->exception() ) if ( $server->{connection}->exception() );
795
                next;
796
            }
797
798
            foreach my $j ( 0..$#$hits ) {
799
                $total_raw_size += length $hits->[$j]->raw();
800
                my ($marcrecord) = MarcToUTF8Record( $hits->[$j]->raw(), C4::Context->preference('marcflavour'), $server->{encoding} ); #ignores charset return values
801
                my $metadata = {};
802
                _add_rowdata( $metadata, $marcrecord );
803
                $options->{on_hit}->( $server, {
804
                    index => $options->{offset} + $j,
805
                    record => $marcrecord,
806
                    metadata => $metadata,
807
                } );
808
            }
809
        }
810
    }
811
812
    $stats->{time} = clock_gettime( CLOCK_MONOTONIC ) - $start;
813
814
    return $stats;
815
}
816
714
1;
817
1;
715
__END__
818
__END__
716
819
(-)a/cataloguing/editor.pl (-1 / +2 lines)
Lines 45-52 $template->{VARS}->{DefaultLanguageField008} = pack( 'A3', C4::Context->preferen Link Here
45
45
46
# Z39.50 servers
46
# Z39.50 servers
47
my $dbh = C4::Context->dbh;
47
my $dbh = C4::Context->dbh;
48
$template->{VARS}->{z3950_targets} = $dbh->selectall_arrayref( q{
48
$template->{VARS}->{z3950_servers} = $dbh->selectall_arrayref( q{
49
    SELECT * FROM z3950servers
49
    SELECT * FROM z3950servers
50
    WHERE recordtype != 'authority'
50
    ORDER BY name
51
    ORDER BY name
51
}, { Slice => {} } );
52
}, { Slice => {} } );
52
53
(-)a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js (-1 / +2 lines)
Lines 1-7 Link Here
1
define( function() {
1
define( function() {
2
    var Preferences = {
2
    var Preferences = {
3
        Load: function( borrowernumber ) {
3
        Load: function( borrowernumber ) {
4
            if ( !borrowernumber ) return;
4
            if ( borrowernumber == null ) return;
5
5
            var saved_prefs;
6
            var saved_prefs;
6
            try {
7
            try {
7
                saved_prefs = JSON.parse( localStorage[ 'cateditor_preferences_' + borrowernumber ] );
8
                saved_prefs = JSON.parse( localStorage[ 'cateditor_preferences_' + borrowernumber ] );
(-)a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/search.js (-77 / +71 lines)
Lines 1-94 Link Here
1
define( [ 'marc-record', 'pz2' ], function( MARC, Pazpar2 ) {
1
define( [ 'marc-record' ], function( MARC ) {
2
    //var _pz;
3
    var _onresults;
4
    var _recordCache = {};
5
    var _options;
2
    var _options;
3
    var _records = {};
4
    var _last;
6
5
7
    var Search = {
6
    var _pqfMapping = {
8
        Init: function( targets, options ) {
7
        author: '1=1004', // s=al',
9
            var initOpts = {};
8
        cn_dewey: '1=13',
9
        cn_lc: '1=16',
10
        date: '1=30', // r=r',
11
        isbn: '1=7',
12
        issn: '1=8',
13
        lccn: '1=9',
14
        local_number: '1=12',
15
        music_identifier: '1=51',
16
        standard_identifier: '1=1007',
17
        subject: '1=21', // s=al',
18
        term: '1=1016', // t=l,r s=al',
19
        title: '1=4', // s=al',
20
    }
10
21
11
            $.each( targets, function ( url, info ) {
22
    var Search = {
12
                initOpts[ 'pz:name[' + url + ']' ] = info.name;
23
        Init: function( options ) {
13
                initOpts[ 'pz:queryencoding[' + url + ']' ] = info.encoding;
24
            _options = options;
14
                initOpts[ 'pz:xslt[' + url + ']' ] = info.kohasyntax.toLowerCase() + '-work-groups.xsl';
25
        },
15
                initOpts[ 'pz:requestsyntax[' + url + ']' ] = info.syntax;
26
        JoinTerms: function( terms ) {
27
            var q = '';
16
28
17
                // Load in default CCL mappings
29
            $.each( terms, function( i, term ) {
18
                // Pazpar2 seems to have a bug where wildcard cclmaps are ignored.
30
                var term = '@attr ' + _pqfMapping[ term[0] ] + ' "' + term[1].replace( '"', '\\"' ) + '"'
19
                // What an incredible surprise.
20
                initOpts[ 'pz:cclmap:term[' + url + ']' ] = 'u=1016 t=l,r s=al';
21
                initOpts[ 'pz:cclmap:Author-name[' + url + ']' ] = 'u=1004 s=al';
22
                initOpts[ 'pz:cclmap:Classification-Dewey[' + url + ']' ] = 'u=13';
23
                initOpts[ 'pz:cclmap:Classification-LC[' + url + ']' ] = 'u=16';
24
                initOpts[ 'pz:cclmap:Date[' + url + ']' ] = 'u=30 r=r';
25
                initOpts[ 'pz:cclmap:Identifier-ISBN[' + url + ']' ] = 'u=7';
26
                initOpts[ 'pz:cclmap:Identifier-ISSN[' + url + ']' ] = 'u=8';
27
                initOpts[ 'pz:cclmap:Identifier-publisher-for-music[' + url + ']' ] = 'u=51';
28
                initOpts[ 'pz:cclmap:Identifier-standard[' + url + ']' ] = 'u=1007';
29
                initOpts[ 'pz:cclmap:LC-card-number[' + url + ']' ] = 'u=9';
30
                initOpts[ 'pz:cclmap:Local-number[' + url + ']' ] = 'u=12';
31
                initOpts[ 'pz:cclmap:Subject[' + url + ']' ] = 'u=21 s=al';
32
                initOpts[ 'pz:cclmap:Title[' + url + ']' ] = 'u=4 s=al';
33
31
34
                if ( info.authentication ) initOpts[ 'pz:authentication[' + url + ']' ] = info.authentication;
32
                if ( q ) {
33
                    q = '@and ' + q + ' ' + term;
34
                } else {
35
                    q = term;
36
                }
35
            } );
37
            } );
36
38
37
            _options =  $.extend( {
39
            return q;
38
                initopts: initOpts,
39
                onshow: Search._onshow,
40
                errorhandler: Search._onerror,
41
            }, options );
42
43
            _pz = new Pazpar2( _options );
44
        },
40
        },
45
        Reconnect: function() {
41
        Run: function( servers, q, options ) {
46
            _pz.reset();
42
            Search.includedServers = [];
47
            _pz = new Pazpar2( _options );
43
            _records = {};
48
        },
44
            _last = {
49
        Start: function( targets, q, limit ) {
45
                servers: servers,
50
            Search.includedTargets = [];
46
                q: q,
51
            recordcache = {};
47
                options: options,
48
            };
52
49
53
            $.each( targets, function ( url, info ) {
50
            options = $.extend( {
54
                if ( !info.disabled ) Search.includedTargets.push( url );
51
                offset: 0,
55
            } );
52
                page_size: 20,
53
            }, _options, options );
56
54
57
            _pz.search( q, limit, 'relevance:0', 'pz:id=' + Search.includedTargets.join( '|' ) );
55
            $.each( servers, function ( id, info ) {
58
            return true;
56
                if ( info.checked ) Search.includedServers.push( id );
59
        },
57
            } );
60
        Fetch: function( offset ) {
61
            _pz.show( offset );
62
        },
63
        GetDetailedRecord: function( recid, callback ) {
64
            if ( _recordCache[recid] ) {
65
                callback( _recordCache[recid] );
66
                return;
67
            }
68
58
69
            _pz.record( recid, 0, undefined, { callback: function(data) {
59
            $.get(
70
                var record = _recordCache[recid] = new MARC.Record();
60
                '/cgi-bin/koha/svc/z3950',
71
                record.loadMARCXML(data.xmlDoc);
61
                {
62
                    q: q,
63
                    servers: Search.includedServers.join( ',' ),
64
                    offset: options.offset,
65
                    page_size: options.page_size
66
                }
67
            )
68
                .done( function( data ) {
69
                    $.each( data.hits, function( undef, hit ) {
70
                        var record = new MARC.Record();
71
                        record.loadMARCXML( hit.record );
72
                        hit.record = record;
73
                    } );
72
74
73
                callback(record);
75
                    _options.onresults( data );
74
            } } );
76
                } )
75
        },
77
                .fail( function( error ) {
76
        IsAvailable: function() {
78
                    _options.onerror( error );
77
            return _pz.initStatusOK;
79
                } );
78
        },
79
        _onshow: function( data ) {
80
            $.each( data.hits, function( undef, hit ) {
81
                hit.id = 'search:' + encodeURIComponent( hit.recid[0] );
82
            } );
83
80
84
            _options.onresults( data );
81
            return true;
85
        },
82
        },
86
        _onerror: function( error ) {
83
        Fetch: function( offset ) {
87
            if ( _options.oniniterror && !_pz.initStatusOK ) {
84
            if ( !_last ) return;
88
                _options.oniniterror( error );
85
            Search.Run( _last.servers, _last.q, $.extend( {}, _last.options, { offset: offset } ) );
89
            } else {
90
                _options.onerror( error );
91
            }
92
        }
86
        }
93
    };
87
    };
94
88
(-)a/koha-tmpl/intranet-tmpl/prog/en/css/cateditor.css (-1 / +1 lines)
Lines 315-321 body { Link Here
315
    line-height: 24px;
315
    line-height: 24px;
316
}
316
}
317
317
318
.results-marc {
318
.marccol {
319
    font-family: monospace;
319
    font-family: monospace;
320
    height: auto;
320
    height: auto;
321
    white-space: pre-wrap;
321
    white-space: pre-wrap;
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc (-68 / +111 lines)
Lines 10-21 require.config( { Link Here
10
            themelang: '[% themelang %]',
10
            themelang: '[% themelang %]',
11
        },
11
        },
12
    },
12
    },
13
    paths: {
14
        pz2: '../../pz2',
15
    },
16
    shim: {
17
        pz2: { exports: 'pz2' },
18
    },
19
} );
13
} );
20
</script>
14
</script>
21
15
Lines 27-56 require.config( { Link Here
27
21
28
<script>
22
<script>
29
require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'resources', 'text-marc', 'widget' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, Resources, TextMARC, Widget ) {
23
require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'preferences', 'resources', 'text-marc', 'widget' ], function( KohaBackend, Search, Macros, MARCEditor, MARC, Preferences, Resources, TextMARC, Widget ) {
30
    var z3950Targets = {
24
    var z3950Servers = {
31
        [% FOREACH target = z3950_targets %]
25
        [% FOREACH server = z3950_servers %]
32
            '[% target.host %]:[% target.port %]/[% target.db %]': {
26
            [% server.id %]: {
33
                'name': '[% target.name %]',
27
                'name': '[% server.name %]',
34
                'authentication': '[% target.userid %]:[% target.password %]',
28
                'kohasyntax': '[% server.syntax == 'USMARC' ? 'MARC21' : server.syntax %]',
35
                'syntax': '[% target.syntax %]',
29
                'checked': [% server.checked ? 'true' : 'false' %],
36
                'kohasyntax': '[% target.syntax == 'USMARC' ? 'MARC21' : target.syntax %]',
37
                'encoding': '[% target.encoding %]',
38
                'checked': [% target.checked ? 'true' : 'false' %],
39
            },
30
            },
40
        [% END %]
31
        [% END %]
41
    };
32
    };
42
33
43
    // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config
34
    // The columns that should show up in a search, in order, and keyed by the corresponding <metadata> tag in the XSL and Pazpar2 config
44
    var z3950Labels = [
35
    var z3950Labels = [
45
		[ "md-work-title", _("Title") ],
36
		[ "title", _("Title") ],
46
		[ "md-series-title", _("Series Title") ],
37
		[ "series", _("Series Title") ],
47
		[ "md-work-author", _("Author") ],
38
		[ "author", _("Author") ],
48
		[ "md-lccn", _("LCCN") ],
39
		[ "lccn", _("LCCN") ],
49
		[ "md-isbn", _("ISBN") ],
40
		[ "isbn", _("ISBN") ],
50
		[ "md-issn", _("ISSN") ],
41
		[ "issn", _("ISSN") ],
51
		[ "md-medium", _("Medium") ],
42
		[ "medium", _("Medium") ],
52
		[ "md-edition", _("Edition") ],
43
		[ "edition", _("Edition") ],
53
		[ "md-description", _("Description") ],
44
		[ "notes", _("Notes") ],
54
    ];
45
    ];
55
46
56
    var state = {
47
    var state = {
Lines 132-141 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
132
123
133
        $('#quicksearch .search-box').each( function() {
124
        $('#quicksearch .search-box').each( function() {
134
            shortcut.add( 'enter', $.proxy( function() {
125
            shortcut.add( 'enter', $.proxy( function() {
135
                var q = this.value;
126
                var terms = [];
136
                if (!q) return false;
127
128
                $('#quicksearch .search-box').each( function() {
129
                    if ( !this.value ) return;
130
131
                    terms.push( [ $(this).data('qualifier'), this.value ] );
132
                } );
133
134
                if ( !terms.length ) return;
137
135
138
                if ( Search.Start( z3950Targets, $(this).data('qualifier') + q, 20 ) ) {
136
                if ( Search.Run( z3950Servers, Search.JoinTerms(terms) ) ) {
137
                    $("#search-overlay").show();
139
                    showResultsBox();
138
                    showResultsBox();
140
                }
139
                }
141
140
Lines 166-172 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
166
        },
165
        },
167
        'catalog': {
166
        'catalog': {
168
            titleForRecord: _("Editing catalog record #{ID}"),
167
            titleForRecord: _("Editing catalog record #{ID}"),
169
            href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}",
168
            links: [
169
                { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}" },
170
                { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber={ID}" },
171
            ],
170
            saveLabel: _("Save to catalog"),
172
            saveLabel: _("Save to catalog"),
171
            get: function( id, callback ) {
173
            get: function( id, callback ) {
172
                if ( !id ) return false;
174
                if ( !id ) return false;
Lines 206-213 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
206
            get: function( id, callback ) {
208
            get: function( id, callback ) {
207
                if ( !id ) return false;
209
                if ( !id ) return false;
208
210
209
                Search.GetDetailedRecord( decodeURIComponent(id), callback );
211
                callback( backends.search.records[ id ] );
210
            },
212
            },
213
            records: {},
211
        },
214
        },
212
    };
215
    };
213
216
Lines 221-233 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
221
224
222
        document.location.hash = '#' + parts[0] + ':' + parts[1];
225
        document.location.hash = '#' + parts[0] + ':' + parts[1];
223
226
224
        if ( backend.href ) {
227
        $('#title').text( backend.titleForRecord.replace( '{ID}', parts[1] ) );
225
            $( '#title' ).html( backend.titleForRecord.replace( '{ID}', parts[1] ) + ' <a target="_blank" href="' + backend.href.replace( '{ID}', parts[1] ) + '">' + _("(view)") + '</a>' );
228
226
        } else {
229
        $.each( backend.links || [], function( i, link ) {
227
            $( '#title' ).text( backend.titleForRecord.replace( '{ID}', parts[1] ) );
230
            $('#title').append(' <a target="_blank" href="' + link.href.replace( '{ID}', parts[1] ) + '">(' + link.title + ')</a>' );
228
        }
231
        } );
229
        $( 'title', document.head ).html( _("Koha &rsaquo; Cataloging &rsaquo; ") + backend.titleForRecord.replace( '{ID}', parts[1] ) );
232
        $( 'title', document.head ).html( _("Koha &rsaquo; Cataloging &rsaquo; ") + backend.titleForRecord.replace( '{ID}', parts[1] ) );
230
        $( '#save-record span' ).text( backends[ state.saveBackend ].saveLabel );
233
        $('#save-record span').text( backends[ state.saveBackend ].saveLabel );
231
    }
234
    }
232
235
233
    function saveRecord( recid, editor, callback ) {
236
    function saveRecord( recid, editor, callback ) {
Lines 313-345 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
313
316
314
        $('#advanced-search-ui').modal('hide');
317
        $('#advanced-search-ui').modal('hide');
315
318
316
        if ( Search.Start( z3950Targets, search, 20 ) ) {
319
        if ( Search.Run( z3950Servers, search, 20 ) ) {
320
            $("#search-overlay").show();
317
            showResultsBox();
321
            showResultsBox();
318
        }
322
        }
319
    }
323
    }
320
324
321
    function showResultsBox(data) {
325
    function showResultsBox(data) {
326
        $('#search-top-pages, #search-bottom-pages').find('.pagination').empty();
322
        $('#searchresults thead tr').empty();
327
        $('#searchresults thead tr').empty();
323
        $('#searchresults tbody').empty();
328
        $('#searchresults tbody').empty();
324
        $('#search-targetsinfo').empty().append('<li>' + _("Loading...") + '</li>');
329
        $('#search-serversinfo').empty().append('<li>' + _("Loading...") + '</li>');
325
        $('#search-results-ui').modal('show');
330
        $('#search-results-ui').modal('show');
326
    }
331
    }
327
332
328
    function showSearchResults( editor, data ) {
333
    function showSearchResults( editor, data ) {
334
        backends.search.records = {};
335
329
        $('#searchresults thead tr').empty();
336
        $('#searchresults thead tr').empty();
330
        $('#searchresults tbody').empty();
337
        $('#searchresults tbody').empty();
338
        $('#search-serversinfo').empty();
339
340
        $.each( data.num_fetched, function( server_id, num_fetched ) {
341
            if ( num_fetched < data.num_hits[server_id] ) {
342
                num_fetched += '+';
343
            }
344
345
            $('#search-serversinfo').append( '<li>' + z3950Servers[server_id].name + ' (' + num_fetched + ')' + '</li>' );
346
        } );
331
347
332
        var seenColumns = {};
348
        var seenColumns = {};
333
349
334
        $.each( data.hits, function( undef, hit ) {
350
        $.each( data.hits, function( undef, hit ) {
335
            for ( key in hit ) {
351
            $.each( hit.metadata, function(key) {
336
                if ( /^md-/.test(key) ) seenColumns[key] = true;
352
                seenColumns[key] = true;
337
            }
338
339
            $.each( hit.location, function( undef, location ) {
340
                for ( key in location ) {
341
                    if ( /^md-/.test(key) ) seenColumns[key] = true;
342
                }
343
            } );
353
            } );
344
        } );
354
        } );
345
355
Lines 354-369 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
354
        $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>');
364
        $('#searchresults thead tr').append('<th>' + _("Tools") + '</th>');
355
365
356
        $.each( data.hits, function( undef, hit ) {
366
        $.each( data.hits, function( undef, hit ) {
367
            backends.search.records[ hit.server + '-' + hit.index ] = hit.record;
368
            hit.id = 'search:' + hit.server + '-' + hit.index;
369
357
            var result = '<tr>';
370
            var result = '<tr>';
358
            result += '<td class="sourcecol">' + hit.location[0]['@name'] + '</td>';
371
            result += '<td class="sourcecol">' + z3950Servers[ hit.server ].name + '</td>';
359
372
360
            $.each( z3950Labels, function( undef, label ) {
373
            $.each( z3950Labels, function( undef, label ) {
361
                if ( !seenColumns[ label[0] ] ) return;
374
                if ( !seenColumns[ label[0] ] ) return;
362
375
363
                if ( hit[ label[0] ] ) {
376
                if ( hit.metadata[ label[0] ] ) {
364
                    result += '<td class="infocol">' + hit[ label[0] ].join('<br/>') + '</td>';
377
                    result += '<td class="infocol">' + hit.metadata[ label[0] ] + '</td>';
365
                } else if ( hit.location[0][ label[0] ] ) {
366
                    result += '<td class="infocol">' + hit.location[0][ label[0] ].join('<br/>') + '</td>';
367
                } else {
378
                } else {
368
                    result += '<td class="infocol">&nbsp;</td>';
379
                    result += '<td class="infocol">&nbsp;</td>';
369
                }
380
                }
Lines 376-385 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
376
387
377
            var $tr = $( result );
388
            var $tr = $( result );
378
            $tr.find( '.marc-link' ).click( function() {
389
            $tr.find( '.marc-link' ).click( function() {
379
                Search.GetDetailedRecord( hit.recid, function( record ) {
390
                var $info_columns = $tr.find( '.infocol' );
380
                    var $columns = $tr.find( '.infocol' );
391
                var $marc_column = $tr.find( '.marccol' );
381
                    CodeMirror.runMode( TextMARC.RecordToText( record ), 'marc', $( '<td class="infocol results-marc" colspan="' + $columns.length + '"></td>' ).replaceAll( $columns.slice(1).remove().end()[0] )[0] );
392
382
                } );
393
                if ( !$marc_column.length ) {
394
                    $marc_column = $( '<td class="marccol" colspan="' + $info_columns.length + '"></td>' ).insertAfter( $info_columns.eq(-1) ).hide();
395
                    CodeMirror.runMode( TextMARC.RecordToText( hit.record ), 'marc', $marc_column[0] );
396
                }
397
398
                if ( $marc_column.is(':visible') ) {
399
                    $tr.find('.marc-link').text( _("View MARC") );
400
                    $info_columns.show();
401
                    $marc_column.hide();
402
                } else {
403
                    $tr.find('.marc-link').text( _("Hide MARC") );
404
                    $marc_column.show();
405
                    $info_columns.hide();
406
                }
383
407
384
                return false;
408
                return false;
385
            } );
409
            } );
Lines 398-409 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
398
            $('#searchresults tbody').append( $tr );
422
            $('#searchresults tbody').append( $tr );
399
        } );
423
        } );
400
424
425
        var pages = [];
426
        var cur_page = data.offset / data.page_size;
427
        var max_page = Math.ceil( data.total_fetched / data.page_size ) - 1;
428
429
        if ( cur_page != 0 ) {
430
            pages.push( '<li><a class="search-nav" href="#" data-offset="' + (data.offset - data.page_size) + '">&laquo; ' + _("Previous") + '</a></li>' );
431
        }
432
433
        for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) {
434
            if ( page == cur_page ) {
435
                pages.push( ' <li class="active"><a href="#">' + ( page + 1 ) + '</a></li>' );
436
            } else {
437
                pages.push( ' <li><a class="search-nav" href="#" data-offset="' + ( page * data.page_size ) + '">' + ( page + 1 ) + '</a></li>' );
438
            }
439
        }
440
441
        if ( cur_page < max_page ) {
442
            pages.push( ' <li><a class="search-nav" href="#" data-offset="' + (data.offset + data.page_size) + '">' + _("Next") + ' &raquo;</a></li>' );
443
        }
444
445
        if ( pages.length > 1 ) $( '#search-top-pages, #search-bottom-pages' ).find( '.pagination' ).html( '<ul>' + pages.join( '' ) + '</ul>');
446
401
        var $overlay = $('#search-overlay');
447
        var $overlay = $('#search-overlay');
402
        $overlay.find('span').text(_("Loading"));
448
        $overlay.find('span').text(_("Loading"));
403
        $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedTargets.length ) + '%' } );
449
        $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
404
450
405
        if ( data.activeclients ) {
451
        if ( data.activeclients ) {
406
            $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedTargets.length ) + '%' } );
452
            $overlay.find('.bar').css( { display: 'block', width: 100 * ( 1 - data.activeclients / Search.includedServers.length ) + '%' } );
407
            $overlay.show();
453
            $overlay.show();
408
        } else {
454
        } else {
409
            $overlay.find('.bar').css( { display: 'block', width: '100%' } );
455
            $overlay.find('.bar').css( { display: 'block', width: '100%' } );
Lines 418-431 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
418
        $overlay.show();
464
        $overlay.show();
419
    }
465
    }
420
466
421
    function showSearchTargets(data) {
422
        $('#search-targetsinfo').empty();
423
424
        $.each( data, function( undef, target ) {
425
            $('#search-targetsinfo').append( '<li>' + target.name + ' (' + target.hits + ')' + '</li>' );
426
        } );
427
    }
428
429
    function handleSearchError(error) {
467
    function handleSearchError(error) {
430
        if (error.code == 1) {
468
        if (error.code == 1) {
431
            invalidateSearchResults();
469
            invalidateSearchResults();
Lines 768-773 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
768
            return false;
806
            return false;
769
        } );
807
        } );
770
808
809
        $( document ).on( 'click', 'a.search-nav', function() {
810
            $("#search-overlay").show();
811
            Search.Fetch( $( this ).data( 'offset' ) );
812
            return false;
813
        });
814
771
        // Key bindings
815
        // Key bindings
772
        bindGlobalKeys();
816
        bindGlobalKeys();
773
817
Lines 805-818 require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Link Here
805
        });
849
        });
806
850
807
        // Start editor
851
        // Start editor
808
        Preferences.Load( [% USER_INFO.0.borrowernumber %] );
852
        Preferences.Load( [% USER_INFO.0.borrowernumber || 0 %] );
809
        displayPreferences(editor);
853
        displayPreferences(editor);
810
        makeAuthorisedValueWidgets( '' );
854
        makeAuthorisedValueWidgets( '' );
811
        Search.Init( z3950Targets, {
855
        Search.Init( {
856
            page_size: 20,
812
            onresults: function(data) { showSearchResults( editor, data ) },
857
            onresults: function(data) { showSearchResults( editor, data ) },
813
            onbytarget: showSearchTargets,
814
            onerror: handleSearchError,
858
            onerror: handleSearchError,
815
            oniniterror: handleSearchInitError,
816
        } );
859
        } );
817
860
818
        function finishCb() {
861
        function finishCb() {
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt (-18 / +29 lines)
Lines 75-87 Link Here
75
    <fieldset class="brief">
75
    <fieldset class="brief">
76
    <ol>
76
    <ol>
77
        <li><label for="search-by-keywords">Keywords:</label></li>
77
        <li><label for="search-by-keywords">Keywords:</label></li>
78
        <li><input class="search-box" data-qualifier="term=" id="search-by-keywords" placeholder="(Ctrl-Alt-K)" /></li>
78
        <li><input class="search-box" data-qualifier="term" id="search-by-keywords" placeholder="(Ctrl-Alt-K)" /></li>
79
        <li><label for="search-by-author">Author:</label></li>
79
        <li><label for="search-by-author">Author:</label></li>
80
        <li><input class="search-box" data-qualifier="Author-name=" id="search-by-author" placeholder="(Ctrl-Alt-A)" /></li>
80
        <li><input class="search-box" data-qualifier="author" id="search-by-author" placeholder="(Ctrl-Alt-A)" /></li>
81
        <li><label for="search-by-isbn">ISBN:</label></li>
81
        <li><label for="search-by-isbn">ISBN:</label></li>
82
        <li><input class="search-box" data-qualifier="Identifier-ISBN=" id="search-by-isbn" placeholder="(Ctrl-Alt-I)" /></li>
82
        <li><input class="search-box" data-qualifier="isbn" id="search-by-isbn" placeholder="(Ctrl-Alt-I)" /></li>
83
        <li><label for="search-by-title">Title:</label></li>
83
        <li><label for="search-by-title">Title:</label></li>
84
        <li><input class="search-box" data-qualifier="Title=" id="search-by-title" placeholder="(Ctrl-Alt-T)" /></li>
84
        <li><input class="search-box" data-qualifier="title" id="search-by-title" placeholder="(Ctrl-Alt-T)" /></li>
85
        <li><a href="#" id="show-advanced-search" title="Show advanced search (Ctrl-Alt-S)">Advanced &raquo;</a></li>
85
        <li><a href="#" id="show-advanced-search" title="Show advanced search (Ctrl-Alt-S)">Advanced &raquo;</a></li>
86
    </fieldset>
86
    </fieldset>
87
</form>
87
</form>
Lines 106-156 Link Here
106
    <ul id="advanced-search-fields">
106
    <ul id="advanced-search-fields">
107
        <li>
107
        <li>
108
            <label for="advanced-search-by-author">Author:</label>
108
            <label for="advanced-search-by-author">Author:</label>
109
            <input class="search-box" data-qualifier="Author-name=" id="advanced-search-by-author" />
109
            <input class="search-box" data-qualifier="author" id="advanced-search-by-author" />
110
        </li>
110
        </li>
111
        <li>
111
        <li>
112
            <label for="advanced-search-by-control-number">Control number:</label>
112
            <label for="advanced-search-by-control-number">Control number:</label>
113
            <input class="search-box" data-qualifier="Local-number=" id="advanced-search-by-control-number" />
113
            <input class="search-box" data-qualifier="local_number" id="advanced-search-by-control-number" />
114
        </li>
114
        </li>
115
        <li>
115
        <li>
116
            <label for="advanced-search-by-dewey">Dewey number:</label>
116
            <label for="advanced-search-by-dewey">Dewey number:</label>
117
            <input class="search-box" data-qualifier="Classification-Dewey=" id="advanced-search-by-dewey" />
117
            <input class="search-box" data-qualifier="cn_dewey" id="advanced-search-by-dewey" />
118
        </li>
118
        </li>
119
        <li>
119
        <li>
120
            <label for="advanced-search-by-isbn">ISBN:</label>
120
            <label for="advanced-search-by-isbn">ISBN:</label>
121
            <input class="search-box" data-qualifier="Identifier-ISBN=" id="advanced-search-by-isbn" />
121
            <input class="search-box" data-qualifier="isbn" id="advanced-search-by-isbn" />
122
        </li>
122
        </li>
123
        <li>
123
        <li>
124
            <label for="advanced-search-by-issn">ISSN:</label>
124
            <label for="advanced-search-by-issn">ISSN:</label>
125
            <input class="search-box" data-qualifier="Identifier-ISSN=" id="advanced-search-by-issn" />
125
            <input class="search-box" data-qualifier="issn" id="advanced-search-by-issn" />
126
        </li>
126
        </li>
127
        <li>
127
        <li>
128
            <label for="advanced-search-by-lccn">LCCN:</label>
128
            <label for="advanced-search-by-lccn">LCCN:</label>
129
            <input class="search-box" data-qualifier="LC-card-number=" id="advanced-search-by-lccn" />
129
            <input class="search-box" data-qualifier="lccn" id="advanced-search-by-lccn" />
130
        </li>
130
        </li>
131
        <li>
131
        <li>
132
            <label for="advanced-search-by-lc-number">LC call number:</label>
132
            <label for="advanced-search-by-lc-number">LC call number:</label>
133
            <input class="search-box" data-qualifier="Classification-LC=" id="advanced-search-by-lc-number" />
133
            <input class="search-box" data-qualifier="cn_lc" id="advanced-search-by-lc-number" />
134
        </li>
134
        </li>
135
        <li>
135
        <li>
136
            <label for="advanced-search-by-publisher-number">Publisher number:</label>
136
            <label for="advanced-search-by-publisher-number">Publisher number:</label>
137
            <input class="search-box" data-qualifier="Identifier-publisher-for-music=" id="advanced-search-by-publisher-number" />
137
            <input class="search-box" data-qualifier="music_identifier" id="advanced-search-by-publisher-number" />
138
        </li>
138
        </li>
139
        <li>
139
        <li>
140
            <label for="advanced-search-by-standard-number">Standard number:</label>
140
            <label for="advanced-search-by-standard-number">Standard number:</label>
141
            <input class="search-box" data-qualifier="Identifier-standard=" id="advanced-search-by-standard-number" />
141
            <input class="search-box" data-qualifier="standard_identifier" id="advanced-search-by-standard-number" />
142
        </li>
142
        </li>
143
        <li>
143
        <li>
144
            <label for="advanced-search-by-subject">Subject:</label>
144
            <label for="advanced-search-by-subject">Subject:</label>
145
            <input class="search-box" data-qualifier="Subject=" id="advanced-search-by-subject" />
145
            <input class="search-box" data-qualifier="subject" id="advanced-search-by-subject" />
146
        </li>
146
        </li>
147
        <li>
147
        <li>
148
            <label for="advanced-search-by-publication-date">Publication date:</label>
148
            <label for="advanced-search-by-publication-date">Publication date:</label>
149
            <input class="search-box" data-qualifier="Date=" id="advanced-search-by-publication-date" />
149
            <input class="search-box" data-qualifier="date" id="advanced-search-by-publication-date" />
150
        </li>
150
        </li>
151
        <li>
151
        <li>
152
            <label for="advanced-search-by-title">Title:</label>
152
            <label for="advanced-search-by-title">Title:</label>
153
            <input class="search-box" data-qualifier="Title=" id="advanced-search-by-title" />
153
            <input class="search-box" data-qualifier="title" id="advanced-search-by-title" />
154
        </li>
154
        </li>
155
    </ul>
155
    </ul>
156
</form>
156
</form>
Lines 168-186 Link Here
168
    <div class="span3">
168
    <div class="span3">
169
        <div id="search-facets">
169
        <div id="search-facets">
170
            <ul>
170
            <ul>
171
                <li>Targets:<ul id="search-targetsinfo"></ul></li>
171
                <li>Servers:<ul id="search-serversinfo"></ul></li>
172
            </ul>
172
            </ul>
173
        </div>
173
        </div>
174
    </div>
174
    </div>
175
    <div class="span9">
175
    <div class="span9">
176
        <div id="searchresults">
176
        <div id="searchresults">
177
            <div id="search-top-pages">
178
                <div class="pagination pagination-small">
179
                </div>
180
            </div>
181
177
            <table>
182
            <table>
178
                <thead>
183
                <thead>
179
                    <tr></tr>
184
                    <tr></tr>
180
                </thead>
185
                </thead>
181
                <tbody></tbody>
186
                <tbody></tbody>
182
            </table>
187
            </table>
183
            <div id="search-overlay"><span>Loading...</span><div class="progress progress-striped active"><div class="bar" style="width: 0"></div></div></div>
188
189
            <div id="search-bottom-pages">
190
                <div class="pagination pagination-small">
191
                </div>
192
            </div>
193
194
            <div id="search-overlay"><span>Loading...</span><div class="progress progress-striped active"><div class="bar" style="width: 100%"></div></div></div>
184
        </div>
195
        </div>
185
    </div>
196
    </div>
186
</div>
197
</div>
(-)a/svc/z3950 (-1 / +102 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
#
3
# Copyright 2014 ByWater Solutions
4
5
# 
6
# This file is part of Koha.
7
# 
8
# Koha is free software; you can redistribute it and/or modify it under the
9
# terms of the GNU General Public License as published by the Free Software
10
# Foundation; either version 3 of the License, or (at your option) any later
11
# version.
12
# 
13
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16
# 
17
# You should have received a copy of the GNU General Public License along
18
# with Koha; if not, write to the Free Software Foundation, Inc.,
19
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
use Modern::Perl;
22
23
use C4::Breeding qw( RunZ3950 );
24
use C4::Service;
25
use Encode qw( encode_utf8 );
26
27
use sort 'stable';
28
29
my ( $query, $response ) = C4::Service->init( catalogue => 1 );
30
31
my ( $query_string, $servers ) = C4::Service->require_params( 'q', 'servers' );
32
33
my $server_hits = {};
34
my $server_errors = {};
35
36
my $offset = $query->param( 'offset' ) || 0;
37
my $page_size = $query->param( 'page_size' ) || 20;
38
my $fetched = $query->param( 'fetched' ) || 100;
39
my $empty_flip = -1; # Determines the flip of ordering for records with empty sort keys.
40
41
my @server_ids = split( /,/, $servers );
42
43
my $stats = RunZ3950( \@server_ids, $query_string, {
44
    on_error => sub {
45
        my ( $server, $exception ) = @_;
46
47
        $server_errors->{ $server->{id} } = $exception->message;
48
    },
49
    on_hit => sub {
50
        my ( $server, $hit ) = @_;
51
52
        push @{ $server_hits->{ $server->{id} } }, { server => $server->{id},
53
            index => $hit->{index},
54
            record => encode_utf8( $hit->{record}->as_xml_record() ),
55
            metadata => $hit->{metadata}
56
        };
57
    },
58
59
    offset => 0,
60
    fetch => $fetched,
61
} );
62
63
my @hits;
64
65
foreach my $id ( @server_ids ) {
66
    warn scalar @{ $server_hits->{$id} ||= [] } . ' hits from ' . $id;
67
}
68
69
# Interleave hits; should be replaced by actual relevance ranking at some point
70
foreach my $i ( 0..$fetched ) {
71
    foreach my $id ( @server_ids ) {
72
        my $hit = shift @{ $server_hits->{ $id } };
73
        next unless ( $hit );
74
75
        ( $hit->{sort_key} = $hit->{metadata}->{title} || '' ) =~ s/\W//g;
76
        push @hits, $hit;
77
    }
78
}
79
80
@hits = sort {
81
    # Sort empty records at the end
82
    return -$empty_flip unless $a->{sort_key};
83
    return $empty_flip unless $b->{sort_key};
84
85
    $a->{sort_key} cmp $b->{sort_key};
86
} @hits;
87
88
my @hits_subset;
89
90
foreach my $i ( $offset..( $offset + $page_size - 1 ) ) {
91
    push @hits_subset, $hits[$i] if $hits[$i];
92
}
93
94
$response->param(
95
    offset => $offset,
96
    page_size => $page_size,
97
    errors => $server_errors,
98
    hits => \@hits_subset,
99
    %$stats
100
);
101
102
C4::Service->return_success( $response );

Return to bug 11559