Bugzilla – Attachment 153208 Details for
Bug 34227
Add persistent selections and batch operations to item search
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 34227: Add persistent selections and batch operations to item search
Bug-34227-Add-persistent-selections-and-batch-oper.patch (text/plain), 27.24 KB, created by
Owen Leonard
on 2023-07-08 16:16:42 UTC
(
hide
)
Description:
Bug 34227: Add persistent selections and batch operations to item search
Filename:
MIME Type:
Creator:
Owen Leonard
Created:
2023-07-08 16:16:42 UTC
Size:
27.24 KB
patch
obsolete
>From e27d41a72969a9626c342419e2043988534afdf3 Mon Sep 17 00:00:00 2001 >From: Owen Leonard <oleonard@myacpl.org> >Date: Fri, 29 Jan 2021 11:48:24 +0000 >Subject: [PATCH] Bug 34227: Add persistent selections and batch operations to > item search > >This patch modifies the item search results page so that user selections >are stored in local storage. This allows the user to make selections on >one page of search results, move to another, and continue to make >selections. > >The patch also adds the option to send selected items to batch item >modification or batch item deletion. > >Changes to the patron search results template have been made to make >some CSS classes more generic so they can be used by both pages. > >To test, apply the patch and rebuild the staff interface CSS. >- Log in to the staff interface as a user with permission to perform > batch item modification and batch item deletion. >- Go to item search and perform a search which will return at > least two pages of results. > - On the search results page you should see a new search header > toolbar with some controls: "Select visible rows," "Clear > selection," "Export all results to" and "Batch operations" > (disabled). > - Without making any selections, confirm that the "Export all results > to" menu items work correctly to export all results to a CSV or a > barcode file. > - Confirm that the "Select visible rows" control works as expected, > selecting all checkboxes on the current page (and on no other pages) > - After selecting all checkboxes the search header controls should be > updated: > - The "Export all..." button should now show the number of > selections: "Export selected results (X) to..." > - The batch operations button should be enabled. > - There should be a new element labeled "Items selected: X" with a > "Clear" link. > - If you uncheck any checkboxes the controls should be updated, > showing the new count of selected records. > - Move to the next page of results and confirm that making selections > on this page works to increment all counters > - Confirm that the "Export selected.." options work and that your CSV > and barcode files now contain only the items you selected. > - Test the batch operations menu: > - Test that the controls correctly reflect the logged-in user's > permissions: > - With permission to batch modify items > - With permission to batch delete items > - With both; with neither > - Both menu options should take you to the correct page and the > list of submitted items should match your selections. > - Test that clicking the "Clear" button next to "Items selected" hides > the items selected box and reverts the "Export all" and "Batch > operations" buttons to their original state. > - Page through the search results to confirm that no checkboxes are > checked. > - Test that your search selections are really persistent: > - Navigate away from the page, return to item search, and perform > another search. > - The "Item selected" box should still show your previous > selections. > - Any items you previously selected which are also in this result > set should have a checked checkbox. > - Click the "Edit search" button from the item search results page > and new search with different parameters. > - The "Items selected" should still show your previous selections. > - Log out of Koha and back in. When you perform an item search now, > there should be no "Item selected" > > - Go to Patrons and test patron searching. As you make selections the > "Patrons selected" box should be updated correctly and look correct, > matching the one on the item search page. >--- > .../prog/css/src/staff-global.scss | 7 +- > .../prog/en/modules/catalogue/itemsearch.tt | 338 +++++++++++------- > .../prog/en/modules/members/member.tt | 12 +- > .../intranet-tmpl/prog/js/staff-global.js | 1 + > 4 files changed, 223 insertions(+), 135 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >index abbc4a82b75..b1da8a08ea9 100644 >--- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >+++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >@@ -2021,22 +2021,23 @@ li { > } > } > >-#patron_search_selected { >+#table_search_selections { > background-color: lighten( $background-color-primary, 60 ); > border: 1px solid $background-color-primary; > border-color: lighten( $background-color-primary, 30 ) lighten( $background-color-primary, 50 ) lighten( $background-color-primary, 50 ) lighten( $background-color-primary, 30 ); >- border-radius: 3px; >+ border-radius: 4px; > color: #333; > display: inline-block; > font-size: 11px; > line-height: 1.5; >+ margin-left: .7em; > padding: 5px 10px; > text-align: center; > vertical-align: middle; > white-space: nowrap; > } > >-#clear-patron-selection { >+#clear-row-selection { > display: inline-block; > margin-left: 1em; > } >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt >index 402001d33db..06e60b17cfa 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt >@@ -266,10 +266,17 @@ > </div> > <div class="row"> > <div class="col-md-12"> >- <div id="results-wrapper"></div> >+ <div id="results-wrapper"> >+ >+ </div> > </div> > </div> > >+ <form method="POST" action="/cgi-bin/koha/tools/batchMod.pl" id="batch_item_operations"> >+ <input type="hidden" name="op" value="show" /> >+ <input type="hidden" id="batch_mod_del" name="del" value="0" /> >+ </form> >+ > [% MACRO jsinclude BLOCK %] > [% INCLUDE 'datatables.inc' %] > [% Asset.js("lib/jquery/plugins/jquery.dataTables.columnFilter.js") | $raw %] >@@ -277,6 +284,20 @@ > <script> > var authorised_values = [% authorised_values_json | $raw %]; > >+ function showItemSelections( number ){ >+ let caret = ' <span class="caret">'; >+ if( number > 0 ){ >+ $("#table_search_selections").show().find("span").text(_("Items selected: " + number ) ); >+ $("#batch_mod_menu").removeClass("disabled").prop("disabled", false); >+ $("#export-button").html(_("Export selected results (%s) to").format ( number ) + caret); >+ } else { >+ $("#table_search_selections").hide(); >+ $("#batch_mod_menu").prop("disabled", true).addClass("disabled"); >+ $("#batch_item_operations").empty(); >+ $("#export-button").html(_("Export all results to") + caret); >+ } >+ } >+ > function loadAuthorisedValuesSelect(select) { > var selected = select.find('option:selected'); > var category = selected.data('authorised-values-category'); >@@ -301,7 +322,7 @@ > } > } > >- function addNewField( link ) { >+ function addNewField( link ) { > var form_field = $('div.form-field-select-text').last(); > var copy = form_field.clone(true); > copy.find('input,select').not('[type="hidden"]').each(function() { >@@ -309,10 +330,36 @@ > }); > copy.find('.form-field-conjunction').prop('disabled', false).val('and'); > form_field.after(copy); >- link.remove(); >+ link.remove(); > copy.find('select.form-field-column').change(); > } > >+ function exportItems(format) { >+ let item_search_selections = JSON.parse( localStorage.getItem("item_search_selections") ) || []; >+ if (item_search_selections.length > 0) { >+ var href = '/cgi-bin/koha/catalogue/item-export.pl?format=' + format; >+ href += '&itemnumber=' + Array.from( item_search_selections ).join('&itemnumber='); >+ location = href; >+ } else { >+ $('#format-' + format).prop('checked', true); >+ $('#itemsearchform').submit(); >+ $('#format-html').prop('checked', true); >+ } >+ } >+ >+ function prepSelections(){ >+ let item_search_selections = JSON.parse( localStorage.getItem("item_search_selections") ) || []; >+ if( item_search_selections.length > 0 ){ >+ showItemSelections( item_search_selections.length ); >+ $("#results input[type='checkbox']").each(function(){ >+ var itemnumber = $(this).val(); >+ if( item_search_selections.indexOf( itemnumber ) >= 0 ){ >+ $(this).prop("checked", true ); >+ } >+ }); >+ } >+ } >+ > function submitForm($form) { > var tr = '' > + ' <tr>' >@@ -335,108 +382,72 @@ > + ' <th id="items_checkouts">' + _("Checkouts") + '</th>' > + ' <th id="items_date_due">' + _("Due date") + '</th>' > + ' <th id=""></th>' >- + ' </tr>' >+ + ' </tr>'; >+ > var table = '' > + '<div class="page-section">' >- + '<table id="results">' >- + ' <thead>' + tr + tr + '</thead>' >- + ' <tbody></tbody>' >- + '</table>' >+ + ' <div id="searchheader" class="searchheader">' >+ + ' <a href="#" id="select_all" class="btn btn-link"><i class="fa fa-check"></i> ' >+ + _("Select visible rows") >+ + ' </a> | ' >+ + ' <a href="#" id="clear_all" class="btn btn-link"><i class="fa fa-times"></i> ' >+ + _("Clear selection") >+ + ' </a>' >+ + ' <div class="btn-group"><button class="btn btn-default btn-sm dropdown-toggle" id="export-button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' + _("Export all results to") + '<span class="caret"></span></button>' >+ + ' <ul class="dropdown-menu">' >+ + ' <li><a href="#" id="csvExportLink">' + _("CSV") + '</a></li>' >+ + ' <li><a href="#" id="barcodesExportLink">' + _("Barcode file") + '</a></li>' >+ + ' </ul>' >+ + ' </div>'; >+ [% IF ( CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel ) %] >+ table += '' >+ + ' <div class="btn-group"><button class="btn btn-default btn-sm dropdown-toggle disabled" disabled="disabled" type="button" id="batch_mod_menu"data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> ' + _("Batch operations") + ' <span class="caret"></span> </button>' >+ + ' <ul class="dropdown-menu" aria-labelledby="batch_mod_menu">'; >+ [% IF ( CAN_user_tools_items_batchmod ) %] >+ table += '' >+ + ' <li> <a href="#" data-submit="batch_item_modification" data-toggle="tooltip" data-placement="right" title="' + _("Send selected items to batch item modification") + '" class="batch_op send_to_item_mod">' + _("Batch item modification") + '</a> </li>'; >+ [% END %] >+ [% IF ( CAN_user_tools_items_batchdel ) %] >+ table += '' >+ + ' <li> <a href="#" data-submit="batch_item_deletion" data-toggle="tooltip" data-placement="right" title="' + _("Send selected items to batch item deletion") + '" class="batch_op send_to_item_del">' + _("Batch item deletion") + '</a> </li>'; >+ [% END %] >+ table += '' >+ + ' </ul>' >+ + ' </div>'; >+ [% END # /IF CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel %] >+ table += '' >+ + ' <div id="table_search_selections" class="btn-group" style="display:none;">' >+ + ' <span></span>' >+ + ' <a href="#" id="clear-row-selection"><i class="fa fa-times"></i> Clear</a>' >+ + ' </div>' >+ + ' </div>' >+ + ' <table id="results">' >+ + ' <thead>' + tr + tr + '</thead>' >+ + ' <tbody></tbody>' >+ + ' </table>' > + '</div>'; >+ > var advSearchLink = $('<a>') > .attr('href', '/cgi-bin/koha/catalogue/search.pl') > .html(_("Go to advanced search")); >+ > var editSearchLink = $('<a>') > .attr('href', '#') >- .html(_("Edit search")) >- .addClass('btn btn-default btn-xs') >+ .html("<i class='fa fa-pencil'></i> " + _("Edit search") ) >+ .addClass('btn btn-default') > .on('click', function(e) { > e.preventDefault(); > $('#item-search-block').show(); > }); > >- function getCheckedItemnumbers () { >- var itemnumbers; >- try { >- itemnumbers = JSON.parse(sessionStorage.getItem('itemsearch_itemnumbers') || '[]'); >- } catch (e) { >- itemnumbers = []; >- } >- >- return new Set(itemnumbers); >- } >- >- function exportItems(format) { >- var itemnumbers = getCheckedItemnumbers(); >- if (itemnumbers.size > 0) { >- var href = '/cgi-bin/koha/catalogue/item-export.pl?format=' + format; >- href += '&itemnumber=' + Array.from(itemnumbers).join('&itemnumber='); >- location = href; >- } else { >- $('#format-' + format).prop('checked', true); >- $('#itemsearchform').submit(); >- $('#format-html').prop('checked', true); >- } >- } >- >- var csvExportLink = $('<a>') >- .attr('href', '#') >- .html("CSV") >- .on('click', function(e) { >- e.preventDefault(); >- exportItems('csv'); >- }); >- var barcodesExportLink = $('<a>') >- .attr('href', '#') >- .html(_("Barcodes file")) >- .on('click', function(e) { >- e.preventDefault(); >- exportItems('barcodes'); >- }); >- >- var exportButton = $('<div>') >- .addClass('btn-group') >- .append($('<button>') >- .addClass('btn btn-default btn-xs dropdown-toggle') >- .attr('id', 'export-button') >- .attr('data-toggle', 'dropdown') >- .attr('aria-haspopup', 'true') >- .attr('aria-expanded', 'false') >- .html(_("Export all results to") + ' <span class="caret"></span>')) >- .append($('<ul>') >- .addClass('dropdown-menu') >- .append($('<li>').append(csvExportLink)) >- .append($('<li>').append(barcodesExportLink))); >- >- var selectVisibleRows = $('<a>') >- .attr('href', '#') >- .append('<i class="fa fa-check"></i> ') >- .append(_("Select visible rows")) >- .on('click', function(e) { >- e.preventDefault(); >- $('#results input[type="checkbox"]').prop('checked', true).change(); >- }); >- var clearSelection = $('<a>') >- .attr('href', '#') >- .append('<i class="fa fa-times"></i> ') >- .append(_("Clear selection")) >- .on('click', function(e) { >- e.preventDefault(); >- sessionStorage.setItem('itemsearch_itemnumbers', '[]'); >- $('#results input[type="checkbox"]').prop('checked', false).change(); >- }); >- var exportLinks = $('<p>') >- .append(selectVisibleRows) >- .append(' ') >- .append(clearSelection) >- .append(' | ') >- .append(exportButton); >- > var results_heading = $('<div>').addClass('results-heading') > .append("<h1>" + _("Item search results") + "</h1>") > .append($('<p>').append(advSearchLink)) >- .append($('<p>').append(editSearchLink)) >- .append(exportLinks); >+ .append($('<div>') >+ .addClass("btn-toolbar") >+ .attr("id","toolbar") >+ .append(editSearchLink) >+ ); > $('#results-wrapper').empty() > .append(results_heading) > .append(table); >@@ -496,6 +507,9 @@ > { 'sName': 'actions', 'bSortable': false } > ], > "sPaginationType": "full_numbers", >+ "drawCallback": function( settings ) { >+ prepSelections(); >+ }, > fixedHeader: false // There is a bug on this view > })).columnFilter({ > 'sPlaceHolder': 'head:after', >@@ -546,51 +560,25 @@ > ] > }); > $('#results').on('draw.dt', function (e, settings) { >- var itemnumbers = getCheckedItemnumbers(); >- $(this).find('input[type="checkbox"][name="itemnumber"]').each(function () { >- var itemnumber = this.value; >- if (itemnumbers.has(itemnumber)) { >- this.checked = true; >- } >+ prepSelections(); >+ var Sticky = $("#searchheader"); >+ Sticky.hcSticky({ >+ stickTo: "#results-wrapper .page-section", >+ stickyClass: "floating" > }); > }); >- >- sessionStorage.setItem('itemsearch_itemnumbers', '[]'); >- >- $('#results').on('change', 'input[type="checkbox"]', function() { >- var itemnumber = this.value; >- var itemnumbers = getCheckedItemnumbers(); >- if (this.checked) { >- itemnumbers.add(itemnumber); >- } else { >- itemnumbers.delete(itemnumber); >- } >- sessionStorage.setItem('itemsearch_itemnumbers', JSON.stringify(Array.from(itemnumbers))); >- >- var caret = ' <span class="caret">'; >- if (itemnumbers.size > 0) { >- $('#export-button').html(_("Export selected results (%s) to").format(itemnumbers.size) + caret); >- } else { >- $('#export-button').html(_("Export all results to") + caret); >- } >- }); > } >- var Sticky; >+ > $(document).ready(function () { >- Sticky = $("#toolbar"); >- Sticky.hcSticky({ >- stickTo: "#item-search-block", >- stickyClass: "floating" >- }); > // Add the "New field" link. > var form_field = $('div.form-field-select-text').last() > var NEW_FIELD = _("New field"); >- var button_field_new = $('<a href="#" class="button-field-new" title="Add a new field"><i class="fa fa-plus"></i> ' + NEW_FIELD + '</a>'); >- button_field_new.click(function(e) { >- e.preventDefault(); >- addNewField( $(this) ); >+ var button_field_new = $('<a href="#" class="button-field-new" title="Add a new field"><i class="fa fa-plus"></i> ' + NEW_FIELD + '</a>'); >+ button_field_new.click(function(e) { >+ e.preventDefault(); >+ addNewField( $(this) ); > }); >- form_field.append(button_field_new); >+ form_field.append(button_field_new); > > // If a field is linked to an authorised values list, display the list. > $('div.form-field-select-text select[name="f"]').change(function() { >@@ -620,6 +608,104 @@ > return false; > } > }); >+ >+ $("body").on("click", "#select_all", function(e) { >+ e.preventDefault(); >+ $("#results input[type='checkbox']").each(function(){ >+ if( $(this).prop("checked") == false ){ >+ $(this).prop( "checked", true ).change(); >+ } >+ }); >+ }); >+ >+ $("body").on("click", "#clear_all", function(e) { >+ e.preventDefault(); >+ $("#results input[type='checkbox']").each(function(){ >+ if( $(this).prop("checked") == true ){ >+ $(this).prop( "checked", false ).change(); >+ } >+ }); >+ }); >+ >+ $("body").on("click", "#clear-row-selection", function(e){ >+ e.preventDefault(); >+ $("#results input[type='checkbox']").prop("checked" ,false ).change(); >+ localStorage.removeItem("item_search_selections"); >+ showItemSelections( 0 ); >+ }); >+ >+ $("body").on('change', '#results input[type="checkbox"]', function() { >+ let item_search_selections = JSON.parse( localStorage.getItem("item_search_selections") ) || []; >+ var itemnumber = $(this).val(); >+ if( $(this).prop("checked") ){ >+ item_search_selections.push( $(this).val() ); >+ localStorage.setItem('item_search_selections', JSON.stringify( item_search_selections )); >+ showItemSelections( item_search_selections.length ); >+ } else { >+ var filtered = item_search_selections.filter(function( value ){ >+ return value !== itemnumber; >+ }); >+ if( filtered.length > 0 ){ >+ localStorage.setItem('item_search_selections', JSON.stringify( filtered )); >+ item_search_selections = filtered; >+ showItemSelections( filtered.length ); >+ } else { >+ item_search_selections = []; >+ localStorage.removeItem('item_search_selections'); >+ showItemSelections( 0 ); >+ } >+ } >+ }); >+ >+ $("body").on("click", "#csvExportLink", function(e){ >+ e.preventDefault(); >+ exportItems('csv'); >+ }); >+ >+ $("body").on("click", "#barcodesExportLink", function(e){ >+ e.preventDefault(); >+ exportItems('barcodes'); >+ }); >+ >+ $("body").on("click", ".batch_op", function(e){ >+ e.preventDefault(); >+ let batch_mod_form = $("#batch_item_operations"); >+ batch_mod_form.empty(); >+ batch_mod_form.append( >+ $("<input>").attr("type","hidden") >+ .attr("name", "op") >+ .val("show") >+ ); >+ batch_mod_form.append( >+ $("<input>").attr("type","hidden") >+ .attr("name", "del") >+ .attr("id", "batch_mod_del") >+ ); >+ let item_search_selections = JSON.parse( localStorage.getItem("item_search_selections") ) || []; >+ // Populate batch forms with itemnumbers in local storage >+ for (let item of item_search_selections){ >+ var field = $("<input>").attr("type","hidden") >+ .attr("name","itemnumber") >+ .val( item ); >+ batch_mod_form.append( field ); >+ } >+ if( $(this).hasClass("send_to_item_mod") ){ >+ $("#batch_mod_del").val(0); >+ } else if ( $(this).hasClass("send_to_item_del") ){ >+ $("#batch_mod_del").val(1); >+ } else { >+ return false; >+ } >+ batch_mod_form.submit(); >+ }); >+ >+ $("body").on('click','#results tbody td',function(e){ >+ var checkbox = $(this).find("input[type=checkbox]"); >+ if (e.target.type != "checkbox") { >+ checkbox.prop('checked', !checkbox.prop("checked")); >+ checkbox.change(); >+ } >+ }); > }); > </script> > [% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >index 7b747e8171b..2a5b7f935de 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >@@ -90,9 +90,9 @@ > </div> > [% END %] > >- <div id="patron_search_selected" class="btn-group" style="display:none;"> >+ <div id="table_search_selections" class="btn-group" style="display:none;"> > <span></span> >- <a href="#" id="clear-patron-selection"><i class="fa fa-times"></i> Clear</a> >+ <a href="#" id="clear-row-selection"><i class="fa fa-times"></i> Clear</a> > </div> > </div> > </div> >@@ -153,7 +153,7 @@ > [% Asset.js("js/members-menu.js") | $raw %] > <script> > function showPatronSelections( number ){ >- $("#patron_search_selected").show().find("span").text(_("Patrons selected: " + number ) ); >+ $("#table_search_selections").show().find("span").text(_("Patrons selected: " + number ) ); > } > > $(document).ready(function() { >@@ -176,7 +176,7 @@ > } else { > patron_search_selections = []; > localStorage.removeItem('patron_search_selections'); >- $("#patron_search_selected").hide(); >+ $("#table_search_selections").hide(); > } > } > if ( patron_search_selections.length > 1 ) { >@@ -199,11 +199,11 @@ > window.location.href = merge_patrons_url; > }); > >- $("#clear-patron-selection").on("click", function(e){ >+ $("#clear-row-selection").on("click", function(e){ > e.preventDefault(); > $("input.selection").prop("checked", false).change(); > localStorage.removeItem("patron_search_selections"); >- $("#patron_search_selected").hide(); >+ $("#table_search_selections").hide(); > $('#merge-patrons, #patronlist-menu, #batch-mod-patrons').prop('disabled', true).addClass("disabled"); > $("#borrowernumberlist").val(""); > }); >diff --git a/koha-tmpl/intranet-tmpl/prog/js/staff-global.js b/koha-tmpl/intranet-tmpl/prog/js/staff-global.js >index 74af1ad89ae..795874fcfe6 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/staff-global.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/staff-global.js >@@ -254,6 +254,7 @@ function logOut(){ > localStorage.removeItem("searches"); > localStorage.removeItem("bibs_selected"); > localStorage.removeItem("patron_search_selections"); >+ localStorage.removeItem("item_search_selections"); > } > > function openHelp(){ >-- >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 34227
:
153184
|
153208
|
153285
|
156007
|
156008
|
158061