From 205ac06fddf70e4b15f3dc1fc9e5c5527e36a80f Mon Sep 17 00:00:00 2001 From: David Cook Date: Tue, 28 Oct 2025 05:55:49 +0000 Subject: [PATCH] Bug 40525: [22.11] Update spreadsheet format to escape formulas --- .../en/includes/columns_settings.inc | 7 +- .../bootstrap/en/includes/datatables.inc | 1 + .../bootstrap/en/modules/opac-user.tt | 5 +- .../opac-tmpl/bootstrap/js/datatables.js | 4 +- .../bootstrap/js/datatables_buttons_shim.js | 112 ++++++++++++++++++ 5 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/datatables_buttons_shim.js diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/columns_settings.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/columns_settings.inc index 78935a9d5cc..2f2b60a5a2f 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/columns_settings.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/columns_settings.inc @@ -1,5 +1,6 @@ [% USE TablesSettings %] - +[% USE raw %] +[% Asset.js("js/datatables_buttons_shim.js") | $raw %] +[% Asset.js("js/datatables_buttons_shim.js") | $raw %] [% Asset.js("js/datatables.js") | $raw %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt index d52152e6930..580fc4c5855 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -1191,6 +1191,8 @@ ); }); + const export_format_spreadsheet = ButtonsShim.export_format_spreadsheet; + var dTables = $("#checkoutst,#holdst,#overduest,#opac-user-relative-issues-table"); dTables.each(function(){ var thIndex = $(this).find("th.psort").index(); @@ -1231,7 +1233,8 @@ extend: "csv", exportOptions: { /* CSV export should include all columns (even invisible ones) unless they are .noExport */ - columns: ":not(.noExport)" + columns: ":not(.noExport)", + format: export_format_spreadsheet } } ] diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js index 566ee9869bc..ad5f27502dc 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js +++ b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js @@ -237,6 +237,8 @@ $.fn.dataTable.ext.buttons.clearFilter = { } }; + const export_format_spreadsheet = ButtonsShim.export_format_spreadsheet; + var export_buttons = [ { extend: 'excelHtml5', @@ -251,7 +253,7 @@ $.fn.dataTable.ext.buttons.clearFilter = { text: __("CSV"), exportOptions: { columns: exportColumns, - format: export_format + format: export_format_spreadsheet }, }, { diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/datatables_buttons_shim.js b/koha-tmpl/opac-tmpl/bootstrap/js/datatables_buttons_shim.js new file mode 100644 index 00000000000..0167c523391 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/js/datatables_buttons_shim.js @@ -0,0 +1,112 @@ +window.ButtonsShim = {}; + +ButtonsShim.export_format_spreadsheet = { + body: function (data, row, column, node) { + const newnode = node.cloneNode(true); + const no_export_nodes = newnode.querySelectorAll(".no-export"); + no_export_nodes.forEach(child => { + child.parentNode.removeChild(child); + }); + //Note: innerHTML is the same thing as the data variable, + //minus the ".no-export" nodes that we've removed + //Note: See dataTables.buttons.js for original function usage + const str = ButtonsShim.stripData(newnode.innerHTML, { + decodeEntities: false, + stripHtml: true, + stripNewlines: true, + trim: true, + escapeExcelFormula: true + }); + return str; + }, +}; + +ButtonsShim._stripHtml = function (input) { + var _max_str_len = Math.pow(2, 28); + var _re_html = /<([^>]*>)/g; + if (! input || typeof input !== 'string') { + return input; + } + // Irrelevant check to workaround CodeQL's false positive on the regex + if (input.length > _max_str_len) { + throw new Error('Exceeded max str len'); + } + var previous; + input = input.replace(_re_html, ''); // Complete tags + // Safety for incomplete script tag - use do / while to ensure that + // we get all instances + do { + previous = input; + input = input.replace(/