@@ -, +, @@ - Add some biblios to a list - In the list check some biblios and click on 'Merge selected records' - Choose the biblio which will be kept, all others will be deleted - On the next page you have all biblios you chose in tabs (left side of the screen) and the preview of result (right side) - Pick some fields or subfields from records that will be deleted or delete some fields from reference record. - Click on 'Merge', if there is no errors you are redirected to the biblio view. --- cataloguing/merge.pl | 308 ++++++------ .../prog/en/modules/cataloguing/merge.tt | 519 ++++++++++---------- .../prog/en/modules/virtualshelves/shelves.tt | 17 +- 3 files changed, 403 insertions(+), 441 deletions(-) --- a/cataloguing/merge.pl +++ a/cataloguing/merge.pl @@ -1,5 +1,4 @@ -#!/usr/bin/perl - +#!/usr/bin/perl # Copyright 2009 BibLibre # Parts Copyright Catalyst IT 2011 @@ -19,8 +18,8 @@ # 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; FIXME - Bug 2505 +use Modern::Perl; + use CGI; use C4::Output; use C4::Auth; @@ -38,7 +37,7 @@ my @errors; my ( $template, $loggedinuser, $cookie ) = get_template_and_user( { - template_name => "cataloguing/merge.tmpl", + template_name => "cataloguing/merge.tt", query => $input, type => "intranet", authnotrequired => 0, @@ -55,152 +54,141 @@ if ($merge) { my $sth; # Creating a new record from the html code - my $record = TransformHtmlToMarc( $input ); - my $tobiblio = $input->param('biblio1'); - my $frombiblio = $input->param('biblio2'); + my $record = TransformHtmlToMarc( $input ); + my $ref_biblionumber = $input->param('ref_biblionumber'); + my @biblionumbers = $input->param('biblionumber'); + @biblionumbers = grep { $_ != $ref_biblionumber } @biblionumbers; # Rewriting the leader - $record->leader(GetMarcBiblio($tobiblio)->leader()); + $record->leader(GetMarcBiblio($ref_biblionumber)->leader()); my $frameworkcode = $input->param('frameworkcode'); my @notmoveditems; # Modifying the reference record - ModBiblio($record, $tobiblio, $frameworkcode); + ModBiblio($record, $ref_biblionumber, $frameworkcode); # Moving items from the other record to the reference record - my $itemnumbers = get_itemnumbers_of($frombiblio); - foreach my $itloop ($itemnumbers->{$frombiblio}) { - foreach my $itemnumber (@$itloop) { - my $res = MoveItemFromBiblio($itemnumber, $frombiblio, $tobiblio); - if (not defined $res) { - push @notmoveditems, $itemnumber; - } - } + foreach my $biblionumber (@biblionumbers) { + my $itemnumbers = get_itemnumbers_of($biblionumber); + foreach my $itemnumber (@{ $itemnumbers->{$biblionumber} }) { + my $res = MoveItemFromBiblio($itemnumber, $biblionumber, $ref_biblionumber); + if (not defined $res) { + push @notmoveditems, $itemnumber; + } + } } # If some items could not be moved : if (scalar(@notmoveditems) > 0) { - my $itemlist = join(' ',@notmoveditems); - push @errors, "The following items could not be moved from the old record to the new one: $itemlist"; + my $itemlist = join(' ',@notmoveditems); + push @errors, "The following items could not be moved from the old record to the new one: $itemlist"; } - # Moving subscriptions from the other record to the reference record - my $subcount = CountSubscriptionFromBiblionumber($frombiblio); - if ($subcount > 0) { - $sth = $dbh->prepare("UPDATE subscription SET biblionumber = ? WHERE biblionumber = ?"); - $sth->execute($tobiblio, $frombiblio); - - $sth = $dbh->prepare("UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ?"); - $sth->execute($tobiblio, $frombiblio); - - } + foreach my $biblionumber (@biblionumbers) { + # Moving subscriptions from the other record to the reference record + my $subcount = CountSubscriptionFromBiblionumber($biblionumber); + if ($subcount > 0) { + $sth = $dbh->prepare(" + UPDATE subscription SET biblionumber = ? WHERE biblionumber = ? + "); + $sth->execute($ref_biblionumber, $biblionumber); + + $sth = $dbh->prepare(" + UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ? + "); + $sth->execute($ref_biblionumber, $biblionumber); + } - # Moving serials - $sth = $dbh->prepare("UPDATE serial SET biblionumber = ? WHERE biblionumber = ?"); - $sth->execute($tobiblio, $frombiblio); + # Moving serials + $sth = $dbh->prepare("UPDATE serial SET biblionumber = ? WHERE biblionumber = ?"); + $sth->execute($ref_biblionumber, $biblionumber); - # TODO : Moving reserves + # TODO : Moving reserves - # Deleting the other record - if (scalar(@errors) == 0) { - # Move holds - MergeHolds($dbh,$tobiblio,$frombiblio); - my $error = DelBiblio($frombiblio); - push @errors, $error if ($error); + # Deleting the other record + if (scalar(@errors) == 0) { + # Move holds + MergeHolds($dbh, $ref_biblionumber, $biblionumber); + my $error = DelBiblio($biblionumber); + push @errors, $error if ($error); + } } # Parameters $template->param( - result => 1, - biblio1 => $input->param('biblio1') + result => 1, + ref_biblionumber => $input->param('ref_biblionumber') ); #------------------------- # Show records to merge #------------------------- } else { - my $mergereference = $input->param('mergereference'); - my $biblionumber = $input->param('biblionumber'); - - if (scalar(@biblionumber) != 2) { - push @errors, "An unexpected number of records was provided for merging. Currently only two records at a time can be merged."; - } - else { - my $data1 = GetBiblioData($biblionumber[0]); - my $record1 = GetMarcBiblio($biblionumber[0]); - - my $data2 = GetBiblioData($biblionumber[1]); - my $record2 = GetMarcBiblio($biblionumber[1]); - - # Checks if both records use the same framework - my $frameworkcode1 = &GetFrameworkCode($biblionumber[0]); - my $frameworkcode2 = &GetFrameworkCode($biblionumber[1]); - - - my $subtitle1 = GetRecordValue('subtitle', $record1, $frameworkcode1); - my $subtitle2 = GetRecordValue('subtitle', $record2, $frameworkcode1); - - if ($mergereference) { - - my $framework; - if ($frameworkcode1 ne $frameworkcode2) { - $framework = $input->param('frameworkcode') - or push @errors, "Famework not selected."; + my $ref_biblionumber = $input->param('ref_biblionumber'); + + if ($ref_biblionumber) { + my $framework = $input->param('frameworkcode'); + $framework //= GetFrameworkCode($ref_biblionumber); + + # Getting MARC Structure + my $tagslib = GetMarcStructure(1, $framework); + + # Creating a loop for display + my @records; + foreach (@biblionumber) { + my $marcrecord = GetMarcBiblio($_); + my $frameworkcode = GetFrameworkCode($_); + my $record = { + biblionumber => $_, + record => $marcrecord, + frameworkcode => $frameworkcode, + display => _createMarcHash($marcrecord, $tagslib), + }; + if ($ref_biblionumber and $ref_biblionumber == $_) { + $record->{reference} = 1; + $template->param(ref_record => $record); + unshift @records, $record; } else { - $framework = $frameworkcode1; + push @records, $record; } - - # Getting MARC Structure - my $tagslib = GetMarcStructure(1, $framework); - - my $notreference = ($biblionumber[0] == $mergereference) ? $biblionumber[1] : $biblionumber[0]; - - # Creating a loop for display - my @record1 = _createMarcHash(GetMarcBiblio($mergereference), $tagslib); - my @record2 = _createMarcHash(GetMarcBiblio($notreference), $tagslib); - - # Parameters - $template->param( - biblio1 => $mergereference, - biblio2 => $notreference, - mergereference => $mergereference, - record1 => @record1, - record2 => @record2, - framework => $framework, - ); } - else { + # Parameters + $template->param( + ref_biblionumber => $ref_biblionumber, + records => \@records, + framework => $framework, + ); + } + else { + my @records; + foreach (@biblionumber) { + my $frameworkcode = GetFrameworkCode($_); + my $record = { + biblionumber => $_, + data => GetBiblioData($_), + frameworkcode => $frameworkcode, + }; + push @records, $record; + } # Ask the user to choose which record will be the kept - $template->param( - choosereference => 1, - biblio1 => $biblionumber[0], - biblio2 => $biblionumber[1], - title1 => $data1->{'title'}, - subtitle1 => $subtitle1, - title2 => $data2->{'title'}, - subtitle2 => $subtitle2 + $template->param( + choosereference => 1, + records => \@records, + ); + + my $frameworks = getframeworks; + my @frameworkselect; + foreach my $thisframeworkcode ( keys %$frameworks ) { + my %row = ( + value => $thisframeworkcode, + frameworktext => $frameworks->{$thisframeworkcode}->{'frameworktext'}, ); - if ($frameworkcode1 ne $frameworkcode2) { - my $frameworks = getframeworks; - my @frameworkselect; - foreach my $thisframeworkcode ( keys %$frameworks ) { - my %row = ( - value => $thisframeworkcode, - frameworktext => $frameworks->{$thisframeworkcode}->{'frameworktext'}, - ); - if ($frameworkcode1 eq $thisframeworkcode){ - $row{'selected'} = 1; - } - push @frameworkselect, \%row; - } - $template->param( - frameworkselect => \@frameworkselect, - frameworkcode1 => $frameworkcode1, - frameworkcode2 => $frameworkcode2, - ); - } + push @frameworkselect, \%row; } + $template->param( + frameworkselect => \@frameworkselect, + ); } } @@ -221,57 +209,51 @@ exit; # Functions # ------------------------ sub _createMarcHash { - my $record = shift; + my $record = shift; my $tagslib = shift; + my $hash = {}; my @array; my @fields = $record->fields(); - foreach my $field (@fields) { - my $fieldtag = $field->tag(); - if ($fieldtag < 10) { - if ($tagslib->{$fieldtag}->{'@'}->{'tab'} >= 0) { - push @array, { - field => [ - { - tag => $fieldtag, - key => createKey(), - value => $field->data(), - } - ] - }; - } - } else { - my @subfields = $field->subfields(); - my @subfield_array; - foreach my $subfield (@subfields) { - if ($tagslib->{$fieldtag}->{@$subfield[0]}->{'tab'} >= 0) { - push @subfield_array, { - subtag => @$subfield[0], - subkey => createKey(), - value => @$subfield[1], - }; - } - - } - - if ($tagslib->{$fieldtag}->{'tab'} >= 0 && $fieldtag ne '995') { - push @array, { - field => [ - { - tag => $fieldtag, - key => createKey(), - indicator1 => $field->indicator(1), - indicator2 => $field->indicator(2), - subfield => [@subfield_array], - } - ] - }; - } - - } + my $fieldtag = $field->tag(); + if ($fieldtag < 10) { + if ($tagslib->{$fieldtag}->{'@'}->{'tab'} >= 0) { + push @{ $hash->{fields} }, { + tag => $fieldtag, + key => createKey(), + value => $field->data(), + }; + } + } else { + if ($fieldtag ne '995' and defined $tagslib->{$fieldtag}) { + my @subfields = $field->subfields(); + my @subfield_array; + foreach my $subfield (@subfields) { + if (defined $tagslib->{$fieldtag}->{@$subfield[0]}->{tab} + and $tagslib->{$fieldtag}->{@$subfield[0]} ne '' + and $tagslib->{$fieldtag}->{@$subfield[0]}->{tab} >= 0) { + push @subfield_array, { + subtag => @$subfield[0], + subkey => createKey(), + value => @$subfield[1], + }; + } + } + + if (defined $tagslib->{$fieldtag} and $fieldtag ne '995') { + push @{ $hash->{fields} }, { + tag => $fieldtag, + key => createKey(), + indicator1 => $field->indicator(1), + indicator2 => $field->indicator(2), + subfield => [@subfield_array], + }; + } + } + } } - return [@array]; + return $hash; } @@ -285,5 +267,3 @@ sub createKey { return int(rand(1000000)); } - - --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt @@ -12,9 +12,8 @@ div#result { margin-top: 1em; } // When submiting the form function mergeformsubmit() { - $("ul#ulrecord1").remove(); - $("ul#ulrecord2").remove(); -} + $("#tabs").remove(); + } $(document).ready(function(){ @@ -24,146 +23,136 @@ $(document).ready(function(){ // Getting marc structure via ajax tagslib = []; $.getJSON("/cgi-bin/koha/cataloguing/merge_ajax.pl", {frameworkcode : "[% framework %]" }, function(json) { - tagslib = json; + tagslib = json; }); // Toggle a field / subfield function toggleField(pField) { - // Getting the key of the clicked checkbox - var ckid = $(pField).attr("id"); - var tab = ckid.split('_'); - var source = tab[1]; // From which record the click came from - var key = tab[2]; - var type = $(pField).attr("class"); - - // Getting field/subfield - var field; - var subfield; - if (type == "subfieldpick") { - - field = $(pField).parent().parent().parent().find("span.field").text(); - subfield = $(pField).parent().find("span.subfield").text(); - } else { - - field = $(pField).parent().find("span.field").text(); - } - - // If the field has just been checked - if (pField.checked) { - - // We check for repeatability - var canbeadded = true; - if (type == "subfieldpick") { - var repeatable = 1; - var alreadyexists = 0; - if (tagslib[field] && tagslib[field][subfield]) { - repeatable = tagslib[field][subfield].repeatable; // Note : we can't use the dot notation here (tagslib.021) because the key is a number - // TODO : Checking for subfields - } - } else { - if (tagslib[field]) { - repeatable = tagslib[field].repeatable; - alreadyexists = $("#resultul span.field:contains(" + field + ")"); - if (repeatable == 0 && alreadyexists.length != 0) { - canbeadded = false; - } - } - } - // If the field is not repeatable, we check if it already exists in the result table - if (canbeadded == false) { - alert(_('The field is non-repeatable and already exists in the destination record. Therefore, you cannot add it.')); - pField.checked = 0; - } else { - - // Cloning the field or subfield we picked - var clone = $(pField).parent().clone(); - - // Removing the checkboxes from it - $(clone).find("input.subfieldpick, input.fieldpick").each(function() { - $(this).remove(); - }); - - - // If we are a subfield - if (type == "subfieldpick") { - // then we need to find who is our parent field... - fieldkey = $(pField).parent().parent().parent().attr("id"); - - // Find where to add the subfield - - // First, check if the field is not already in the destination record - if ($("#resultul li#" + fieldkey).length > 0) { - // If so, we add our field to it - $("#resultul li#" + fieldkey + " ul").append(clone); - } else { - // If not, we add the subfield to the first matching field - var where = 0; - $("#resultul li span.field").each(function() { - if (where == 0 && $(this).text() == field) { - where = this; - } - }); - - // If there is no matching field in the destination record - if (where == 0) { - - // TODO: - // We select the whole field and removing non-selected subfields, instead of... - - // Alerting the user - alert(_('This subfield cannot be added: there is no ' + field + ' field in the destination record.')); - pField.checked = false; - - } else { - $(where).nextAll("ul").append(clone); - } - - } - - - - } else { - // If we are a field - var where = 0; - // Find where to add the field - $("#resultul li span.field").each(function() { - if (where == 0 && $(this).text() > field) { - where = this; - } - }); - - $(where).parent().before(clone); - } - } - } else { - - // Else, we remove it from the results tab - $("ul#resultul li#k" + key).remove(); - } -} + // Getting the key of the clicked checkbox + var ckid = $(pField).attr("id"); + var tab = ckid.split('_'); + var source = tab[1]; // From which record the click came from + var key = tab[2]; + var type = $(pField).attr("class"); + + // Getting field/subfield + var field; + var subfield; + if (type == "subfieldpick") { + field = $(pField).parent().parent().parent().find("span.field").text(); + subfield = $(pField).parent().find("span.subfield").text(); + } else { + field = $(pField).parent().find("span.field").text(); + } + + // If the field has just been checked + if (pField.checked) { + // We check for repeatability + var canbeadded = true; + if (type == "subfieldpick") { + var repeatable = 1; + var alreadyexists = 0; + if (tagslib[field] && tagslib[field][subfield]) { + // Note : we can't use the dot notation here (tagslib.021) + // because the key is a number + repeatable = tagslib[field][subfield].repeatable; + // TODO : Checking for subfields + } + } else { + if (tagslib[field]) { + repeatable = tagslib[field].repeatable; + alreadyexists = $("#resultul span.field:contains(" + field + ")"); + if (repeatable == 0 && alreadyexists.length != 0) { + canbeadded = false; + } + } + } + // If the field is not repeatable, we check if it already exists in the result table + if (canbeadded == false) { + alert(_('The field is non-repeatable and already exists in the destination record. Therefore, you cannot add it.')); + pField.checked = 0; + } else { + // Cloning the field or subfield we picked + var clone = $(pField).parent().clone(); + + // Removing the checkboxes from it + $(clone).find("input.subfieldpick, input.fieldpick").each(function() { + $(this).remove(); + }); + + // If we are a subfield + if (type == "subfieldpick") { + // then we need to find who is our parent field... + fieldkey = $(pField).parent().parent().parent().attr("id"); + + // Find where to add the subfield + + // First, check if the field is not already in the destination record + if ($("#resultul li#" + fieldkey).length > 0) { + // If so, we add our field to it + $("#resultul li#" + fieldkey + " ul").append(clone); + } else { + // If not, we add the subfield to the first matching field + var where = 0; + $("#resultul li span.field").each(function() { + if (where == 0 && $(this).text() == field) { + where = this; + } + }); + + // If there is no matching field in the destination record + if (where == 0) { + // TODO: + // We select the whole field and removing non-selected subfields, instead of... + + // Alerting the user + alert(_('This subfield cannot be added: there is no ' + field + ' field in the destination record.')); + pField.checked = false; + + } else { + $(where).nextAll("ul").append(clone); + } + } + } else { + // If we are a field + var where = 0; + // Find where to add the field + $("#resultul li span.field").each(function() { + if (where == 0 && $(this).text() > field) { + where = this; + } + }); + $(where).parent().before(clone); + } + } + } else { + // Else, we remove it from the results tab + $("ul#resultul li#k" + key).remove(); + } + } // When a field is checked / unchecked $('input.fieldpick').click(function() { - toggleField(this); - // (un)check all subfields - var ischecked = this.checked; - $(this).parent().find("input.subfieldpick").each(function() { - this.checked = ischecked; - }); + toggleField(this); + // (un)check all subfields + var ischecked = this.checked; + $(this).parent().find("input.subfieldpick").each(function() { + this.checked = ischecked; + }); }); // When a field or subfield is checked / unchecked $("input.subfieldpick").click(function() { - toggleField(this); + toggleField(this); }); }); function changeFramework(fw) { - $("#Frameworks").val(fw); + $("#frameworkcode").val(fw); } //]]> @@ -182,14 +171,14 @@ function changeFramework(fw) {

Merging records

[% IF ( result ) %] - [% IF ( errors ) %] - [% FOREACH error IN errors %] -
[% error.error %].
Therefore, the record to be merged has not been deleted.
- [% END %] - - [% ELSE %] - -

The merging was successful. Click here to see the merged record.

+ [% IF ( errors.size ) %] + [% FOREACH error IN errors %] +
[% error.error %].
Therefore, the record to be merged has not been deleted.
+ [% END %] + + [% ELSE %] + +

The merging was successful. Click here to see the merged record.

[% END %] [% ELSE %] @@ -198,31 +187,50 @@ function changeFramework(fw) {

Please choose which record will be the reference for the merge. The record chosen as reference will be kept, and the other will be deleted.

- Merge reference -
    -
  1. -
  2. - - [% IF frameworkselect %] -
  3. -
  4. + Merge reference +
      + [% FOREACH record IN records %] +
    1. + [% IF loop.first %] + + [% ELSE %] + + [% END %] + +
    2. + [% END %] + + [% IF frameworkselect.size %] +
    3. + + +
    4. + [% END %] +
    + + [% FOREACH record IN records %] + [% END %] -
- - - -
+
+ +
[% ELSE %] @@ -238,132 +246,105 @@ function changeFramework(fw) {

Source records

-
- [% IF ( record1 ) %] - -
-
    - [% FOREACH record IN record1 %] - [% FOREACH fiel IN record.field %] -
  • - - [% fiel.tag %] - - - - [% IF ( fiel.value ) %] / [% fiel.value %] - - - [% END %] - - [% IF ( fiel.subfield ) %] -
      - [% FOREACH subfiel IN fiel.subfield %] -
    • - - [% subfiel.subtag %] / [% subfiel.value %] - - -
    • - [% END %] -
    - [% END %] - [% END %] -
  • - [% END %] -
-
-[% END %] -
-
- [% IF ( record2 ) %] - -
-
    - [% FOREACH record IN record2 %] - [% FOREACH fiel IN record.field %] -
  • - - [% fiel.tag %] - - - - [% IF ( fiel.value ) %] / [% fiel.value %] - - - [% END %] - - [% IF ( fiel.subfield ) %] -
      - [% FOREACH subfiel IN fiel.subfield %] -
    • - - [% subfiel.subtag %] / [% subfiel.value %] - - -
    • - [% END %] -
    - [% END %] - [% END %] -
  • - [% END %] -
-
- - - - - - [% END %] -
+ [% IF ( records.size ) %] + [% FOREACH record IN records %] +
+
+
    + [% FOREACH field IN record.display.fields %] +
  • + [% IF (record.reference) %] + + [% ELSE %] + + [% END %] + [% field.tag %] + + + + [% IF ( field.value ) %] + / [% field.value %] + + + [% END %] + + [% IF ( field.subfield.size ) %] +
      + [% FOREACH subfield IN field.subfield %] +
    • + [% IF (record.reference) %] + + [% ELSE %] + + [% END %] + [% subfield.subtag %] / [% subfield.value %] + + +
    • + [% END %] +
    + [% END %] +
  • + [% END %] +
+
+
+ [% END %] + [% END %]

Destination record

-
    - [% FOREACH record IN record1 %] - [% FOREACH fiel IN record.field %]
  • [% fiel.tag %] - - - [% IF ( fiel.value ) %] / - [% fiel.value %] - - - [% END %] - - [% IF ( fiel.subfield ) %] -
      - [% FOREACH subfiel IN fiel.subfield %] -
    • - [% subfiel.subtag %] / [% subfiel.value %] - - -
    • - [% END %] -
    - [% END %] - - [% END %] -
  • - [% END %] - -
+
    + [% FOREACH field IN ref_record.display.fields %]
  • [% field.tag %] + + + [% IF ( field.value ) %] / + [% field.value %] + + + [% END %] + + [% IF ( field.subfield ) %] +
      + [% FOREACH subfield IN field.subfield %] +
    • + [% subfield.subtag %] / [% subfield.value %] + + +
    • + [% END %] +
    + [% END %] + + [% END %] +
  • + +
- - - + +[% FOREACH record IN records %] + +[% END %] -
+
+ +
[% END %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/virtualshelves/shelves.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/virtualshelves/shelves.tt @@ -34,14 +34,15 @@ $(document).ready(function(){ * This function checks if the adequate number of records are checked for merging */ function MergeItems() { - var checkboxes = $("input:checkbox:checked"); - var nbCheckbox = checkboxes.length; - if (nbCheckbox != 2) { - alert(_('Two records must be selected for merging.')); - } else { - location.href='/cgi-bin/koha/cataloguing/merge.pl?biblionumber=' + checkboxes[0].value + '&biblionumber=' + checkboxes[1].value; - } - return false; + var checkboxes = $("input:checkbox:checked"); + if (checkboxes.length > 0) { + var url = '/cgi-bin/koha/cataloguing/merge.pl?biblionumber=' + checkboxes[0].value; + for(var i = 1; i < checkboxes.length; i++) { + url += '&biblionumber=' + checkboxes[i].value; + } + location.href = url; + } + return false; } /** --