Bugzilla – Attachment 168354 Details for
Bug 31109
Prevent overwriting bibliographic records in case of simultaneous modification
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 31109: Prevent overwriting bibliographic records in case of simultaneous modification
Bug-31109-Prevent-overwriting-bibliographic-record.patch (text/plain), 27.33 KB, created by
Janusz Kaczmarek
on 2024-07-02 11:53:11 UTC
(
hide
)
Description:
Bug 31109: Prevent overwriting bibliographic records in case of simultaneous modification
Filename:
MIME Type:
Creator:
Janusz Kaczmarek
Created:
2024-07-02 11:53:11 UTC
Size:
27.33 KB
patch
obsolete
>From 5b3d0f4f3ae16f4e90eab2247c517f7ef4a51757 Mon Sep 17 00:00:00 2001 >From: Janusz Kaczmarek <januszop@gmail.com> >Date: Tue, 2 Jul 2024 10:59:38 +0000 >Subject: [PATCH] Bug 31109: Prevent overwriting bibliographic records in case > of simultaneous modification > >Koha, till now, lacks the controll of concurrent modification of data. >Perhaps the most urgent case is parallel modifications of a bibliograhic >record by two independent agents. > >The proposed procedure, collision detection when saving a record, comes >from Nick, who is also the author of the original version of the patch. >This idea is simpler to implement than introducing a record lock. The >idea of using a checksum to confirm that a record has not changed during >editing comes from Jonathan. The other elements, including the >implementation of redirecting to the merge page after a failed attempt to >modify a record, come from Janusz. > >The checksum is passed to C4::Biblio::ModBiblio as an optional >original_digest parameter, which in the future should perhaps be >mandatory for all record modifications, including authority records and >other Koha objects. > >The patch prevents also a possible collision between edit and merge. > >Test plan: >========== >1. In two independent browsers, A and B, open in parallel for edit the same > bibliographic record. Make different changes in each browser. Save > first A, then B. Reload the record in each browser. You should see, > in both browsers, the version of the record modified in B. Note that you > have lost the changes made in browser A without any warning. This is > a serious problem. >2. Apply the patch; restart_all. >3. Repeat p. 1. While trying to save the record in browser B, you will > be redirect to a merge page, with a warning about a conflict in > modification, and with a possibility to merge your modification to the > current version of the record, and, possibly, amend the record again > in the regular editor. > >N.B., this should work both with the basic and with the advanced editor. > >Sponsored-by: Ignatianum University in Cracow >--- > C4/Biblio.pm | 81 ++++++++++++------- > cataloguing/addbiblio.pl | 48 +++++++++-- > cataloguing/merge.pl | 50 +++++++++++- > .../prog/en/includes/cateditor-ui.inc | 28 +++++++ > .../prog/en/includes/merge-record.inc | 6 +- > .../prog/en/modules/cataloguing/addbiblio.tt | 1 + > .../prog/en/modules/cataloguing/merge.tt | 13 +++ > svc/bib | 43 +++++++--- > svc/mid_air_collision | 68 ++++++++++++++++ > 9 files changed, 288 insertions(+), 50 deletions(-) > create mode 100755 svc/mid_air_collision > >diff --git a/C4/Biblio.pm b/C4/Biblio.pm >index 4b32ad43d5..a9ccb5efa0 100644 >--- a/C4/Biblio.pm >+++ b/C4/Biblio.pm >@@ -116,6 +116,7 @@ use Koha::SearchEngine::Indexer; > use Koha::SimpleMARC; > use Koha::Libraries; > use Koha::Util::MARC; >+use Koha::Util::Misc; > > =head1 NAME > >@@ -377,9 +378,13 @@ Used when the indexing schedulling will be handled by the caller > > Set as the record source when saving the record. > >+=item C<original_digest> >+ >+Digest of the original version of the modified record. >+ > =back > >-Returns 1 on success 0 on failure >+Returns 1 on success, -1 in case of modification collision, 0 on other failure. > > =cut > >@@ -401,20 +406,6 @@ sub ModBiblio { > return 0; > } > >- if ( C4::Context->preference("CataloguingLog") ) { >- my $biblio = Koha::Biblios->find($biblionumber); >- my $record; >- my $decoding_error = ""; >- eval { $record = $biblio->metadata->record }; >- if ($@) { >- my $exception = $@; >- $exception->rethrow unless ( $exception->isa('Koha::Exceptions::Metadata::Invalid') ); >- $decoding_error = "There was an error with this bibliographic record: " . $exception; >- $record = $biblio->metadata->record_strip_nonxml; >- } >- logaction( "CATALOGUING", "MODIFY", $biblionumber, "biblio $decoding_error BEFORE=>" . $record->as_formatted ); >- } >- > if ( !$options->{disable_autolink} && C4::Context->preference('AutoLinkBiblios') ) { > BiblioAutoLink( $record, $frameworkcode ); > } >@@ -465,27 +456,57 @@ sub ModBiblio { > # update MARC subfield that stores biblioitems.cn_sort > _koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode ); > >- # update the MARC record (that now contains biblio and items) with the new record data >- ModBiblioMarc( $record, $biblionumber, $mod_biblio_marc_options ); >+ my $schema = Koha::Database->schema; >+ my $result = 0; >+ $schema->txn_do( >+ sub { > >- # modify the other koha tables >- _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode ); >- _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio ); >+ my $biblio = Koha::Biblios->find($biblionumber); >+ my $orig_rec; >+ my $decoding_error = ""; >+ eval { $orig_rec = $biblio->metadata->record }; >+ if ($@) { >+ my $exception = $@; >+ $exception->rethrow unless ( $exception->isa('Koha::Exceptions::Metadata::Invalid') ); >+ $decoding_error = "There was an error with this bibliographic record: " . $exception; >+ $orig_rec = $biblio->metadata->record_strip_nonxml; >+ } >+ if ( !$options->{original_digest} || $options->{original_digest} eq Koha::Util::Misc::digest($orig_rec) ) { > >- _after_biblio_action_hooks({ action => 'modify', biblio_id => $biblionumber }); >+ if ( C4::Context->preference("CataloguingLog") ) { >+ logaction( >+ "CATALOGUING", "MODIFY", $biblionumber, >+ "biblio $decoding_error BEFORE=>" . $orig_rec->as_formatted >+ ); >+ } > >- # update OAI-PMH sets >- if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) { >- C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record); >- } >+ # update the MARC record (that now contains biblio and items) with the new record data >+ ModBiblioMarc( $record, $biblionumber, $mod_biblio_marc_options ); > >- Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue->new->enqueue( >- { >- biblio_ids => [ $biblionumber ] >+ # modify the other koha tables >+ _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode ); >+ _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio ); >+ >+ _after_biblio_action_hooks( { action => 'modify', biblio_id => $biblionumber } ); >+ >+ # update OAI-PMH sets >+ if ( C4::Context->preference("OAI-PMH:AutoUpdateSets") ) { >+ C4::OAI::Sets::UpdateOAISetsBiblio( $biblionumber, $record ); >+ } >+ >+ Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue->new->enqueue( { biblio_ids => [$biblionumber] } ) >+ unless $options->{skip_holds_queue} >+ or !C4::Context->preference('RealTimeHoldsQueue'); >+ >+ $result = 1; >+ >+ } else { >+ $result = -1; >+ } > } >- ) unless $options->{skip_holds_queue} or !C4::Context->preference('RealTimeHoldsQueue'); >+ ); > >- return 1; >+ return $result; > } > > =head2 _strip_item_fields >diff --git a/cataloguing/addbiblio.pl b/cataloguing/addbiblio.pl >index 99c20580b5..5036f4fb0c 100755 >--- a/cataloguing/addbiblio.pl >+++ b/cataloguing/addbiblio.pl >@@ -57,6 +57,7 @@ use Koha::Libraries; > use Koha::BiblioFrameworks; > use Koha::Patrons; > use Koha::UI::Form::Builder::Biblio; >+use Koha::Util::Misc; > > use MARC::File::USMARC; > use MARC::File::XML; >@@ -603,6 +604,7 @@ my ( > $biblioitemnumber > ); > >+my $digest; > if ( $biblio && !$breedingid ) { > eval { $record = $biblio->metadata->record }; > if ($@) { >@@ -611,6 +613,7 @@ if ( $biblio && !$breedingid ) { > $record = $biblio->metadata->record_strip_nonxml; > $template->param( INVALID_METADATA => $exception ); > } >+ $digest = Koha::Util::Misc::digest($record) if $record; > } > if ($breedingid) { > ( $record, $encoding ) = MARCfindbreeding( $breedingid ) ; >@@ -701,11 +704,12 @@ if ( $op eq "cud-addbiblio" ) { > ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($record); > } > my $confirm_not_duplicate = $input->param('confirm_not_duplicate'); >+ my $digest_orig = $input->param('digest'); > # it is not a duplicate (determined either by Koha itself or by user checking it's not a duplicate) > if ( !$duplicatebiblionumber or $confirm_not_duplicate ) { > my $oldbibitemnum; > if ( $is_a_modif ) { >- ModBiblio( >+ my $mod_biblio_result = ModBiblio( > $record, > $biblionumber, > $frameworkcode, >@@ -714,9 +718,40 @@ if ( $op eq "cud-addbiblio" ) { > source => $z3950 ? 'z3950' : 'intranet', > categorycode => $logged_in_patron->categorycode, > userid => $logged_in_patron->userid, >- } >+ }, >+ original_digest => $digest_orig, > } > ); >+ >+ # in case of unsuccessful record modification let's redirect to merge >+ if ( $mod_biblio_result == -1 ) { >+ ( $template, undef, undef ) = get_template_and_user( >+ { >+ template_name => "cataloguing/merge.tt", >+ query => $input, >+ type => "intranet", >+ flagsrequired => { editcatalogue => 'edit_catalogue' }, >+ } >+ ); >+ >+ my ( $records, $framework_database ) = Koha::Util::Misc::prepare_mid_air_collision_merge({ biblionumber => $biblionumber, modified_record => $record }); >+ >+ my ($biblionumbertag) = GetMarcFromKohaField('biblio.biblionumber'); >+ >+ # Parameters >+ $template->param( >+ ref_biblionumber => $biblionumber, >+ records => $records, >+ ref_record => $records->[0], >+ framework => $framework_database, >+ biblionumbertag => $biblionumbertag, >+ mid_air_collision => 1, >+ digest => Koha::Util::Misc::digest( $records->[0]->{record} ), >+ ); >+ >+ output_html_with_http_headers $input, $cookie, $template->output; >+ exit; >+ } > } > else { > ( $biblionumber, $oldbibitemnum ) = AddBiblio( $record, $frameworkcode ); >@@ -845,13 +880,14 @@ elsif ( $op eq "cud-delete" ) { > build_tabs( $template, $record, $dbh, $encoding,$input ); > $template->param( > biblionumber => $biblionumber, >- biblionumbertagfield => $biblionumbertagfield, >- biblionumbertagsubfield => $biblionumbertagsubfield, >+ biblionumbertagfield => $biblionumbertagfield, >+ biblionumbertagsubfield => $biblionumbertagsubfield, > biblioitemnumtagfield => $biblioitemnumtagfield, > biblioitemnumtagsubfield => $biblioitemnumtagsubfield, > biblioitemnumber => $biblioitemnumber, >- hostbiblionumber => $hostbiblionumber, >- hostitemnumber => $hostitemnumber >+ hostbiblionumber => $hostbiblionumber, >+ hostitemnumber => $hostitemnumber, >+ digest => $digest, > ); > } > >diff --git a/cataloguing/merge.pl b/cataloguing/merge.pl >index 9fe7bc4f8b..cae6d91e21 100755 >--- a/cataloguing/merge.pl >+++ b/cataloguing/merge.pl >@@ -32,14 +32,16 @@ use C4::Biblio qw( > ModBiblio > TransformHtmlToMarc > ); >-use C4::Serials qw( CountSubscriptionFromBiblionumber ); >-use C4::Reserves qw( MergeHolds ); >+use C4::Serials qw( CountSubscriptionFromBiblionumber ); >+use C4::Reserves qw( MergeHolds ); > use C4::Acquisition qw( ModOrder GetOrdersByBiblionumber ); >+use C4::Search qw( enabled_staff_search_views ); > > use Koha::BiblioFrameworks; > use Koha::Biblios; > use Koha::Items; > use Koha::MetadataRecord; >+use Koha::Util::Misc; > > my $input = CGI->new; > my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >@@ -88,8 +90,49 @@ if ($op eq 'cud-merge') { > #Take new framework code > my $frameworkcode = $input->param('frameworkcode'); > >+ my $digest = $input->param('digest'); >+ > # Modifying the reference record >- ModBiblio($record, $ref_biblionumber, $frameworkcode); >+ my $mod_biblio_result = ModBiblio( $record, $ref_biblionumber, $frameworkcode, { original_digest => $digest } ); >+ >+ # in case of unsuccessful record modification let's redirect to merge again >+ if ( $mod_biblio_result == -1 ) { >+ my ( $records, $framework_database ) = Koha::Util::Misc::prepare_mid_air_collision_merge( >+ { biblionumber => $ref_biblionumber, modified_record => $record } ); >+ >+ my ($biblionumbertag) = GetMarcFromKohaField('biblio.biblionumber'); >+ >+ # Parameters >+ $template->param( >+ ref_biblionumber => $ref_biblionumber, >+ records => $records, >+ ref_record => $records->[0], >+ framework => $framework_database, >+ biblionumbertag => $biblionumbertag, >+ mid_air_collision => 1, >+ digest => Koha::Util::Misc::digest( $records->[0]->{record} ), >+ ); >+ >+ output_html_with_http_headers $input, $cookie, >+ $template->output; >+ exit; >+ } >+ >+ # This is a merge after a mid-air collision, not a real merge >+ if ( $biblionumbers[0] == -1 ) { >+ my $defaultview = C4::Context->preference('IntranetBiblioDefaultView'); >+ my $views = {C4::Search::enabled_staff_search_views}; >+ if ( $defaultview eq 'isbd' && $views->{can_view_ISBD} ) { >+ print $input->redirect("/cgi-bin/koha/catalogue/ISBDdetail.pl?biblionumber=$ref_biblionumber"); >+ } elsif ( $defaultview eq 'marc' && $views->{can_view_MARC} ) { >+ print $input->redirect("/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=$ref_biblionumber"); >+ } elsif ( $defaultview eq 'labeled_marc' && $views->{can_view_labeledMARC} ) { >+ print $input->redirect("/cgi-bin/koha/catalogue/labeledMARCdetail.pl?biblionumber=$ref_biblionumber"); >+ } else { >+ print $input->redirect("/cgi-bin/koha/catalogue/detail.pl?biblionumber=$ref_biblionumber"); >+ } >+ exit; >+ } > > my $report_header = {}; > foreach my $biblionumber ($ref_biblionumber, @biblionumbers) { >@@ -174,6 +217,7 @@ if ($op eq 'cud-merge') { > if ($ref_biblionumber and $ref_biblionumber == $biblionumber) { > $record->{reference} = 1; > $template->param(ref_record => $record); >+ $template->param(digest => Koha::Util::Misc::digest($marcrecord)); > unshift @records, $record; > } else { > push @records, $record; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc >index c767a21212..bf39aec3fc 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc >@@ -393,6 +393,9 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > return; > } > >+ if ( state.digest ) { >+ record.addField( new MARC.Field( 'DGS', ' ', ' ', [ [ 'a', state.digest ] ] ) ); >+ } > backends[ parts[0] ].save( parts[1], record, function(data) { > state.saving = false; > >@@ -400,6 +403,10 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > var record = new MARC.Record(); > record.loadMARCXML(data.newRecord); > record.frameworkcode = data.newRecord.frameworkcode; >+ if ( record.hasField("DGS") ) { >+ state.digest = record.field("DGS").subfield("a"); >+ record.removeField("DGS"); >+ } > editor.displayRecord( record ); > } > >@@ -423,6 +430,10 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > if ( !record.error ) { > var remove_control_num = [% IF Koha.Preference('autoControlNumber') == 'OFF' %] false [% ELSE %] true [% END %]; > if( remove_control_num ){ record.removeField("001"); } >+ if ( record.hasField("DGS") ) { >+ state.digest = record.field("DGS").subfield("a"); >+ record.removeField("DGS"); >+ } > editor.displayRecord( record ); > editor.focus(); > } >@@ -1108,6 +1119,23 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > humanMsg.displayAlert( _("Incorrect syntax, cannot save"), { className: 'humanError' } ); > } else if ( result.error == 'invalid' ) { > humanMsg.displayAlert( _("Record structure invalid, cannot save"), { className: 'humanError' } ); >+ } else if ( result.error == 'mid-air-collision' ) { >+ humanMsg.displayAlert( _("Record edited elsewhere, cannot save. You can merge your modifications with the current database version of the record"), { className: 'humanError' } ); >+ const xhr = new XMLHttpRequest; >+ xhr.onreadystatechange=function(){ >+ if (xhr.readyState === 4 && xhr.status === 200) { >+ const merge_window = window.open(); >+ merge_window.document.write(xhr.responseText); >+ merge_window.document.close(); >+ } >+ } >+ const merge_url = "/cgi-bin/koha/svc/mid_air_collision/"+result.newId[1]; >+ xhr.open("POST", merge_url, true); >+ const csrf_token = $('meta[name="csrf-token"]').attr("content"); >+ xhr.setRequestHeader("Content-Type", "text/xml"); >+ xhr.setRequestHeader("CSRF-TOKEN", csrf_token); >+ const xml_editor_record = `<?xml version="1.0" encoding="UTF-8"?>\n${result.newRecord.outerHTML}`; >+ xhr.send(xml_editor_record); > } else if ( result.error ) { > humanMsg.displayAlert( _("Something went wrong, cannot save"), { className: 'humanError' } ); > } else if ( !result.error ) { >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/merge-record.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/merge-record.inc >index d9a9db593d..a26228e4fe 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/merge-record.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/merge-record.inc >@@ -49,7 +49,11 @@ > [% WRAPPER tabs_nav %] > [% FOREACH record IN sourcerecords %] > [% WRAPPER tab_item tabname= "tabrecord${record.recordid}" %] >- [% record.recordid | html %] >+ [% IF record.recordid == -1 %] >+ your version >+ [% ELSE %] >+ [% record.recordid | html %] >+ [% END %] > [% IF record.reference %]<span>(ref)</span>[% END %] > <span>([% record.frameworktext | html %])</span> > [% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >index 85b41b2daf..1a57575beb 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt >@@ -920,6 +920,7 @@ $(document).ready(function(){ > <input type="hidden" value="" id="current_tab" name="current_tab" /> > <input type="hidden" name="original_op" value="[% op | html %]" /> > <input type="hidden" value="0" id="confirm_not_duplicate" name="confirm_not_duplicate" /> >+ <input type="hidden" value="[% digest | html %]" id="digest" name="digest" /> > [% END %] > > <div id="toolbar" class="btn-toolbar"> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt >index 6688d4e828..ec7cf00eac 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/merge.tt >@@ -40,6 +40,12 @@ div#result { margin-top: 1em; } > [% INCLUDE 'messages.inc' %] > > <h1>Merging records</h1> >+ [% IF mid_air_collision %] >+ <div class="dialog alert"> >+ <h3>Record edited elsewhere</h3> >+ You can try to merge your version with the current database version of the record or cancel and abandon changes. >+ </div> >+ [% END %] > [% IF ( result ) %] > [% IF ( errors.size ) %] > [% FOREACH error IN errors %] >@@ -179,6 +185,7 @@ div#result { margin-top: 1em; } > </div> <!-- .col-sm-6 --> > > <input type="hidden" name="ref_biblionumber" value="[% ref_biblionumber | html %]" /> >+<input type="hidden" id="digest" name="digest" value="[% digest | html %]" /> > [% FOREACH record IN records %] > <input type="hidden" name="biblionumber" value="[% record.recordid | html %]" /> > [% END %] >@@ -187,9 +194,15 @@ div#result { margin-top: 1em; } > <fieldset class="action"> > <input type="hidden" name="op" value="cud-merge" /> > <input type="submit" name="merge" class="btn btn-primary" value="Merge" /> >+ [% IF ! mid_air_collision %] > <label for="report_fields">Fields to display in report:</label> > <input type="text" name="report_fields" id="report_fields" value="[% MergeReportFields | html %]" /> > <span class="hint">(Example: "001,245ab,600")</span> >+ [% ELSE %] >+ <div class="btn-group"> >+ <a href="[% PROCESS biblio_a_href biblionumber => ref_biblionumber %]" class="btn btn-link" id="cancel">Cancel</a> >+ </div> >+ [% END %] > </fieldset> > </form> > [% END %] >diff --git a/svc/bib b/svc/bib >index da96e01f9a..395f079d7d 100755 >--- a/svc/bib >+++ b/svc/bib >@@ -27,6 +27,7 @@ use C4::Biblio qw( GetFrameworkCode ); > use C4::Items; > use XML::Simple; > use Koha::Biblios; >+use Koha::Util::Misc; > > my $query = CGI->new; > binmode STDOUT, ':encoding(UTF-8)'; >@@ -79,6 +80,8 @@ sub fetch_bib { > $invalid_metadata = 1; > } > print $query->header( -type => 'text/xml', -charset => 'utf-8', -invalid_metadata => $invalid_metadata ); >+ my $digest = Koha::Util::Misc::digest($record); >+ $record->append_fields( MARC::Field->new( 'DGS', ' ', ' ', a => $digest ) ); > print $record->as_xml_record(); > } else { > print $query->header( -type => 'text/xml', -status => '404 Not Found' ); >@@ -124,16 +127,36 @@ sub update_bib { > } > } > >- C4::Biblio::ModBiblio( $record, $biblionumber, $frameworkcode ); >- my $biblio = Koha::Biblios->find( $biblionumber ); >- my $new_record = $biblio->metadata->record({ embed_items => scalar $query->url_param('items') }); >- >- $result->{'status'} = "ok"; >- $result->{'biblionumber'} = $biblionumber; >- my $xml = $new_record->as_xml_record(); >- $xml =~ s/<\?xml.*?\?>//i; >- $result->{'marcxml'} = $xml; >- $do_not_escape = 1; >+ my $mod_biblio_options; >+ if ( $record->subfield( 'DGS', 'a' ) ) { >+ $mod_biblio_options->{original_digest} = $record->subfield( 'DGS', 'a' ); >+ $record->delete_fields( $record->field('DGS') ); >+ } >+ my $res = C4::Biblio::ModBiblio( $record, $biblionumber, $frameworkcode, $mod_biblio_options ); >+ if ( $res == 1 ) { >+ my $biblio = Koha::Biblios->find($biblionumber); >+ my $new_record = $biblio->metadata->record( { embed_items => scalar $query->url_param('items') } ); >+ >+ $result->{'status'} = "ok"; >+ $result->{'biblionumber'} = $biblionumber; >+ my $digest = Koha::Util::Misc::digest($new_record); >+ $new_record->append_fields( MARC::Field->new( 'DGS', ' ', ' ', a => $digest ) ); >+ my $xml = $new_record->as_xml_record(); >+ $xml =~ s/<\?xml.*?\?>//i; >+ $result->{'marcxml'} = $xml; >+ $do_not_escape = 1; >+ } elsif ( $res == -1 ) { >+ $result->{'status'} = "failed"; >+ $result->{'error'} = "mid-air-collision"; >+ $result->{'biblionumber'} = $biblionumber; >+ my $xml = $record->as_xml_record(); >+ $xml =~ s/<\?xml.*?\?>//i; >+ $result->{'marcxml'} = $xml; >+ $do_not_escape = 1; >+ } else { >+ $result->{'status'} = "failed"; >+ $result->{'error'} = "unexpected modification status"; >+ } > } > > print XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => $do_not_escape); >diff --git a/svc/mid_air_collision b/svc/mid_air_collision >new file mode 100755 >index 0000000000..76f1e8a14a >--- /dev/null >+++ b/svc/mid_air_collision >@@ -0,0 +1,68 @@ >+#!/usr/bin/perl >+use Modern::Perl; >+use CGI qw ( -utf8 ); >+use C4::Auth qw( get_template_and_user check_api_auth ); >+use C4::Context; >+use C4::Output qw( output_html_with_http_headers ); >+use C4::Biblio qw( GetFrameworkCode GetMarcStructure GetMarcFromKohaField ); >+ >+use Koha::Biblios; >+use Koha::MetadataRecord; >+use Koha::Util::Misc; >+ >+my $query = CGI->new; >+binmode STDOUT, ':encoding(UTF-8)'; >+ >+my ( $status, $cookie, $sessionID ) = check_api_auth( $query, { editcatalogue => 'edit_catalogue' } ); >+unless ( $status eq "ok" ) { >+ print $query->header( -type => 'text/xml', -status => '403 Forbidden' ); >+ print XMLout( { auth_status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 ); >+ exit 0; >+} >+ >+# do initial validation >+my $path_info = $query->path_info(); >+ >+my $biblionumber = undef; >+if ( $path_info =~ m!^/(\d+)$! ) { >+ $biblionumber = $1; >+} else { >+ print $query->header( -type => 'text/xml', -status => '400 Bad Request' ); >+} >+ >+my $frameworkcode = $query->url_param('frameworkcode') // GetFrameworkCode($biblionumber); >+my $inxml = $query->param('POSTDATA'); >+my $modified_record = eval { >+ MARC::Record::new_from_xml( >+ $inxml, "UTF-8", >+ C4::Context->preference('marcflavour') >+ ); >+}; >+ >+my ( $template, undef, undef ) = get_template_and_user( >+ { >+ template_name => "cataloguing/merge.tt", >+ query => $query, >+ type => "intranet", >+ flagsrequired => { editcatalogue => 'edit_catalogue' }, >+ } >+); >+ >+my ( $records, $framework_database ) = >+ Koha::Util::Misc::prepare_mid_air_collision_merge( >+ { biblionumber => $biblionumber, modified_record => $modified_record } ); >+ >+my ($biblionumbertag) = GetMarcFromKohaField('biblio.biblionumber'); >+ >+# Parameters >+$template->param( >+ ref_biblionumber => $biblionumber, >+ records => $records, >+ ref_record => $records->[0], >+ framework => $framework_database, >+ biblionumbertag => $biblionumbertag, >+ mid_air_collision => 1, >+ digest => Koha::Util::Misc::digest( $records->[0]->{record} ), >+); >+ >+output_html_with_http_headers $query, $cookie, $template->output; >-- >2.39.2
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 31109
:
139046
|
167192
|
167373
|
167374
|
167375
|
167415
|
167416
|
167417
|
167436
|
167437
|
167438
|
167439
|
167492
|
167493
|
167494
|
168352
|
168353
|
168354
|
168472
|
168473
|
168474