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

(-)a/C4/Installer/PerlDependencies.pm (+5 lines)
Lines 552-557 our $PERL_DEPS = { Link Here
552
        'required' => '1',
552
        'required' => '1',
553
        'min_ver'  => '0.03',
553
        'min_ver'  => '0.03',
554
    },
554
    },
555
    'Template::Plugin::JSON::Escape' => {
556
        'usage'    => 'Core',
557
        'required' => '1',
558
        'min_ver'  => '0.02',
559
    },
555
    'Data::Paginator' => {
560
    'Data::Paginator' => {
556
        'usage'    => 'Core',
561
        'usage'    => 'Core',
557
        'required' => '0',
562
        'required' => '0',
(-)a/C4/Items.pm (-20 / +177 lines)
Lines 35-40 use DateTime::Format::MySQL; Link Here
35
use Data::Dumper; # used as part of logging item record changes, not just for
35
use Data::Dumper; # used as part of logging item record changes, not just for
36
                  # debugging; so please don't remove this
36
                  # debugging; so please don't remove this
37
use Koha::DateUtils qw/dt_from_string/;
37
use Koha::DateUtils qw/dt_from_string/;
38
use C4::SQLHelper qw(GetColumns);
38
39
39
use vars qw($VERSION @ISA @EXPORT);
40
use vars qw($VERSION @ISA @EXPORT);
40
41
Lines 85-90 BEGIN { Link Here
85
	GetAnalyticsCount
86
	GetAnalyticsCount
86
        GetItemHolds
87
        GetItemHolds
87
88
89
        SearchItemsByField
88
        SearchItems
90
        SearchItems
89
91
90
        PrepareItemrecordDisplay
92
        PrepareItemrecordDisplay
Lines 2572-2610 sub GetItemHolds { Link Here
2572
    return $holds;
2574
    return $holds;
2573
}
2575
}
2574
2576
2575
# Return the list of the column names of items table
2577
=head2 SearchItemsByField
2576
sub _get_items_columns {
2578
2577
    my $dbh = C4::Context->dbh;
2579
    my $items = SearchItemsByField($field, $value);
2578
    my $sth = $dbh->column_info(undef, undef, 'items', '%');
2580
2579
    $sth->execute;
2581
SearchItemsByField will search for items on a specific given field.
2580
    my $results = $sth->fetchall_hashref('COLUMN_NAME');
2582
For instance you can search all items with a specific stocknumber like this:
2581
    return keys %$results;
2583
2584
    my $items = SearchItemsByField('stocknumber', $stocknumber);
2585
2586
=cut
2587
2588
sub SearchItemsByField {
2589
    my ($field, $value) = @_;
2590
2591
    my $filters = [ {
2592
            field => $field,
2593
            query => $value,
2594
    } ];
2595
2596
    my ($results) = SearchItems($filters);
2597
    return $results;
2598
}
2599
2600
sub _SearchItems_build_where_fragment {
2601
    my ($filter) = @_;
2602
2603
    my $where_fragment;
2604
    if (exists($filter->{conjunction})) {
2605
        my (@where_strs, @where_args);
2606
        foreach my $f (@{ $filter->{filters} }) {
2607
            my $fragment = _SearchItems_build_where_fragment($f);
2608
            if ($fragment) {
2609
                push @where_strs, $fragment->{str};
2610
                push @where_args, @{ $fragment->{args} };
2611
            }
2612
        }
2613
        my $where_str = '';
2614
        if (@where_strs) {
2615
            $where_str = '(' . join (' ' . $filter->{conjunction} . ' ', @where_strs) . ')';
2616
            $where_fragment = {
2617
                str => $where_str,
2618
                args => \@where_args,
2619
            };
2620
        }
2621
    } else {
2622
        my @columns = GetColumns('items');
2623
        push @columns, GetColumns('biblio');
2624
        push @columns, GetColumns('biblioitems');
2625
        my @operators = qw(= != > < >= <= like);
2626
        my $field = $filter->{field};
2627
        if ( (0 < grep /^$field$/, @columns) or (substr($field, 0, 5) eq 'marc:') ) {
2628
            my $op = $filter->{operator};
2629
            my $query = $filter->{query};
2630
2631
            if (!$op or (0 == grep /^$op$/, @operators)) {
2632
                $op = '='; # default operator
2633
            }
2634
2635
            my $column;
2636
            if ($field =~ /^marc:(\d{3})(?:\$(\w))?$/) {
2637
                my $marcfield = $1;
2638
                my $marcsubfield = $2;
2639
                my $xpath;
2640
                if ($marcfield < 10) {
2641
                    $xpath = "//record/controlfield[\@tag=\"$marcfield\"]";
2642
                } else {
2643
                    $xpath = "//record/datafield[\@tag=\"$marcfield\"]/subfield[\@code=\"$marcsubfield\"]";
2644
                }
2645
                $column = "ExtractValue(marcxml, '$xpath')";
2646
            } else {
2647
                $column = $field;
2648
            }
2649
2650
            if (ref $query eq 'ARRAY') {
2651
                if ($op eq '=') {
2652
                    $op = 'IN';
2653
                } elsif ($op eq '!=') {
2654
                    $op = 'NOT IN';
2655
                }
2656
                $where_fragment = {
2657
                    str => "$column $op (" . join (',', ('?') x @$query) . ")",
2658
                    args => $query,
2659
                };
2660
            } else {
2661
                $where_fragment = {
2662
                    str => "$column $op ?",
2663
                    args => [ $query ],
2664
                };
2665
            }
2666
        }
2667
    }
2668
2669
    return $where_fragment;
2582
}
2670
}
2583
2671
2584
=head2 SearchItems
2672
=head2 SearchItems
2585
2673
2586
    my $items = SearchItems($field, $value);
2674
    my ($items, $total) = SearchItemsByField($filters, $params);
2587
2675
2588
SearchItems will search for items on a specific given field.
2676
Perform a search among items
2589
For instance you can search all items with a specific stocknumber like this:
2590
2677
2591
    my $items = SearchItems('stocknumber', $stocknumber);
2678
$filters is a reference to an array of filters, where each filter is a hash with
2679
the following keys:
2680
2681
=over 2
2682
2683
=item * field: the name of a SQL column in table items
2684
2685
=item * query: the value to search in this column
2686
2687
=item * operator: comparison operator. Can be one of = != > < >= <= like
2688
2689
=back
2690
2691
A logical AND is used to combine filters.
2692
2693
$params is a reference to a hash that can contain the following parameters:
2694
2695
=over 2
2696
2697
=item * rows: Number of items to return. 0 returns everything (default: 0)
2698
2699
=item * page: Page to return (return items from (page-1)*rows to (page*rows)-1)
2700
               (default: 1)
2701
2702
=item * sortby: A SQL column name in items table to sort on
2703
2704
=item * sortorder: 'ASC' or 'DESC'
2705
2706
=back
2592
2707
2593
=cut
2708
=cut
2594
2709
2595
sub SearchItems {
2710
sub SearchItems {
2596
    my ($field, $value) = @_;
2711
    my ($filter, $params) = @_;
2712
2713
    $filter //= {};
2714
    $params //= {};
2715
    return unless ref $filter eq 'HASH';
2716
    return unless ref $params eq 'HASH';
2717
2718
    # Default parameters
2719
    $params->{rows} ||= 0;
2720
    $params->{page} ||= 1;
2721
    $params->{sortby} ||= 'itemnumber';
2722
    $params->{sortorder} ||= 'ASC';
2723
2724
    my ($where_str, @where_args);
2725
    my $where_fragment = _SearchItems_build_where_fragment($filter);
2726
    if ($where_fragment) {
2727
        $where_str = $where_fragment->{str};
2728
        @where_args = @{ $where_fragment->{args} };
2729
    }
2597
2730
2598
    my $dbh = C4::Context->dbh;
2731
    my $dbh = C4::Context->dbh;
2599
    my @columns = _get_items_columns;
2732
    my $query = q{
2600
    my $results = [];
2733
        SELECT SQL_CALC_FOUND_ROWS items.*
2601
    if(0 < grep /^$field$/, @columns) {
2734
        FROM items
2602
        my $query = "SELECT $field FROM items WHERE $field = ?";
2735
          LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
2603
        my $sth = $dbh->prepare( $query );
2736
          LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
2604
        $sth->execute( $value );
2737
    };
2605
        $results = $sth->fetchall_arrayref({});
2738
    if (defined $where_str and $where_str ne '') {
2739
        $query .= qq{ WHERE $where_str };
2606
    }
2740
    }
2607
    return $results;
2741
2742
    my @columns = GetColumns('items');
2743
    push @columns, GetColumns('biblio');
2744
    push @columns, GetColumns('biblioitems');
2745
    my $sortby = (0 < grep {$params->{sortby} eq $_} @columns)
2746
        ? $params->{sortby} : 'itemnumber';
2747
    my $sortorder = (uc($params->{sortorder}) eq 'ASC') ? 'ASC' : 'DESC';
2748
    $query .= qq{ ORDER BY $sortby $sortorder };
2749
2750
    my $rows = $params->{rows};
2751
    my @limit_args;
2752
    if ($rows > 0) {
2753
        my $offset = $rows * ($params->{page}-1);
2754
        $query .= qq { LIMIT ?, ? };
2755
        push @limit_args, $offset, $rows;
2756
    }
2757
2758
    my $sth = $dbh->prepare($query);
2759
    my $rv = $sth->execute(@where_args, @limit_args);
2760
2761
    return unless ($rv);
2762
    my ($total_rows) = $dbh->selectrow_array(q{ SELECT FOUND_ROWS() });
2763
2764
    return ($sth->fetchall_arrayref({}), $total_rows);
2608
}
2765
}
2609
2766
2610
2767
(-)a/C4/SQLHelper.pm (+33 lines)
Lines 56-61 BEGIN { Link Here
56
	UpdateInTable
56
	UpdateInTable
57
	GetPrimaryKeys
57
	GetPrimaryKeys
58
        clear_columns_cache
58
        clear_columns_cache
59
    GetColumns
59
);
60
);
60
	%EXPORT_TAGS = ( all =>[qw( InsertInTable DeleteInTable SearchInTable UpdateInTable GetPrimaryKeys)]
61
	%EXPORT_TAGS = ( all =>[qw( InsertInTable DeleteInTable SearchInTable UpdateInTable GetPrimaryKeys)]
61
				);
62
				);
Lines 296-301 sub _get_columns { Link Here
296
    return $hashref->{$tablename};
297
    return $hashref->{$tablename};
297
}
298
}
298
299
300
=head2 GetColumns
301
302
    my @columns = GetColumns($tablename);
303
304
Given a tablename, returns an array of columns names.
305
306
=cut
307
308
sub GetColumns {
309
    my ($tablename) = @_;
310
311
    return unless $tablename;
312
    return unless $tablename =~ /^[a-zA-Z0-9_]+$/;
313
314
    # Get the database handle.
315
    my $dbh = C4::Context->dbh;
316
317
    # Pure ANSI SQL goodness.
318
    my $sql = "SELECT * FROM $tablename WHERE 1=0;";
319
320
    # Run the SQL statement to load STH's readonly properties.
321
    my $sth = $dbh->prepare($sql);
322
    my $rv = $sth->execute();
323
324
    my @data;
325
    if ($rv) {
326
        @data = @{$sth->{NAME}};
327
    }
328
329
    return @data;
330
}
331
299
=head2 _filter_columns
332
=head2 _filter_columns
300
333
301
=over 4
334
=over 4
(-)a/Koha/Item/Search/Field.pm (+107 lines)
Line 0 Link Here
1
package Koha::Item::Search::Field;
2
3
use Modern::Perl;
4
use base qw( Exporter );
5
6
our @EXPORT_OK = qw(
7
    AddItemSearchField
8
    ModItemSearchField
9
    DelItemSearchField
10
    GetItemSearchField
11
    GetItemSearchFields
12
);
13
14
use C4::Context;
15
16
sub AddItemSearchField {
17
    my ($field) = @_;
18
19
    my ( $name, $label, $tagfield, $tagsubfield, $av_category ) =
20
      @$field{qw(name label tagfield tagsubfield authorised_values_category)};
21
22
    my $dbh = C4::Context->dbh;
23
    my $query = q{
24
        INSERT INTO items_search_fields (name, label, tagfield, tagsubfield, authorised_values_category)
25
        VALUES (?, ?, ?, ?, ?)
26
    };
27
    my $sth = $dbh->prepare($query);
28
    my $rv = $sth->execute($name, $label, $tagfield, $tagsubfield, $av_category);
29
30
    return ($rv) ? $field : undef;
31
}
32
33
sub ModItemSearchField {
34
    my ($field) = @_;
35
36
    my ( $name, $label, $tagfield, $tagsubfield, $av_category ) =
37
      @$field{qw(name label tagfield tagsubfield authorised_values_category)};
38
39
    my $dbh = C4::Context->dbh;
40
    my $query = q{
41
        UPDATE items_search_fields
42
        SET label = ?,
43
            tagfield = ?,
44
            tagsubfield = ?,
45
            authorised_values_category = ?
46
        WHERE name = ?
47
    };
48
    my $sth = $dbh->prepare($query);
49
    my $rv = $sth->execute($label, $tagfield, $tagsubfield, $av_category, $name);
50
51
    return ($rv) ? $field : undef;
52
}
53
54
sub DelItemSearchField {
55
    my ($name) = @_;
56
57
    my $dbh = C4::Context->dbh;
58
    my $query = q{
59
        DELETE FROM items_search_fields
60
        WHERE name = ?
61
    };
62
    my $sth = $dbh->prepare($query);
63
    my $rv = $sth->execute($name);
64
65
    my $is_deleted = $rv ? int($rv) : 0;
66
    if (!$is_deleted) {
67
        warn "DelItemSearchField: Field '$name' doesn't exist";
68
    }
69
70
    return $is_deleted;
71
}
72
73
sub GetItemSearchField {
74
    my ($name) = @_;
75
76
    my $dbh = C4::Context->dbh;
77
    my $query = q{
78
        SELECT * FROM items_search_fields
79
        WHERE name = ?
80
    };
81
    my $sth = $dbh->prepare($query);
82
    my $rv = $sth->execute($name);
83
84
    my $field;
85
    if ($rv) {
86
        $field = $sth->fetchrow_hashref;
87
    }
88
89
    return $field;
90
}
91
92
sub GetItemSearchFields {
93
    my $dbh = C4::Context->dbh;
94
    my $query = q{
95
        SELECT * FROM items_search_fields
96
    };
97
    my $sth = $dbh->prepare($query);
98
    my $rv = $sth->execute();
99
100
    my @fields;
101
    if ($rv) {
102
        my $fields = $sth->fetchall_arrayref( {} );
103
        @fields = @$fields;
104
    }
105
106
    return @fields;
107
}
(-)a/acqui/check_uniqueness.pl (-1 / +1 lines)
Lines 43-49 my @value = $input->param('value[]'); Link Here
43
my $r = {};
43
my $r = {};
44
my $i = 0;
44
my $i = 0;
45
for ( my $i=0; $i<@field; $i++ ) {
45
for ( my $i=0; $i<@field; $i++ ) {
46
    my $items = C4::Items::SearchItems($field[$i], $value[$i]);
46
    my $items = C4::Items::SearchItemsByField($field[$i], $value[$i]);
47
47
48
    if ( @$items ) {
48
    if ( @$items ) {
49
        push @{ $r->{$field[$i]} }, $value[$i];
49
        push @{ $r->{$field[$i]} }, $value[$i];
(-)a/admin/items_search_field.pl (+63 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
# Copyright 2013 BibLibre
3
#
4
# This file is part of Koha
5
#
6
# Koha is free software; you can redistribute it and/or modify it under the
7
# terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 3 of the License, or (at your option) any later
9
# version.
10
#
11
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along
16
# with Koha; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19
use Modern::Perl;
20
use CGI;
21
22
use C4::Auth;
23
use C4::Output;
24
use C4::Koha;
25
26
use Koha::Item::Search::Field qw(GetItemSearchField ModItemSearchField);
27
28
my $cgi = new CGI;
29
30
my ($template, $borrowernumber, $cookie) = get_template_and_user({
31
    template_name => 'admin/items_search_field.tt',
32
    query => $cgi,
33
    type => 'intranet',
34
    authnotrequired => 0,
35
    flagsrequired   => { catalogue => 1 },
36
});
37
38
my $op = $cgi->param('op') || '';
39
my $name = $cgi->param('name');
40
41
if ($op eq 'mod') {
42
    my %vars = $cgi->Vars;
43
    my $field = { name => $name };
44
    my @params = qw(label tagfield tagsubfield authorised_values_category);
45
    @$field{@params} = @vars{@params};
46
    if ( $field->{authorised_values_category} eq '' ) {
47
        $field->{authorised_values_category} = undef;
48
    }
49
    $field = ModItemSearchField($field);
50
    my $updated = ($field) ? 1 : 0;
51
    print $cgi->redirect('/cgi-bin/koha/admin/items_search_fields.pl?updated=' . $updated);
52
    exit;
53
}
54
55
my $field = GetItemSearchField($name);
56
my $authorised_values_categories = C4::Koha::GetAuthorisedValueCategories();
57
58
$template->param(
59
    field => $field,
60
    authorised_values_categories => $authorised_values_categories,
61
);
62
63
output_html_with_http_headers $cgi, $cookie, $template->output;
(-)a/admin/items_search_fields.pl (+81 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
# Copyright 2013 BibLibre
3
#
4
# This file is part of Koha
5
#
6
# Koha is free software; you can redistribute it and/or modify it under the
7
# terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 3 of the License, or (at your option) any later
9
# version.
10
#
11
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along
16
# with Koha; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19
use Modern::Perl;
20
use CGI;
21
22
use C4::Auth;
23
use C4::Output;
24
use C4::Koha;
25
26
use Koha::Item::Search::Field qw(AddItemSearchField GetItemSearchFields DelItemSearchField);
27
28
my $cgi = new CGI;
29
30
my ($template, $borrowernumber, $cookie) = get_template_and_user({
31
    template_name => 'admin/items_search_fields.tt',
32
    query => $cgi,
33
    type => 'intranet',
34
    authnotrequired => 0,
35
    flagsrequired   => { catalogue => 1 },
36
});
37
38
my $op = $cgi->param('op') || '';
39
40
if ($op eq 'add') {
41
    my %vars = $cgi->Vars;
42
    my $field;
43
    my @params = qw(name label tagfield tagsubfield authorised_values_category);
44
    @$field{@params} = @vars{@params};
45
    if ( $field->{authorised_values_category} eq '' ) {
46
        $field->{authorised_values_category} = undef;
47
    }
48
    $field = AddItemSearchField($field);
49
    if ($field) {
50
        $template->param(field_added => $field);
51
    } else {
52
        $template->param(field_not_added => 1);
53
    }
54
} elsif ($op eq 'del') {
55
    my $name = $cgi->param('name');
56
    my $rv = DelItemSearchField($name);
57
    if ($rv) {
58
        $template->param(field_deleted => 1);
59
    } else {
60
        $template->param(field_not_deleted => 1);
61
    }
62
} else {
63
    my $updated = $cgi->param('updated');
64
    if (defined $updated) {
65
        if ($updated) {
66
            $template->param(field_updated => 1);
67
        } else {
68
            $template->param(field_not_updated => 1);
69
        }
70
    }
71
}
72
73
my @fields = GetItemSearchFields();
74
my $authorised_values_categories = C4::Koha::GetAuthorisedValueCategories();
75
76
$template->param(
77
    fields => \@fields,
78
    authorised_values_categories => $authorised_values_categories,
79
);
80
81
output_html_with_http_headers $cgi, $cookie, $template->output;
(-)a/catalogue/itemsearch.pl (+298 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
# Copyright 2013 BibLibre
3
#
4
# This file is part of Koha
5
#
6
# Koha is free software; you can redistribute it and/or modify it under the
7
# terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 3 of the License, or (at your option) any later
9
# version.
10
#
11
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along
16
# with Koha; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19
use Modern::Perl;
20
use CGI;
21
22
use JSON;
23
24
use C4::Auth;
25
use C4::Output;
26
use C4::Items;
27
use C4::Biblio;
28
use C4::Branch;
29
use C4::Koha;
30
use C4::ItemType;
31
32
use Koha::Item::Search::Field qw(GetItemSearchFields);
33
34
my $cgi = new CGI;
35
my %params = $cgi->Vars;
36
37
my $format = $cgi->param('format');
38
my ($template_name, $content_type);
39
if (defined $format and $format eq 'json') {
40
    $template_name = 'catalogue/itemsearch.json.tt';
41
    $content_type = 'json';
42
43
    # Map DataTables parameters with 'regular' parameters
44
    $cgi->param('rows', $cgi->param('iDisplayLength'));
45
    $cgi->param('page', ($cgi->param('iDisplayStart') / $cgi->param('iDisplayLength')) + 1);
46
    my @columns = split /,/, $cgi->param('sColumns');
47
    $cgi->param('sortby', $columns[ $cgi->param('iSortCol_0') ]);
48
    $cgi->param('sortorder', $cgi->param('sSortDir_0'));
49
50
    my @f = $cgi->param('f');
51
    my @q = $cgi->param('q');
52
    push @q, '' if @q == 0;
53
    my @op = $cgi->param('op');
54
    my @c = $cgi->param('c');
55
    foreach my $i (0 .. ($cgi->param('iColumns') - 1)) {
56
        my $sSearch = $cgi->param("sSearch_$i");
57
        if ($sSearch) {
58
            my @words = split /\s+/, $sSearch;
59
            foreach my $word (@words) {
60
                push @f, $columns[$i];
61
                push @q, "%$word%";
62
                push @op, 'like';
63
                push @c, 'and';
64
            }
65
        }
66
    }
67
    $cgi->param('f', @f);
68
    $cgi->param('q', @q);
69
    $cgi->param('op', @op);
70
    $cgi->param('c', @c);
71
} elsif (defined $format and $format eq 'csv') {
72
    $template_name = 'catalogue/itemsearch.csv.tt';
73
74
    # Retrieve all results
75
    $cgi->param('rows', 0);
76
} else {
77
    $format = 'html';
78
    $template_name = 'catalogue/itemsearch.tt';
79
    $content_type = 'html';
80
}
81
82
my ($template, $borrowernumber, $cookie) = get_template_and_user({
83
    template_name => $template_name,
84
    query => $cgi,
85
    type => 'intranet',
86
    authnotrequired => 0,
87
    flagsrequired   => { catalogue => 1 },
88
});
89
90
my $notforloan_avcode = GetAuthValCode('items.notforloan');
91
my $notforloan_values = GetAuthorisedValues($notforloan_avcode);
92
93
if (scalar keys %params > 0) {
94
    # Parameters given, it's a search
95
96
    my $filter = {
97
        conjunction => 'AND',
98
        filters => [],
99
    };
100
101
    foreach my $p (qw(homebranch location itype ccode issues datelastborrowed)) {
102
        if (my @q = $cgi->param($p)) {
103
            if ($q[0] ne '') {
104
                my $f = {
105
                    field => $p,
106
                    query => \@q,
107
                };
108
                if (my $op = $cgi->param($p . '_op')) {
109
                    $f->{operator} = $op;
110
                }
111
                push @{ $filter->{filters} }, $f;
112
            }
113
        }
114
    }
115
116
    my @c = $cgi->param('c');
117
    my @fields = $cgi->param('f');
118
    my @q = $cgi->param('q');
119
    my @op = $cgi->param('op');
120
121
    my $f;
122
    for (my $i = 0; $i < @fields; $i++) {
123
        my $field = $fields[$i];
124
        my $q = shift @q;
125
        my $op = shift @op;
126
        if (defined $q and $q ne '') {
127
            if ($i == 0) {
128
                $f = {
129
                    field => $field,
130
                    query => $q,
131
                    operator => $op,
132
                };
133
            } else {
134
                my $c = shift @c;
135
                $f = {
136
                    conjunction => $c,
137
                    filters => [
138
                        $f, {
139
                            field => $field,
140
                            query => $q,
141
                            operator => $op,
142
                        }
143
                    ],
144
                };
145
            }
146
        }
147
    }
148
    push @{ $filter->{filters} }, $f;
149
150
    # Yes/No parameters
151
    foreach my $p (qw(damaged itemlost)) {
152
        my $v = $cgi->param($p) // '';
153
        my $f = {
154
            field => $p,
155
            query => 0,
156
        };
157
        if ($v eq 'yes') {
158
            $f->{operator} = '!=';
159
            push @{ $filter->{filters} }, $f;
160
        } elsif ($v eq 'no') {
161
            $f->{operator} = '=';
162
            push @{ $filter->{filters} }, $f;
163
        }
164
    }
165
166
    if (my $itemcallnumber_from = $cgi->param('itemcallnumber_from')) {
167
        push @{ $filter->{filters} }, {
168
            field => 'itemcallnumber',
169
            query => $itemcallnumber_from,
170
            operator => '>=',
171
        };
172
    }
173
    if (my $itemcallnumber_to = $cgi->param('itemcallnumber_to')) {
174
        push @{ $filter->{filters} }, {
175
            field => 'itemcallnumber',
176
            query => $itemcallnumber_to,
177
            operator => '<=',
178
        };
179
    }
180
181
    my $search_params = {
182
        rows => $cgi->param('rows') // 20,
183
        page => $cgi->param('page') || 1,
184
        sortby => $cgi->param('sortby') || 'itemnumber',
185
        sortorder => $cgi->param('sortorder') || 'asc',
186
    };
187
188
    my ($results, $total_rows) = SearchItems($filter, $search_params);
189
    if ($results) {
190
        # Get notforloan labels
191
        my $notforloan_map = {};
192
        foreach my $nfl_value (@$notforloan_values) {
193
            $notforloan_map->{$nfl_value->{authorised_value}} = $nfl_value->{lib};
194
        }
195
196
        foreach my $item (@$results) {
197
            $item->{biblio} = GetBiblio($item->{biblionumber});
198
            ($item->{biblioitem}) = GetBiblioItemByBiblioNumber($item->{biblionumber});
199
            $item->{status} = $notforloan_map->{$item->{notforloan}};
200
        }
201
    }
202
203
    $template->param(
204
        filter => $filter,
205
        search_params => $search_params,
206
        results => $results,
207
        total_rows => $total_rows,
208
        search_done => 1,
209
    );
210
211
    if ($format eq 'html') {
212
        # Build pagination bar
213
        my $url = $cgi->url(-absolute => 1);
214
        my @params;
215
        foreach my $p (keys %params) {
216
            my @v = $cgi->param($p);
217
            push @params, map { "$p=" . $_ } @v;
218
        }
219
        $url .= '?' . join ('&', @params);
220
        my $nb_pages = 1 + int($total_rows / $search_params->{rows});
221
        my $current_page = $search_params->{page};
222
        my $pagination_bar = pagination_bar($url, $nb_pages, $current_page, 'page');
223
224
        $template->param(pagination_bar => $pagination_bar);
225
    }
226
}
227
228
if ($format eq 'html') {
229
    # Retrieve data required for the form.
230
231
    my $branches = GetBranches();
232
    my @branches;
233
    foreach my $branchcode (keys %$branches) {
234
        push @branches, {
235
            value => $branchcode,
236
            label => $branches->{$branchcode}->{branchname},
237
        };
238
    }
239
    my $locations = GetAuthorisedValues('LOC');
240
    my @locations;
241
    foreach my $location (@$locations) {
242
        push @locations, {
243
            value => $location->{authorised_value},
244
            label => $location->{lib},
245
        };
246
    }
247
    my @itemtypes = C4::ItemType->all();
248
    foreach my $itemtype (@itemtypes) {
249
        $itemtype->{value} = $itemtype->{itemtype};
250
        $itemtype->{label} = $itemtype->{description};
251
    }
252
    my $ccode_avcode = GetAuthValCode('items.ccode') || 'CCODE';
253
    my $ccodes = GetAuthorisedValues($ccode_avcode);
254
    my @ccodes;
255
    foreach my $ccode (@$ccodes) {
256
        push @ccodes, {
257
            value => $ccode->{authorised_value},
258
            label => $ccode->{lib},
259
        };
260
    }
261
262
    my @notforloans;
263
    foreach my $value (@$notforloan_values) {
264
        push @notforloans, {
265
            value => $value->{authorised_value},
266
            label => $value->{lib},
267
        };
268
    }
269
270
    my @items_search_fields = GetItemSearchFields();
271
272
    my $authorised_values = {};
273
    foreach my $field (@items_search_fields) {
274
        if (my $category = ($field->{authorised_values_category})) {
275
            $authorised_values->{$category} = GetAuthorisedValues($category);
276
        }
277
    }
278
279
    $template->param(
280
        branches => \@branches,
281
        locations => \@locations,
282
        itemtypes => \@itemtypes,
283
        ccodes => \@ccodes,
284
        notforloans => \@notforloans,
285
        items_search_fields => \@items_search_fields,
286
        authorised_values_json => to_json($authorised_values),
287
    );
288
}
289
290
if ($format eq 'csv') {
291
    print $cgi->header({
292
        type => 'text/csv',
293
        attachment => 'items.csv',
294
    });
295
    print $template->output;
296
} else {
297
    output_with_http_headers $cgi, $cookie, $template->output, $content_type;
298
}
(-)a/installer/data/mysql/kohastructure.sql (+17 lines)
Lines 3422-3427 CREATE TABLE IF NOT EXISTS `misc_files` ( -- miscellaneous files attached to rec Link Here
3422
  KEY `record_id` (`record_id`)
3422
  KEY `record_id` (`record_id`)
3423
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3423
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3424
3424
3425
--
3426
-- Table structure for table 'items_search_fields'
3427
--
3428
3429
DROP TABLE IF EXISTS items_search_fields;
3430
CREATE TABLE items_search_fields (
3431
  name VARCHAR(255) NOT NULL,
3432
  label VARCHAR(255) NOT NULL,
3433
  tagfield CHAR(3) NOT NULL,
3434
  tagsubfield CHAR(1) NULL DEFAULT NULL,
3435
  authorised_values_category VARCHAR(16) NULL DEFAULT NULL,
3436
  PRIMARY KEY(name),
3437
  CONSTRAINT items_search_fields_authorised_values_category
3438
    FOREIGN KEY (authorised_values_category) REFERENCES authorised_values (category)
3439
    ON DELETE SET NULL ON UPDATE CASCADE
3440
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3441
3425
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3442
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3426
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3443
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3427
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
3444
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
(-)a/installer/data/mysql/updatedatabase.pl (+19 lines)
Lines 8577-8582 if ( CheckVersion($DBversion) ) { Link Here
8577
    SetVersion ($DBversion);
8577
    SetVersion ($DBversion);
8578
}
8578
}
8579
8579
8580
$DBversion = "3.17.00.XXX";
8581
if ( CheckVersion($DBversion) ) {
8582
    $dbh->do(q{
8583
        CREATE TABLE IF NOT EXISTS items_search_fields (
8584
          name VARCHAR(255) NOT NULL,
8585
          label VARCHAR(255) NOT NULL,
8586
          tagfield CHAR(3) NOT NULL,
8587
          tagsubfield CHAR(1) NULL DEFAULT NULL,
8588
          authorised_values_category VARCHAR(16) NULL DEFAULT NULL,
8589
          PRIMARY KEY(name),
8590
          CONSTRAINT items_search_fields_authorised_values_category
8591
            FOREIGN KEY (authorised_values_category) REFERENCES authorised_values (category)
8592
            ON DELETE SET NULL ON UPDATE CASCADE
8593
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
8594
    });
8595
    print "Upgrade to $DBversion done (Bug 11425: Add items_search_fields table)\n";
8596
    SetVersion($DBversion);
8597
}
8598
8580
=head1 FUNCTIONS
8599
=head1 FUNCTIONS
8581
8600
8582
=head2 TableExists($table)
8601
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/css/itemsearchform.css (+44 lines)
Line 0 Link Here
1
.form-field {
2
  margin: 5px 0;
3
}
4
5
.form-field > * {
6
  vertical-align: middle;
7
}
8
9
.form-field-label {
10
  display: inline-block;
11
  text-align: right;
12
  width: 10em;
13
}
14
15
.form-field-conjunction[disabled] {
16
  visibility: hidden;
17
}
18
19
.form-field-radio > * {
20
  vertical-align: middle;
21
}
22
23
.form-actions {
24
  margin-top: 20px;
25
}
26
27
th.active {
28
  padding-right: 21px;
29
  background-repeat: no-repeat;
30
  background-position: 100% 50%;
31
}
32
33
th.sort-asc {
34
  background-image: url('../../img/asc.gif');
35
}
36
37
th.sort-desc {
38
  background-image: url('../../img/desc.gif');
39
}
40
41
th select {
42
  width: 100%;
43
  font-weight: normal;
44
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-items-search-field-form.inc (+58 lines)
Line 0 Link Here
1
<ul>
2
  <li>
3
    <label for="name">Name</label>
4
    [% IF field %]
5
      <input type="text" name="name" value="[% field.name %]" disabled="disabled">
6
      <input type="hidden" name="name" value="[% field.name %]">
7
    [% ELSE %]
8
      <input type="text" name="name" />
9
    [% END %]
10
  </li>
11
  <li>
12
    <label for="label">Label</label>
13
    [% IF field %]
14
      <input type="text" name="label" value="[% field.label %]" />
15
    [% ELSE %]
16
      <input type="text" name="label" />
17
    [% END %]
18
  </li>
19
  <li>
20
    <label for="tagfield">MARC field</label>
21
    <select name="tagfield">
22
      [% FOREACH tagfield IN ['001'..'999'] %]
23
        [% IF field && field.tagfield == tagfield %]
24
          <option value="[% tagfield %]" selected="selected">[% tagfield %]</option>
25
        [% ELSE %]
26
          <option value="[% tagfield %]">[% tagfield %]</option>
27
        [% END %]
28
      [% END %]
29
    </select>
30
  </li>
31
  <li>
32
    <label for="tagsubfield">MARC subfield</label>
33
    <select name="tagsubfield">
34
      [% codes = [''] %]
35
      [% codes = codes.merge([0..9], ['a'..'z']) %]
36
      [% FOREACH tagsubfield IN codes %]
37
        [% IF field && field.tagsubfield == tagsubfield %]
38
          <option value="[% tagsubfield %]" selected="selected">[% tagsubfield %]</option>
39
        [% ELSE %]
40
          <option value="[% tagsubfield %]">[% tagsubfield %]</option>
41
        [% END %]
42
      [% END %]
43
    </select>
44
  </li>
45
  <li>
46
    <label for="authorised_values_category">Authorised values category</label>
47
    <select name="authorised_values_category">
48
      <option value="">- None -</option>
49
      [% FOREACH category IN authorised_values_categories %]
50
        [% IF field && field.authorised_values_category == category %]
51
          <option value="[% category %]" selected="selected">[% category %]</option>
52
        [% ELSE %]
53
          <option value="[% category %]">[% category %]</option>
54
        [% END %]
55
      [% END %]
56
    </select>
57
  </li>
58
</ul>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc (+1 lines)
Lines 45-50 Link Here
45
    <li><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></li>
45
    <li><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></li>
46
    <li><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></li>
46
    <li><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></li>
47
    <li><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></li>
47
    <li><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></li>
48
    <li><a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a></li>
48
</ul>
49
</ul>
49
50
50
<h5>Acquisition parameters</h5>
51
<h5>Acquisition parameters</h5>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc (+4 lines)
Line 0 Link Here
1
[%- USE Branches -%]
2
[%- biblio = item.biblio -%]
3
[%- biblioitem = item.biblioitem -%]
4
"[% biblio.title |html %] by [% biblio.author |html %]", "[% biblioitem.publicationyear |html %]", "[% biblioitem.publishercode |html %]", "[% biblioitem.collectiontitle |html %]", "[% item.barcode |html %]", "[% item.itemcallnumber |html %]", "[% Branches.GetName(item.homebranch) |html %]", "[% Branches.GetName(item.holdingbranch) |html %]", "[% item.location |html %]", "[% item.stocknumber |html %]", "[% item.status |html %]", "[% (item.issues || 0) |html %]"
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.inc (+23 lines)
Line 0 Link Here
1
[%- USE Branches -%]
2
[% biblio = item.biblio %]
3
[% biblioitem = item.biblioitem %]
4
<tr>
5
  <td>
6
    <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber %]" title="Go to record detail page">[% biblio.title %]</a>
7
    by [% biblio.author %]
8
  </td>
9
  <td>[% biblioitem.publicationyear %]</td>
10
  <td>[% biblioitem.publishercode %]</td>
11
  <td>[% biblioitem.collectiontitle %]</td>
12
  <td>
13
    <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% biblio.biblionumber %]#item[% item.itemnumber %]" title="Go to item details">[% item.barcode %]</a>
14
  </td>
15
  <td>[% item.itemcallnumber %]</td>
16
  <td>[% Branches.GetName(item.homebranch) %]</td>
17
  <td>[% Branches.GetName(item.holdingbranch) %]</td>
18
  <td>[% item.location %]</td>
19
  <td>[% item.stocknumber %]</td>
20
  <td>[% item.status %]</td>
21
  <td>[% item.issues || 0 %]</td>
22
  <td><a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&biblionumber=[% item.biblionumber %]&itemnumber=[% item.itemnumber %]">Modify</a></td>
23
</tr>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc (+18 lines)
Line 0 Link Here
1
[%- USE Branches -%]
2
[%- biblio = item.biblio -%]
3
[%- biblioitem = item.biblioitem -%]
4
[
5
  "<a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber %]' title='Go to record detail page'>[% biblio.title |html %]</a> by [% biblio.author |html %]",
6
  "[% biblioitem.publicationyear |html %]",
7
  "[% biblioitem.publishercode |html %]",
8
  "[% biblioitem.collectiontitle |html %]",
9
  "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% biblio.biblionumber %]#item[% item.itemnumber %]' title='Go to item details'>[% item.barcode |html %]</a>",
10
  "[% item.itemcallnumber |html %]",
11
  "[% Branches.GetName(item.homebranch) |html %]",
12
  "[% Branches.GetName(item.holdingbranch) |html %]",
13
  "[% item.location |html %]",
14
  "[% item.stocknumber |html %]",
15
  "[% item.status |html %]",
16
  "[% (item.issues || 0) |html %]",
17
  "<a href='/cgi-bin/koha/cataloguing/additem.pl?op=edititem&biblionumber=[% item.biblionumber %]&itemnumber=[% item.itemnumber %]'>Modify</a>"
18
]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_items.inc (+49 lines)
Line 0 Link Here
1
[% names = CGI.param() %]
2
[% params = [] %]
3
[% FOREACH name IN names %]
4
  [% IF name != 'sortby' AND name != 'sortorder' %]
5
    [% params.push(name _ "=" _ CGI.param(name)) %]
6
  [% END %]
7
[% END %]
8
[% base_url = "/cgi-bin/koha/catalogue/itemsearch.pl?" _ params.join('&') %]
9
10
[% BLOCK itemsearch_header %]
11
  [% sortorder = 'asc' %]
12
  [% classes = [] %]
13
  [% IF CGI.param('sortby') == name %]
14
    [% classes.push('active') %]
15
    [% classes.push('sort-' _ CGI.param('sortorder')) %]
16
    [% IF CGI.param('sortorder') == 'asc' %]
17
      [% sortorder = 'desc' %]
18
    [% END %]
19
  [% END %]
20
  [% url = base_url _ '&sortby=' _ name _ '&sortorder=' _ sortorder %]
21
  <th class="[% classes.join(' ') %]">
22
    <a href="[% url %]" title="Sort on [% label %] ([% sortorder %])">[% text %]</a>
23
  </th>
24
[% END %]
25
26
<table>
27
  <thead>
28
    <tr>
29
      [% INCLUDE itemsearch_header name='title' label='title' text='Bibliographic reference' %]
30
      [% INCLUDE itemsearch_header name='publicationyear' label='publication date' text='Publication date' %]
31
      [% INCLUDE itemsearch_header name='publishercode' label='publisher' text='Publisher' %]
32
      [% INCLUDE itemsearch_header name='collectiontitle' label='collection' text='Collection' %]
33
      [% INCLUDE itemsearch_header name='barcode' label='barcode' text='Barcode' %]
34
      [% INCLUDE itemsearch_header name='itemcallnumber' label='callnumber' text='Callnumber' %]
35
      [% INCLUDE itemsearch_header name='homebranch' label='home branch' text='Home branch' %]
36
      [% INCLUDE itemsearch_header name='holdingbranch' label='holding branch' text='Holding branch' %]
37
      [% INCLUDE itemsearch_header name='location' label='location' text='Location' %]
38
      [% INCLUDE itemsearch_header name='stocknumber' label='stock number' text='Stock number' %]
39
      [% INCLUDE itemsearch_header name='notforloan' label='status' text='Status' %]
40
      [% INCLUDE itemsearch_header name='issues' label='issues' text='Issues' %]
41
      <th></th>
42
    </tr>
43
  </thead>
44
  <tbody>
45
    [% FOREACH item IN items %]
46
      [% INCLUDE 'catalogue/itemsearch_item.inc' item = item %]
47
    [% END %]
48
  </tbody>
49
</table>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt (+2 lines)
Lines 77-82 Link Here
77
    <dd>Manage rules for automatically matching MARC records during record imports.</dd>
77
    <dd>Manage rules for automatically matching MARC records during record imports.</dd>
78
    <dt><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></dt>
78
    <dt><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></dt>
79
    <dd>Manage OAI Sets</dd>
79
    <dd>Manage OAI Sets</dd>
80
    <dt><a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a></dt>
81
    <dd>Manage custom fields for items search</dd>
80
    [% IF ( SearchEngine != 'Zebra' ) %]
82
    [% IF ( SearchEngine != 'Zebra' ) %]
81
      <dt><a href="/cgi-bin/koha/admin/searchengine/solr/indexes.pl">Search engine configuration</a></dt>
83
      <dt><a href="/cgi-bin/koha/admin/searchengine/solr/indexes.pl">Search engine configuration</a></dt>
82
      <dd>Manage indexes, facets, and their mappings to MARC fields and subfields.</dd>
84
      <dd>Manage indexes, facets, and their mappings to MARC fields and subfields.</dd>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_field.tt (+41 lines)
Line 0 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
  <title>Koha &rsaquo; Administration &rsaquo; Items search fields</title>
3
  [% INCLUDE 'doc-head-close.inc' %]
4
</head>
5
<body id="admin_itemssearchfields" class="admin">
6
  [% INCLUDE 'header.inc' %]
7
  [% INCLUDE 'cat-search.inc' %]
8
  <div id="breadcrumbs">
9
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
10
    <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo;
11
    <a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a> &rsaquo;
12
    [% field.name %]
13
  </div>
14
15
  <div id="doc3" class="yui-t2">
16
    <div id="bd">
17
      <div id="yui-main">
18
        <div class="yui-b">
19
          <h1>Items search field: [% field.label %]</h1>
20
21
          <form action="" method="POST">
22
            <fieldset class="rows">
23
              <legend>Edit field</legend>
24
              [% INCLUDE 'admin-items-search-field-form.inc' field=field %]
25
              <div>
26
                <input type="hidden" name="op" value="mod" />
27
              </div>
28
              <fieldset class="action">
29
                <input type="submit" value="Update" />
30
              </fieldset>
31
            </fieldset>
32
          </form>
33
          <a href="/cgi-bin/koha/admin/items_search_fields.pl">Return to items search fields overview page</a>
34
        </div>
35
      </div>
36
      <div class="yui-b">
37
        [% INCLUDE 'admin-menu.inc' %]
38
      </div>
39
    </div>
40
41
    [% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_fields.tt (+95 lines)
Line 0 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
  <title>Koha &rsaquo; Administration &rsaquo; Items search fields</title>
3
  [% INCLUDE 'doc-head-close.inc' %]
4
</head>
5
<body id="admin_itemssearchfields" class="admin">
6
  [% INCLUDE 'header.inc' %]
7
  [% INCLUDE 'cat-search.inc' %]
8
  <div id="breadcrumbs">
9
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
10
    <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo;
11
    Items search fields
12
  </div>
13
14
  <div id="doc3" class="yui-t2">
15
    <div id="bd">
16
      <div id="yui-main">
17
        <div class="yui-b">
18
          [% IF field_added %]
19
            <div class="dialog">
20
              Field successfully added: [% field_added.label %]
21
            </div>
22
          [% ELSIF field_not_added %]
23
            <div class="alert">
24
              <p>Failed to add field. Please check if the field name doesn't already exist.</p>
25
              <p>Check logs for more details.</p>
26
            </div>
27
          [% ELSIF field_deleted %]
28
            <div class="dialog">
29
              Field successfully deleted.
30
            </div>
31
          [% ELSIF field_not_deleted %]
32
            <div class="alert">
33
              <p>Failed to delete field.</p>
34
              <p>Check logs for more details.</p>
35
            </div>
36
          [% ELSIF field_updated %]
37
            <div class="dialog">
38
              Field successfully updated: [% field_updated.label %]
39
            </div>
40
          [% ELSIF field_not_updated %]
41
            <div class="alert">
42
              <p>Failed to update field.</p>
43
              <p>Check logs for more details.</p>
44
            </div>
45
          [% END %]
46
          <h1>Items search fields</h1>
47
          [% IF fields.size %]
48
            <table>
49
              <thead>
50
                <tr>
51
                  <th>Name</th>
52
                  <th>Label</th>
53
                  <th>MARC field</th>
54
                  <th>MARC subfield</th>
55
                  <th>Authorised values category</th>
56
                  <th>Operations</th>
57
                </tr>
58
              </thead>
59
              <tbody>
60
                [% FOREACH field IN fields %]
61
                  <tr>
62
                    <td>[% field.name %]</td>
63
                    <td>[% field.label %]</td>
64
                    <td>[% field.tagfield %]</td>
65
                    <td>[% field.tagsubfield %]</td>
66
                    <td>[% field.authorised_values_category %]</td>
67
                    <td>
68
                      <a href="/cgi-bin/koha/admin/items_search_field.pl?name=[% field.name %]" title="Edit [% field.name %] field">Edit</a>
69
                      <a href="/cgi-bin/koha/admin/items_search_fields.pl?op=del&name=[% field.name %]" title="Delete [% field.name %] field">Delete</a>
70
                    </td>
71
                  </tr>
72
                [% END %]
73
              </tbody>
74
            </table>
75
          [% END %]
76
          <form action="" method="POST">
77
            <fieldset class="rows">
78
              <legend>Add a new field</legend>
79
              [% INCLUDE 'admin-items-search-field-form.inc' field=undef %]
80
              <div>
81
                <input type="hidden" name="op" value="add" />
82
              </div>
83
              <fieldset class="action">
84
                <input type="submit" value="Add this field" />
85
              </fieldset>
86
            </fieldset>
87
          </form>
88
        </div>
89
      </div>
90
      <div class="yui-b">
91
        [% INCLUDE 'admin-menu.inc' %]
92
      </div>
93
    </div>
94
95
    [% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt (+1 lines)
Lines 30-35 Link Here
30
<form action="search.pl" method="get">
30
<form action="search.pl" method="get">
31
<div id="advanced-search">
31
<div id="advanced-search">
32
<h1>Advanced search</h1>
32
<h1>Advanced search</h1>
33
<a href="/cgi-bin/koha/catalogue/itemsearch.pl">Go to item search</a>
33
34
34
[% IF ( outer_servers_loop ) %]
35
[% IF ( outer_servers_loop ) %]
35
<!-- DATABASES -->
36
<!-- DATABASES -->
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.csv.tt (+4 lines)
Line 0 Link Here
1
Bibliographic reference, Publication Date, Publisher, Collection, Barcode, Callnumber, Home branch, Holding branch, Location, Stock number, Status, Issues
2
[% FOREACH item IN results -%]
3
  [%- INCLUDE 'catalogue/itemsearch_item.csv.inc' item = item -%]
4
[%- END -%]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.json.tt (+12 lines)
Line 0 Link Here
1
[%- USE CGI -%]
2
{
3
  "sEcho": [% CGI.param('sEcho') %],
4
  "iTotalRecords": [% total_rows %],
5
  "iTotalDisplayRecords": [% total_rows %],
6
  "aaData": [
7
  [%- FOREACH item IN results -%]
8
    [%- INCLUDE 'catalogue/itemsearch_item.json.inc' item = item -%]
9
    [%- UNLESS loop.last %],[% END -%]
10
  [%- END -%]
11
  ]
12
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt (-1 / +425 lines)
Line 0 Link Here
0
- 
1
[% USE CGI %]
2
[% USE JSON.Escape %]
3
4
[% BLOCK form_field_select %]
5
  <div class="form-field form-field-select">
6
    <label class="form-field-label" for="[% name %]">[% label %]</label>
7
    <select id="[% name %]_op" name="[% name %]_op">
8
      <option value="=">is</option>
9
      [% IF CGI.param(name _ '_op') == '!=' %]
10
        <option value="!=" selected="selected">is not</option>
11
      [% ELSE %]
12
        <option value="!=" >is not</option>
13
      [% END %]
14
    </select>
15
    [% values = CGI.param(name) %]
16
    <select id="[% name %]" name="[% name %]" multiple="multiple" size="[% options.size < 4 ? options.size + 1 : 4 %]">
17
      [% IF (values == '') %]
18
        <option value="" selected="selected">
19
      [% ELSE %]
20
        <option value="">
21
      [% END %]
22
        [% empty_option || "All" %]
23
      </option>
24
      [% FOREACH option IN options %]
25
        [% IF values.grep(option.value).size %]
26
          <option value="[% option.value %]" selected="selected">[% option.label %]</option>
27
        [% ELSE %]
28
          <option value="[% option.value %]">[% option.label %]</option>
29
        [% END %]
30
      [% END %]
31
    </select>
32
  </div>
33
[% END %]
34
35
[% BLOCK form_field_select_option %]
36
  [% IF params.f == value %]
37
    <option value="[% value %]" selected="selected">[% label %]</option>
38
  [% ELSE %]
39
    <option value="[% value %]">[% label %]</option>
40
  [% END %]
41
[% END %]
42
43
[% BLOCK form_field_select_text %]
44
  <div class="form-field form-field-select-text">
45
    [% IF params.exists('c') %]
46
      <select name="c" class="form-field-conjunction">
47
        <option value="and">AND</option>
48
        [% IF params.c == 'or' %]
49
          <option value="or" selected="selected">OR</option>
50
        [% ELSE %]
51
          <option value="or">OR</option>
52
        [% END %]
53
      </select>
54
    [% ELSE %]
55
      <select name="c" class="form-field-conjunction" disabled="disabled">
56
        <option value="and">AND</option>
57
        <option value="or">OR</option>
58
      </select>
59
    [% END %]
60
    <select name="f" class="form-field-column">
61
      [% INCLUDE form_field_select_option value='barcode' label='Barcode' %]
62
      [% INCLUDE form_field_select_option value='itemcallnumber' label='Callnumber' %]
63
      [% INCLUDE form_field_select_option value='stocknumber' label='Stock number' %]
64
      [% INCLUDE form_field_select_option value='title' label='Title' %]
65
      [% INCLUDE form_field_select_option value='author' label='Author' %]
66
      [% INCLUDE form_field_select_option value='publishercode' label='Publisher' %]
67
      [% INCLUDE form_field_select_option value='publicationdate' label='Publication date' %]
68
      [% INCLUDE form_field_select_option value='collectiontitle' label='Collection' %]
69
      [% INCLUDE form_field_select_option value='isbn' label='ISBN' %]
70
      [% INCLUDE form_field_select_option value='issn' label='ISSN' %]
71
      [% IF items_search_fields.size %]
72
        <optgroup label="Custom search fields">
73
          [% FOREACH field IN items_search_fields %]
74
            [% marcfield = field.tagfield %]
75
            [% IF field.tagsubfield %]
76
              [% marcfield = marcfield _ '$' _ field.tagsubfield %]
77
            [% END %]
78
            [% IF params.f == "marc:$marcfield" %]
79
              <option value="marc:[% marcfield %]" data-authorised-values-category="[% field.authorised_values_category %]" selected="selected">[% field.label %] ([% marcfield %])</option>
80
            [% ELSE %]
81
              <option value="marc:[% marcfield %]" data-authorised-values-category="[% field.authorised_values_category %]">[% field.label %] ([% marcfield %])</option>
82
            [% END %]
83
          [% END %]
84
        </optgroup>
85
      [% END %]
86
    </select>
87
    <input type="text" name="q" class="form-field-value" value="[% params.q %]" />
88
    <input type="hidden" name="op" value="like" />
89
  </div>
90
[% END %]
91
92
[% BLOCK form_field_select_text_block %]
93
  [% c = CGI.param('c').list %]
94
  [% f = CGI.param('f').list %]
95
  [% q = CGI.param('q').list %]
96
  [% op = CGI.param('op').list %]
97
  [% IF q.size %]
98
    [% size = q.size - 1 %]
99
    [% FOREACH i IN [0 .. size] %]
100
      [%
101
        params = {
102
          f => f.$i
103
          q = q.$i
104
          op = op.$i
105
        }
106
      %]
107
      [% IF i > 0 %]
108
        [% j = i - 1 %]
109
        [% params.c = c.$j %]
110
      [% END %]
111
      [% INCLUDE form_field_select_text params=params %]
112
    [% END %]
113
  [% ELSE %]
114
    [% INCLUDE form_field_select_text %]
115
  [% END %]
116
[% END %]
117
118
[% BLOCK form_field_radio_yes_no %]
119
  <div class="form-field">
120
    <label class="form-field-label" for="[% name %]">[% label %]:</label>
121
    <input type="radio" name="[% name %]" id="[% name %]_indifferent" value="" checked="checked"/>
122
    <label for="[% name %]_indifferent">Indifferent</label>
123
    <input type="radio" name="[% name %]" id="[% name %]_yes" value="yes" />
124
    <label for="[% name %]_yes">Yes</label>
125
    <input type="radio" name="[% name %]" id="[% name %]_no" value="no" />
126
    <label for="[% name %]_no">No</label>
127
  </div>
128
[% END %]
129
130
[%# Page starts here %]
131
132
[% INCLUDE 'doc-head-open.inc' %]
133
  <title>Koha &rsaquo; Catalog &rsaquo; Advanced search</title>
134
  [% INCLUDE 'doc-head-close.inc' %]
135
  [% INCLUDE 'datatables.inc' %]
136
  <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.columnFilter.js"></script>
137
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
138
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/itemsearchform.css" />
139
  <script type="text/javascript">
140
    //<![CDATA[
141
    var authorised_values = [% authorised_values_json %];
142
143
    function loadAuthorisedValuesSelect(select) {
144
      var selected = select.find('option:selected');
145
      var category = selected.data('authorised-values-category');
146
      var form_field_value = select.siblings('.form-field-value');
147
      if (category && category in authorised_values) {
148
        var values = authorised_values[category];
149
        var html = '<select name="q" class="form-field-value">\n';
150
        for (i in values) {
151
          var value = values[i];
152
          html += '<option value="' + value.authorised_value + '">' + value.lib + '</option>\n';
153
        }
154
        html += '</select>\n';
155
        var new_form_field_value = $(html);
156
        new_form_field_value.val(form_field_value.val());
157
        form_field_value.replaceWith(new_form_field_value);
158
      } else {
159
        if (form_field_value.prop('tagName').toLowerCase() == 'select') {
160
          html = '<input name="q" type="text" class="form-field-value" />';
161
          var new_form_field_value = $(html);
162
          form_field_value.replaceWith(new_form_field_value);
163
        }
164
      }
165
    }
166
167
    function addNewField() {
168
      var form_field = $('div.form-field-select-text').last();
169
      var copy = form_field.clone(true);
170
      copy.find('input,select').not('[type="hidden"]').each(function() {
171
        $(this).val('');
172
      });
173
      copy.find('.form-field-conjunction').removeAttr('disabled');
174
      form_field.after(copy);
175
      copy.find('select.form-field-column').change();
176
    }
177
178
    function submitForm($form) {
179
      var tr = ''
180
        + '    <tr>'
181
        + '      <th>' + _("Bibliographic reference") + '</th>'
182
        + '      <th>' + _("Publication date") + '</th>'
183
        + '      <th>' + _("Publisher") + '</th>'
184
        + '      <th>' + _("Collection") + '</th>'
185
        + '      <th>' + _("Barcode") + '</th>'
186
        + '      <th>' + _("Callnumber") + '</th>'
187
        + '      <th>' + _("Home branch") + '</th>'
188
        + '      <th>' + _("Holding branch") + '</th>'
189
        + '      <th>' + _("Location") + '</th>'
190
        + '      <th>' + _("Stock number") + '</th>'
191
        + '      <th>' + _("Status") + '</th>'
192
        + '      <th>' + _("Issues") + '</th>'
193
        + '      <th></th>'
194
        + '    </tr>'
195
      var table = ''
196
        + '<table id="results">'
197
        + '  <thead>' + tr + tr + '</thead>'
198
        + '  <tbody></tbody>'
199
        + '</table>';
200
      $('#results-wrapper').empty().html(table);
201
202
      var params = [];
203
      $form.find('select,input[type="text"],input[type="hidden"]').not('[disabled]').each(function () {
204
        params.push({ 'name': $(this).attr('name'), 'value': $(this).val() });
205
      });
206
      $form.find('input[type="radio"]:checked').each(function() {
207
        params.push({ 'name': $(this).attr('name'), 'value': $(this).val() });
208
      });
209
210
      $('#results').dataTable($.extend(true, {}, dataTablesDefaults, {
211
        'bDestroy': true,
212
        'bServerSide': true,
213
        'sAjaxSource': '/cgi-bin/koha/catalogue/itemsearch.pl',
214
        'fnServerParams': function(aoData) {
215
          aoData.push( { 'name': 'format', 'value': 'json' } );
216
          for (i in params) {
217
            aoData.push(params[i]);
218
          }
219
        },
220
        'sDom': '<"top pager"ilp>t<"bottom pager"ip>',
221
        'aoColumns': [
222
          { 'sName': 'title' },
223
          { 'sName': 'publicationyear' },
224
          { 'sName': 'publishercode' },
225
          { 'sName': 'collectiontitle' },
226
          { 'sName': 'barcode' },
227
          { 'sName': 'itemcallnumber' },
228
          { 'sName': 'homebranch' },
229
          { 'sName': 'holdingbranch' },
230
          { 'sName': 'location' },
231
          { 'sName': 'stocknumber' },
232
          { 'sName': 'notforloan' },
233
          { 'sName': 'issues' },
234
          { 'sName': 'checkbox', 'bSortable': false }
235
        ]
236
      })).columnFilter({
237
        'sPlaceHolder': 'head:after',
238
        'aoColumns': [
239
          { 'type': 'text' },
240
          { 'type': 'text' },
241
          { 'type': 'text' },
242
          { 'type': 'text' },
243
          { 'type': 'text' },
244
          { 'type': 'text' },
245
          { 'type': 'select', 'values': [% branches.json %] },
246
          { 'type': 'select', 'values': [% branches.json %] },
247
          { 'type': 'select', 'values': [% locations.json %] },
248
          { 'type': 'text' },
249
          { 'type': 'select', 'values': [% notforloans.json %] },
250
          { 'type': 'text' },
251
          null
252
        ]
253
      });
254
    }
255
256
    function hideForm($form) {
257
      $form.hide();
258
      $('#editsearchlink').show();
259
    }
260
261
    $(document).ready(function () {
262
      // Add the "New field" link.
263
      var form_field = $('div.form-field-select-text').last()
264
      var button_field_new = $('<a href="#" class="button-field-new" title="Add a new field">New field</a>');
265
      button_field_new.click(function() {
266
        addNewField();
267
        return false;
268
      });
269
      form_field.after(button_field_new);
270
271
      // If a field is linked to an authorised values list, display the list.
272
      $('div.form-field-select-text select').change(function() {
273
        loadAuthorisedValuesSelect($(this));
274
      }).change();
275
276
      // Prevent user to select the 'All ...' option with other options.
277
      $('div.form-field-select').each(function() {
278
        $(this).find('select').filter(':last').change(function() {
279
          values = $(this).val();
280
          if (values.length > 1) {
281
            var idx = $.inArray('', values);
282
            if (idx != -1) {
283
              values.splice(idx, 1);
284
              $(this).val(values);
285
            }
286
          }
287
        });
288
      });
289
290
      $('#itemsearchform').submit(function() {
291
        var format = $(this).find('input[name="format"]:checked').val();
292
        if (format == 'html') {
293
          submitForm($(this));
294
          hideForm($(this));
295
          return false;
296
        }
297
      });
298
299
      $('#editsearchlink').click(function() {
300
        $('#itemsearchform').show();
301
        $(this).hide();
302
        return false;
303
      });
304
    });
305
    //]]>
306
  </script>
307
</head>
308
<body id="catalog_itemsearch" class="catalog">
309
  [% INCLUDE 'header.inc' %]
310
  <div id="breadcrumbs">
311
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; Item search
312
  </div>
313
314
  <div id="doc" class="yui-t7">
315
    <div id="bd">
316
      <h1>Item search</h1>
317
      <a href="/cgi-bin/koha/catalogue/search.pl">Go to advanced search</a>
318
      <form action="" method="get" id="itemsearchform">
319
        <fieldset>
320
          <legend>Item search</legend>
321
          <fieldset>
322
            [% INCLUDE form_field_select
323
              name="homebranch"
324
              label="Home branch"
325
              options = branches
326
              empty_option = "All branches"
327
            %]
328
            [% INCLUDE form_field_select
329
              name="location"
330
              label="Location"
331
              options = locations
332
              empty_option = "All locations"
333
            %]
334
          </fieldset>
335
          <fieldset>
336
            [% INCLUDE form_field_select
337
              name="itype"
338
              label="Item type"
339
              options = itemtypes
340
              empty_option = "All item types"
341
            %]
342
            [% INCLUDE form_field_select
343
              name="ccode"
344
              label="Collection code"
345
              options = ccodes
346
              empty_option = "All collection codes"
347
            %]
348
          </fieldset>
349
          <fieldset>
350
            [% INCLUDE form_field_select_text_block %]
351
            <p class="hint">You can use the following joker characters: % _</p>
352
          </fieldset>
353
          <fieldset>
354
            <div class="form-field">
355
              <label class="form-field-label" for="itemcallnumber_from">From call number:</label>
356
              [% value = CGI.param('itemcallnumber_from') %]
357
              <input type="text" id="itemcallnumber_from" name="itemcallnumber_from" value="[% value %]" />
358
              <span class="hint">(inclusive)</span>
359
            </div>
360
            <div class="form-field">
361
              [% value = CGI.param('itemcallnumber_to') %]
362
              <label class="form-field-label" for="itemcallnumber_to">To call number:</label>
363
              <input type="text" id="itemcallnumber_to" name="itemcallnumber_to" value="[% value %]" />
364
              <span class="hint">(inclusive)</span>
365
            </div>
366
            [% INCLUDE form_field_radio_yes_no name="damaged" label="Damaged" %]
367
            [% INCLUDE form_field_radio_yes_no name="itemlost" label="Lost" %]
368
            <div class="form-field">
369
              <label class="form-field-label" for="issues">Issues count:</label>
370
              <select id="issues_op" name="issues_op">
371
                <option value=">">&gt;</option>
372
                <option value="<">&lt;</option>
373
                <option value="=">=</option>
374
                <option value="!=">!=</option>
375
              </select>
376
              <input type="text" name="issues" />
377
            </div>
378
            <div class="form-field">
379
              <label class="form-field-label" for="datelastborrowed">Last issue date:</label>
380
              <select id="datelastborrowed_op" name="datelastborrowed_op">
381
                <option value=">">After</option>
382
                <option value="<">Before</option>
383
                <option value="=">On</option>
384
              </select>
385
              <input type="text" name="datelastborrowed" />
386
              <span class="hint">ISO Format (AAAA-MM-DD)</span>
387
            </div>
388
          </fieldset>
389
          <fieldset>
390
            <div class="form-field-radio">
391
              <label>Output:</label>
392
              <input type="radio" id="format-html" name="format" value="html" checked="checked" /> <label for="format-html">Screen</label>
393
              <input type="radio" id="format-csv" name="format" value="csv" /> <label for="format-csv">CSV</label>
394
            </div>
395
            <div class="form-actions">
396
              <input type="submit" value="Search" />
397
            </div>
398
          </fieldset>
399
        </fieldset>
400
      </form>
401
402
      <p><a id="editsearchlink" href="#" style="display:none">Edit search</a></p>
403
404
      <div id="results-wrapper">
405
        [% IF search_done %]
406
407
          [% IF total_rows > 0 %]
408
            <p>Found [% total_rows %] results.</p>
409
          [% ELSE %]
410
            <p>No results found.</p>
411
          [% END %]
412
413
          [% IF results %]
414
            [% INCLUDE 'catalogue/itemsearch_items.inc' items = results %]
415
          [% END %]
416
417
          <div id="pagination-bar">
418
            [% pagination_bar %]
419
          </div>
420
421
        [% END %]
422
      </div>
423
    </div>
424
425
    [% INCLUDE 'intranet-bottom.inc' %]

Return to bug 11425