Bugzilla – Attachment 26555 Details for
Bug 11425
Search form for items
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 11425: Add item search form in staff interface
Bug-11425-Add-item-search-form-in-staff-interface.patch (text/plain), 67.41 KB, created by
Julian Maurice
on 2014-03-25 10:47:12 UTC
(
hide
)
Description:
Bug 11425: Add item search form in staff interface
Filename:
MIME Type:
Creator:
Julian Maurice
Created:
2014-03-25 10:47:12 UTC
Size:
67.41 KB
patch
obsolete
>From 77a917fabba9787d18a50d2dfba258de70f89535 Mon Sep 17 00:00:00 2001 >From: Julian Maurice <julian.maurice@biblibre.com> >Date: Thu, 4 Jul 2013 16:52:58 +0200 >Subject: [PATCH] Bug 11425: Add item search form in staff interface > >Item search is available at catalogue/itemsearch.pl (link is in >catalogue/search.pl) >It only uses SQL (not Zebra) >* Use DataTables and server-side processing to be able to filter on > individual columns after the first search is done. >* Allow to export results in CSV >* With Javascript disabled, search form still works (and CSV export too) > >There is the possibility to define "Custom search fields" in a new admin >page admin/items_search_fields.pl (link is in admin/admin-home.pl) >A custom item search field is defined by: >* a name: its unique identifier >* a label: the text displayed to the user >* a MARC field/subfield: the field/subfield to query (it uses > ExtractValue) >* an authorised values list (optional): if defined the list is displayed > in the search form > >New Perl dependency: Template::Plugin::JSON::Escape > >Test plan: >1/ Apply the patch and run updatedatabase.pl >2/ Go to advanced search (staff interface), then click on "Go to item >search" >3/ Play with the search form! :) >In the 3rd fieldset you can add as many fields as you want and combine them with >boolean operators (AND, OR). You can use SQL jokers characters (%, _) >You can output to screen (in a DataTables table) or to a CSV file. >4/ In the DataTables table, play with filters and try sorting columns. >5/ Disable Javascript (with Firefox: extensions NoScript or YesScript, >or in about:config 'javascript.enabled' = false >6/ Reload the search page and do some searches on screen output. (there >is no sorting or filtering features, but there is still pagination) >7/ Try again CSV output. >8/ You can re-enable Javascript. >9/ Go to Administration > Items search fields >10/ Add a new field. Example for title (in UNIMARC): > Name: title > Label: Title > MARC field: 200 > MARC subfield: a > Authorised values category: None >(add another field with an authorised values category to see the >difference). >11/ As you are there try to update and delete some fields. >12/ Go back to items search form. You can see in the 3rd fieldset that >your fields have appeared in the selects. >13/ Try searching on them. >14/ I think you're done :) >--- > C4/Installer/PerlDependencies.pm | 5 + > C4/Items.pm | 197 ++++++++- > C4/SQLHelper.pm | 33 ++ > Koha/Item/Search/Field.pm | 107 +++++ > acqui/check_uniqueness.pl | 2 +- > admin/items_search_field.pl | 63 +++ > admin/items_search_fields.pl | 81 ++++ > catalogue/itemsearch.pl | 298 ++++++++++++++ > installer/data/mysql/kohastructure.sql | 17 + > installer/data/mysql/updatedatabase.pl | 20 + > .../intranet-tmpl/prog/en/css/itemsearchform.css | 44 ++ > .../en/includes/admin-items-search-field-form.inc | 58 +++ > .../intranet-tmpl/prog/en/includes/admin-menu.inc | 1 + > .../en/includes/catalogue/itemsearch_item.csv.inc | 4 + > .../prog/en/includes/catalogue/itemsearch_item.inc | 23 ++ > .../en/includes/catalogue/itemsearch_item.json.inc | 18 + > .../en/includes/catalogue/itemsearch_items.inc | 49 +++ > .../prog/en/modules/admin/admin-home.tt | 2 + > .../prog/en/modules/admin/items_search_field.tt | 41 ++ > .../prog/en/modules/admin/items_search_fields.tt | 95 +++++ > .../prog/en/modules/catalogue/advsearch.tt | 1 + > .../prog/en/modules/catalogue/itemsearch.csv.tt | 4 + > .../prog/en/modules/catalogue/itemsearch.json.tt | 12 + > .../prog/en/modules/catalogue/itemsearch.tt | 425 ++++++++++++++++++++ > 24 files changed, 1579 insertions(+), 21 deletions(-) > create mode 100644 Koha/Item/Search/Field.pm > create mode 100755 admin/items_search_field.pl > create mode 100755 admin/items_search_fields.pl > create mode 100755 catalogue/itemsearch.pl > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/css/itemsearchform.css > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/admin-items-search-field-form.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_items.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_field.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_fields.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.csv.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.json.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt > >diff --git a/C4/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm >index 2bea1c6..29a84e4 100644 >--- a/C4/Installer/PerlDependencies.pm >+++ b/C4/Installer/PerlDependencies.pm >@@ -539,6 +539,11 @@ our $PERL_DEPS = { > 'required' => '1', > 'min_ver' => '0.03', > }, >+ 'Template::Plugin::JSON::Escape' => { >+ 'usage' => 'Core', >+ 'required' => '1', >+ 'min_ver' => '0.02', >+ }, > 'Data::Paginator' => { > 'usage' => 'Core', > 'required' => '0', >diff --git a/C4/Items.pm b/C4/Items.pm >index 12dce85..adafe47 100644 >--- a/C4/Items.pm >+++ b/C4/Items.pm >@@ -35,6 +35,7 @@ use DateTime::Format::MySQL; > use Data::Dumper; # used as part of logging item record changes, not just for > # debugging; so please don't remove this > use Koha::DateUtils qw/dt_from_string/; >+use C4::SQLHelper qw(GetColumns); > > use vars qw($VERSION @ISA @EXPORT); > >@@ -85,6 +86,7 @@ BEGIN { > GetAnalyticsCount > GetItemHolds > >+ SearchItemsByField > SearchItems > > PrepareItemrecordDisplay >@@ -2549,39 +2551,194 @@ sub GetItemHolds { > return $holds; > } > >-# Return the list of the column names of items table >-sub _get_items_columns { >- my $dbh = C4::Context->dbh; >- my $sth = $dbh->column_info(undef, undef, 'items', '%'); >- $sth->execute; >- my $results = $sth->fetchall_hashref('COLUMN_NAME'); >- return keys %$results; >+=head2 SearchItemsByField >+ >+ my $items = SearchItemsByField($field, $value); >+ >+SearchItemsByField will search for items on a specific given field. >+For instance you can search all items with a specific stocknumber like this: >+ >+ my $items = SearchItemsByField('stocknumber', $stocknumber); >+ >+=cut >+ >+sub SearchItemsByField { >+ my ($field, $value) = @_; >+ >+ my $filters = [ { >+ field => $field, >+ query => $value, >+ } ]; >+ >+ my ($results) = SearchItems($filters); >+ return $results; >+} >+ >+sub _SearchItems_build_where_fragment { >+ my ($filter) = @_; >+ >+ my $where_fragment; >+ if (exists($filter->{conjunction})) { >+ my (@where_strs, @where_args); >+ foreach my $f (@{ $filter->{filters} }) { >+ my $fragment = _SearchItems_build_where_fragment($f); >+ if ($fragment) { >+ push @where_strs, $fragment->{str}; >+ push @where_args, @{ $fragment->{args} }; >+ } >+ } >+ my $where_str = ''; >+ if (@where_strs) { >+ $where_str = '(' . join (' ' . $filter->{conjunction} . ' ', @where_strs) . ')'; >+ $where_fragment = { >+ str => $where_str, >+ args => \@where_args, >+ }; >+ } >+ } else { >+ my @columns = GetColumns('items'); >+ push @columns, GetColumns('biblio'); >+ push @columns, GetColumns('biblioitems'); >+ my @operators = qw(= != > < >= <= like); >+ my $field = $filter->{field}; >+ if ( (0 < grep /^$field$/, @columns) or (substr($field, 0, 5) eq 'marc:') ) { >+ my $op = $filter->{operator}; >+ my $query = $filter->{query}; >+ >+ if (!$op or (0 == grep /^$op$/, @operators)) { >+ $op = '='; # default operator >+ } >+ >+ my $column; >+ if ($field =~ /^marc:(\d{3})(?:\$(\w))?$/) { >+ my $marcfield = $1; >+ my $marcsubfield = $2; >+ my $xpath; >+ if ($marcfield < 10) { >+ $xpath = "//record/controlfield[\@tag=\"$marcfield\"]"; >+ } else { >+ $xpath = "//record/datafield[\@tag=\"$marcfield\"]/subfield[\@code=\"$marcsubfield\"]"; >+ } >+ $column = "ExtractValue(marcxml, '$xpath')"; >+ } else { >+ $column = $field; >+ } >+ >+ if (ref $query eq 'ARRAY') { >+ if ($op eq '=') { >+ $op = 'IN'; >+ } elsif ($op eq '!=') { >+ $op = 'NOT IN'; >+ } >+ $where_fragment = { >+ str => "$column $op (" . join (',', ('?') x @$query) . ")", >+ args => $query, >+ }; >+ } else { >+ $where_fragment = { >+ str => "$column $op ?", >+ args => [ $query ], >+ }; >+ } >+ } >+ } >+ >+ return $where_fragment; > } > > =head2 SearchItems > >- my $items = SearchItems($field, $value); >+ my ($items, $total) = SearchItemsByField($filters, $params); > >-SearchItems will search for items on a specific given field. >-For instance you can search all items with a specific stocknumber like this: >+Perform a search among items > >- my $items = SearchItems('stocknumber', $stocknumber); >+$filters is a reference to an array of filters, where each filter is a hash with >+the following keys: >+ >+=over 2 >+ >+=item * field: the name of a SQL column in table items >+ >+=item * query: the value to search in this column >+ >+=item * operator: comparison operator. Can be one of = != > < >= <= like >+ >+=back >+ >+A logical AND is used to combine filters. >+ >+$params is a reference to a hash that can contain the following parameters: >+ >+=over 2 >+ >+=item * rows: Number of items to return. 0 returns everything (default: 0) >+ >+=item * page: Page to return (return items from (page-1)*rows to (page*rows)-1) >+ (default: 1) >+ >+=item * sortby: A SQL column name in items table to sort on >+ >+=item * sortorder: 'ASC' or 'DESC' >+ >+=back > > =cut > > sub SearchItems { >- my ($field, $value) = @_; >+ my ($filter, $params) = @_; >+ >+ $filter //= {}; >+ $params //= {}; >+ return unless ref $filter eq 'HASH'; >+ return unless ref $params eq 'HASH'; >+ >+ # Default parameters >+ $params->{rows} ||= 0; >+ $params->{page} ||= 1; >+ $params->{sortby} ||= 'itemnumber'; >+ $params->{sortorder} ||= 'ASC'; >+ >+ my ($where_str, @where_args); >+ my $where_fragment = _SearchItems_build_where_fragment($filter); >+ if ($where_fragment) { >+ $where_str = $where_fragment->{str}; >+ @where_args = @{ $where_fragment->{args} }; >+ } > > my $dbh = C4::Context->dbh; >- my @columns = _get_items_columns; >- my $results = []; >- if(0 < grep /^$field$/, @columns) { >- my $query = "SELECT $field FROM items WHERE $field = ?"; >- my $sth = $dbh->prepare( $query ); >- $sth->execute( $value ); >- $results = $sth->fetchall_arrayref({}); >+ my $query = q{ >+ SELECT SQL_CALC_FOUND_ROWS items.* >+ FROM items >+ LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber >+ LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber >+ }; >+ if (defined $where_str and $where_str ne '') { >+ $query .= qq{ WHERE $where_str }; > } >- return $results; >+ >+ my @columns = GetColumns('items'); >+ push @columns, GetColumns('biblio'); >+ push @columns, GetColumns('biblioitems'); >+ my $sortby = (0 < grep {$params->{sortby} eq $_} @columns) >+ ? $params->{sortby} : 'itemnumber'; >+ my $sortorder = (uc($params->{sortorder}) eq 'ASC') ? 'ASC' : 'DESC'; >+ $query .= qq{ ORDER BY $sortby $sortorder }; >+ >+ my $rows = $params->{rows}; >+ my @limit_args; >+ if ($rows > 0) { >+ my $offset = $rows * ($params->{page}-1); >+ $query .= qq { LIMIT ?, ? }; >+ push @limit_args, $offset, $rows; >+ } >+ >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute(@where_args, @limit_args); >+ >+ return unless ($rv); >+ my ($total_rows) = $dbh->selectrow_array(q{ SELECT FOUND_ROWS() }); >+ >+ return ($sth->fetchall_arrayref({}), $total_rows); > } > > >diff --git a/C4/SQLHelper.pm b/C4/SQLHelper.pm >index b867094..55204eb 100644 >--- a/C4/SQLHelper.pm >+++ b/C4/SQLHelper.pm >@@ -56,6 +56,7 @@ BEGIN { > UpdateInTable > GetPrimaryKeys > clear_columns_cache >+ GetColumns > ); > %EXPORT_TAGS = ( all =>[qw( InsertInTable DeleteInTable SearchInTable UpdateInTable GetPrimaryKeys)] > ); >@@ -296,6 +297,38 @@ sub _get_columns { > return $hashref->{$tablename}; > } > >+=head2 GetColumns >+ >+ my @columns = GetColumns($tablename); >+ >+Given a tablename, returns an array of columns names. >+ >+=cut >+ >+sub GetColumns { >+ my ($tablename) = @_; >+ >+ return unless $tablename; >+ return unless $tablename =~ /^[a-zA-Z0-9_]+$/; >+ >+ # Get the database handle. >+ my $dbh = C4::Context->dbh; >+ >+ # Pure ANSI SQL goodness. >+ my $sql = "SELECT * FROM $tablename WHERE 1=0;"; >+ >+ # Run the SQL statement to load STH's readonly properties. >+ my $sth = $dbh->prepare($sql); >+ my $rv = $sth->execute(); >+ >+ my @data; >+ if ($rv) { >+ @data = @{$sth->{NAME}}; >+ } >+ >+ return @data; >+} >+ > =head2 _filter_columns > > =over 4 >diff --git a/Koha/Item/Search/Field.pm b/Koha/Item/Search/Field.pm >new file mode 100644 >index 0000000..6b1e137 >--- /dev/null >+++ b/Koha/Item/Search/Field.pm >@@ -0,0 +1,107 @@ >+package Koha::Item::Search::Field; >+ >+use Modern::Perl; >+use base qw( Exporter ); >+ >+our @EXPORT_OK = qw( >+ AddItemSearchField >+ ModItemSearchField >+ DelItemSearchField >+ GetItemSearchField >+ GetItemSearchFields >+); >+ >+use C4::Context; >+ >+sub AddItemSearchField { >+ my ($field) = @_; >+ >+ my ( $name, $label, $tagfield, $tagsubfield, $av_category ) = >+ @$field{qw(name label tagfield tagsubfield authorised_values_category)}; >+ >+ my $dbh = C4::Context->dbh; >+ my $query = q{ >+ INSERT INTO items_search_fields (name, label, tagfield, tagsubfield, authorised_values_category) >+ VALUES (?, ?, ?, ?, ?) >+ }; >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute($name, $label, $tagfield, $tagsubfield, $av_category); >+ >+ return ($rv) ? $field : undef; >+} >+ >+sub ModItemSearchField { >+ my ($field) = @_; >+ >+ my ( $name, $label, $tagfield, $tagsubfield, $av_category ) = >+ @$field{qw(name label tagfield tagsubfield authorised_values_category)}; >+ >+ my $dbh = C4::Context->dbh; >+ my $query = q{ >+ UPDATE items_search_fields >+ SET label = ?, >+ tagfield = ?, >+ tagsubfield = ?, >+ authorised_values_category = ? >+ WHERE name = ? >+ }; >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute($label, $tagfield, $tagsubfield, $av_category, $name); >+ >+ return ($rv) ? $field : undef; >+} >+ >+sub DelItemSearchField { >+ my ($name) = @_; >+ >+ my $dbh = C4::Context->dbh; >+ my $query = q{ >+ DELETE FROM items_search_fields >+ WHERE name = ? >+ }; >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute($name); >+ >+ my $is_deleted = $rv ? int($rv) : 0; >+ if (!$is_deleted) { >+ warn "DelItemSearchField: Field '$name' doesn't exist"; >+ } >+ >+ return $is_deleted; >+} >+ >+sub GetItemSearchField { >+ my ($name) = @_; >+ >+ my $dbh = C4::Context->dbh; >+ my $query = q{ >+ SELECT * FROM items_search_fields >+ WHERE name = ? >+ }; >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute($name); >+ >+ my $field; >+ if ($rv) { >+ $field = $sth->fetchrow_hashref; >+ } >+ >+ return $field; >+} >+ >+sub GetItemSearchFields { >+ my $dbh = C4::Context->dbh; >+ my $query = q{ >+ SELECT * FROM items_search_fields >+ }; >+ my $sth = $dbh->prepare($query); >+ my $rv = $sth->execute(); >+ >+ my @fields; >+ if ($rv) { >+ my $fields = $sth->fetchall_arrayref( {} ); >+ @fields = @$fields; >+ } >+ >+ return @fields; >+} >diff --git a/acqui/check_uniqueness.pl b/acqui/check_uniqueness.pl >index 8deb195..d8f29fa 100755 >--- a/acqui/check_uniqueness.pl >+++ b/acqui/check_uniqueness.pl >@@ -43,7 +43,7 @@ my @value = $input->param('value[]'); > my $r = {}; > my $i = 0; > for ( my $i=0; $i<@field; $i++ ) { >- my $items = C4::Items::SearchItems($field[$i], $value[$i]); >+ my $items = C4::Items::SearchItemsByField($field[$i], $value[$i]); > > if ( @$items ) { > push @{ $r->{$field[$i]} }, $value[$i]; >diff --git a/admin/items_search_field.pl b/admin/items_search_field.pl >new file mode 100755 >index 0000000..a544959 >--- /dev/null >+++ b/admin/items_search_field.pl >@@ -0,0 +1,63 @@ >+#!/usr/bin/perl >+# Copyright 2013 BibLibre >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use CGI; >+ >+use C4::Auth; >+use C4::Output; >+use C4::Koha; >+ >+use Koha::Item::Search::Field qw(GetItemSearchField ModItemSearchField); >+ >+my $cgi = new CGI; >+ >+my ($template, $borrowernumber, $cookie) = get_template_and_user({ >+ template_name => 'admin/items_search_field.tt', >+ query => $cgi, >+ type => 'intranet', >+ authnotrequired => 0, >+ flagsrequired => { catalogue => 1 }, >+}); >+ >+my $op = $cgi->param('op') || ''; >+my $name = $cgi->param('name'); >+ >+if ($op eq 'mod') { >+ my %vars = $cgi->Vars; >+ my $field = { name => $name }; >+ my @params = qw(label tagfield tagsubfield authorised_values_category); >+ @$field{@params} = @vars{@params}; >+ if ( $field->{authorised_values_category} eq '' ) { >+ $field->{authorised_values_category} = undef; >+ } >+ $field = ModItemSearchField($field); >+ my $updated = ($field) ? 1 : 0; >+ print $cgi->redirect('/cgi-bin/koha/admin/items_search_fields.pl?updated=' . $updated); >+ exit; >+} >+ >+my $field = GetItemSearchField($name); >+my $authorised_values_categories = C4::Koha::GetAuthorisedValueCategories(); >+ >+$template->param( >+ field => $field, >+ authorised_values_categories => $authorised_values_categories, >+); >+ >+output_html_with_http_headers $cgi, $cookie, $template->output; >diff --git a/admin/items_search_fields.pl b/admin/items_search_fields.pl >new file mode 100755 >index 0000000..1ebf243 >--- /dev/null >+++ b/admin/items_search_fields.pl >@@ -0,0 +1,81 @@ >+#!/usr/bin/perl >+# Copyright 2013 BibLibre >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use CGI; >+ >+use C4::Auth; >+use C4::Output; >+use C4::Koha; >+ >+use Koha::Item::Search::Field qw(AddItemSearchField GetItemSearchFields DelItemSearchField); >+ >+my $cgi = new CGI; >+ >+my ($template, $borrowernumber, $cookie) = get_template_and_user({ >+ template_name => 'admin/items_search_fields.tt', >+ query => $cgi, >+ type => 'intranet', >+ authnotrequired => 0, >+ flagsrequired => { catalogue => 1 }, >+}); >+ >+my $op = $cgi->param('op') || ''; >+ >+if ($op eq 'add') { >+ my %vars = $cgi->Vars; >+ my $field; >+ my @params = qw(name label tagfield tagsubfield authorised_values_category); >+ @$field{@params} = @vars{@params}; >+ if ( $field->{authorised_values_category} eq '' ) { >+ $field->{authorised_values_category} = undef; >+ } >+ $field = AddItemSearchField($field); >+ if ($field) { >+ $template->param(field_added => $field); >+ } else { >+ $template->param(field_not_added => 1); >+ } >+} elsif ($op eq 'del') { >+ my $name = $cgi->param('name'); >+ my $rv = DelItemSearchField($name); >+ if ($rv) { >+ $template->param(field_deleted => 1); >+ } else { >+ $template->param(field_not_deleted => 1); >+ } >+} else { >+ my $updated = $cgi->param('updated'); >+ if (defined $updated) { >+ if ($updated) { >+ $template->param(field_updated => 1); >+ } else { >+ $template->param(field_not_updated => 1); >+ } >+ } >+} >+ >+my @fields = GetItemSearchFields(); >+my $authorised_values_categories = C4::Koha::GetAuthorisedValueCategories(); >+ >+$template->param( >+ fields => \@fields, >+ authorised_values_categories => $authorised_values_categories, >+); >+ >+output_html_with_http_headers $cgi, $cookie, $template->output; >diff --git a/catalogue/itemsearch.pl b/catalogue/itemsearch.pl >new file mode 100755 >index 0000000..cb025a5 >--- /dev/null >+++ b/catalogue/itemsearch.pl >@@ -0,0 +1,298 @@ >+#!/usr/bin/perl >+# Copyright 2013 BibLibre >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use CGI; >+ >+use JSON; >+ >+use C4::Auth; >+use C4::Output; >+use C4::Items; >+use C4::Biblio; >+use C4::Branch; >+use C4::Koha; >+use C4::ItemType; >+ >+use Koha::Item::Search::Field qw(GetItemSearchFields); >+ >+my $cgi = new CGI; >+my %params = $cgi->Vars; >+ >+my $format = $cgi->param('format'); >+my ($template_name, $content_type); >+if (defined $format and $format eq 'json') { >+ $template_name = 'catalogue/itemsearch.json.tt'; >+ $content_type = 'json'; >+ >+ # Map DataTables parameters with 'regular' parameters >+ $cgi->param('rows', $cgi->param('iDisplayLength')); >+ $cgi->param('page', ($cgi->param('iDisplayStart') / $cgi->param('iDisplayLength')) + 1); >+ my @columns = split /,/, $cgi->param('sColumns'); >+ $cgi->param('sortby', $columns[ $cgi->param('iSortCol_0') ]); >+ $cgi->param('sortorder', $cgi->param('sSortDir_0')); >+ >+ my @f = $cgi->param('f'); >+ my @q = $cgi->param('q'); >+ push @q, '' if @q == 0; >+ my @op = $cgi->param('op'); >+ my @c = $cgi->param('c'); >+ foreach my $i (0 .. ($cgi->param('iColumns') - 1)) { >+ my $sSearch = $cgi->param("sSearch_$i"); >+ if ($sSearch) { >+ my @words = split /\s+/, $sSearch; >+ foreach my $word (@words) { >+ push @f, $columns[$i]; >+ push @q, "%$word%"; >+ push @op, 'like'; >+ push @c, 'and'; >+ } >+ } >+ } >+ $cgi->param('f', @f); >+ $cgi->param('q', @q); >+ $cgi->param('op', @op); >+ $cgi->param('c', @c); >+} elsif (defined $format and $format eq 'csv') { >+ $template_name = 'catalogue/itemsearch.csv.tt'; >+ >+ # Retrieve all results >+ $cgi->param('rows', 0); >+} else { >+ $format = 'html'; >+ $template_name = 'catalogue/itemsearch.tt'; >+ $content_type = 'html'; >+} >+ >+my ($template, $borrowernumber, $cookie) = get_template_and_user({ >+ template_name => $template_name, >+ query => $cgi, >+ type => 'intranet', >+ authnotrequired => 0, >+ flagsrequired => { catalogue => 1 }, >+}); >+ >+my $notforloan_avcode = GetAuthValCode('items.notforloan'); >+my $notforloan_values = GetAuthorisedValues($notforloan_avcode); >+ >+if (scalar keys %params > 0) { >+ # Parameters given, it's a search >+ >+ my $filter = { >+ conjunction => 'AND', >+ filters => [], >+ }; >+ >+ foreach my $p (qw(homebranch location itype ccode issues datelastborrowed)) { >+ if (my @q = $cgi->param($p)) { >+ if ($q[0] ne '') { >+ my $f = { >+ field => $p, >+ query => \@q, >+ }; >+ if (my $op = $cgi->param($p . '_op')) { >+ $f->{operator} = $op; >+ } >+ push @{ $filter->{filters} }, $f; >+ } >+ } >+ } >+ >+ my @c = $cgi->param('c'); >+ my @fields = $cgi->param('f'); >+ my @q = $cgi->param('q'); >+ my @op = $cgi->param('op'); >+ >+ my $f; >+ for (my $i = 0; $i < @fields; $i++) { >+ my $field = $fields[$i]; >+ my $q = shift @q; >+ my $op = shift @op; >+ if (defined $q and $q ne '') { >+ if ($i == 0) { >+ $f = { >+ field => $field, >+ query => $q, >+ operator => $op, >+ }; >+ } else { >+ my $c = shift @c; >+ $f = { >+ conjunction => $c, >+ filters => [ >+ $f, { >+ field => $field, >+ query => $q, >+ operator => $op, >+ } >+ ], >+ }; >+ } >+ } >+ } >+ push @{ $filter->{filters} }, $f; >+ >+ # Yes/No parameters >+ foreach my $p (qw(damaged itemlost)) { >+ my $v = $cgi->param($p) // ''; >+ my $f = { >+ field => $p, >+ query => 0, >+ }; >+ if ($v eq 'yes') { >+ $f->{operator} = '!='; >+ push @{ $filter->{filters} }, $f; >+ } elsif ($v eq 'no') { >+ $f->{operator} = '='; >+ push @{ $filter->{filters} }, $f; >+ } >+ } >+ >+ if (my $itemcallnumber_from = $cgi->param('itemcallnumber_from')) { >+ push @{ $filter->{filters} }, { >+ field => 'itemcallnumber', >+ query => $itemcallnumber_from, >+ operator => '>=', >+ }; >+ } >+ if (my $itemcallnumber_to = $cgi->param('itemcallnumber_to')) { >+ push @{ $filter->{filters} }, { >+ field => 'itemcallnumber', >+ query => $itemcallnumber_to, >+ operator => '<=', >+ }; >+ } >+ >+ my $search_params = { >+ rows => $cgi->param('rows') // 20, >+ page => $cgi->param('page') || 1, >+ sortby => $cgi->param('sortby') || 'itemnumber', >+ sortorder => $cgi->param('sortorder') || 'asc', >+ }; >+ >+ my ($results, $total_rows) = SearchItems($filter, $search_params); >+ if ($results) { >+ # Get notforloan labels >+ my $notforloan_map = {}; >+ foreach my $nfl_value (@$notforloan_values) { >+ $notforloan_map->{$nfl_value->{authorised_value}} = $nfl_value->{lib}; >+ } >+ >+ foreach my $item (@$results) { >+ $item->{biblio} = GetBiblio($item->{biblionumber}); >+ ($item->{biblioitem}) = GetBiblioItemByBiblioNumber($item->{biblionumber}); >+ $item->{status} = $notforloan_map->{$item->{notforloan}}; >+ } >+ } >+ >+ $template->param( >+ filter => $filter, >+ search_params => $search_params, >+ results => $results, >+ total_rows => $total_rows, >+ search_done => 1, >+ ); >+ >+ if ($format eq 'html') { >+ # Build pagination bar >+ my $url = $cgi->url(-absolute => 1); >+ my @params; >+ foreach my $p (keys %params) { >+ my @v = $cgi->param($p); >+ push @params, map { "$p=" . $_ } @v; >+ } >+ $url .= '?' . join ('&', @params); >+ my $nb_pages = 1 + int($total_rows / $search_params->{rows}); >+ my $current_page = $search_params->{page}; >+ my $pagination_bar = pagination_bar($url, $nb_pages, $current_page, 'page'); >+ >+ $template->param(pagination_bar => $pagination_bar); >+ } >+} >+ >+if ($format eq 'html') { >+ # Retrieve data required for the form. >+ >+ my $branches = GetBranches(); >+ my @branches; >+ foreach my $branchcode (keys %$branches) { >+ push @branches, { >+ value => $branchcode, >+ label => $branches->{$branchcode}->{branchname}, >+ }; >+ } >+ my $locations = GetAuthorisedValues('LOC'); >+ my @locations; >+ foreach my $location (@$locations) { >+ push @locations, { >+ value => $location->{authorised_value}, >+ label => $location->{lib}, >+ }; >+ } >+ my @itemtypes = C4::ItemType->all(); >+ foreach my $itemtype (@itemtypes) { >+ $itemtype->{value} = $itemtype->{itemtype}; >+ $itemtype->{label} = $itemtype->{description}; >+ } >+ my $ccode_avcode = GetAuthValCode('items.ccode') || 'CCODE'; >+ my $ccodes = GetAuthorisedValues($ccode_avcode); >+ my @ccodes; >+ foreach my $ccode (@$ccodes) { >+ push @ccodes, { >+ value => $ccode->{authorised_value}, >+ label => $ccode->{lib}, >+ }; >+ } >+ >+ my @notforloans; >+ foreach my $value (@$notforloan_values) { >+ push @notforloans, { >+ value => $value->{authorised_value}, >+ label => $value->{lib}, >+ }; >+ } >+ >+ my @items_search_fields = GetItemSearchFields(); >+ >+ my $authorised_values = {}; >+ foreach my $field (@items_search_fields) { >+ if (my $category = ($field->{authorised_values_category})) { >+ $authorised_values->{$category} = GetAuthorisedValues($category); >+ } >+ } >+ >+ $template->param( >+ branches => \@branches, >+ locations => \@locations, >+ itemtypes => \@itemtypes, >+ ccodes => \@ccodes, >+ notforloans => \@notforloans, >+ items_search_fields => \@items_search_fields, >+ authorised_values_json => to_json($authorised_values), >+ ); >+} >+ >+if ($format eq 'csv') { >+ print $cgi->header({ >+ type => 'text/csv', >+ attachment => 'items.csv', >+ }); >+ print $template->output; >+} else { >+ output_with_http_headers $cgi, $cookie, $template->output, $content_type; >+} >diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql >index 3cf9c65..3a2db23 100644 >--- a/installer/data/mysql/kohastructure.sql >+++ b/installer/data/mysql/kohastructure.sql >@@ -3374,6 +3374,23 @@ CREATE TABLE IF NOT EXISTS marc_modification_template_actions ( > CONSTRAINT `mmta_ibfk_1` FOREIGN KEY (`template_id`) REFERENCES `marc_modification_templates` (`template_id`) ON DELETE CASCADE ON UPDATE CASCADE > ) ENGINE=InnoDB DEFAULT CHARSET=utf8; > >+-- >+-- Table structure for table 'items_search_fields' >+-- >+ >+DROP TABLE IF EXISTS items_search_fields; >+CREATE TABLE items_search_fields ( >+ name VARCHAR(255) NOT NULL, >+ label VARCHAR(255) NOT NULL, >+ tagfield CHAR(3) NOT NULL, >+ tagsubfield CHAR(1) NULL DEFAULT NULL, >+ authorised_values_category VARCHAR(16) NULL DEFAULT NULL, >+ PRIMARY KEY(name), >+ CONSTRAINT items_search_fields_authorised_values_category >+ FOREIGN KEY (authorised_values_category) REFERENCES authorised_values (category) >+ ON DELETE SET NULL ON UPDATE CASCADE >+) ENGINE=InnoDB DEFAULT CHARSET=utf8; >+ > /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; > /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; > /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; >diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl >index 062ecc1..c0cf33b 100755 >--- a/installer/data/mysql/updatedatabase.pl >+++ b/installer/data/mysql/updatedatabase.pl >@@ -8083,6 +8083,26 @@ if ( CheckVersion($DBversion) ) { > SetVersion($DBversion); > } > >+$DBversion = "XXX"; >+if ( CheckVersion($DBversion) ) { >+ $dbh->do(q{ >+ CREATE TABLE IF NOT EXISTS items_search_fields ( >+ name VARCHAR(255) NOT NULL, >+ label VARCHAR(255) NOT NULL, >+ tagfield CHAR(3) NOT NULL, >+ tagsubfield CHAR(1) NULL DEFAULT NULL, >+ authorised_values_category VARCHAR(16) NULL DEFAULT NULL, >+ PRIMARY KEY(name), >+ CONSTRAINT items_search_fields_authorised_values_category >+ FOREIGN KEY (authorised_values_category) REFERENCES authorised_values (category) >+ ON DELETE SET NULL ON UPDATE CASCADE >+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8; >+ }); >+ print "Upgrade to $DBversion done (Bug 11425: Add items_search_fields table)\n"; >+ SetVersion($DBversion); >+} >+ >+ > =head1 FUNCTIONS > > =head2 TableExists($table) >diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/itemsearchform.css b/koha-tmpl/intranet-tmpl/prog/en/css/itemsearchform.css >new file mode 100644 >index 0000000..34c76de >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/css/itemsearchform.css >@@ -0,0 +1,44 @@ >+.form-field { >+ margin: 5px 0; >+} >+ >+.form-field > * { >+ vertical-align: middle; >+} >+ >+.form-field-label { >+ display: inline-block; >+ text-align: right; >+ width: 10em; >+} >+ >+.form-field-conjunction[disabled] { >+ visibility: hidden; >+} >+ >+.form-field-radio > * { >+ vertical-align: middle; >+} >+ >+.form-actions { >+ margin-top: 20px; >+} >+ >+th.active { >+ padding-right: 21px; >+ background-repeat: no-repeat; >+ background-position: 100% 50%; >+} >+ >+th.sort-asc { >+ background-image: url('../../img/asc.gif'); >+} >+ >+th.sort-desc { >+ background-image: url('../../img/desc.gif'); >+} >+ >+th select { >+ width: 100%; >+ font-weight: normal; >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-items-search-field-form.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-items-search-field-form.inc >new file mode 100644 >index 0000000..995e1f6 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-items-search-field-form.inc >@@ -0,0 +1,58 @@ >+<ul> >+ <li> >+ <label for="name">Name</label> >+ [% IF field %] >+ <input type="text" name="name" value="[% field.name %]" disabled="disabled"> >+ <input type="hidden" name="name" value="[% field.name %]"> >+ [% ELSE %] >+ <input type="text" name="name" /> >+ [% END %] >+ </li> >+ <li> >+ <label for="label">Label</label> >+ [% IF field %] >+ <input type="text" name="label" value="[% field.label %]" /> >+ [% ELSE %] >+ <input type="text" name="label" /> >+ [% END %] >+ </li> >+ <li> >+ <label for="tagfield">MARC field</label> >+ <select name="tagfield"> >+ [% FOREACH tagfield IN ['001'..'999'] %] >+ [% IF field && field.tagfield == tagfield %] >+ <option value="[% tagfield %]" selected="selected">[% tagfield %]</option> >+ [% ELSE %] >+ <option value="[% tagfield %]">[% tagfield %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="tagsubfield">MARC subfield</label> >+ <select name="tagsubfield"> >+ [% codes = [''] %] >+ [% codes = codes.merge([0..9], ['a'..'z']) %] >+ [% FOREACH tagsubfield IN codes %] >+ [% IF field && field.tagsubfield == tagsubfield %] >+ <option value="[% tagsubfield %]" selected="selected">[% tagsubfield %]</option> >+ [% ELSE %] >+ <option value="[% tagsubfield %]">[% tagsubfield %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="authorised_values_category">Authorised values category</label> >+ <select name="authorised_values_category"> >+ <option value="">- None -</option> >+ [% FOREACH category IN authorised_values_categories %] >+ [% IF field && field.authorised_values_category == category %] >+ <option value="[% category %]" selected="selected">[% category %]</option> >+ [% ELSE %] >+ <option value="[% category %]">[% category %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+</ul> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc >index ebd345c..2ca1d44 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc >@@ -45,6 +45,7 @@ > <li><a href="/cgi-bin/koha/admin/classsources.pl">Classification sources</a></li> > <li><a href="/cgi-bin/koha/admin/matching-rules.pl">Record matching rules</a></li> > <li><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></li> >+ <li><a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a></li> > </ul> > > <h5>Acquisition parameters</h5> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc >new file mode 100644 >index 0000000..fd6e835 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc >@@ -0,0 +1,4 @@ >+[%- USE Branches -%] >+[%- biblio = item.biblio -%] >+[%- biblioitem = item.biblioitem -%] >+"[% 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 %]" >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.inc >new file mode 100644 >index 0000000..bb5bd41 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.inc >@@ -0,0 +1,23 @@ >+[%- USE Branches -%] >+[% biblio = item.biblio %] >+[% biblioitem = item.biblioitem %] >+<tr> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber %]" title="Go to record detail page">[% biblio.title %]</a> >+ by [% biblio.author %] >+ </td> >+ <td>[% biblioitem.publicationyear %]</td> >+ <td>[% biblioitem.publishercode %]</td> >+ <td>[% biblioitem.collectiontitle %]</td> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% biblio.biblionumber %]#item[% item.itemnumber %]" title="Go to item details">[% item.barcode %]</a> >+ </td> >+ <td>[% item.itemcallnumber %]</td> >+ <td>[% Branches.GetName(item.homebranch) %]</td> >+ <td>[% Branches.GetName(item.holdingbranch) %]</td> >+ <td>[% item.location %]</td> >+ <td>[% item.stocknumber %]</td> >+ <td>[% item.status %]</td> >+ <td>[% item.issues || 0 %]</td> >+ <td><a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&biblionumber=[% item.biblionumber %]&itemnumber=[% item.itemnumber %]">Modify</a></td> >+</tr> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc >new file mode 100644 >index 0000000..9115ead >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc >@@ -0,0 +1,18 @@ >+[%- USE Branches -%] >+[%- biblio = item.biblio -%] >+[%- biblioitem = item.biblioitem -%] >+[ >+ "<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 %]", >+ "[% biblioitem.publicationyear |html %]", >+ "[% biblioitem.publishercode |html %]", >+ "[% biblioitem.collectiontitle |html %]", >+ "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% biblio.biblionumber %]#item[% item.itemnumber %]' title='Go to item details'>[% item.barcode |html %]</a>", >+ "[% 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 href='/cgi-bin/koha/cataloguing/additem.pl?op=edititem&biblionumber=[% item.biblionumber %]&itemnumber=[% item.itemnumber %]'>Modify</a>" >+] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_items.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_items.inc >new file mode 100644 >index 0000000..33b4c36 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_items.inc >@@ -0,0 +1,49 @@ >+[% names = CGI.param() %] >+[% params = [] %] >+[% FOREACH name IN names %] >+ [% IF name != 'sortby' AND name != 'sortorder' %] >+ [% params.push(name _ "=" _ CGI.param(name)) %] >+ [% END %] >+[% END %] >+[% base_url = "/cgi-bin/koha/catalogue/itemsearch.pl?" _ params.join('&') %] >+ >+[% BLOCK itemsearch_header %] >+ [% sortorder = 'asc' %] >+ [% classes = [] %] >+ [% IF CGI.param('sortby') == name %] >+ [% classes.push('active') %] >+ [% classes.push('sort-' _ CGI.param('sortorder')) %] >+ [% IF CGI.param('sortorder') == 'asc' %] >+ [% sortorder = 'desc' %] >+ [% END %] >+ [% END %] >+ [% url = base_url _ '&sortby=' _ name _ '&sortorder=' _ sortorder %] >+ <th class="[% classes.join(' ') %]"> >+ <a href="[% url %]" title="Sort on [% label %] ([% sortorder %])">[% text %]</a> >+ </th> >+[% END %] >+ >+<table> >+ <thead> >+ <tr> >+ [% INCLUDE itemsearch_header name='title' label='title' text='Bibliographic reference' %] >+ [% INCLUDE itemsearch_header name='publicationyear' label='publication date' text='Publication date' %] >+ [% INCLUDE itemsearch_header name='publishercode' label='publisher' text='Publisher' %] >+ [% INCLUDE itemsearch_header name='collectiontitle' label='collection' text='Collection' %] >+ [% INCLUDE itemsearch_header name='barcode' label='barcode' text='Barcode' %] >+ [% INCLUDE itemsearch_header name='itemcallnumber' label='callnumber' text='Callnumber' %] >+ [% INCLUDE itemsearch_header name='homebranch' label='home branch' text='Home branch' %] >+ [% INCLUDE itemsearch_header name='holdingbranch' label='holding branch' text='Holding branch' %] >+ [% INCLUDE itemsearch_header name='location' label='location' text='Location' %] >+ [% INCLUDE itemsearch_header name='stocknumber' label='stock number' text='Stock number' %] >+ [% INCLUDE itemsearch_header name='notforloan' label='status' text='Status' %] >+ [% INCLUDE itemsearch_header name='issues' label='issues' text='Issues' %] >+ <th></th> >+ </tr> >+ </thead> >+ <tbody> >+ [% FOREACH item IN items %] >+ [% INCLUDE 'catalogue/itemsearch_item.inc' item = item %] >+ [% END %] >+ </tbody> >+</table> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >index 217b069..6772b01 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt >@@ -77,6 +77,8 @@ > <dd>Manage rules for automatically matching MARC records during record imports.</dd> > <dt><a href="/cgi-bin/koha/admin/oai_sets.pl">OAI sets configuration</a></dt> > <dd>Manage OAI Sets</dd> >+ <dt><a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a></dt> >+ <dd>Manage custom fields for items search</dd> > [% IF ( SearchEngine != 'Zebra' ) %] > <dt><a href="/cgi-bin/koha/admin/searchengine/solr/indexes.pl">Search engine configuration</a></dt> > <dd>Manage indexes, facets, and their mappings to MARC fields and subfields.</dd> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_field.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_field.tt >new file mode 100644 >index 0000000..6e0f373 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_field.tt >@@ -0,0 +1,41 @@ >+[% INCLUDE 'doc-head-open.inc' %] >+ <title>Koha › Administration › Items search fields</title> >+ [% INCLUDE 'doc-head-close.inc' %] >+</head> >+<body id="admin_itemssearchfields" class="admin"> >+ [% INCLUDE 'header.inc' %] >+ [% INCLUDE 'cat-search.inc' %] >+ <div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> › >+ <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> › >+ <a href="/cgi-bin/koha/admin/items_search_fields.pl">Items search fields</a> › >+ [% field.name %] >+ </div> >+ >+ <div id="doc3" class="yui-t2"> >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ <h1>Items search field: [% field.label %]</h1> >+ >+ <form action="" method="POST"> >+ <fieldset class="rows"> >+ <legend>Edit field</legend> >+ [% INCLUDE 'admin-items-search-field-form.inc' field=field %] >+ <div> >+ <input type="hidden" name="op" value="mod" /> >+ </div> >+ <fieldset class="action"> >+ <input type="submit" value="Update" /> >+ </fieldset> >+ </fieldset> >+ </form> >+ <a href="/cgi-bin/koha/admin/items_search_fields.pl">Return to items search fields overview page</a> >+ </div> >+ </div> >+ <div class="yui-b"> >+ [% INCLUDE 'admin-menu.inc' %] >+ </div> >+ </div> >+ >+ [% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_fields.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_fields.tt >new file mode 100644 >index 0000000..200e614 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/items_search_fields.tt >@@ -0,0 +1,95 @@ >+[% INCLUDE 'doc-head-open.inc' %] >+ <title>Koha › Administration › Items search fields</title> >+ [% INCLUDE 'doc-head-close.inc' %] >+</head> >+<body id="admin_itemssearchfields" class="admin"> >+ [% INCLUDE 'header.inc' %] >+ [% INCLUDE 'cat-search.inc' %] >+ <div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> › >+ <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> › >+ Items search fields >+ </div> >+ >+ <div id="doc3" class="yui-t2"> >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ [% IF field_added %] >+ <div class="dialog"> >+ Field successfully added: [% field_added.label %] >+ </div> >+ [% ELSIF field_not_added %] >+ <div class="alert"> >+ <p>Failed to add field. Please check if the field name doesn't already exist.</p> >+ <p>Check logs for more details.</p> >+ </div> >+ [% ELSIF field_deleted %] >+ <div class="dialog"> >+ Field successfully deleted. >+ </div> >+ [% ELSIF field_not_deleted %] >+ <div class="alert"> >+ <p>Failed to delete field.</p> >+ <p>Check logs for more details.</p> >+ </div> >+ [% ELSIF field_updated %] >+ <div class="dialog"> >+ Field successfully updated: [% field_updated.label %] >+ </div> >+ [% ELSIF field_not_updated %] >+ <div class="alert"> >+ <p>Failed to update field.</p> >+ <p>Check logs for more details.</p> >+ </div> >+ [% END %] >+ <h1>Items search fields</h1> >+ [% IF fields.size %] >+ <table> >+ <thead> >+ <tr> >+ <th>Name</th> >+ <th>Label</th> >+ <th>MARC field</th> >+ <th>MARC subfield</th> >+ <th>Authorised values category</th> >+ <th>Operations</th> >+ </tr> >+ </thead> >+ <tbody> >+ [% FOREACH field IN fields %] >+ <tr> >+ <td>[% field.name %]</td> >+ <td>[% field.label %]</td> >+ <td>[% field.tagfield %]</td> >+ <td>[% field.tagsubfield %]</td> >+ <td>[% field.authorised_values_category %]</td> >+ <td> >+ <a href="/cgi-bin/koha/admin/items_search_field.pl?name=[% field.name %]" title="Edit [% field.name %] field">Edit</a> >+ <a href="/cgi-bin/koha/admin/items_search_fields.pl?op=del&name=[% field.name %]" title="Delete [% field.name %] field">Delete</a> >+ </td> >+ </tr> >+ [% END %] >+ </tbody> >+ </table> >+ [% END %] >+ <form action="" method="POST"> >+ <fieldset class="rows"> >+ <legend>Add a new field</legend> >+ [% INCLUDE 'admin-items-search-field-form.inc' field=undef %] >+ <div> >+ <input type="hidden" name="op" value="add" /> >+ </div> >+ <fieldset class="action"> >+ <input type="submit" value="Add this field" /> >+ </fieldset> >+ </fieldset> >+ </form> >+ </div> >+ </div> >+ <div class="yui-b"> >+ [% INCLUDE 'admin-menu.inc' %] >+ </div> >+ </div> >+ >+ [% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt >index c982eb4..b41690d 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt >@@ -30,6 +30,7 @@ > <form action="search.pl" method="get"> > <div id="advanced-search"> > <h1>Advanced search</h1> >+<a href="/cgi-bin/koha/catalogue/itemsearch.pl">Go to item search</a> > > [% IF ( outer_servers_loop ) %] > <!-- DATABASES --> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.csv.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.csv.tt >new file mode 100644 >index 0000000..b69f610 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.csv.tt >@@ -0,0 +1,4 @@ >+Bibliographic reference, Publication Date, Publisher, Collection, Barcode, Callnumber, Home branch, Holding branch, Location, Stock number, Status, Issues >+[% FOREACH item IN results -%] >+ [%- INCLUDE 'catalogue/itemsearch_item.csv.inc' item = item -%] >+[%- END -%] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.json.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.json.tt >new file mode 100644 >index 0000000..35e020f >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.json.tt >@@ -0,0 +1,12 @@ >+[%- USE CGI -%] >+{ >+ "sEcho": [% CGI.param('sEcho') %], >+ "iTotalRecords": [% total_rows %], >+ "iTotalDisplayRecords": [% total_rows %], >+ "aaData": [ >+ [%- FOREACH item IN results -%] >+ [%- INCLUDE 'catalogue/itemsearch_item.json.inc' item = item -%] >+ [%- UNLESS loop.last %],[% END -%] >+ [%- END -%] >+ ] >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt >new file mode 100644 >index 0000000..d967b36 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt >@@ -0,0 +1,425 @@ >+[% USE CGI %] >+[% USE JSON.Escape %] >+ >+[% BLOCK form_field_select %] >+ <div class="form-field form-field-select"> >+ <label class="form-field-label" for="[% name %]">[% label %]</label> >+ <select id="[% name %]_op" name="[% name %]_op"> >+ <option value="=">is</option> >+ [% IF CGI.param(name _ '_op') == '!=' %] >+ <option value="!=" selected="selected">is not</option> >+ [% ELSE %] >+ <option value="!=" >is not</option> >+ [% END %] >+ </select> >+ [% values = CGI.param(name) %] >+ <select id="[% name %]" name="[% name %]" multiple="multiple" size="[% options.size < 4 ? options.size + 1 : 4 %]"> >+ [% IF (values == '') %] >+ <option value="" selected="selected"> >+ [% ELSE %] >+ <option value=""> >+ [% END %] >+ [% empty_option || "All" %] >+ </option> >+ [% FOREACH option IN options %] >+ [% IF values.grep(option.value).size %] >+ <option value="[% option.value %]" selected="selected">[% option.label %]</option> >+ [% ELSE %] >+ <option value="[% option.value %]">[% option.label %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </div> >+[% END %] >+ >+[% BLOCK form_field_select_option %] >+ [% IF params.f == value %] >+ <option value="[% value %]" selected="selected">[% label %]</option> >+ [% ELSE %] >+ <option value="[% value %]">[% label %]</option> >+ [% END %] >+[% END %] >+ >+[% BLOCK form_field_select_text %] >+ <div class="form-field form-field-select-text"> >+ [% IF params.exists('c') %] >+ <select name="c" class="form-field-conjunction"> >+ <option value="and">AND</option> >+ [% IF params.c == 'or' %] >+ <option value="or" selected="selected">OR</option> >+ [% ELSE %] >+ <option value="or">OR</option> >+ [% END %] >+ </select> >+ [% ELSE %] >+ <select name="c" class="form-field-conjunction" disabled="disabled"> >+ <option value="and">AND</option> >+ <option value="or">OR</option> >+ </select> >+ [% END %] >+ <select name="f" class="form-field-column"> >+ [% INCLUDE form_field_select_option value='barcode' label='Barcode' %] >+ [% INCLUDE form_field_select_option value='itemcallnumber' label='Callnumber' %] >+ [% INCLUDE form_field_select_option value='stocknumber' label='Stock number' %] >+ [% INCLUDE form_field_select_option value='title' label='Title' %] >+ [% INCLUDE form_field_select_option value='author' label='Author' %] >+ [% INCLUDE form_field_select_option value='publishercode' label='Publisher' %] >+ [% INCLUDE form_field_select_option value='publicationdate' label='Publication date' %] >+ [% INCLUDE form_field_select_option value='collectiontitle' label='Collection' %] >+ [% INCLUDE form_field_select_option value='isbn' label='ISBN' %] >+ [% INCLUDE form_field_select_option value='issn' label='ISSN' %] >+ [% IF items_search_fields.size %] >+ <optgroup label="Custom search fields"> >+ [% FOREACH field IN items_search_fields %] >+ [% marcfield = field.tagfield %] >+ [% IF field.tagsubfield %] >+ [% marcfield = marcfield _ '$' _ field.tagsubfield %] >+ [% END %] >+ [% IF params.f == "marc:$marcfield" %] >+ <option value="marc:[% marcfield %]" data-authorised-values-category="[% field.authorised_values_category %]" selected="selected">[% field.label %] ([% marcfield %])</option> >+ [% ELSE %] >+ <option value="marc:[% marcfield %]" data-authorised-values-category="[% field.authorised_values_category %]">[% field.label %] ([% marcfield %])</option> >+ [% END %] >+ [% END %] >+ </optgroup> >+ [% END %] >+ </select> >+ <input type="text" name="q" class="form-field-value" value="[% params.q %]" /> >+ <input type="hidden" name="op" value="like" /> >+ </div> >+[% END %] >+ >+[% BLOCK form_field_select_text_block %] >+ [% c = CGI.param('c').list %] >+ [% f = CGI.param('f').list %] >+ [% q = CGI.param('q').list %] >+ [% op = CGI.param('op').list %] >+ [% IF q.size %] >+ [% size = q.size - 1 %] >+ [% FOREACH i IN [0 .. size] %] >+ [% >+ params = { >+ f => f.$i >+ q = q.$i >+ op = op.$i >+ } >+ %] >+ [% IF i > 0 %] >+ [% j = i - 1 %] >+ [% params.c = c.$j %] >+ [% END %] >+ [% INCLUDE form_field_select_text params=params %] >+ [% END %] >+ [% ELSE %] >+ [% INCLUDE form_field_select_text %] >+ [% END %] >+[% END %] >+ >+[% BLOCK form_field_radio_yes_no %] >+ <div class="form-field"> >+ <label class="form-field-label" for="[% name %]">[% label %]:</label> >+ <input type="radio" name="[% name %]" id="[% name %]_indifferent" value="" checked="checked"/> >+ <label for="[% name %]_indifferent">Indifferent</label> >+ <input type="radio" name="[% name %]" id="[% name %]_yes" value="yes" /> >+ <label for="[% name %]_yes">Yes</label> >+ <input type="radio" name="[% name %]" id="[% name %]_no" value="no" /> >+ <label for="[% name %]_no">No</label> >+ </div> >+[% END %] >+ >+[%# Page starts here %] >+ >+[% INCLUDE 'doc-head-open.inc' %] >+ <title>Koha › Catalog › Advanced search</title> >+ [% INCLUDE 'doc-head-close.inc' %] >+ [% INCLUDE 'datatables.inc' %] >+ <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.columnFilter.js"></script> >+ <link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" /> >+ <link rel="stylesheet" type="text/css" href="[% themelang %]/css/itemsearchform.css" /> >+ <script type="text/javascript"> >+ //<![CDATA[ >+ var authorised_values = [% authorised_values_json %]; >+ >+ function loadAuthorisedValuesSelect(select) { >+ var selected = select.find('option:selected'); >+ var category = selected.data('authorised-values-category'); >+ var form_field_value = select.siblings('.form-field-value'); >+ if (category && category in authorised_values) { >+ var values = authorised_values[category]; >+ var html = '<select name="q" class="form-field-value">\n'; >+ for (i in values) { >+ var value = values[i]; >+ html += '<option value="' + value.authorised_value + '">' + value.lib + '</option>\n'; >+ } >+ html += '</select>\n'; >+ var new_form_field_value = $(html); >+ new_form_field_value.val(form_field_value.val()); >+ form_field_value.replaceWith(new_form_field_value); >+ } else { >+ if (form_field_value.prop('tagName').toLowerCase() == 'select') { >+ html = '<input name="q" type="text" class="form-field-value" />'; >+ var new_form_field_value = $(html); >+ form_field_value.replaceWith(new_form_field_value); >+ } >+ } >+ } >+ >+ function addNewField() { >+ var form_field = $('div.form-field-select-text').last(); >+ var copy = form_field.clone(true); >+ copy.find('input,select').not('[type="hidden"]').each(function() { >+ $(this).val(''); >+ }); >+ copy.find('.form-field-conjunction').removeAttr('disabled'); >+ form_field.after(copy); >+ copy.find('select.form-field-column').change(); >+ } >+ >+ function submitForm($form) { >+ var tr = '' >+ + ' <tr>' >+ + ' <th>' + _("Bibliographic reference") + '</th>' >+ + ' <th>' + _("Publication date") + '</th>' >+ + ' <th>' + _("Publisher") + '</th>' >+ + ' <th>' + _("Collection") + '</th>' >+ + ' <th>' + _("Barcode") + '</th>' >+ + ' <th>' + _("Callnumber") + '</th>' >+ + ' <th>' + _("Home branch") + '</th>' >+ + ' <th>' + _("Holding branch") + '</th>' >+ + ' <th>' + _("Location") + '</th>' >+ + ' <th>' + _("Stock number") + '</th>' >+ + ' <th>' + _("Status") + '</th>' >+ + ' <th>' + _("Issues") + '</th>' >+ + ' <th></th>' >+ + ' </tr>' >+ var table = '' >+ + '<table id="results">' >+ + ' <thead>' + tr + tr + '</thead>' >+ + ' <tbody></tbody>' >+ + '</table>'; >+ $('#results-wrapper').empty().html(table); >+ >+ var params = []; >+ $form.find('select,input[type="text"],input[type="hidden"]').not('[disabled]').each(function () { >+ params.push({ 'name': $(this).attr('name'), 'value': $(this).val() }); >+ }); >+ $form.find('input[type="radio"]:checked').each(function() { >+ params.push({ 'name': $(this).attr('name'), 'value': $(this).val() }); >+ }); >+ >+ $('#results').dataTable($.extend(true, {}, dataTablesDefaults, { >+ 'bDestroy': true, >+ 'bServerSide': true, >+ 'sAjaxSource': '/cgi-bin/koha/catalogue/itemsearch.pl', >+ 'fnServerParams': function(aoData) { >+ aoData.push( { 'name': 'format', 'value': 'json' } ); >+ for (i in params) { >+ aoData.push(params[i]); >+ } >+ }, >+ 'sDom': '<"top pager"ilp>t<"bottom pager"ip>', >+ 'aoColumns': [ >+ { 'sName': 'title' }, >+ { 'sName': 'publicationyear' }, >+ { 'sName': 'publishercode' }, >+ { 'sName': 'collectiontitle' }, >+ { 'sName': 'barcode' }, >+ { 'sName': 'itemcallnumber' }, >+ { 'sName': 'homebranch' }, >+ { 'sName': 'holdingbranch' }, >+ { 'sName': 'location' }, >+ { 'sName': 'stocknumber' }, >+ { 'sName': 'notforloan' }, >+ { 'sName': 'issues' }, >+ { 'sName': 'checkbox', 'bSortable': false } >+ ] >+ })).columnFilter({ >+ 'sPlaceHolder': 'head:after', >+ 'aoColumns': [ >+ { 'type': 'text' }, >+ { 'type': 'text' }, >+ { 'type': 'text' }, >+ { 'type': 'text' }, >+ { 'type': 'text' }, >+ { 'type': 'text' }, >+ { 'type': 'select', 'values': [% branches.json %] }, >+ { 'type': 'select', 'values': [% branches.json %] }, >+ { 'type': 'select', 'values': [% locations.json %] }, >+ { 'type': 'text' }, >+ { 'type': 'select', 'values': [% notforloans.json %] }, >+ { 'type': 'text' }, >+ null >+ ] >+ }); >+ } >+ >+ function hideForm($form) { >+ $form.hide(); >+ $('#editsearchlink').show(); >+ } >+ >+ $(document).ready(function () { >+ // Add the "New field" link. >+ var form_field = $('div.form-field-select-text').last() >+ var button_field_new = $('<a href="#" class="button-field-new" title="Add a new field">New field</a>'); >+ button_field_new.click(function() { >+ addNewField(); >+ return false; >+ }); >+ form_field.after(button_field_new); >+ >+ // If a field is linked to an authorised values list, display the list. >+ $('div.form-field-select-text select').change(function() { >+ loadAuthorisedValuesSelect($(this)); >+ }).change(); >+ >+ // Prevent user to select the 'All ...' option with other options. >+ $('div.form-field-select').each(function() { >+ $(this).find('select').filter(':last').change(function() { >+ values = $(this).val(); >+ if (values.length > 1) { >+ var idx = $.inArray('', values); >+ if (idx != -1) { >+ values.splice(idx, 1); >+ $(this).val(values); >+ } >+ } >+ }); >+ }); >+ >+ $('#itemsearchform').submit(function() { >+ var format = $(this).find('input[name="format"]:checked').val(); >+ if (format == 'html') { >+ submitForm($(this)); >+ hideForm($(this)); >+ return false; >+ } >+ }); >+ >+ $('#editsearchlink').click(function() { >+ $('#itemsearchform').show(); >+ $(this).hide(); >+ return false; >+ }); >+ }); >+ //]]> >+ </script> >+</head> >+<body id="catalog_itemsearch" class="catalog"> >+ [% INCLUDE 'header.inc' %] >+ <div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> › Item search >+ </div> >+ >+ <div id="doc" class="yui-t7"> >+ <div id="bd"> >+ <h1>Item search</h1> >+ <a href="/cgi-bin/koha/catalogue/search.pl">Go to advanced search</a> >+ <form action="" method="get" id="itemsearchform"> >+ <fieldset> >+ <legend>Item search</legend> >+ <fieldset> >+ [% INCLUDE form_field_select >+ name="homebranch" >+ label="Home branch" >+ options = branches >+ empty_option = "All branches" >+ %] >+ [% INCLUDE form_field_select >+ name="location" >+ label="Location" >+ options = locations >+ empty_option = "All locations" >+ %] >+ </fieldset> >+ <fieldset> >+ [% INCLUDE form_field_select >+ name="itype" >+ label="Item type" >+ options = itemtypes >+ empty_option = "All item types" >+ %] >+ [% INCLUDE form_field_select >+ name="ccode" >+ label="Collection code" >+ options = ccodes >+ empty_option = "All collection codes" >+ %] >+ </fieldset> >+ <fieldset> >+ [% INCLUDE form_field_select_text_block %] >+ <p class="hint">You can use the following joker characters: % _</p> >+ </fieldset> >+ <fieldset> >+ <div class="form-field"> >+ <label class="form-field-label" for="itemcallnumber_from">From call number:</label> >+ [% value = CGI.param('itemcallnumber_from') %] >+ <input type="text" id="itemcallnumber_from" name="itemcallnumber_from" value="[% value %]" /> >+ <span class="hint">(inclusive)</span> >+ </div> >+ <div class="form-field"> >+ [% value = CGI.param('itemcallnumber_to') %] >+ <label class="form-field-label" for="itemcallnumber_to">To call number:</label> >+ <input type="text" id="itemcallnumber_to" name="itemcallnumber_to" value="[% value %]" /> >+ <span class="hint">(inclusive)</span> >+ </div> >+ [% INCLUDE form_field_radio_yes_no name="damaged" label="Damaged" %] >+ [% INCLUDE form_field_radio_yes_no name="itemlost" label="Lost" %] >+ <div class="form-field"> >+ <label class="form-field-label" for="issues">Issues count:</label> >+ <select id="issues_op" name="issues_op"> >+ <option value=">">></option> >+ <option value="<"><</option> >+ <option value="=">=</option> >+ <option value="!=">!=</option> >+ </select> >+ <input type="text" name="issues" /> >+ </div> >+ <div class="form-field"> >+ <label class="form-field-label" for="datelastborrowed">Last issue date:</label> >+ <select id="datelastborrowed_op" name="datelastborrowed_op"> >+ <option value=">">After</option> >+ <option value="<">Before</option> >+ <option value="=">On</option> >+ </select> >+ <input type="text" name="datelastborrowed" /> >+ <span class="hint">ISO Format (AAAA-MM-DD)</span> >+ </div> >+ </fieldset> >+ <fieldset> >+ <div class="form-field-radio"> >+ <label>Output:</label> >+ <input type="radio" id="format-html" name="format" value="html" checked="checked" /> <label for="format-html">Screen</label> >+ <input type="radio" id="format-csv" name="format" value="csv" /> <label for="format-csv">CSV</label> >+ </div> >+ <div class="form-actions"> >+ <input type="submit" value="Search" /> >+ </div> >+ </fieldset> >+ </fieldset> >+ </form> >+ >+ <p><a id="editsearchlink" href="#" style="display:none">Edit search</a></p> >+ >+ <div id="results-wrapper"> >+ [% IF search_done %] >+ >+ [% IF total_rows > 0 %] >+ <p>Found [% total_rows %] results.</p> >+ [% ELSE %] >+ <p>No results found.</p> >+ [% END %] >+ >+ [% IF results %] >+ [% INCLUDE 'catalogue/itemsearch_items.inc' items = results %] >+ [% END %] >+ >+ <div id="pagination-bar"> >+ [% pagination_bar %] >+ </div> >+ >+ [% END %] >+ </div> >+ </div> >+ >+ [% INCLUDE 'intranet-bottom.inc' %] >-- >1.7.10.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 11425
:
23674
|
23675
|
26334
|
26335
|
26555
|
26556
|
26557
|
27111
|
27112
|
27113
|
27162
|
27975
|
27976
|
27977
|
27978
|
27979
|
29965
|
30195
|
30196
|
30197
|
30198
|
30199
|
30200
|
32304
|
32305
|
32306
|
32307
|
32308
|
32309
|
32669
|
32670
|
32671
|
32672
|
32673
|
32674
|
32675
|
32918
|
32926
|
32947
|
32948