From 8c2564b6a23decaddfee167a87c5c7ccc459ef92 Mon Sep 17 00:00:00 2001
From: Ivan Dziuba <ivan.dziuba@inlibro.com>
Date: Thu, 22 Apr 2021 18:22:41 -0400
Subject: [PATCH] Bug 27428: Show the number of records in the bibliographic
 record detail page with ElasticSearch.

Tests:
1. Apply this patch;
2. In the Preference system - OPACDisplayRecordCount -> Show;
3. We can see the number of records next to search links in the bibliographic record detail page in the OPAC.

Signed-off-by: Stina Hallin <stina.hallin@ub.lu.se>
---
 Koha/SearchEngine/Elasticsearch/Browse.pm     | 42 +++++++++
 ...ber_records_next_to_search_links_opac.perl |  8 ++
 installer/data/mysql/mandatory/sysprefs.sql   |  3 +-
 .../modules/admin/preferences/searching.pref  |  8 ++
 .../bootstrap/en/includes/doc-head-close.inc  |  3 +
 .../bootstrap/en/modules/opac-detail.tt       |  4 +-
 .../bootstrap/js/opac-display-record-count.js | 88 +++++++++++++++++++
 opac/svc/elasticsearch/opac-elasticsearch.pl  | 51 +++++++++++
 8 files changed, 205 insertions(+), 2 deletions(-)
 create mode 100644 installer/data/mysql/atomicupdate/bug_27428-number_records_next_to_search_links_opac.perl
 create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/opac-display-record-count.js
 create mode 100755 opac/svc/elasticsearch/opac-elasticsearch.pl

diff --git a/Koha/SearchEngine/Elasticsearch/Browse.pm b/Koha/SearchEngine/Elasticsearch/Browse.pm
index 85070730a7..5d4282ed2f 100644
--- a/Koha/SearchEngine/Elasticsearch/Browse.pm
+++ b/Koha/SearchEngine/Elasticsearch/Browse.pm
@@ -48,6 +48,7 @@ as "suggestible" in the database when indexing takes place.
 
 use base qw(Koha::SearchEngine::Elasticsearch);
 use Modern::Perl;
+use JSON;
 
 =head2 browse
 
@@ -162,6 +163,47 @@ sub _build_query {
     return $query;
 }
 
+sub _build_query_count_record {
+    my ($self, $cgi_q, $key, $item) = @_;
+    my $query = {
+        query => {
+            match_phrase => {
+                $key  => $item
+            }
+        }
+    };
+    return $query;
+}
+
+sub count_record {
+    my ($self, $cgi_q) = @_;
+    my @results_return;
+    my $decoded;
+    $cgi_q =~ s/\{\'/\{\"/g;
+    $cgi_q =~ s/\'\}/\"\}/g;
+    #$cgi_q =~ s/\'\:\'/\"\:\"}/g;
+    eval {
+        $decoded = JSON::XS::decode_json($cgi_q);
+    };
+    if ($@) {
+        warn "Caught JSON::XS decode error: $_";
+    }
+
+    foreach my $item (@$decoded)
+    {
+        for my $key ( keys %$item ){
+            my $elasticsearch = $self->get_elasticsearch();
+            my $query = $self->_build_query_count_record($cgi_q, $key, $$item{$key});
+            my $res = $elasticsearch->search(
+                index => $self->index_name,
+                body => $query
+            );
+        push(@results_return, $res->{'hits'}->{'total'});
+        }
+    }
+    return \@results_return;
+}
+
 1;
 
 __END__
diff --git a/installer/data/mysql/atomicupdate/bug_27428-number_records_next_to_search_links_opac.perl b/installer/data/mysql/atomicupdate/bug_27428-number_records_next_to_search_links_opac.perl
new file mode 100644
index 0000000000..1a63dbb788
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/bug_27428-number_records_next_to_search_links_opac.perl
@@ -0,0 +1,8 @@
+$DBversion = 'XXX'; # will be replaced by the RM
+if( CheckVersion( $DBversion ) ) {
+    # you can use $dbh here like:
+    $dbh->do(q{INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('OPACDisplayRecordCount', '0', NULL, NULL, 'YesNo')});
+
+    # Always end with this (adjust the bug info)
+    NewVersion( $DBversion, 27428, "Show the number of records in the detail page in the OPAC");
+}
diff --git a/installer/data/mysql/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql
index deaf6b6fc2..0f52163e59 100644
--- a/installer/data/mysql/mandatory/sysprefs.sql
+++ b/installer/data/mysql/mandatory/sysprefs.sql
@@ -738,5 +738,6 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `
 ('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
 ('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
 ('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
-('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo')
+('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'),
+('OPACDisplayRecordCount','0',NULL,NULL,'YesNo')
 ;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref
index a0d7ec430d..f07e49dc3e 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref
@@ -292,3 +292,11 @@ Searching:
             - LIBRIS base URL
             - pref: LibrisURL
             - "Please only change this if you are sure it needs changing."
+        -
+            - pref: OPACDisplayRecordCount
+              type: boolean
+              default: no
+              choices:
+                  1: Show
+                  0: "Don't show"
+            - the number of records next to search links in the bibliographic record detail page in the OPAC.
\ No newline at end of file
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/doc-head-close.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/doc-head-close.inc
index 8e9ec50ec6..154867bd68 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/doc-head-close.inc
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/doc-head-close.inc
@@ -47,6 +47,9 @@
 [% IF ( Koha.Preference('OPACUserCSS') ) %]
     <style>[% Koha.Preference('OPACUserCSS') | $raw %]</style>
 [% END %]
+[% IF ( Koha.Preference('OPACDisplayRecordCount') ) %]
+    [% Asset.js("js/opac-display-record-count.js") %]
+[% END %]
 [% IF SCO_login %]
     [% SET SCOUserCSS = Koha.Preference('SCOUserCSS') %]
     [% IF SCOUserCSS %]
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt
index f6dc6014a2..deceafcb77 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt
@@ -1401,6 +1401,9 @@
     [% END %]
 
     <script>
+        [% IF ( Koha.Preference('OPACDisplayRecordCount') ) %]
+            NumberResultsRecord();
+        [% END %]
         window.emojiPicker = new EmojiPicker({
         emojiable_selector: '[data-emojiable=true]',
         assetsPath: '[% interface | html %]/lib/emoji-picker/img/',
@@ -1817,7 +1820,6 @@
                     });
                 }());
             [% END # /IF ( OPACShelfBrowser ) %]
-
             [% IF ( OpacStarRatings != 'disable' ) %]
                 // -----------------------------------------------------
                 // star-ratings code
diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/opac-display-record-count.js b/koha-tmpl/opac-tmpl/bootstrap/js/opac-display-record-count.js
new file mode 100644
index 0000000000..47265534f3
--- /dev/null
+++ b/koha-tmpl/opac-tmpl/bootstrap/js/opac-display-record-count.js
@@ -0,0 +1,88 @@
+var url_request = '/cgi-bin/koha/svc/elasticsearch/opac-elasticsearch.pl?q=';
+
+//const url_param = '_search?filter_path=hits.total&source_content_type=application/json'
+/* Regex pour tous les URL avec un recherche */
+var regex_es_link = "/cgi-bin/koha/opac-search.pl?q=";
+/* key API */
+var key_display = 'nb_notice';
+/* idx de recherche */
+var es_builder = {
+    'an' : 'koha-auth-number',
+    'Provider' : 'provider',
+    'se,phr' : 'title-series',
+    'au' : 'author',
+    'su,complete-subfield' : 'subject'
+}
+/* On remplace tous les caractères suivants  */
+//https://www.obkb.com/dcljr/charstxt.html
+var not_symbols = {
+    '\'':'%27',
+    '.':'%2E',
+    '+': '',
+    ',': '%2C',
+    ';': '%3B',
+    '-': '',
+    '=': '',
+    '&': '%26',
+    '|': '',
+    '>': '',
+    '<': '',
+    '!': '',
+    '(': '',
+    ')': '',
+    '{': '',
+    '}': '',
+    '[': '',
+    ']': '',
+    '^': '',
+    '"': '',
+    '~': '',
+    '*': '',
+    '?': '',
+    ':': '%3A',
+    '/': '',
+    "\\": '%5C'
+};
+
+function NumberResultsRecord(){
+
+    var regex = `a[href*="${regex_es_link}"]`;
+    var all_link = $(regex);
+    /* array json data to server */
+    var json_data=[];
+
+    $.each( all_link, function( i, val ) {
+        var url = '';
+        var str = val.href;
+        /* on prend param de q= */
+        str = decodeURIComponent(str.split('?q=').pop());
+        /* split query for fields for search [0] */
+		var es_search = str.split(':');
+        /* Remplacement pour un requete */
+		var str_search = str.substring(str.indexOf(":") + 1);
+		/* [0] this is field for search */
+		str_search = str_search.replace(/[&\/\\#,+()$~%.'":;*?<>{}]/g, function(matched){
+			return not_symbols[matched];
+        });
+        es_search[0] = es_search[0][0] == '(' ? es_search[0].substring(1) : es_search[0];
+        var json = {};
+        json[es_builder[es_search[0]]] = str_search;
+        json_data.push(json);
+  });
+
+  $.ajax({
+    type: 'GET',
+    url: url_request + JSON.stringify(json_data) + '&key=' + key_display,
+    success: function (data) {
+        //consoleconsole.log(data);
+        jQuery.each( all_link, function( i, val ) {
+            $(val).after("&nbsp;<span style=\"color:black\"><b>["+data[i]+"]</b></span>");
+            //console.log(val);
+        });
+    },
+    error: function (data) {
+        console.log(data);
+    },
+  });
+
+}
diff --git a/opac/svc/elasticsearch/opac-elasticsearch.pl b/opac/svc/elasticsearch/opac-elasticsearch.pl
new file mode 100755
index 0000000000..dddc19465a
--- /dev/null
+++ b/opac/svc/elasticsearch/opac-elasticsearch.pl
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use CGI qw ( -utf8 );
+use JSON;
+use utf8;
+use Unicode::Normalize;
+use CGI::Session;
+use Koha::SearchEngine::Elasticsearch::Browse;
+
+my $browser = Koha::SearchEngine::Elasticsearch::Browse->new( { index => 'biblios' } );
+my $cgi = CGI->new;
+my $session = CGI::Session->new();
+
+$session->param(-name=>'q', -value=>$cgi->param("q"));
+$session->param(-name=>'key', -value=>$cgi->param("key"));
+$session->expire('+1h');
+
+my $iskey = 0;
+
+#Chose GET in key parametre
+if ($session->param("key") == "nb_notice") {
+  $iskey = 1;
+  my $ses = NFKD( $session->param("q") );
+  $ses =~ s/\p{NonspacingMark}//g;
+  my $res = $browser->count_record($ses);
+  print $cgi->header("application/json");
+  print to_json($res);
+}
+
+if ($iskey == 0) {
+  response404JSON();
+}
+
+sub response404JSON {
+  my $json = JSON->new->utf8;
+  my $header_type = "application/json";
+  my $header_status = "404";
+  my $output = $json->encode({
+    "error" => "No data",
+    "description" => "Bad request",
+  });
+  print $cgi->header(
+    -type => $header_type,
+    -charset => "utf-8",
+    -status => $header_status
+  );
+  print $output;
+  print "\n";
+}
-- 
2.25.1