Bugzilla – Attachment 181608 Details for
Bug 39600
Use the API to render the OPAC ILL requests table
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 39600: Add server-side rendering to OPAC datatables
Bug-39600-Add-server-side-rendering-to-OPAC-datata.patch (text/plain), 14.46 KB, created by
Tomás Cohen Arazi (tcohen)
on 2025-04-28 12:46:53 UTC
(
hide
)
Description:
Bug 39600: Add server-side rendering to OPAC datatables
Filename:
MIME Type:
Creator:
Tomás Cohen Arazi (tcohen)
Created:
2025-04-28 12:46:53 UTC
Size:
14.46 KB
patch
obsolete
>From 09a8715490b886dd3ddf8724d7bcba888731d052 Mon Sep 17 00:00:00 2001 >From: Tomas Cohen Arazi <tomascohen@theke.io> >Date: Mon, 28 Apr 2025 09:45:14 -0300 >Subject: [PATCH] Bug 39600: Add server-side rendering to OPAC datatables > >If commit 03dd9efb72d06dee6dc256b8d2e138ed7fc7b3ca gets reverted, we >will still need to implement the server-side rendering feature from >Staff. > >This patch does that. > >Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> >--- > .../opac-tmpl/bootstrap/js/datatables.js | 316 +++++++++++++++++- > 1 file changed, 315 insertions(+), 1 deletion(-) > >diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js >index 0fc33651d15..23abc55bb6e 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js >+++ b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js >@@ -61,6 +61,309 @@ function toggledClearFilter(searchText, tableId) { > } > } > >+function _dt_default_ajax(params) { >+ let default_filters = params.default_filters; >+ let options = params.options; >+ >+ if ( >+ !options.criteria || >+ ["contains", "starts_with", "ends_with", "exact"].indexOf( >+ options.criteria.toLowerCase() >+ ) === -1 >+ ) >+ options.criteria = "contains"; >+ options.criteria = options.criteria.toLowerCase(); >+ >+ return { >+ type: "GET", >+ cache: true, >+ dataSrc: "data", >+ beforeSend: function (xhr, settings) { >+ this._xhr = xhr; >+ if (options.embed) { >+ xhr.setRequestHeader( >+ "x-koha-embed", >+ Array.isArray(options.embed) >+ ? options.embed.join(",") >+ : options.embed >+ ); >+ } >+ }, >+ dataFilter: function (data, type) { >+ var json = { data: JSON.parse(data) }; >+ if ((total = this._xhr.getResponseHeader("x-total-count"))) { >+ json.recordsTotal = total; >+ json.recordsFiltered = total; >+ } >+ if ((total = this._xhr.getResponseHeader("x-base-total-count"))) { >+ json.recordsTotal = total; >+ } >+ if ((draw = this._xhr.getResponseHeader("x-koha-request-id"))) { >+ json.draw = draw; >+ } >+ >+ return JSON.stringify(json); >+ }, >+ data: function (data, settings) { >+ var length = data.length; >+ var start = data.start; >+ >+ let api = new $.fn.dataTable.Api(settings); >+ const global_search = api.search(); >+ >+ var dataSet = { >+ _page: Math.floor(start / length) + 1, >+ _per_page: length, >+ }; >+ >+ function build_query(col, value) { >+ var parts = []; >+ var attributes = col.data.split(":"); >+ for (var i = 0; i < attributes.length; i++) { >+ var part = {}; >+ var attr = attributes[i]; >+ let default_build = true; >+ let criteria = options.criteria; >+ if (value.match(/^\^(.*)\$$/)) { >+ value = value.replace(/^\^/, "").replace(/\$$/, ""); >+ criteria = "exact"; >+ } else { >+ // escape SQL LIKE special characters % >+ value = value.replace(/(\%|\\)/g, "\\$1"); >+ } >+ >+ let built_value; >+ if (col.type == "date") { >+ let rfc3339 = $date_to_rfc3339(value); >+ if (rfc3339 != "Invalid Date") { >+ built_value = rfc3339; >+ } >+ } >+ >+ if (col.datatype !== undefined) { >+ default_build = false; >+ let coded_datatype = >+ col.datatype.match(/^coded_value:(.*)/); >+ if (col.datatype == "related-object") { >+ let query_term = value; >+ >+ if (criteria != "exact") { >+ query_term = { >+ like: >+ (["contains", "ends_with"].indexOf( >+ criteria >+ ) !== -1 >+ ? "%" >+ : "") + >+ value + >+ (["contains", "starts_with"].indexOf( >+ criteria >+ ) !== -1 >+ ? "%" >+ : ""), >+ }; >+ } >+ >+ part = { >+ [col.related + "." + col.relatedKey]: >+ col.relatedValue, >+ [col.related + "." + col.relatedSearchOn]: >+ query_term, >+ }; >+ } else if ( >+ coded_datatype && >+ coded_datatype.length > 1 >+ ) { >+ if (global_search.length || value.length) { >+ coded_datatype = coded_datatype[1]; >+ const search_value = value.length >+ ? value >+ : global_search; >+ const regex = new RegExp( >+ `^${search_value}`, >+ "i" >+ ); >+ if ( >+ coded_values && >+ coded_values.hasOwnProperty(coded_datatype) >+ ) { >+ let codes = [ >+ ...coded_values[ >+ coded_datatype >+ ].entries(), >+ ] >+ .filter(([label]) => regex.test(label)) >+ .map(([, code]) => code); >+ >+ if (codes.length) { >+ part[ >+ !attr.includes(".") >+ ? "me." + attr >+ : attr >+ ] = codes; >+ } else { >+ // Coded value not found using the description, fallback to code >+ default_build = true; >+ } >+ } else { >+ console.log( >+ "coded datatype %s not supported yet".format( >+ coded_datatype >+ ) >+ ); >+ } >+ } else { >+ default_build = true; >+ } >+ } else { >+ console.log( >+ "datatype %s not supported yet".format( >+ col.datatype >+ ) >+ ); >+ } >+ } >+ if (default_build) { >+ let value_part; >+ if (criteria === "exact") { >+ value_part = built_value >+ ? [value, built_value] >+ : value; >+ } else { >+ let like = { >+ like: >+ (["contains", "ends_with"].indexOf( >+ criteria >+ ) !== -1 >+ ? "%" >+ : "") + >+ value + >+ (["contains", "starts_with"].indexOf( >+ criteria >+ ) !== -1 >+ ? "%" >+ : ""), >+ }; >+ value_part = built_value >+ ? [like, built_value] >+ : like; >+ } >+ >+ part[!attr.includes(".") ? "me." + attr : attr] = >+ value_part; >+ } >+ >+ if (Object.keys(part).length) parts.push(part); >+ } >+ return parts; >+ } >+ >+ var filter = data.search.value; >+ // Build query for each column filter >+ var and_query_parameters = settings.aoColumns >+ .filter(function (col) { >+ return ( >+ col.bSearchable && >+ typeof col.data == "string" && >+ data.columns[col.idx].search.value != "" >+ ); >+ }) >+ .map(function (col) { >+ var value = data.columns[col.idx].search.value; >+ return build_query(col, value); >+ }) >+ .map(function r(e) { >+ return $.isArray(e) ? $.map(e, r) : e; >+ }); >+ >+ // Build query for the global search filter >+ var or_query_parameters = settings.aoColumns >+ .filter(function (col) { >+ return col.bSearchable && filter != ""; >+ }) >+ .map(function (col) { >+ var value = filter; >+ return build_query(col, value); >+ }) >+ .map(function r(e) { >+ return $.isArray(e) ? $.map(e, r) : e; >+ }); >+ >+ if (default_filters) { >+ let additional_filters = {}; >+ for (f in default_filters) { >+ let k; >+ let v; >+ if (typeof default_filters[f] === "function") { >+ let val = default_filters[f](); >+ if (val != undefined && val != "") { >+ k = f; >+ v = val; >+ } >+ } else { >+ k = f; >+ v = default_filters[f]; >+ } >+ >+ // Pass to -or if you want a separate OR clause >+ // It's not the usual DBIC notation! >+ if (f == "-or") { >+ if (v) or_query_parameters.push(v); >+ } else if (f == "-and") { >+ if (v) and_query_parameters.push(v); >+ } else if (v) { >+ additional_filters[k] = v; >+ } >+ } >+ if (Object.keys(additional_filters).length) { >+ and_query_parameters.push(additional_filters); >+ } >+ } >+ query_parameters = and_query_parameters; >+ if (or_query_parameters.length) { >+ query_parameters.push(or_query_parameters); >+ } >+ >+ if (query_parameters.length) { >+ query_parameters = JSON.stringify( >+ query_parameters.length === 1 >+ ? query_parameters[0] >+ : { "-and": query_parameters } >+ ); >+ dataSet.q = query_parameters; >+ } >+ >+ dataSet._match = options.criteria; >+ >+ if (data["draw"] !== undefined) { >+ settings.ajax.headers = { "x-koha-request-id": data.draw }; >+ } >+ >+ if (options.columns) { >+ var order = data.order; >+ var orderArray = new Array(); >+ order.forEach(function (e, i) { >+ var order_col = e.column; >+ var order_by = options.columns[order_col].data; >+ order_by = order_by.split(":"); >+ var order_dir = e.dir == "asc" ? "+" : "-"; >+ Array.prototype.push.apply( >+ orderArray, >+ order_by.map( >+ x => order_dir + (!x.includes(".") ? "me." + x : x) >+ ) >+ ); >+ }); >+ dataSet._order_by = orderArray >+ .filter((v, i, a) => a.indexOf(v) === i) >+ .join(","); >+ } >+ >+ return dataSet; >+ }, >+ }; >+} >+ > (function () { > /* Plugin to allow text sorting to ignore articles > * >@@ -280,7 +583,7 @@ function _dt_visibility(table_settings, table_dt) { > * available from the columns_settings template toolkit include > * @return {Object} The dataTables instance > */ >- $.fn.kohaTable = function (options = {}, table_settings = undefined) { >+ $.fn.kohaTable = function (options = {}, table_settings = undefined, default_filters) { > // Early return if the node does not exist > if (!this.length) return; > >@@ -293,6 +596,17 @@ function _dt_visibility(table_settings, table_dt) { > dataTablesDefaults.initComplete(settings, json); > }; > } >+ >+ if (options.ajax && !options.bKohaAjaxSVC) { >+ options.ajax = Object.assign( >+ {}, >+ options.ajax, >+ _dt_default_ajax({ default_filters, options }) >+ ); >+ options.serverSide = true; >+ options.processing = true; >+ options.pagingType = "full_numbers"; >+ } > } > > var settings = $.extend( >-- >2.49.0
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 39600
:
180758
|
180759
|
180760
|
180761
|
180762
|
180763
|
180764
|
180778
|
180781
|
180785
|
180786
|
180787
|
180821
|
181132
|
181133
|
181134
|
181135
|
181437
|
181438
|
181439
|
181440
|
181441
|
181442
|
181443
|
181524
|
181525
|
181526
|
181532
|
181533
|
181534
|
181535
|
181536
|
181537
|
181538
|
181608
|
181652