From 78bde8594493a5497d778bd1657d21acd2240467 Mon Sep 17 00:00:00 2001
From: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Date: Mon, 11 Jan 2021 15:42:18 +0100
Subject: [PATCH] Bug 20212: Use the DT column filtering provided by the
 wrapper

Bug 27402 is adding it, we do not longer need the query_from_filters JS
function.

This patch also remove the filters on the left. As we have DT
remembering the filter on the table we don't need them anymore.

Technical note:
Prior to this patch, the search on biblio.author, biblio.title and
biblio.isbn was done by adding hidden columns. Now we are using:
  "data": "biblio.author:biblio.title:biblio.isbn"
to tell the wrapper we are going to build a search on these 3
attributes.

Another trick is to pass a default_filters parameters to the wrapper, to
tell it we want to filter on the orders of a given vendor (this is a
bugfix, the original implementation was returning all the orders).
However We should not use /acq/orders?vendor_id=42 but /acq/vendor/42/orders instead (which does not exist yet),
otherwise (with bug 27353 ) we are going to display the wrong number of non-filtering rows.

The change in Orders.pm is only formatting to match what's done in
  Bug 27353: Set X-Base-Total-Count header for REST API

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Andrew Fuerste-Henry <andrew@bywatersolutions.com>
---
 Koha/REST/V1/Acquisitions/Orders.pm                |  20 +-
 acqui/finishreceive.pl                             |   2 +-
 acqui/parcel.pl                                    |  20 --
 .../prog/en/modules/acqui/orderreceive.tt          |   2 +-
 .../intranet-tmpl/prog/en/modules/acqui/parcel.tt  | 251 ++-------------------
 koha-tmpl/intranet-tmpl/prog/js/datatables.js      |  46 +++-
 6 files changed, 68 insertions(+), 273 deletions(-)

diff --git a/Koha/REST/V1/Acquisitions/Orders.pm b/Koha/REST/V1/Acquisitions/Orders.pm
index 4cb0fe982f..5419ce5c45 100644
--- a/Koha/REST/V1/Acquisitions/Orders.pm
+++ b/Koha/REST/V1/Acquisitions/Orders.pm
@@ -143,19 +143,15 @@ sub list {
 
         # Perform search
         my $orders = $orders_rs->search( $filtered_params, $attributes );
+        my $total  = $orders_rs->search->count;
 
-        if ($orders->is_paged) {
-            $c->add_pagination_headers({
-                total => $orders->pager->total_entries,
-                params => $args,
-            });
-        }
-        else {
-            $c->add_pagination_headers({
-                total => $orders->count,
-                params => $args,
-            });
-        }
+        $c->add_pagination_headers(
+            {
+                total      => ($orders->is_paged ? $orders->pager->total_entries : $orders->count),
+                base_total => $total,
+                params     => $args,
+            }
+        );
 
         return $c->render(
             status  => 200,
diff --git a/acqui/finishreceive.pl b/acqui/finishreceive.pl
index 0ed88506c7..961e3d566f 100755
--- a/acqui/finishreceive.pl
+++ b/acqui/finishreceive.pl
@@ -190,4 +190,4 @@ if ($suggestion_id) {
     $suggestion->update( { reason => $reason } ) if $suggestion;
 }
 
-print $input->redirect("/cgi-bin/koha/acqui/parcel.pl?invoiceid=$invoiceid&sticky_filters=1");
+print $input->redirect("/cgi-bin/koha/acqui/parcel.pl?invoiceid=$invoiceid");
diff --git a/acqui/parcel.pl b/acqui/parcel.pl
index 9e4974ba90..1e42ed9174 100755
--- a/acqui/parcel.pl
+++ b/acqui/parcel.pl
@@ -109,25 +109,6 @@ unless( $invoiceid and $invoice->{invoiceid} ) {
     exit;
 }
 
-my $sticky_filters = $input->param('sticky_filters') || 0;
-
-if ($sticky_filters) {
-    my $search          = $input->cookie("filter_parcel_summary");
-    my $ean             = $input->cookie("filter_parcel_ean");
-    my $basketname      = $input->cookie("filter_parcel_basketname");
-    my $orderno         = $input->cookie("filter_parcel_orderno");
-    my $basketgroupname = $input->cookie("filter_parcel_basketgroupname");
-
-    $template->param(
-        summaryfilter         => $search,
-        eanfilter             => $ean,
-        basketfilter          => $basketname,
-        orderfilter           => $orderno,
-        basketgroupnamefilter => $basketgroupname,
-    );
-}
-
-
 my $booksellerid = $invoice->{booksellerid};
 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
 
@@ -211,6 +192,5 @@ $template->param(
     total_tax_excluded    => $total_tax_excluded,
     total_tax_included    => $total_tax_included,
     subtotal_for_funds    => $subtotal_for_funds,
-    sticky_filters        => $sticky_filters,
 );
 output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/orderreceive.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/orderreceive.tt
index 578a08ae65..61468aff99 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/orderreceive.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/orderreceive.tt
@@ -370,7 +370,7 @@
 </div>
 </div><div class="row"><fieldset class="action">
         <input type="submit"  value="Save" class="button" accesskey="w" />
-        <a class="cancel" href="/cgi-bin/koha/acqui/parcel.pl?invoiceid=[% invoiceid | html %]&sticky_filters=1">Cancel</a>
+        <a class="cancel" href="/cgi-bin/koha/acqui/parcel.pl?invoiceid=[% invoiceid | html %]">Cancel</a>
 </fieldset></div>    </form>
 [% ELSE %]
     This ordernumber does not exist.
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt
index 99d6459c94..4b19420edf 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt
@@ -105,9 +105,6 @@
                     <th>Basket</th>
                     <th>Basket group</th>
                     <th>Order line</th>
-                    <th class="hidden">Title</th>
-                    <th class="hidden">Author</th>
-                    <th class="hidden">ISBN</th>
                     <th>Summary</th>
                     <th>More</th>
                     <th>Replacement price</th>
@@ -118,24 +115,6 @@
                     <th>&nbsp;</th>
                     <th>&nbsp;</th>
                 </tr>
-                <tr>
-                    <th><div class="input-group input-group-sm"><input id="basket_name_column_search" type="text" class="form-control column-filter" data-search-fields='["basket.name"]'></div></th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["basket.basket_group.name"]'></div></th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["me.order_id"]'></div></th>
-                    <th class="hidden">&nbsp;</th>
-                    <th class="hidden">&nbsp;</th>
-                    <th class="hidden">&nbsp;</th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["me.internal_note","me.vendor_note","biblio.title","biblio.author","biblio.isbn","biblio.publisher","biblio.publication_year","biblio.copyright_date","biblio.suggestions.suggester.surname","biblio.suggestions.suggester.firstname"]'></div></th>
-                    <th>&nbsp;</th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["me.replacement_price"]'></div></th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["me.quantity"]'></div></th>
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["me.ecost"]'></div></th>
-                    <th>&nbsp;</th>
-                    <!--th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["order_cost"]'></div></th-->
-                    <th><div class="input-group input-group-sm"><input type="text" class="form-control column-filter" data-search-fields='["fund.name"]'></div></th>
-                    <th>&nbsp;</th>
-                    <th>&nbsp;</th>
-                </tr>
             </thead>
         </table>
     </div>
@@ -340,41 +319,6 @@
 <div class="col-sm-2 col-sm-pull-10">
 <aside>
 
-<form action="/cgi-bin/koha/acqui/parcel.pl" id="filterform" method="post">
-  <fieldset class="brief">
-    <h4>Filter</h4>
-    <ol>
-      <li>
-        <label for="summaryfilter">ISBN, author or title:</label>
-        <input type="text" name="summaryfilter" id="summaryfilter" value="[% summaryfilter | html %]"/>
-      </li>
-      <li>
-        <label for="basketfilter">Basket name:</label>
-        <input type="text" name="basketfilter" id="basketfilter" value="[% basketfilter | html %]"/>
-      </li>
-      <li>
-          <label for="basketgroupnamefilter">Basket group name:</label>
-          <input type="text" name="basketgroupnamefilter" id="basketgroupnamefilter" value="[% basketgroupnamefilter | html %]" />
-      </li>
-      <li>
-        <label for="orderfilter">Order line:</label>
-        <input type="text" name="orderfilter" id="orderfilter" value="[% orderfilter | html %]"/>
-      </li>
-      [% IF (UNIMARC) %]
-        <li>
-          <label for="eanfilter">EAN:</label>
-          <input type="text" name="eanfilter" id="eanfilter" value="[% eanfilter | html %]"/>
-        </li>
-      [% END %]
-    </ol>
-    <fieldset class="action">
-      <input type="hidden" value="search" name="op" />
-      <input type="hidden" value="[% invoiceid | html %]" name="invoiceid" />
-      <input type="submit" value="Filter" />
-      <a href="#" id="clear_form_filters">Clear</a>
-    </fieldset>
-  </fieldset>
-</form>
 [% INCLUDE 'acquisitions-menu.inc' %]
     </aside>
 </div> <!-- /.col-sm-2.col-sm-pull-10 -->
@@ -388,86 +332,8 @@
     <style>#dataPreview { width : 80%; } @media (max-width: 767px) { #dataPreview { margin: 0; width : auto; } }</style>
     <script>
         dt_overwrite_html_sorting_localeCompare();
-        var sticky_filters = [% sticky_filters | html %];
         var columns_filter = {};
 
-        function query_from_filters( base_query ) {
-
-            var query_and = [];
-
-            var summary      = $("#summaryfilter").val();
-            var basket_group = $("#basketgroupnamefilter").val();
-            var basket_name  = $("#basketfilter").val();
-            var order_id     = $("#orderfilter").val();
-            var ean          = $("#eanfilter").val();
-
-            // ean == undefined if the HTML doesn't have it
-            ean = (ean === undefined) ? '' : ean;
-
-            if ( basket_name != "" ) {
-                query_and.push( { "basket.name": { "like": '%'+basket_name+'%' } } );
-            }
-            if (basket_group != "") {
-                query_and.push( { "basket.basket_group.name": { "like": '%'+basket_group+'%' } } );
-            }
-            if (summary != "") {
-                query_and.push( { "-or": [{"biblio.title":  { "like": '%'+summary+'%' } },
-                            {"biblio.author": { "like": '%'+summary+'%' } },
-                            {"biblio.isbn":   { "like": '%'+summary+'%' } } ] } );
-            }
-            if (ean != "") {
-                query_and.push( { "biblio.ean": ean } );
-            }
-
-            $(".column-filter").each(function () {
-                var search_fields = $(this).data('search-fields');
-                var value = this.value;
-
-                if ( value != '' &&
-                     value != undefined ) { // skip if empty
-
-                    var columns_filters = [];
-
-                    search_fields.forEach( function (item) {
-                        if ( item === "me.order_id" ||
-                             item === "me.replacement_price" ||
-                             item === "me.quantity" ||
-                             item === "me.ecost" ) {
-                            // For numbers, we do 'starts with'
-                            var object = {};
-                            object[item] = { "like": value+'%' };
-                            columns_filters.push( object );
-                        }
-                        else {
-                            var object = {};
-                            object[item] = { "like": '%'+value+'%' };
-                            columns_filters.push( object );
-                        }
-                    });
-
-                    if ( columns_filters.length > 1 ) {
-                        query_and.push( { "-or": columns_filters } );
-                    }
-                    else if ( columns_filters.length > 0 ) {
-                        query_and.push( columns_filters[0] );
-                    }
-                }
-            });
-
-            var query_params = [];
-
-            if ( query_and.length > 0 ) {
-                query_and.push(base_query);
-                query_params.push('q=' + encodeURIComponent(JSON.stringify({ "-and": query_and })));
-            }
-
-            if ( order_id != "" ) {
-                query_params.push("order_id="+order_id);
-            }
-
-            return query_params;
-        }
-
         function _escape_str(s){
             return s != null ? s.escapeHtml() : "";
         }
@@ -480,7 +346,6 @@
             if ( $("#receivedt").length ) {
                 var receivedt = $("#receivedt").dataTable($.extend(true, {}, dataTablesDefaults, {
                     "bStateSave": true,
-                    "iCookieDuration": 60*60*24*1000, // 1000 days
                     "iDisplayLength": 10,
                     "aLengthMenu": [[5, 10, 20, 50, 100, -1], [5, 10, 20, 50, 100, _("All")]],
                     "aoColumnDefs": [
@@ -505,15 +370,9 @@
                 }));
             }
 
-            var base_query   = { "basket.vendor_id": [% booksellerid | html %]  };
-            var query_params = query_from_filters( base_query );
-
-            var THE_query = query_params.join("&");
-
-            var pending_orders_url = '/api/v1/acquisitions/orders?only_active=1';
             var pending_orders_table = $("#pending_orders").api({
                 "ajax": {
-                    "url": pending_orders_url + '&' + THE_query
+                    "url": '/api/v1/acquisitions/orders?only_active=1'
                 },
                 "header_filter": true,
                 "embed": [
@@ -527,8 +386,6 @@
                     "items"
                 ],
                 "stateSave": true, // remember state on page reload
-                "orderCellsTop": true, // tell the datatable sorting is done on the top row
-                'dom': 'C<"top pager"ilpfB><"#filter_c">tr<"bottom pager"ip>',
                 "drawCallback": function (settings) {
                     $(".previewData").on("click", function(e){
                         e.preventDefault();
@@ -539,20 +396,9 @@
                         $('#dataPreview').modal({show:true});
                     });
                 },
-                "columnDefs": [ {
-                    "targets": [3,4,5,9],
-                    "render": function (data, type, row, meta) {
-                        return _escape_str(data);
-                    }
-                },
-                {
-                    "targets": [8,10],
-                    "render": function (data, type, row, meta) {
-                        return _escape_price(data);
-                    }
-                } ],
                 "columns": [
                     { "data": "basket.name",
+                    "searchable": true,
                       "orderable": true,
                       "render": function(data, type, row, meta) {
                         if (type != 'display') return _escape_str(data);
@@ -584,27 +430,10 @@
                         }
                     },
                     {
-                        "data": "biblio.author",
-                        "visible": false,
-                        "searchable": true,
-                        "orderable": false
-                    },
-                    {
-                        "data": "biblio.title",
-                        "visible": false,
-                        "searchable": true,
-                        "orderable": false
-                    },
-                    {
-                        "data": "biblio.isbn",
-                        "visible": false,
-                        "searchable": true,
-                        "orderable": false
-                    },
-                    {
-                        "data": function(row, type, val, meta) {
+                        "data": "biblio.author:biblio.title:biblio.isbn",
+                        "render": function(data, type, row, meta) {
                             var result = '';
-                            if ( row.biblio_id != null ) {
+                            if ( row && row.biblio_id != null ) {
                                 result = "<p><a href=\"/cgi-bin/koha/catalogue/detail.pl?biblionumber="+encodeURIComponent(row.biblio_id)+"\">"+row.biblio.title.escapeHtml()+"</a>";
                                 if ( row.biblio.author != null )
                                     result += _(" by ") + row.biblio.author.escapeHtml();
@@ -672,16 +501,18 @@
 
                             return result;
                         },
-                        "orderable": false
+                        "orderable": false,
                     },
                     {
-                        "data": function( row, type, val, meta) {
+                        "data": "",
+                        "render": function(data, type, row, meta) {
                             var result = '<a href="/cgi-bin/koha/acqui/showorder.pl?ordernumber=' + encodeURIComponent(row.order_id) + '" class="previewData">' + _("Order") + '</a><br>'
                                 + '<a href="/cgi-bin/koha/catalogue/showmarc.pl?id=' + encodeURIComponent(row.biblio_id) + '" class="previewData">' + _("MARC") + '</a><br>'
                                 + '<a href="/cgi-bin/koha/catalogue/showmarc.pl?viewas=card&amp;id=' + encodeURIComponent(row.biblio_id) + '" class="previewData">' + _("Card") + '</a>';
                             return result;
                         },
-                        "orderable": false
+                        "orderable": false,
+                        "searchable": false
                     },
                     {
                         "data": "replacement_price"
@@ -694,10 +525,12 @@
                         "data": "ecost"
                     },
                     {
-                        "data": function ( row, type, val, meta ) {
+                        "data": "",
+                        "render": function(data, type, row, meta) {
                             return (row.quantity * row.ecost).format_price();
                         },
-                        "orderable": false // FIXME: How can we do it in DBIC?
+                        "orderable": false, // FIXME: How can we do it in DBIC?
+                        "searchable": false
                     },
                     {
                         "data": "fund.name",
@@ -707,17 +540,20 @@
                         }
                     },
                     {
-                        "data": function( row, type, val, meta ) {
+                        "data": "",
+                        "render": function(data, type, row, meta) {
                             return '<a href="orderreceive.pl?ordernumber='
                                     + encodeURIComponent(row.order_id) + '&amp;invoiceid=[% invoiceid | uri %]' + '">'
                                     + _("Receive") + '</a><br/>'
                                     + '<a href="#" onclick="transfer_order_popup(' + row.order_id.escapeHtml() + '); return false;">'
                                     + _("Transfer") + '</a>';
                         },
-                        "orderable": false
+                        "orderable": false,
+                        "searchable": false
                     },
                     {
-                        "data": function( row, type, val, meta ) {
+                        "data": "",
+                        "render": function(data, type, row, meta) {
                             var result = "";
 
                             if ( row.current_holds_count > 0 ) {
@@ -775,52 +611,11 @@
 
                             return result;
                         },
-                        "orderable": false
+                        "orderable": false,
+                        "searchable": false
                     }
                 ]
-            });
-
-            // column filter events handling
-            $(".column-filter").each(function () {
-                $(this).on( 'keyup change', function () {
-                    // recalculate the query
-                    var query_params = query_from_filters( base_query );
-                    // refresh the datatable as appropriate
-                    if ( query_params.length > 0 ) {
-                        pending_orders_table.api().ajax.url( pending_orders_url + '&' + query_params.join("&") );
-                    }
-                    else {
-                        pending_orders_table.api().ajax.url( pending_orders_url + '&q=' + encodeURI(JSON.stringify(base_query)) );
-                    }
-                    pending_orders_table.api().ajax.reload( null, false );
-                });
-            });
-
-            $("#filterform").on("submit", function(e) {
-                e.preventDefault();
-
-                // Save the filters in the cookie
-                $.cookie("filter_parcel_summary", $("#summaryfilter").val());
-                $.cookie("filter_parcel_basketname", $("#basketfilter").val());
-                $.cookie("filter_parcel_orderno", $("#orderfilter").val());
-                $.cookie("filter_parcel_basketgroupname", $("#basketgroupnamefilter").val());
-                $.cookie("filter_parcel_ean", $("#eanfilter").val());
-
-                var query_params = query_from_filters( base_query );
-
-                if ( query_params.length > 0 ) {
-                    pending_orders_table.api().ajax.url( pending_orders_url + '&' + query_params.join("&") );
-                }
-                else {
-                    pending_orders_table.api().ajax.url( pending_orders_url + '&q=' + encodeURI(JSON.stringify(base_query)) );
-                }
-                pending_orders_table.api().ajax.reload( null, false );
-            });
-
-            $('#clear_form_filters').on("click", function(){
-                $(this).closest('form').find("input[type=text], textarea").val("");
-                pending_orders_table.api().ajax.url(pending_orders_url + '&q=' + encodeURI(JSON.stringify(base_query))).draw();
-            });
+            }, {}, 1, {"basket.vendor_id": [% booksellerid | html %]});
 
             $(".previewData").on("click", function(e){
                 e.preventDefault();
diff --git a/koha-tmpl/intranet-tmpl/prog/js/datatables.js b/koha-tmpl/intranet-tmpl/prog/js/datatables.js
index be2da8c976..33a21d8287 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/datatables.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/datatables.js
@@ -509,7 +509,7 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
 
 (function($) {
 
-    $.fn.api = function(options, columns_settings, add_filters) {
+    $.fn.api = function(options, columns_settings, add_filters, default_filters) {
         var settings = null;
 
         if ( add_filters ) {
@@ -563,6 +563,22 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
                                     _per_page: length
                                 };
 
+
+                                function build_query(col){
+                                    var parts = [];
+                                    var attributes = col.data.split(':');
+                                    for (var i=0;i<attributes.length;i++){
+                                        var part = {};
+                                        var attr = attributes[i];
+                                        var value = data.columns[col.idx].search.value;
+                                        part[!attr.includes('.')?'me.'+attr:attr] = options.criteria === 'exact'
+                                            ? value
+                                            : {like: (['contains', 'ends_with'].indexOf(options.criteria) !== -1?'%':'') + value + (['contains', 'starts_with'].indexOf(options.criteria) !== -1?'%':'')};
+                                        parts.push(part);
+                                    }
+                                    return parts;
+                                }
+
                                 var filter = data.search.value;
                                 // Build query for each column filter
                                 var and_query_parameters = settings.aoColumns
@@ -570,10 +586,10 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
                                     return col.bSearchable && typeof col.data == 'string' && data.columns[col.idx].search.value != ''
                                 })
                                 .map(function(col) {
-                                    var part = {};
-                                    var value = data.columns[col.idx].search.value;
-                                    part[!col.data.includes('.')?'me.'+col.data:col.data] = options.criteria === 'exact'?value:{like: (['contains', 'ends_with'].indexOf(options.criteria) !== -1?'%':'')+value+(['contains', 'starts_with'].indexOf(options.criteria) !== -1?'%':'')};
-                                    return part;
+                                    return build_query(col)
+                                })
+                                .map(function r(e){
+                                    return ($.isArray(e) ? $.map(e, r) : e);
                                 });
 
                                 // Build query for the global search filter
@@ -582,12 +598,15 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
                                     return col.bSearchable && typeof col.data == 'string' && data.columns[col.idx].search.value == '' && filter != ''
                                 })
                                 .map(function(col) {
-                                    var part = {};
-                                    value = filter;
-                                    part[!col.data.includes('.')?'me.'+col.data:col.data] = options.criteria === 'exact'?value:{like: (['contains', 'ends_with'].indexOf(options.criteria) !== -1?'%':'')+value+(['contains', 'starts_with'].indexOf(options.criteria) !== -1?'%':'')};
-                                    return part;
+                                    return build_query(col)
+                                })
+                                .map(function r(e){
+                                    return ($.isArray(e) ? $.map(e, r) : e);
                                 });
 
+                                if ( default_filters ) {
+                                    and_query_parameters.push(default_filters);
+                                }
                                 query_parameters = and_query_parameters;
                                 query_parameters.push(or_query_parameters);
                                 if(query_parameters.length) {
@@ -748,8 +767,13 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
                 var is_searchable = table_dt.settings()[0].aoColumns[i].bSearchable;
                 if ( is_searchable ) {
                     var title = $(this).text();
-                    var search_title = _("Search %s").format(title);
-                    $(this).html( '<input type="text" placeholder="%s" />'.format(search_title) );
+                    var existing_search = table_dt.column(i).search();
+                    if ( existing_search ) {
+                        $(this).html( '<input type="text" value="%s" style="width: 100%" />'.format(existing_search) );
+                    } else {
+                        var search_title = _("Search %s").format(title);
+                        $(this).html( '<input type="text" placeholder="%s" style="width: 100%" />'.format(search_title) );
+                    }
 
                     $( 'input', this ).on( 'keyup change', function () {
                         if ( table_dt.column(i).search() !== this.value ) {
-- 
2.11.0