From 25eb03c320e867b799b2cc5fd80b0aa547c0560c Mon Sep 17 00:00:00 2001 From: Owen Leonard Date: Thu, 14 Aug 2014 14:52:34 -0400 Subject: [PATCH] [SIGNED OFF] Bug 11876 [Follow-up] Add a diff view to staged MARC Records This follow up makes some corrections to the previously added files and adds some functionality to the diff: A Javascript diff plugin highlights the differences between the records in the two columns. Corrections made: Converted tab indentation to spaces, corrected GPL statement. File organization: Moved showdiffmarc.pl and .tt to /tools/ to match the location of the page with which it functions, tools/manage-marc-import.pl. Corrected permissions on showdiffmarc.pl accordingly. Updates to the template: Added standard includes inclucing header menu and breadcrumbs; converted custom layout to YUI Grid standard. To test, follow the test plan previously defined: - Stage a MARC record batch which contains at least one record match for something already in your catalog. - Locate the staged MARC record batch in Tools -> Manage staged records and click to view the contents. - Find the record which matched an existing record and click the "View" link in the Diff column. - The compare screen should include the header menu and breadcrumbs. The differences between your staged file and the existing record should be higlighted. - You should be able to return to the MARC batch you were previously viewing by following the link in the breadcrumbs or the link at the bottom of the page. - Confirm that the "About" page includes information about the new JavaScript plugin on the Licenses tab. Signed-off-by: Nick Clemens --- catalogue/showdiffmarc.pl | 99 ------------ koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.js | 160 ++++++++++++++++++++ koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.min.js | 10 ++ koha-tmpl/intranet-tmpl/prog/en/modules/about.tt | 3 + .../prog/en/modules/catalogue/showdiffmarc.tt | 50 ------ .../prog/en/modules/tools/showdiffmarc.tt | 55 +++++++ tools/batch_records_ajax.pl | 2 +- tools/showdiffmarc.pl | 101 ++++++++++++ 8 files changed, 330 insertions(+), 150 deletions(-) delete mode 100755 catalogue/showdiffmarc.pl create mode 100644 koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.js create mode 100644 koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.min.js delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/showdiffmarc.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/showdiffmarc.tt create mode 100755 tools/showdiffmarc.pl diff --git a/catalogue/showdiffmarc.pl b/catalogue/showdiffmarc.pl deleted file mode 100755 index 8d02643..0000000 --- a/catalogue/showdiffmarc.pl +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/perl - -# Koha library project www.koha-community.org - -# Copyright 2011 Libéo -# -# This file is part of Koha. -# -# Koha is free software; you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# Koha is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with Koha; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -use strict; -use warnings; - -# standard or CPAN modules used -use CGI qw(:standard); -use DBI; - -# Koha modules used -use C4::Context; -use C4::Output; -use C4::Auth; -use C4::Biblio; -use C4::ImportBatch; -use XML::LibXSLT; -use XML::LibXML; - - -# Input params -my $input = new CGI; -my $biblionumber = $input->param('id'); -my $importid = $input->param('importid'); - - -if ( $biblionumber and $importid ) { - - # Init vars - my ($recordBiblionumber, $recordImportid, $biblioTitle, $importTitle, $formatted1, $formatted2, - $errorFormatted1, $errorFormatted2); - - - # Prepare template - my ( $template, $loggedinuser, $cookie ) = get_template_and_user( - { - template_name => "catalogue/showdiffmarc.tt", - query => $input, - type => "intranet", - authnotrequired => 0, - flagsrequired => { catalogue => 1 }, - debug => 1, - } - ); - - - $recordBiblionumber =GetMarcBiblio($biblionumber); - if( $recordBiblionumber ) { - $formatted1 = $recordBiblionumber->as_formatted; - my $data = GetBiblioData($biblionumber); - $biblioTitle = $data->{title}; - } else { - $errorFormatted1 = 1; - } - - my ($marc,$encoding) = GetImportRecordMarc($importid); - if( $marc ) { - $recordImportid = MARC::Record->new_from_usmarc($marc) ; - $formatted2 = $recordImportid->as_formatted; - my $biblio = GetImportBiblios($importid); - $importTitle = $biblio->[0]->{'title'}; - } else { - $errorFormatted2 = 1; - } - - - $template->param( SCRIPT_NAME => $ENV{'SCRIPT_NAME'}, - BIBLIONUMBER => $biblionumber, - IMPORTID => $importid, - BIBLIOTITLE => $biblioTitle, - IMPORTTITLE => $importTitle, - MARC_FORMATTED1 => $formatted1, - MARC_FORMATTED2 => $formatted2, - ERROR_FORMATTED1 => $errorFormatted1, - ERROR_FORMATTED2 => $errorFormatted2 - ); - - output_html_with_http_headers $input, $cookie, $template->output; -} else { - exit; -} diff --git a/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.js b/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.js new file mode 100644 index 0000000..3e3cc47 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.js @@ -0,0 +1,160 @@ +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + */ + +function escape(s) { + var n = s; + n = n.replace(/&/g, "&"); + n = n.replace(//g, ">"); + n = n.replace(/"/g, """); + + return n; +} + +function diffString( o, n ) { + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = ["\n"]; + } else { + oSpace.push("\n"); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = ["\n"]; + } else { + nSpace.push("\n"); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + escape(out.o[i]) + oSpace[i] + ""; + } + } else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + escape(out.o[n]) + oSpace[n] + ""; + } + } + + for ( var i = 0; i < out.n.length; i++ ) { + if (out.n[i].text == null) { + str += '' + escape(out.n[i]) + nSpace[i] + ""; + } else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { + pre += '' + escape(out.o[n]) + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + + return str; +} + +function randomColor() { + return "rgb(" + (Math.random() * 100) + "%, " + + (Math.random() * 100) + "%, " + + (Math.random() * 100) + "%)"; +} +function diffString2( o, n ) { + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = ["\n"]; + } else { + oSpace.push("\n"); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = ["\n"]; + } else { + nSpace.push("\n"); + } + + var os = ""; + var colors = new Array(); + for (var i = 0; i < out.o.length; i++) { + colors[i] = randomColor(); + + if (out.o[i].text != null) { + os += '' + + escape(out.o[i].text) + oSpace[i] + ""; + } else { + os += "" + escape(out.o[i]) + oSpace[i] + ""; + } + } + + var ns = ""; + for (var i = 0; i < out.n.length; i++) { + if (out.n[i].text != null) { + ns += '' + + escape(out.n[i].text) + nSpace[i] + ""; + } else { + ns += "" + escape(out.n[i]) + nSpace[i] + ""; + } + } + + return { o : os , n : ns }; +} + +function diff( o, n ) { + var ns = new Object(); + var os = new Object(); + + for ( var i = 0; i < n.length; i++ ) { + if ( ns[ n[i] ] == null ) + ns[ n[i] ] = { rows: new Array(), o: null }; + ns[ n[i] ].rows.push( i ); + } + + for ( var i = 0; i < o.length; i++ ) { + if ( os[ o[i] ] == null ) + os[ o[i] ] = { rows: new Array(), n: null }; + os[ o[i] ].rows.push( i ); + } + + for ( var i in ns ) { + if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) { + n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; + o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; + } + } + + for ( var i = 0; i < n.length - 1; i++ ) { + if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && + n[i+1] == o[ n[i].row + 1 ] ) { + n[i+1] = { text: n[i+1], row: n[i].row + 1 }; + o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; + } + } + + for ( var i = n.length - 1; i > 0; i-- ) { + if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && + n[i-1] == o[ n[i].row - 1 ] ) { + n[i-1] = { text: n[i-1], row: n[i].row - 1 }; + o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; + } + } + + return { o: o, n: n }; +} diff --git a/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.min.js b/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.min.js new file mode 100644 index 0000000..9420f1a --- /dev/null +++ b/koha-tmpl/intranet-tmpl/lib/jsdiff/jsdiff.min.js @@ -0,0 +1,10 @@ +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + */function escape(a){var b=a;return b=b.replace(/&/g,"&"),b=b.replace(//g,">"),b=b.replace(/"/g,"""),b}function diffString(a,b){a=a.replace(/\s+$/,""),b=b.replace(/\s+$/,"");var c=diff(a==""?[]:a.split(/\s+/),b==""?[]:b.split(/\s+/)),d="",e=a.match(/\s+/g);e==null?e=["\n"]:e.push("\n");var f=b.match(/\s+/g);f==null?f=["\n"]:f.push("\n");if(c.n.length==0)for(var g=0;g"+escape(c.o[g])+e[g]+"";else{if(c.n[0].text==null)for(b=0;b"+escape(c.o[b])+e[b]+"";for(var g=0;g"+escape(c.n[g])+f[g]+"";else{var h="";for(b=c.n[g].row+1;b"+escape(c.o[b])+e[b]+"";d+=" "+c.n[g].text+f[g]+h}}return d}function randomColor(){return"rgb("+Math.random()*100+"%, "+Math.random()*100+"%, "+Math.random()*100+"%)"}function diffString2(a,b){a=a.replace(/\s+$/,""),b=b.replace(/\s+$/,"");var c=diff(a==""?[]:a.split(/\s+/),b==""?[]:b.split(/\s+/)),d=a.match(/\s+/g);d==null?d=["\n"]:d.push("\n");var e=b.match(/\s+/g);e==null?e=["\n"]:e.push("\n");var f="",g=new Array;for(var h=0;h'+escape(c.o[h].text)+d[h]+"":f+=""+escape(c.o[h])+d[h]+"";var i="";for(var h=0;h'+escape(c.n[h].text)+e[h]+"":i+=""+escape(c.n[h])+e[h]+"";return{o:f,n:i}}function diff(a,b){var c=new Object,d=new Object;for(var e=0;e0;e--)b[e].text!=null&&b[e-1].text==null&&b[e].row>0&&a[b[e].row-1].text==null&&b[e-1]==a[b[e].row-1]&&(b[e-1]={text:b[e-1],row:b[e].row-1},a[b[e].row-1]={text:a[b[e].row-1],row:e-1});return{o:a,n:b}}; \ No newline at end of file diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt index 0f01f8c..43938ce 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt @@ -607,6 +607,9 @@

jQuery Colvis plugin

The controls for column visiblity in DataTables by Allan Jardine is licensed under the BSD 3 and GPL v2 license.

+ +

Javascript Diff Algorithm

+

The Javascript Diff Algorithm plugin by John Resig is licensed under the MIT License.

diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/showdiffmarc.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/showdiffmarc.tt deleted file mode 100644 index 7f8f6cb..0000000 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/showdiffmarc.tt +++ /dev/null @@ -1,50 +0,0 @@ -[% INCLUDE 'doc-head-open.inc' %] -Koha -- Cataloging: MARC Import - - - -[% INCLUDE 'doc-head-close.inc' %] - - - - -
-
-

Original

- [% IF ( ERROR_FORMATTED1 ) %] -

The biblionumber [% BIBLIONUMBER %] doesn't match to any record.

- [% ELSE %] -

[% BIBLIOTITLE %]

-
[% MARC_FORMATTED1 %]
- [% END %] -
-
-

Imported

- [% IF ( ERROR_FORMATTED2 ) %] -

The importid [% IMPORTID %] doesn't match to any record.

- [% ELSE %] -

[% IMPORTTITLE %]

-
[% MARC_FORMATTED2 %] 
- [% END %] -
-
- -[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/showdiffmarc.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/showdiffmarc.tt new file mode 100644 index 0000000..93edb39 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/showdiffmarc.tt @@ -0,0 +1,55 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Tools › Manage staged MARC records › Compare matched records +[% INCLUDE 'doc-head-close.inc' %] + + + + + + +[% INCLUDE 'header.inc' %] + + + +
+
+
+

Original

+ [% IF ( ERROR_FORMATTED1 ) %] +
+

The biblionumber [% BIBLIONUMBER %] doesn't match any existing record.

+
+ [% ELSE %] +

[% BIBLIOTITLE %]

+
[% MARC_FORMATTED1 %]
+ [% END %] +
+
+

Imported

+ [% IF ( ERROR_FORMATTED2 ) %] +
+

The import id number [% IMPORTID %] doesn't match any existing record.

+
+ [% ELSE %] +

[% IMPORTTITLE %]

+
[% MARC_FORMATTED2 %] 
+ [% END %] +
+
+ +

Return to staged MARC batch [% batchid %]

+[% INCLUDE 'intranet-bottom.inc' %] diff --git a/tools/batch_records_ajax.pl b/tools/batch_records_ajax.pl index 400358a..e3dec83 100755 --- a/tools/batch_records_ajax.pl +++ b/tools/batch_records_ajax.pl @@ -107,7 +107,7 @@ foreach my $record (@$records) { || q{}, score => $#$match > -1 ? $match->[0]->{'score'} : 0, match_id => $match_id, - diff_url => $match_id ? "/cgi-bin/koha/catalogue/showdiffmarc.pl?importid=$record->{import_record_id}&id=$match_id" : undef + diff_url => $match_id ? "/cgi-bin/koha/tools/showdiffmarc.pl?batchid=$import_batch_id&importid=$record->{import_record_id}&id=$match_id" : undef }; } diff --git a/tools/showdiffmarc.pl b/tools/showdiffmarc.pl new file mode 100755 index 0000000..3b12795 --- /dev/null +++ b/tools/showdiffmarc.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# Koha library project www.koha-community.org + +# Copyright 2011 Libéo +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +# standard or CPAN modules used +use CGI qw(:standard); +use DBI; + +# Koha modules used +use C4::Context; +use C4::Output; +use C4::Auth; +use C4::Biblio; +use C4::ImportBatch; +use XML::LibXSLT; +use XML::LibXML; + + +# Input params +my $input = new CGI; +my $biblionumber = $input->param('id'); +my $importid = $input->param('importid'); +my $batchid = $input->param('batchid'); + + +if ( $biblionumber and $importid ) { + + # Init vars + my ($recordBiblionumber, $recordImportid, $biblioTitle, $importTitle, $formatted1, $formatted2, + $errorFormatted1, $errorFormatted2); + + + # Prepare template + my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "tools/showdiffmarc.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { tools => 'manage_staged_marc' }, + debug => 1, + } + ); + + + $recordBiblionumber =GetMarcBiblio($biblionumber); + if( $recordBiblionumber ) { + $formatted1 = $recordBiblionumber->as_formatted; + my $data = GetBiblioData($biblionumber); + $biblioTitle = $data->{title}; + } else { + $errorFormatted1 = 1; + } + + my ($marc,$encoding) = GetImportRecordMarc($importid); + if( $marc ) { + $recordImportid = MARC::Record->new_from_usmarc($marc) ; + $formatted2 = $recordImportid->as_formatted; + my $biblio = GetImportBiblios($importid); + $importTitle = $biblio->[0]->{'title'}; + } else { + $errorFormatted2 = 1; + } + + + $template->param( SCRIPT_NAME => $ENV{'SCRIPT_NAME'}, + BIBLIONUMBER => $biblionumber, + IMPORTID => $importid, + BIBLIOTITLE => $biblioTitle, + IMPORTTITLE => $importTitle, + MARC_FORMATTED1 => $formatted1, + MARC_FORMATTED2 => $formatted2, + ERROR_FORMATTED1 => $errorFormatted1, + ERROR_FORMATTED2 => $errorFormatted2, + batchid => $batchid + ); + + output_html_with_http_headers $input, $cookie, $template->output; +} else { + exit; +} -- 1.7.10.4