Bugzilla – Attachment 137812 Details for
Bug 17170
Add the ability to create 'saved searches' for use as filters when searching the catalog
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17170: Add search filters to staff and opac interfaces
Bug-17170-Add-search-filters-to-staff-and-opac-int.patch (text/plain), 16.83 KB, created by
Nick Clemens (kidclamp)
on 2022-07-18 13:32:18 UTC
(
hide
)
Description:
Bug 17170: Add search filters to staff and opac interfaces
Filename:
MIME Type:
Creator:
Nick Clemens (kidclamp)
Created:
2022-07-18 13:32:18 UTC
Size:
16.83 KB
patch
obsolete
>From 4abeb7be2013a0c97758f3fbece6c44b29c36020 Mon Sep 17 00:00:00 2001 >From: Nick Clemens <nick@bywatersolutions.com> >Date: Fri, 14 Jan 2022 14:08:51 +0000 >Subject: [PATCH] Bug 17170: Add search filters to staff and opac interfaces > >This patch displays the filters on the results pages with the facets >--- > Koha/SearchFilter.pm | 2 +- > catalogue/search.pl | 24 ++++ > .../intranet-tmpl/prog/en/includes/facets.inc | 18 +++ > .../prog/en/modules/catalogue/results.tt | 120 ++++++++++++++++++ > .../bootstrap/en/includes/opac-facets.inc | 17 +++ > opac/opac-search.pl | 10 ++ > 6 files changed, 190 insertions(+), 1 deletion(-) > >diff --git a/Koha/SearchFilter.pm b/Koha/SearchFilter.pm >index f400a8f703..493e1fc1ee 100644 >--- a/Koha/SearchFilter.pm >+++ b/Koha/SearchFilter.pm >@@ -15,7 +15,7 @@ package Koha::SearchFilter; > # along with Koha; if not, see <http://www.gnu.org/licenses>. > > use Modern::Perl; >- >+use JSON qw( encode_json decode_json ); > > use Koha::Database; > >diff --git a/catalogue/search.pl b/catalogue/search.pl >index 24e8692d27..c1996f9499 100755 >--- a/catalogue/search.pl >+++ b/catalogue/search.pl >@@ -156,8 +156,10 @@ use Koha::SearchEngine::Search; > use Koha::SearchEngine::QueryBuilder; > use Koha::Virtualshelves; > use Koha::SearchFields; >+use Koha::SearchFilters; > > use URI::Escape; >+use JSON qw( decode_json encode_json ); > > my $DisplayMultiPlaceHold = C4::Context->preference("DisplayMultiPlaceHold"); > # create a new CGI object >@@ -477,6 +479,7 @@ $template->param ( QUERY_INPUTS => \@query_inputs, > > ## parse the limit_cgi string and put it into a form suitable for <input>s > my @limit_inputs; >+my %active_filters; > if ($limit_cgi) { > for my $this_cgi ( split('&', $limit_cgi) ) { > next unless $this_cgi; >@@ -490,6 +493,11 @@ if ($limit_cgi) { > my $input_value = $2; > $input_name =~ s/=$//; > push @limit_inputs, { input_name => $input_name, input_value => Encode::decode_utf8( uri_unescape($input_value) ) }; >+ if( $input_value =~ /search_filter/ ){ >+ my ($filter_id) = ( uri_unescape($input_value) =~ /^search_filter:(.*)$/ ); >+ $active_filters{$filter_id} = 1; >+ } >+ > } > } > $template->param ( LIMIT_INPUTS => \@limit_inputs ); >@@ -574,9 +582,19 @@ for (my $i=0;$i<@servers;$i++) { > $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_json => encode_json({ >+ operators => \@operators, >+ operands => \@operands, >+ indexes => \@indexes >+ })); >+ $template->param(limit_json => encode_json({ >+ limits => \@limits >+ })); > $template->param(query_desc => $query_desc); > $template->param(limit_desc => $limit_desc); > $template->param(offset => $offset); >+ $template->param(offset => $offset); >+ > > if ($hits) { > $template->param(total => $hits); >@@ -665,6 +683,12 @@ for my $facet ( @$facets ) { > } > } > >+ >+$template->param( >+ search_filters => Koha::SearchFilters->search({ staff_client => 1 }, { order_by => "name" }), >+ active_filters => \%active_filters, >+) if C4::Context->preference('SavedSearchFilters'); >+ > $template->param( > #classlist => $classlist, > total => $total, >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/facets.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/facets.inc >index d31ade3d23..c4c854cadb 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/facets.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/facets.inc >@@ -1,11 +1,29 @@ > [% USE raw %] > [% USE AuthorisedValues %] > [% USE Languages %] >+ > [% IF ( opacfacets ) %] > [% IF ( facets_loop ) %] > <div id="search-facets"> > <h4>Refine your search</h4> > <ul> >+[% IF Koha.Preference("SavedSearchFilters") && search_filters.count > 0 %] >+<li id="search-filters"> >+<span id="filter_facets">Custom search filters</span> >+<ul> >+ [% SET base_url = "/cgi-bin/koha/catalogue/search.pl?" _ query_cgi _ limit_cgi %] >+ [% FOREACH search_filter IN search_filters %] >+ <li> >+ [% IF active_filters.${search_filter.id} %] >+ <span class="filter_label">[% search_filter.name | html %]<a href="[% base_url _ '&nolimit=search_filter:' _ search_filter.id | url %]">[X]</a></span> >+ [% ELSE %] >+ <span class="filter_label"><a href="[% base_url _ '&limit=search_filter:' _ search_filter.id | url %]">[% search_filter.name | html %]</a></span> >+ [% END %] >+ </li> >+ [% END %] >+</ul> >+</li> >+[% END %] > <li id="availability_facet">Availability<ul><li>[% IF ( available ) %]<strong>Showing only available items</strong></li><li><a href="/cgi-bin/koha/catalogue/search.pl?[% query_cgi | $raw %][% limit_cgi_not_availablity | $raw %][% IF ( sort_by ) %]&sort_by=[% sort_by | uri %][% END %]">Show all items</a>[% ELSE %]<a href="/cgi-bin/koha/catalogue/search.pl?[% query_cgi | $raw %][% limit_cgi | $raw %][% IF ( sort_by ) %]&sort_by=[% sort_by | uri %][% END %]&limit=available">Limit to currently available items</a>[% END %]</li></ul> > [% IF ( related ) %] <li>(related searches: [% FOREACH relate IN related %][% relate.related_search | html %][% END %])</li>[% END %] > </li> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt >index f9b80cffe6..8e05f71e4c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt >@@ -69,6 +69,9 @@ > [% total | html %] result(s) found [% IF ( query_desc ) %]for <span class="results_query_desc">'[% query_desc | html %]'</span>[% END %][% IF limit_desc %] 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> >+ [% IF ( Koha.Preference('SavedSearchFilters') && CAN_user_parameters_manage_search_filters ) %] >+ | <a href="#" id="save_search_filter">Save search as filter</a> >+ [% END %] > > <div class="searchheader" id="searchheader"> > <div id="selection_ops"> >@@ -331,6 +334,9 @@ > <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> >+ [% IF ( Koha.Preference('SavedSearchFilters') &&CAN_user_parameters_manage_search_filters ) %] >+ | <a href="#" id="save_search_filter">Save search as filter</a> >+ [% END %] > [% ELSE %] > <p>You did not specify any search criteria.</p> > [% END %] >@@ -687,6 +693,39 @@ > </div> > [% END #/IF outer_sup_results_loop %] > >+ [% IF ( Koha.Preference('SavedSearchFilters') && CAN_user_parameters_manage_search_filters ) %] >+ <div id="search_filters_modal" class="modal" role="dialog" aria-hidden="true"> >+ <div class="modal-dialog"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">Ã</button> >+ <h3 id="search_filters_label">Save: '[% query_desc | html %]' with limits '[% limit_desc | html %]' as filter</h3> >+ </div> >+ <div class="modal-body"> >+ <h6>Visibility:</h6> >+ <label for="show_filter_opac">Show in OPAC?</label> >+ <input type="checkbox" id="show_filter_opac" name="show_filter_opac"> >+ <label for="show_filter_staff_client">Show in Staff client?</label> >+ <input type="checkbox" id="show_filter_staff_client" name="show_filter_staff_client"> >+ <hr> >+ <h6>Save as new search filter:</h6> >+ <label for="new_filter">Name:</label> >+ <input id="new_filter" name="new_filter" type="text"> >+ <a id="save_new_filter" class="btn btn-default btn-xs" href="#">Save</a> >+ </br>OR</br> >+ <h6>Replace existing search filter:</h6> >+ <label for="existing_filters">Choose</label> >+ <select id="existing_filters" name="existing_filters" class="search_filters_list"></select> >+ <a id="replace_existing_filter" class="btn btn-default btn-xs" href="#">Replace</a> >+ </div> >+ <div class="modal-footer"> >+ <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a> >+ </div> >+ </div> >+ </div> >+ </div> >+ [% END %] >+ > <form id="hold_form" method="get" action="/cgi-bin/koha/reserve/request.pl"> > <!-- Value will be set here by placeHold() --> > <input type="hidden" name="findborrower" id="holdFor" value="" /> >@@ -712,6 +751,9 @@ > [% Asset.js("js/browser.js") | $raw %] > [% Asset.js("lib/hc-sticky.js") | $raw %] > [% Asset.js("js/coce.js") | $raw %] >+ [% Asset.css("css/humanmsg.css") | $raw %] >+ [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %] >+ [% INCLUDE 'select2.inc' %] > <script> > var PREF_AmazonCoverImages = parseInt( "[% Koha.Preference('AmazonCoverImages') | html %]", 10); > var q_array = new Array(); // will hold search terms, if present >@@ -745,6 +787,8 @@ > query_desc: "[% To.json( query_desc ) | html %]", > query_cgi: "[% query_cgi | html %]", > limit_cgi: "[% limit_cgi | html %]", >+ query_json: [% query_json | $raw %], >+ limit_json: [% limit_json | $raw %], > sort_by: "[% sort_by | html %]", > gotoPage: "[% gotoPage | html %]", > gotoNumber: "[% gotoNumber | html %]", >@@ -754,6 +798,82 @@ > first_biblionumber: "[% SEARCH_RESULTS.first.biblionumber | html %]", > last_biblionumber: "[% SEARCH_RESULTS.last.biblionumber | html %]", > } >+ [% IF ( CAN_user_parameters_manage_search_filters ) %] >+ function getFilters(id) { >+ const select = $("#existing_filters"); >+ $("option[value!='']", select).remove(); >+ return new Promise(function(resolve, reject) { >+ $.ajax("/api/v1/search_filters") >+ .then(resolve, reject); >+ }) >+ .then(function(filters) { >+ filters.forEach(function(filter) { >+ const opt = $("<option/>"); >+ select.append(opt); >+ if(id && filter.search_filter_id == id) { >+ opt.prop('selected', true); >+ } >+ opt.attr("value", filter.search_filter_id); >+ opt.attr("title", "Query:" + filter.filter_query + " Limit:"+filter.filter_limits); >+ opt.text(filter.name); >+ opt.data("filter", filter); >+ }); >+ }) >+ .then(function(){ >+ select.change(); >+ }); >+ } >+ $("#save_search_filter").click(function(){ >+ getFilters(); >+ $("#search_filters_modal").modal('show'); >+ }); >+ $("#save_new_filter").click(function(){ >+ let name = $("#new_filter").val().trim(); >+ if(!name){ >+ humanMsg.displayAlert( _("You must provide a name for the new filter"), { className: 'human Error' } ); >+ return; >+ } >+ let opac = $("#show_filter_opac").prop('checked'); >+ let staff = $("#show_filter_staff_client").prop('checked'); >+ save_search_filter(name,null,opac,staff); >+ }); >+ $("#replace_existing_filter").click(function(){ >+ let id = $("#existing_filters").val(); >+ let name = $("#existing_filters option[value="+id+"]").text(); >+ console.log(id,name); >+ save_search_filter(name,id); >+ }); >+ function save_search_filter(name,id,opac,staff){ >+ let options = { >+ url: '/api/v1/search_filters', >+ contentType: "application/json", >+ data: JSON.stringify({ >+ name: name, >+ filter_query: JSON.stringify(search_result.query_json), >+ filter_limits: JSON.stringify(search_result.limit_json), >+ opac: opac, >+ staff_client: staff, >+ }) >+ }; >+ if (id){ >+ options.method = "PUT"; >+ options.url = options.url+"/"+id >+ } else { >+ options.method = "POST"; >+ } >+ $.ajax(options) >+ .then(function(result) { >+ $("#search_filters_modal").modal('hide'); >+ humanMsg.displayAlert( _("Saved filter: ") + name , { className: 'human Success' } ); >+ getFilters(); >+ }) >+ .fail(function(err) { >+ console.log(err); >+ humanMsg.displayAlert( _("There was an error during saving:") + err.responseText, { className: 'humanError' } ); >+ }); >+ } >+ >+ [% END %] > </script> > [% Asset.js("js/pages/results.js") | $raw %] > [% END %] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-facets.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-facets.inc >index 5e6e638c0b..af51f0b605 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-facets.inc >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-facets.inc >@@ -5,6 +5,23 @@ > <div id="search-facets"> > <h2><a href="#" class="menu-collapse-toggle">Refine your search</a></h2> > <ul class="menu-collapse"> >+ [% IF Koha.Preference("SavedSearchFilters") && search_filters.size > 0 %] >+ <li id="search-filters"> >+ <h3 id="filter_facets">Custom search filters</h3> >+ <ul> >+ [% SET base_url = "/cgi-bin/koha/opac-search.pl?" _ query_cgi _ limit_cgi %] >+ [% FOREACH search_filter IN search_filters %] >+ <li> >+ [% IF active_filters.${search_filter.id} %] >+ <span class="filter_label">[% search_filter.name | html %]<a href="[% base_url _ '&nolimit=search_filter:' _ search_filter.id | url %]">[X]</a></span> >+ [% ELSE %] >+ <span class="filter_label"><a href="[% base_url _ '&limit=search_filter:' _ search_filter.id | url %]">[% search_filter.name | html %]</a></span> >+ [% END %] >+ </li> >+ [% END %] >+ </ul> >+ </li> >+ [% END %] > <li id="availability_facet"><h3 id="facet-availability">Availability</h3> > <ul> > <li> >diff --git a/opac/opac-search.pl b/opac/opac-search.pl >index fb13bc5c56..43e52c1834 100755 >--- a/opac/opac-search.pl >+++ b/opac/opac-search.pl >@@ -833,6 +833,16 @@ for my $facet ( @$facets ) { > } > } > >+my @search_filters = Koha::SearchFilters->search({ opac => 1 }, { order_by => "name" })->as_list; >+my %active_filters; >+foreach my $sf (@search_filters){ >+ $active_filters{$sf->id} = 1 if grep { "search_filter:".$sf->id eq $_->{input_value} } @limit_inputs; >+} >+$template->param( >+ search_filters => \@search_filters, >+ active_filters => \%active_filters, >+) if C4::Context->preference('SavedSearchFilters'); >+ > > $template->param( > #classlist => $classlist, >-- >2.30.2
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 17170
:
133263
|
133264
|
133265
|
133266
|
133267
|
133268
|
133269
|
133270
|
133361
|
133376
|
137027
|
137028
|
137029
|
137030
|
137031
|
137032
|
137033
|
137034
|
137035
|
137036
|
137186
|
137187
|
137188
|
137189
|
137190
|
137191
|
137192
|
137193
|
137194
|
137195
|
137808
|
137809
|
137810
|
137811
|
137812
|
137813
|
137814
|
137815
|
137816
|
137817
|
138505
|
138506
|
138507
|
138508
|
138509
|
138510
|
138511
|
138512
|
138513
|
138514
|
139948
|
139949
|
139950
|
139951
|
139952
|
139953
|
139954
|
139955
|
139956
|
139957
|
141228
|
141229
|
141230
|
141231
|
141232
|
141233
|
141234
|
141235
|
141236
|
141237
|
141316
|
141317
|
141318
|
141319
|
141320
|
141321
|
141322
|
141323
|
141324
|
141325
|
141326
|
141327
|
141328
|
141348
|
141349
|
141350
|
141351
|
141352
|
141353
|
141354
|
141355
|
141356
|
141357
|
141358
|
141359
|
141360
|
141422
|
141423
|
141424
|
141425
|
141426
|
141427
|
141428
|
141429
|
141430
|
141431
|
141432
|
141433
|
141434
|
141435
|
141436
|
141437
|
141438
|
141461
|
141462
|
142365
|
142367
|
142368
|
142369
|
142370
|
142371
|
142372
|
142373
|
142374
|
142375
|
142376
|
142377
|
142378
|
142379
|
142380
|
142381
|
142382
|
142383
|
142384
|
142385
|
142494
|
142498