Bug 22605: Add ability to edit a search on staff interface

This patch adds the ability to parse the current search and populate the advanced search page on the staff side accordingly.
When searching you will now have an 'Edit this search' link underneath your current search terms

A few notes:
1 - Previous selected items appear with a checkbox, unchecking 'disables' them and removes from the search
2 - Facets and unrecognized limits will appear at the end of the form under 'Other limits' - there could be future enhancement to parse there better, but I believe this is sufficient for now
3 - Previously selected branch or group selections will populate the dropdowns, only one is allowed

To test:
1 - Perform a variety of search on the staff interface
2 - Confirm the 'edit search' button appear below your search
3 - Confirm that options are retained and correctly parsed
4 - Test with library groups - you should be able to combine groups after initial search, or remove individual libraries when editing
5 - Test that selecting a goup disables the individual libraries options

QA note: Missing filters on deletion of keys form hash are false positives

https://bugs.koha-community.org/show_bug.cgi?id=26605

Signed-off-by: Andrew Fuerste-Henry <andrew@bywatersolutions.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Fridolin Somers <fridolin.somers@biblibre.com>
This commit is contained in:
Nick Clemens 2021-08-12 15:28:40 +00:00 committed by Fridolin Somers
parent ab495c64bd
commit 90d658ae56
4 changed files with 217 additions and 79 deletions

View File

@ -168,8 +168,15 @@ my $cgi = CGI->new;
# decide which template to use
my $template_name;
my $template_type;
my @params = $cgi->multi_param("limit");
if ((@params>=1) || (defined $cgi->param("q") && $cgi->param("q") ne "" ) || ($cgi->param('multibranchlimit')) || ($cgi->param('limit-yr')) ) {
# limits are used to limit to results to a pre-defined category such as branch or language
my @limits = map uri_unescape($_), $cgi->multi_param("limit");
my @nolimits = map uri_unescape($_), $cgi->multi_param('nolimit');
my %is_nolimit = map { $_ => 1 } @nolimits;
@limits = grep { not $is_nolimit{$_} } @limits;
if (
!$cgi->param('edit_search') &&
( (@limits>=1) || (defined $cgi->param("q") && $cgi->param("q") ne "" ) || ($cgi->param('limit-yr')) )
) {
$template_name = 'catalogue/results.tt';
$template_type = 'results';
}
@ -248,6 +255,32 @@ $template->param( searchid => scalar $cgi->param('searchid'), );
# The following should only be loaded if we're bringing up the advanced search template
if ( $template_type eq 'advsearch' ) {
my $expanded = $cgi->param('expanded_options');
if( $cgi->param('edit_search') ){
my @operators = $cgi->multi_param('op');
my @indexes = $cgi->multi_param('idx');
my %limit_hash;
foreach my $limit (@limits){
if ( $limit eq 'available' ){
$template->param( limit_available => 1 );
} else {
my ($index,$value) = split(':',$limit);
$value =~ s/"//g;
if ( $index =~ /mc-/ ){
$limit_hash{$index . "_" . $value} = 1;
} else {
push @{$limit_hash{$index}}, $value;
}
}
};
$expanded = 1 if scalar @operators || scalar @limits;
$template->param( operators => \@operators );
$template->param( limits => \%limit_hash );
$template->param(
indexes => \@indexes,
sort => $cgi->param('sort_by'),
);
}
# load the servers (used for searching -- to do federated searching, etc.)
my $primary_servers_loop;# = displayPrimaryServers();
$template->param(outer_servers_loop => $primary_servers_loop,);
@ -264,44 +297,21 @@ if ( $template_type eq 'advsearch' ) {
$template->param( sort_by => $default_sort_by );
}
# determine what to display next to the search boxes (ie, boolean option
# shouldn't appear on the first one, scan indexes should, adding a new
# box should only appear on the last, etc.
my @search_boxes_array;
my $search_boxes_count = 3; # begin with 3 boxes
# FIXME: all this junk can be done in TMPL using __first__ and __last__
for (my $i=1;$i<=$search_boxes_count;$i++) {
# if it's the first one, don't display boolean option, but show scan indexes
if ($i==1) {
push @search_boxes_array, {scan_index => 1};
}
# if it's the last one, show the 'add field' box
elsif ($i==$search_boxes_count) {
push @search_boxes_array,
{
boolean => 1,
add_field => 1,
};
}
else {
push @search_boxes_array,
{
boolean => 1,
};
}
# determine what to display next to the search boxes
my @queries = $cgi->multi_param('q');
while( scalar @queries < 3 ){
push @queries, "";
}
$template->param(uc(C4::Context->preference("marcflavour")) => 1,
search_boxes_loop => \@search_boxes_array);
$template->param(uc(C4::Context->preference("marcflavour")) =>1 );
# load the language limits (for search)
my $languages_limit_loop = getLanguages($lang, 1);
$template->param(search_languages_loop => $languages_limit_loop,);
$template->param( queries => \@queries );
# Expanded search options in advanced search:
# use the global setting by default, but let the user override it
{
my $expanded = $cgi->param('expanded_options');
$expanded = C4::Context->preference("expandedSearchOption") || 0
if !defined($expanded) || $expanded !~ /^0|1$/;
$template->param( expanded_options => $expanded );
@ -372,12 +382,6 @@ if ($operands[0] && !$operands[1]) {
$template->param(ms_value => $ms_query);
}
# limits are use to limit to results to a pre-defined category such as branch or language
my @limits = map uri_unescape($_), $cgi->multi_param('limit');
my @nolimits = map uri_unescape($_), $cgi->multi_param('nolimit');
my %is_nolimit = map { $_ => 1 } @nolimits;
@limits = grep { not $is_nolimit{$_} } @limits;
my $available;
foreach my $limit(@limits) {
if ($limit =~/available/) {
@ -391,7 +395,7 @@ my $limit_yr;
my $limit_yr_value;
if ($params->{'limit-yr'}) {
if ($params->{'limit-yr'} =~ /\d{4}/) {
$limit_yr = "yr,st-numeric=$params->{'limit-yr'}";
$limit_yr = "yr,st-numeric:$params->{'limit-yr'}";
$limit_yr_value = $params->{'limit-yr'};
}
push @limits,$limit_yr;
@ -570,6 +574,11 @@ for (my $i=0;$i<@servers;$i++) {
# set up parameters if user wishes to re-run the search
# as a Z39.50 search
$template->param (z3950_search_params => C4::Search::z3950_search_args($z3950par || $query_desc));
$template->param(limit_cgi => $limit_cgi);
$template->param(query_cgi => $query_cgi);
$template->param(query_desc => $query_desc);
$template->param(limit_desc => $limit_desc);
$template->param(offset => $offset);
if ($hits) {
$template->param(total => $hits);
@ -578,11 +587,6 @@ for (my $i=0;$i<@servers;$i++) {
$limit_cgi_not_availablity =~ s/&limit=available//g;
$template->param(limit_cgi_not_availablity => $limit_cgi_not_availablity);
}
$template->param(limit_cgi => $limit_cgi);
$template->param(query_cgi => $query_cgi);
$template->param(query_desc => $query_desc);
$template->param(limit_desc => $limit_desc);
$template->param(offset => $offset);
$template->param(DisplayMultiPlaceHold => $DisplayMultiPlaceHold);
if ($query_desc || $limit_desc) {
$template->param(searchdesc => 1);

View File

@ -0,0 +1,49 @@
[%- BLOCK subtype_limits_description -%]
[%- SWITCH subtype_limit -%]
[%- CASE 'aud:a' -%]<span>Preschool</span>
[%- CASE 'aud:b' -%]<span>Primary</span>
[%- CASE 'aud:c' -%]<span>Pre-adolescent</span>
[%- CASE 'aud:d' -%]<span>Adolescent</span>
[%- CASE 'aud:e' -%]<span>Adult</span>
[%- CASE 'aud:f' -%]<span>Specialized</span>
[%- CASE 'aud:g' -%]<span>General</span>
[%- CASE 'aud:j' -%]<span>Juvenile</span>
[%- CASE 'fic:1' -%]<span>Fiction</span>
[%- CASE 'fic:0' -%]<span>Non-fiction</span>
[%- CASE 'bio:b' -%]<span>Biography</span>
[%- CASE 'mus:j' -%]<span>Musical recording</span>
[%- CASE 'mus:i' -%]<span>Non-musical recording</span>
[%- CASE 'l-format:ta' -%]<span>Regular print</span>
[%- CASE 'l-format:tb' -%]<span>Large print</span>
[%- CASE 'l-format:tc or l-format:fb' -%]<span>Braille</span>
[%- CASE '' -%]<span>-----------</span>
[%- CASE 'l-format:sd' -%]<span>CD audio</span>
[%- CASE 'l-format:ss' -%]<span>Cassette recording</span>
[%- CASE 'l-format:vf' -%]<span>VHS tape / Videocassette</span>
[%- CASE 'l-format:vd' -%]<span>DVD video / Videodisc</span>
[%- CASE 'l-format:co' -%]<span>CD software</span>
[%- CASE 'l-format:cr' -%]<span>Website</span>
[%- CASE 'ctype:a' -%]<span>Abstracts / Summaries</span>
[%- CASE 'ctype:b' -%]<span>Bibliographies</span>
[%- CASE 'ctype:c' -%]<span>Catalogs</span>
[%- CASE 'ctype:d' -%]<span>Dictionaries</span>
[%- CASE 'ctype:e' -%]<span>Encyclopedias </span>
[%- CASE 'ctype:f' -%]<span>Handbooks</span>
[%- CASE 'ctype:g' -%]<span>Legal articles</span>
[%- CASE 'ctype:i' -%]<span>Indexes</span>
[%- CASE 'ctype:j' -%]<span>Patent document</span>
[%- CASE 'ctype:k' -%]<span>Discographies</span>
[%- CASE 'ctype:l' -%]<span>Legislation</span>
[%- CASE 'ctype:m' -%]<span>Theses</span>
[%- CASE 'ctype:n' -%]<span>Surveys</span>
[%- CASE 'ctype:o' -%]<span>Reviews</span>
[%- CASE 'ctype:p' -%]<span>Programmed texts</span>
[%- CASE 'ctype:q' -%]<span>Filmographies</span>
[%- CASE 'ctype:r' -%]<span>Directories</span>
[%- CASE 'ctype:s' -%]<span>Statistics</span>
[%- CASE 'ctype:t' -%]<span>Technical reports</span>
[%- CASE 'ctype:v' -%]<span>Legal cases and case notes</span>
[%- CASE 'ctype:w' -%]<span>Law reports and digests</span>
[%- CASE 'ctype:z' -%]<span>Treaties </span>
[%- END -%]
[%- END -%]

View File

@ -12,7 +12,7 @@
<select name="limit" id="[% ln_id | html %]">
<option value="">No limit</option>
[% FOREACH ln_loo IN ln_loop %]
[% IF ( ln_loo.selected ) %]
[% IF ( ln_loo.selected or selected == ln_loo.iso639_2_code ) %]
<option value="[% ln_index | html %],rtrn:[% ln_loo.iso639_2_code | html %]" selected="selected">
[% ln_loo.language_description | html %]
</option>
@ -105,31 +105,37 @@
<!-- BOOLEAN SEARCH OPTIONS -->
<fieldset id="searchterms">
<legend>Search for </legend>
[% FOREACH search_box IN search_boxes_loop %]
[% IF ( search_boxes_label ) %]
<div class="search-term-row" style="text-indent: 4.5em;">
[% ELSE %]
[% FOREACH query IN queries %]
[% IF ( expanded_options ) %]
<div class="search-term-row">
[% END %]
[% IF ( expanded_options ) %]
[% IF ( search_box.boolean ) %]
<select name="op">
<option value="AND" selected="selected">and</option>
<option value="OR">or</option>
<option value="NOT">not</option>
</select>
[% END %]
[% END %]
[% INCLUDE 'search_indexes.inc' %]
<input type="text" size="30" name="q" title="Enter search terms" value="" />
[% IF ( expanded_options ) %]
[% IF ( search_box.add_field ) %]
<a href="JavaScript:add_field();" id="ButtonPlus" title="Add another field">[+]</a>
[% END %]
[% IF ( search_box.scan_index ) %]
<label for="scan">Scan indexes:</label> <input type="checkbox" name="scan" id="scan" value="1" />
[% END %]
[% SET opindex = loop.index - 1 %]
<select name="op">
[% IF operators.$opindex == 'OR' %]
<option value="AND">and</option>
<option value="OR" selected="selected">or</option>
<option value="NOT">not</option>
[% ELSIF operators.$opindex == 'NOT' %]
<option value="AND">and</option>
<option value="OR">or</option>
<option value="NOT" selected="selected">not</option>
[% ELSE %]
<option value="AND" selected="selected">and</option>
<option value="OR">or</option>
<option value="NOT">not</option>
[% END %]
</select>
[% ELSE %]
<div>
[% END %]
[% SET preselect = 'ms_' _ indexes.${loop.index}.replace(',','comma') %]
[% INCLUDE 'search_indexes.inc' %]
<input type="text" size="30" name="q" title="Enter search terms" value="[% query | html %]" />
[% IF ( expanded_options ) %]
<a href="JavaScript:add_field();" id="ButtonPlus" title="Add another field">[+]</a>
[% IF ( loop.first ) %]
<label for="scan">Scan indexes:</label> <input type="checkbox" name="scan" id="scan" value="1" />
[% END %]
[% END %]
</div>
[% END %]
[% IF Koha.Preference('SearchEngine') == 'Elasticsearch' %]
@ -171,9 +177,32 @@
<table>
<tr>
[% FOREACH itemtypeloo IN advsearchloo.code_loop %]
<td><input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') | html %]-[% itemtypeloo.number | html %]" name="limit" value="mc-[% itemtypeloo.ccl | html %]:[% itemtypeloo.code | html %]"/><label for="[% itemtypeloo.ccl FILTER remove(',') | html %]-[% itemtypeloo.number | html %]">[% UNLESS ( Koha.Preference('OpacNoItemTypeImages') ) %][% IF ( itemtypeloo.imageurl ) %]<img src="[% itemtypeloo.imageurl | html %]" alt="[% itemtypeloo.description | html %]" />[% END %]&nbsp;[% END %]
[% itemtypeloo.description | html %]</label></td>
[% IF ( loop.last ) %]</tr>[% ELSE %][% UNLESS ( loop.count % 5 ) %]</tr><tr>[% END %][% END %]
<td>
[% SET limit_key = 'mc-' _ itemtypeloo.ccl _ "_" _ itemtypeloo.code %]
[% IF limits.$limit_key.defined %]
[% limits.delete( limit_key ) %]
<input type="checkbox" checked="checked" id="[% itemtypeloo.ccl FILTER remove(',') | html %]-[% itemtypeloo.number | html %]" name="limit" value="mc-[% itemtypeloo.ccl | html %]:[% itemtypeloo.code | html %]"/>
[% ELSE %]
<input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') | html %]-[% itemtypeloo.number | html %]" name="limit" value="mc-[% itemtypeloo.ccl | html %]:[% itemtypeloo.code | html %]"/>
[% END %]
<label for="[% itemtypeloo.ccl FILTER remove(',') | html %]-[% itemtypeloo.number | html %]">
[% UNLESS ( Koha.Preference('OpacNoItemTypeImages') ) %]
[% IF ( itemtypeloo.imageurl ) %]
<img src="[% itemtypeloo.imageurl | html %]" alt="[% itemtypeloo.description | html %]" />
[% END %]
&nbsp;
[% END %]
[% itemtypeloo.description | html %]
</label>
</td>
[% IF ( loop.last ) %]
</tr>
[% ELSE %]
[% UNLESS ( loop.count % 5 ) %]
</tr>
<tr>
[% END %]
[% END %]
[% END %]
</table>
</div>
@ -186,16 +215,22 @@
<fieldset id="pubrange">
<!-- PUB / COPY YEAR LIMIT --><!-- FIXME: add publication,copyright,acquisition options -->
<p><label for="limit-yr">Year: </label>
<input type="text" size="15" name="limit-yr" id="limit-yr" value=""/>&nbsp;&nbsp;(format: yyyy-yyyy)</p>
[% SET year_limit_key = 'yr,st-numeric' %]
<input type="text" size="15" name="limit-yr" id="limit-yr" value="[% limits.$year_limit_key.0 | html %]"/>&nbsp;&nbsp;(format: yyyy-yyyy)</p>
[% IF limits.$year_limit_key.defined %]
[% limits.delete(year_limit_key) %]
[% END %]
<!-- /PUB / COPY YEAR LIMIT -->
</fieldset>
<fieldset id="language">
<!-- LANGUAGE LIMIT -->
<p>
<label for="language-limit">Language: </label>
[% PROCESS language_limit_select ln_loop=search_languages_loop ln_id='language-limit' ln_index='ln' %]
[% PROCESS language_limit_select ln_loop=search_languages_loop ln_id='language-limit' ln_index='ln' selected = limits.${'ln,rtrn'}.0 %]
[% limits.delete( 'ln,rtrn' ) %]
<label for="language-original-limit">Language of original: </label>
[% PROCESS language_limit_select ln_loop=search_languages_loop ln_id='language-original-limit' ln_index='language-original' %]
[% PROCESS language_limit_select ln_loop=search_languages_loop ln_id='language-original-limit' ln_index='language-original' selected = limits.${'language-original,rtrn'}.0 %]
[% limits.delete( 'language-original,rtrn' ) %]
</p>
<!-- /LANGUAGE LIMIT -->
</fieldset>
@ -207,10 +242,24 @@
[% IF ( UNIMARC ) %]
[% INCLUDE 'subtypes_unimarc.inc' %]
[% ELSE %]
[% PROCESS 'subtype_limits.inc' %]
<!-- SUBTYPE LIMITS -->
<fieldset id="subtype">
<legend>Subtype limits</legend><p>
<fieldset id="current_subtype_limits"><legend>Current subtype limits</legend>
[% FOREACH subtype IN ['aud','fic','bio','ctype','l-format'] %]
[% FOREACH limit IN limits.$subtype %]
<span>
<input name="limit" value="[% subtype | html %]:[% limit | html %]" type="hidden">
<label for="subtype_limit_[% loop.index() | html %]">[% PROCESS subtype_limits_description subtype_limit = subtype _':' _ limit %]</label>
<input type="checkbox" class="toggle_limit" name="subtype_limit_[% loop.index() | html %]" checked="checked">
</span>
[% END %]
[% limits.delete( subtype ) %]
[% END %]
</fieldset>
<select name="limit" class="subtype">
<option value="" selected="selected" class="menuheader">Any audience</option>
<option value="aud:a">Preschool</option>
@ -280,14 +329,19 @@
<!-- AVAILABILITY LIMITS -->
<fieldset id="availability"><legend>Location and availability</legend>
<fieldset id="currently-avail">
<p><label for="available-items">Only items currently available:</label> <input type="checkbox" id="available-items" name="limit" value="available" /></p>
[% IF limit_available %]
<p><label for="available-items">Only items currently available:</label> <input type="checkbox" id="available-items" name="limit" value="available" checked="checked" /></p>
[% ELSE %]
<p><label for="available-items">Only items currently available:</label> <input type="checkbox" id="available-items" name="limit" value="available" /></p>
[% END %]
</fieldset>
<fieldset id="select-libs">
<p><label for="branchloop">Individual libraries:</label><select name="limit" id="branchloop">
<option value="">All libraries</option>
[%# FIXME Should not we filter the libraries displayed? %]
[% PROCESS options_for_libraries prefix => "branch:" libraries => Branches.all( unfiltered => 1, do_not_select_my_library => 1 ) %]
[% PROCESS options_for_libraries prefix => "branch:" libraries => Branches.all( unfiltered => 1, do_not_select_my_library => 1, selected => limits.branch.0 ) %]
[% limits.delete('branch') %]
</select></p>
[% IF search_groups %]
<p>OR</p>
@ -298,9 +352,14 @@
<option value=""> -- none -- </option>
[% FOREACH sg IN search_groups %]
[% UNLESS sg.branchcode %]
<option value="multibranchlimit:[% sg.id | html %]">[% sg.title | html %]</option>
[% IF limits.multibranchlimit.0 == sg.id %]
<option selected="selected" value="multibranchlimit:[% sg.id | html %]">[% sg.title | html %]</option>
[% ELSE %]
<option value="multibranchlimit:[% sg.id | html %]">[% sg.title | html %]</option>
[% END %]
[% END %]
[% END %]
[% limits.delete('multibranchlimit') %]
</select>
</p>
[% END %]
@ -308,11 +367,29 @@
</fieldset>
<!-- /AVAILABILITY LIMITS -->
<!-- OTHER LIMITS (facets, etc.) -->
[% IF limits.size %]
<fieldset id=""><legend>Other limits</legend>
<p>
[% FOREACH key IN limits.keys %]
[% FOREACH limit IN limits.$key %]
<span>
<input type="hidden" name="limit" value="[% key | html %]:[% limit | html %]">
<label for="other_[% key _ loop.index() | html %]">[% key | html %]:[% limit | html %]</label>
<input type="checkbox" class="toggle_limit" name="other_[% key _ loop.index() | html %]" checked="checked">
</span>
[% END %]
[% END %]
</p>
</fieldset>
[% END %]
<!-- /OTHER LIMITS (facets, etc.) -->
<!-- RANK LIMITS -->
<fieldset id="sortby"><legend>Sorting</legend>
<p>
<label for="sort_by">Sort by: </label><select id="sort_by" name="sort_by">
[% INCLUDE 'resort_form.inc' %]
[% INCLUDE 'resort_form.inc' sort_by = sort %]
</select>
</p>
</fieldset>
@ -366,6 +443,13 @@
});
[% END %]
if( $("#current_subtype_limits input").length == 0 ){
$("#current_subtype_limits").hide();
}
$(".toggle_limit").on('click',function(){
$(this).siblings("[type='hidden']").prop('disabled', !this.checked);
});
[% IF searchid %]
browser = KOHA.browser('[% searchid | html %]');
browser.show_back_link();

View File

@ -74,6 +74,7 @@
<h3>
[% total | html %] result(s) found [% IF ( query_desc ) %]for <span class="results_query_desc">'[% query_desc | html %]'</span>[% END %][% IF limit_desc %]&nbsp;with limit(s): <span class="results_limit_desc">'[% limit_desc | html %]'</span>[% END %][% IF ( LibraryName ) %] in [% LibraryName | html %] Catalog[% END %].
</h3>
<a href="/cgi-bin/koha/catalogue/search.pl?advsearch=1&edit_search=1&[% query_cgi | $raw %]&[% limit_cgi | $raw %]&sort_by=[% sort_by | $raw %]">Edit this search</a>
<div class="searchheader" id="searchheader">
<div id="selection_ops">
@ -327,6 +328,7 @@
[% IF CAN_user_acquisition_order_manage && searchtoorder_basketno && searchtoorder_vendorid %][%# Coming from the basket or vendor view %]
<p><a href="/cgi-bin/koha/acqui/basket.pl?basketno=[% searchtoorder_basketno | uri %]">View basket<a></p>
[% END %]
<a href="/cgi-bin/koha/catalogue/search.pl?advsearch=1&edit_search=1&[% query_cgi | $raw %]&[% limit_cgi | $raw %]&sort_by=[% sort_by | $raw %]">Edit this search</a>
[% ELSE %]
<p>You did not specify any search criteria.</p>
[% END %]
@ -729,7 +731,6 @@
query_desc: "[% To.json( query_desc ) | html %]",
query_cgi: "[% query_cgi | html %]",
limit_cgi: "[% limit_cgi | html %]",
sort_cgi: "[% sort_cgi | html %]",
sort_by: "[% sort_by | html %]",
gotoPage: "[% gotoPage | html %]",
gotoNumber: "[% gotoNumber | html %]",