From 14dbe7481b5178d170acbe7d404493056552a7ac Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Fri, 7 Dec 2018 14:44:04 +0100 Subject: [PATCH] Bug 21716: Do not use TT to produce JSON for item search results It was initially done this way to have translatable strings in the results table, but this method has flaws and it can be done in a better way. This patch moves the rendering code to client-side javascript, so the JSON returned by the server now contains only unprocessed data. Also, this patch makes use of the new i18n macros for translation and fixes a whitespace bug in i18n.inc Reported-by: Claire Gravely --- catalogue/itemsearch.pl | 33 +++- .../catalogue/itemsearch_item.json.inc | 26 --- .../intranet-tmpl/prog/en/includes/i18n.inc | 36 ++-- .../prog/en/modules/catalogue/itemsearch.tt | 164 ++++++++++++++++-- .../en/modules/catalogue/itemsearch_json.tt | 11 -- 5 files changed, 198 insertions(+), 72 deletions(-) delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_json.tt diff --git a/catalogue/itemsearch.pl b/catalogue/itemsearch.pl index ab833db3e2..fc0d94c460 100755 --- a/catalogue/itemsearch.pl +++ b/catalogue/itemsearch.pl @@ -40,8 +40,6 @@ my $format = $cgi->param('format'); my $template_name = 'catalogue/itemsearch.tt'; if (defined $format and $format eq 'json') { - $template_name = 'catalogue/itemsearch_json.tt'; - # Map DataTables parameters with 'regular' parameters $cgi->param('rows', scalar $cgi->param('iDisplayLength')); $cgi->param('page', (scalar $cgi->param('iDisplayStart') / scalar $cgi->param('iDisplayLength')) + 1); @@ -260,8 +258,35 @@ if (scalar keys %params > 0) { print "$line\n" unless $line =~ m|^\s*$|; } } elsif ($format eq 'json') { - $template->param(sEcho => scalar $cgi->param('sEcho')); - output_with_http_headers $cgi, $cookie, $template->output, 'json'; + my $response = { + sEcho => scalar $cgi->param('sEcho'), + iTotalRecords => $total_rows, + iTotalDisplayRecords => $total_rows, + aaData => [ map { + my $av = Koha::AuthorisedValues->search({ category => 'CCODE', authorised_value => $_->{ccode} }); + my $ccode = $av->count ? $av->next->lib : $_->{ccode}; + my $homebranch = Koha::Libraries->find($_->{homebranch}); + my $holdingbranch = Koha::Libraries->find($_->{holdingbranch}); + { + biblionumber => $_->{biblionumber}, + itemnumber => $_->{itemnumber}, + author => $_->{biblio}->author, + title => $_->{biblio}->title, + publicationyear => $_->{biblioitem}->{publicationyear} || $_->{biblio}->copyrightdate, + publishercode => $_->{biblioitem}->{publishercode}, + ccode => $ccode, + barcode => $_->{barcode}, + itemcallnumber => $_->{itemcallnumber}, + homebranch => $homebranch ? $homebranch->branchname : '', + holdingbranch => $holdingbranch ? $holdingbranch->branchname : '', + location => $_->{location}, + stocknumber => $_->{stocknumber}, + notforloan => $_->{status}, + issues => $_->{issues} || 0, + }; + } @$results ], + }; + output_with_http_headers $cgi, $cookie, encode_json($response), 'json'; } exit; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc deleted file mode 100644 index 45b43f647b..0000000000 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.json.inc +++ /dev/null @@ -1,26 +0,0 @@ -[%- USE Branches -%] -[%- USE Koha -%] -[% USE AuthorisedValues %] -[%- biblio = item.biblio -%] -[%- biblioitem = item.biblioitem -%] -[ - "[% FILTER escape_quotes = replace('"', '\"') ~%] - [% biblio.title | html %][% IF ( Koha.Preference( 'marcflavour' ) == 'UNIMARC' && biblio.author ) %] by[% END %] [% biblio.author | html %] - [%~ END %]", - "[% (biblioitem.publicationyear || biblio.copyrightdate) | html %]", - "[% biblioitem.publishercode | html %]", - "[% AuthorisedValues.GetByCode( 'CCODE', item.ccode ) | html %]", - "[% FILTER escape_quotes ~%] - [% item.barcode | html %] - [%~ END %]", - "[% item.itemcallnumber | html %]", - "[% Branches.GetName(item.homebranch) | html %]", - "[% Branches.GetName(item.holdingbranch) | html %]", - "[% item.location | html %]", - "[% item.stocknumber | html %]", - "[% item.status | html %]", - "[% (item.issues || 0) | html %]", - "[% FILTER escape_quotes ~%] - - [%~ END %]" -] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc index 3ad521e843..a95b7562d6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc @@ -1,38 +1,38 @@ [% USE I18N %] [% USE raw %] -[% MACRO t(msgid) BLOCK %] +[% MACRO t(msgid) BLOCK ~%] [% I18N.t(msgid) | $raw %] -[% END %] +[%~ END %] -[% MACRO tx(msgid, vars) BLOCK %] +[% MACRO tx(msgid, vars) BLOCK ~%] [% I18N.tx(msgid, vars) | $raw %] -[% END %] +[%~ END %] -[% MACRO tn(msgid, msgid_plural, count) BLOCK %] +[% MACRO tn(msgid, msgid_plural, count) BLOCK ~%] [% I18N.tn(msgid, msgid_plural, count) | $raw %] -[% END %] +[%~ END %] -[% MACRO tnx(msgid, msgid_plural, count, vars) BLOCK %] +[% MACRO tnx(msgid, msgid_plural, count, vars) BLOCK ~%] [% I18N.tnx(msgid, msgid_plural, count, vars) | $raw %] -[% END %] +[%~ END %] -[% MACRO txn(msgid, msgid_plural, count, vars) BLOCK %] +[% MACRO txn(msgid, msgid_plural, count, vars) BLOCK ~%] [% I18N.txn(msgid, msgid_plural, count, vars) | $raw %] -[% END %] +[%~ END %] -[% MACRO tp(msgctxt, msgid) BLOCK %] +[% MACRO tp(msgctxt, msgid) BLOCK ~%] [% I18N.tp(msgctxt, msgid) | $raw %] -[% END %] +[%~ END %] -[% MACRO tpx(msgctxt, msgid, vars) BLOCK %] +[% MACRO tpx(msgctxt, msgid, vars) BLOCK ~%] [% I18N.tpx(msgctxt, msgid, vars) | $raw %] -[% END %] +[%~ END %] -[% MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK %] +[% MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK ~%] [% I18N.tnp(msgctxt, msgid, msgid_plural, count) | $raw %] -[% END %] +[%~ END %] -[% MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK %] +[% MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK ~%] [% I18N.tnpx(msgctxt, msgid, msgid_plural, count, vars) | $raw %] -[% END %] +[%~ END %] 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 a7abe0f9f7..ee275f1971 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch.tt @@ -1,6 +1,9 @@ [% USE raw %] [% USE To %] [% USE Asset %] +[% USE Koha %] +[% USE JSON.Escape %] +[% PROCESS 'i18n.inc' %] [% BLOCK form_label %] [% SWITCH label %] @@ -364,19 +367,154 @@ }, 'sDom': '<"top pager"ilp>t<"bottom pager"ip>r', 'aoColumns': [ - { 'sName': 'title' }, - { 'sName': 'publicationyear' }, - { 'sName': 'publishercode' }, - { 'sName': 'ccode' }, - { 'sName': 'barcode' }, - { 'sName': 'itemcallnumber' }, - { 'sName': 'homebranch' }, - { 'sName': 'holdingbranch' }, - { 'sName': 'location' }, - { 'sName': 'stocknumber' }, - { 'sName': 'notforloan' }, - { 'sName': 'issues' }, - { 'sName': 'checkbox', 'bSortable': false } + { + 'sName': 'title', + 'mData': 'title', + 'mRender': function (sData, sType, oRow) { + const a = document.createElement('a'); + a.appendChild(document.createTextNode(oRow.title)); + a.href = '/cgi-bin/koha/catalogue/detail.pl?biblionumber=' + oRow.biblionumber; + a.setAttribute('title', [% t('Go to record detail page').json %]); + + let output = a.outerHTML; + if (oRow.author) { + const marcflavour = [% Koha.Preference('marcflavour').json %]; + if (marcflavour === 'UNIMARC') { + output += ' ' + [% t('by').json %]; + } + output += ' ' + oRow.author; + } + + return output; + } + }, + { + 'sName': 'publicationyear', + 'mData': 'publicationyear', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'publishercode', + 'mData': 'publishercode', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'ccode', + 'mData': 'ccode', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'barcode', + 'mData': 'barcode', + 'mRender': function (sData, sType, oRow) { + let output = ''; + if (oRow.barcode) { + const a = document.createElement('a'); + a.href = '/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=' + oRow.biblionumber + '#item' + oRow.itemnumber; + a.setAttribute('title', [% t('Go to item details').json %]); + a.appendChild(document.createTextNode(oRow.barcode)); + output = a.outerHTML; + } + + return output; + } + }, + { + 'sName': 'itemcallnumber', + 'mData': 'itemcallnumber', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'homebranch', + 'mData': 'homebranch', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'holdingbranch', + 'mData': 'holdingbranch', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'location', + 'mData': 'location', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'stocknumber', + 'mData': 'stocknumber', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'notforloan', + 'mData': 'notforloan', + 'mRender': function (sData) { + return sData ? sData.escapeHtml() : ''; + } + }, + { + 'sName': 'issues', + 'mData': 'issues', + }, + { + 'sName': 'checkbox', + 'bSortable': false, + 'mData': null, + 'mRender': function (sData, sType, oRow) { + const pencil = document.createElement('i'); + pencil.classList.add('fa', 'fa-pencil'); + + const edit = document.createTextNode(' ' + [% t('Edit').json %] + ' '); + + const caret = document.createElement('span'); + caret.classList.add('caret') + + const button = document.createElement('button'); + button.type = 'button'; + button.classList.add('btn', 'btn-xs', 'btn-default', 'dropdown-toggle'); + button.setAttribute('data-toggle', 'dropdown'); + button.setAttribute('aria-haspopup', 'true'); + button.setAttribute('aria-expanded', 'false'); + button.appendChild(pencil); + button.appendChild(edit); + button.appendChild(caret); + + const edit_item = document.createElement('a'); + edit_item.href = '/cgi-bin/koha/cataloguing/additem.pl?op=edititem&biblionumber=' + oRow.biblionumber + '&itemnumber=' + oRow.itemnumber; + edit_item.appendChild(document.createTextNode([% t('Edit item').json %])); + + const edit_record = document.createElement('a'); + edit_record.href = '/cgi-bin/koha/cataloguing/addbiblio.pl?biblionumber=' + oRow.biblionumber; + edit_record.appendChild(document.createTextNode([% t('Edit record').json %])); + + const ul = document.createElement('ul'); + ul.classList.add('dropdown-menu'); + ul.appendChild(document.createElement('li').appendChild(edit_item).parentNode); + ul.appendChild(document.createElement('li').appendChild(edit_record).parentNode); + + const div = document.createElement('div'); + div.classList.add('btn-group', 'dropup'); + div.appendChild(button); + div.appendChild(ul); + + return div.outerHTML; + } + } ], "sPaginationType": "full_numbers" })).columnFilter({ diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_json.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_json.tt deleted file mode 100644 index 3c7b17d61b..0000000000 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_json.tt +++ /dev/null @@ -1,11 +0,0 @@ -{ - "sEcho": [% sEcho | html %], - "iTotalRecords": [% total_rows | html %], - "iTotalDisplayRecords": [% total_rows | html %], - "aaData": [ - [%- FOREACH item IN results -%] - [%- INCLUDE 'catalogue/itemsearch_item.json.inc' item = item -%] - [%- UNLESS loop.last %],[% END -%] - [%- END -%] - ] -} -- 2.17.1