From ac55f17333c6436d2421a24033a2e7c113743c9f Mon Sep 17 00:00:00 2001 From: Cori Lynn Arnold Date: Thu, 18 Oct 2018 17:30:50 +0000 Subject: [PATCH] Bug 18823: Rancor - add support for editing/searching records rebase. Signed-off-by: cori Rebased-on: 2019-03-20 Alex Arnaud --- C4/Biblio.pm | 28 +- C4/ImportBatch.pm | 20 +- cataloguing/editor.pl | 2 +- .../lib/koha/cateditor/koha-backend.js | 52 +++ .../lib/koha/cateditor/preferences.js | 2 + koha-tmpl/intranet-tmpl/prog/css/cateditor.css | 44 ++- .../prog/en/includes/cateditor-ui.inc | 347 +++++++++++++++++++-- .../prog/en/modules/cataloguing/editor.tt | 44 ++- svc/cataloguing/import_batches | 212 +++++++++++++ 9 files changed, 697 insertions(+), 54 deletions(-) create mode 100755 svc/cataloguing/import_batches diff --git a/C4/Biblio.pm b/C4/Biblio.pm index fc23bb5..98d1f4a 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -3264,6 +3264,27 @@ sub _koha_delete_biblio_metadata { =head1 UNEXPORTED FUNCTIONS +=head2 Update005Time + + &Update005Time( $record ); + +Updates the 005 timestamp of the given record to the current time. + +=cut + +sub UpdateMarcTimestamp { + my ( $record ) = @_; + + my $encoding = C4::Context->preference("marcflavour"); + + if ( $encoding =~ /MARC21|UNIMARC/ ) { + my @a = (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++; + # YY MM DD HH MM SS (update year and month) + my $f005 = $record->field('005'); + $f005->update( sprintf( "%4d%02d%02d%02d%02d%04.1f",@a ) ) if $f005; + } +} + =head2 ModBiblioMarc &ModBiblioMarc($newrec,$biblionumber,$frameworkcode); @@ -3316,12 +3337,7 @@ sub ModBiblioMarc { } #enhancement 5374: update transaction date (005) for marc21/unimarc - if($encoding =~ /MARC21|UNIMARC/) { - my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++; - # YY MM DD HH MM SS (update year and month) - my $f005= $record->field('005'); - $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005; - } + UpdateMarcTimestamp( $record ); my $metadata = { biblionumber => $biblionumber, diff --git a/C4/ImportBatch.pm b/C4/ImportBatch.pm index 99bc428..852c8ba 100644 --- a/C4/ImportBatch.pm +++ b/C4/ImportBatch.pm @@ -28,6 +28,8 @@ use C4::Charset; use C4::AuthoritiesMarc; use C4::MarcModificationTemplates; use Koha::Items; +use DateTime; +use DateTime::Format::Strptime; use Koha::Plugins::Handler; use Koha::Logger; @@ -1609,10 +1611,12 @@ sub _create_import_record { sub _update_import_record_marc { my ($import_record_id, $marc_record, $marc_type) = @_; + my $upload_timestamp = _get_import_record_timestamp($marc_record); + my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ? + my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?, upload_timestamp = ? WHERE import_record_id = ?"); - $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id); + $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $upload_timestamp, $import_record_id); $sth->finish(); } @@ -1633,14 +1637,18 @@ sub _add_auth_fields { sub _add_biblio_fields { my ($import_record_id, $marc_record) = @_; + my $controlnumber; + if ($marc_record->field('001')) { + $controlnumber = $marc_record->field('001')->data(); + } my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record); my $dbh = C4::Context->dbh; - # FIXME no controlnumber, originalsource + # FIXME no originalsource $isbn = C4::Koha::GetNormalizedISBN($isbn); - my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)"); - $sth->execute($import_record_id, $title, $author, $isbn, $issn); + my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn, control_number) VALUES (?, ?, ?, ?, ?, ?)"); + $sth->execute($import_record_id, $title, $author, $isbn, $issn, $controlnumber); $sth->finish(); - + } sub _update_biblio_fields { diff --git a/cataloguing/editor.pl b/cataloguing/editor.pl index 42f35b3..656eb2a 100755 --- a/cataloguing/editor.pl +++ b/cataloguing/editor.pl @@ -50,7 +50,7 @@ my $schema = Koha::Database->new->schema; $template->{VARS}->{editable_batches} = [ $schema->resultset('ImportBatch')->search( { batch_type => [ 'batch', 'webservice' ], - import_status => 'staged', + import_status => [ 'staged', 'staging' ], }, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' }, ) ]; diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js index eeca779..373f385 100644 --- a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js +++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js @@ -168,6 +168,58 @@ define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=defin } ); }, + GetBatchRecord: function( batch_id, id, callback ) { + $.get( + '/cgi-bin/koha/svc/cataloguing/import_batches/' + batch_id + '/' + id + ).done( function( data ) { + var record = new MARC.Record(); + record.loadMARCXML( data.record ); + callback(record); + } ).fail( function( data ) { + callback( { error: data } ); + } ); + }, + + CreateBatch: function( batch_name, callback ) { + $.ajax( { + type: 'POST', + url: '/cgi-bin/koha/svc/cataloguing/import_batches/', + data: { batch_name: batch_name }, + } ).done( function( data ) { + callback( data ); + } ).fail( function( data ) { + callback( { error: data } ); + } ); + }, + + CreateBatchRecord: function( record, batch_id, callback, options ) { + $.ajax( { + type: 'POST', + url: '/cgi-bin/koha/svc/cataloguing/import_batches/' + batch_id + '/', + data: { record: record.toXML(), allow_control_number_conflict: options.allow_control_number_conflict ? 1 : undefined }, + } ).done( function( data ) { + callback( data ); + } ).fail( function( data ) { + callback( { error: $.parseJSON( data.responseText ) || data.responseText } ); + } ); + }, + + SaveBatchRecord: function( batch_id, id, record, callback, options ) { + $.ajax( { + type: 'POST', + url: '/cgi-bin/koha/svc/cataloguing/import_batches/' + batch_id + '/' + id, + data: { record: record.toXML(), allow_control_number_conflict: options.allow_control_number_conflict ? 1 : undefined }, + } ).done( function( data ) { + callback( data ); + } ).fail( function( data ) { + callback( { data: { error: data } } ); + } ); + }, + + StartBatchExport: function( batch_id, options ) { + window.open( '/cgi-bin/koha/svc/cataloguing/import_batches/' + batch_id + '?download=1&' + $.param( options ) ); + }, + GetTagsBy: function( frameworkcode, field, value ) { var result = {}; diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js index 5cf81da..d684934 100644 --- a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js +++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js @@ -29,10 +29,12 @@ define( function() { Preferences.user = $.extend( { // Preference defaults + enabledBatches: {}, fieldWidgets: true, font: 'monospace', fontSize: '1em', macros: {}, + selected_save_targets: {}, selected_search_targets: {}, }, saved_prefs ); }, diff --git a/koha-tmpl/intranet-tmpl/prog/css/cateditor.css b/koha-tmpl/intranet-tmpl/prog/css/cateditor.css index 989e8dd..53e2002 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/cateditor.css +++ b/koha-tmpl/intranet-tmpl/prog/css/cateditor.css @@ -65,6 +65,33 @@ body { font-size: 12px; } +#save-targets input { + display: inline-block; + margin: 1px; + vertical-align: middle; +} + +#save-targets input:checked + label { + color: #000; +} + +#save-targets label { + color: #666; + display: inline-block; + margin-left: 0.3em; + padding: 0.3em 0; + vertical-align: middle; + width: 10em; +} + +#save-targets li { + overflow: hidden; +} + +#shortcuts-container { + font-size: 12px; +} + /*> MARC editor */ #editor .CodeMirror { line-height: 1.2; @@ -223,7 +250,8 @@ body { /*> Search */ -#advanced-search-ui .modal-lg, #search-results-ui .modal-lg, #macro-ui .modal-lg { +#advanced-search-ui .modal-lg, #search-results-ui .modal-lg, #macro-ui .modal-lg .ui-modal { + padding: 5px; width: 90%; } @@ -456,3 +484,17 @@ body { .CodeMirror-gutter-wrapper { position: absolute; } + +/* >Batches */ +#batches-list > li { + border: 2px solid #F0F0F0; + border-radius: 6px; + display: block; + line-height: 3em; + font-size: 115%; +} + +#batches-list > li input { + display: inline-block; + margin: 0 1em; +} 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 5bf7472..91306ea 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc @@ -39,6 +39,13 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr recordtype: 'biblio', checked: false, }, + [%- FOREACH batch = editable_batches -%] + 'batch:[% batch.import_batch_id %]': { + name: _("Batch: ") + '[% batch.file_name %]', + recordtype: 'biblio', + checked: false, + }, + [%- END -%] [%- FOREACH server = z3950_servers -%] [% server.id | html %]: { name: '[% server.servername | html %]', @@ -64,8 +71,8 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr var state = { backend: '', - saveBackend: 'catalog', - recordID: undefined + recordID: undefined, + saveTargets: {}, }; var editor; @@ -235,16 +242,29 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr }, }, 'catalog': { - titleForRecord: _("Editing catalog record #{ID}"), + titleForRecord: _("Editing catalog record #%s"), links: [ - { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber={ID}" }, - { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber={ID}" }, + { title: _("view"), href: "/cgi-bin/koha/catalogue/detail.pl?biblionumber=%s" }, + { title: _("edit items"), href: "/cgi-bin/koha/cataloguing/additem.pl?biblionumber=%s" }, ], - saveLabel: _("Save to catalog"), + saveLabel: _("New catalog record"), + saveExistingLabel: _("Catalog record #%s"), get: function( id, callback ) { if ( !id ) return false; - KohaBackend.GetRecord( id, callback ); + KohaBackend.GetRecord( id, function( data ) { + if ( !data.error ) { + setSaveTargetChecked( 'catalog/', false ); + addSaveTarget( { + label: backends.catalog.saveExistingLabel.format( id ), + id: 'catalog/' + id, + description: '', + checked: true + } ); + } + + callback(data); + } ); }, save: function( id, record, done ) { function finishCb( data ) { @@ -267,7 +287,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr } }, 'marcxml': { - saveLabel: _("Save as MARCXML (.xml) file"), + saveLabel: _("New MARCXML (.xml) file"), save: function( id, record, done ) { saveAs( new Blob( [record.toXML()], { 'type': 'application/octet-stream;charset=utf-8' } ), 'record.xml' ); @@ -289,6 +309,63 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr }, }; + var editable_batches = { + [%- FOREACH batch = editable_batches -%] + [% batch.import_batch_id %]: { + 'name': '[% batch.file_name %]', + }, + [%- END -%] + }; + + function addSaveTarget( target ) { + state.saveTargets[target.id] = target; + if (target.enabled == null) target.enabled = true; + + // Have to check that Preferences has been initialized + var saved_value = Preferences.user && Preferences.user.selected_save_targets[target.id]; + if ( saved_value != null ) target.checked = saved_value; + + var target_list = $.map( state.saveTargets, function( target ) { + return target; + } ); + + target_list.sort( function( a, b ) { + return a.label.localeCompare(b.label); + } ); + + $('#save-targets ol').empty(); + + $.each( target_list, function( i, target ) { + var $new_target = $( + '
  • ' + ); + + $new_target.find('input').change( function() { + target.checked = this.checked; + } ); + + $('#save-targets ol').append($new_target); + + if (!target.enabled) $new_target.hide(); + } ); + } + + function setSaveTargetChecked( target_id, checked ) { + if ( state.saveTargets[target_id] == null ) return; + + state.saveTargets[target_id].checked = checked; + $( '#save-targets input[data-target-id="' + target_id + '"]' )[0].checked = checked; + } + + function setSaveTargetEnabled( target_id, enabled ) { + if ( !enabled ) { + setSaveTargetChecked( target_id, false ); + } + + state.saveTargets[target_id].enabled = enabled; + $( '#save-targets input[data-target-id="' + target_id + '"]' ).closest('li').toggle(enabled); + } + function setSource(parts) { state.backend = parts[0]; state.recordID = parts[1]; @@ -299,13 +376,12 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr document.location.hash = '#' + parts[0] + '/' + parts[1]; - $('#title').text( backend.titleForRecord.replace( '{ID}', parts[1] ) ); + $('#title').text( backend.titleForRecord.format( parts[1] ) ); $.each( backend.links || [], function( i, link ) { - $('#title').append(' (' + link.title + ')' ); + $('#title').append(' (' + link.title + ')' ); } ); - $( 'title', document.head ).html( _("Koha › Cataloging › ") + backend.titleForRecord.replace( '{ID}', parts[1] ) ); - $('#save-record span').text( backends[ state.saveBackend ].saveLabel ); + $( 'title', document.head ).html( _("Koha › Cataloging › ") + backend.titleForRecord.format( parts[1] ) ); } function saveRecord( recid, editor, callback ) { @@ -342,6 +418,16 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr if (data.newId) { setSource(data.newId); + + var backend = backends[ parts[0] ]; + + setSaveTargetChecked( recid, false ); + addSaveTarget( { + label: backend.saveExistingLabel.format( data.newId[1] ), + id: data.newId.join('/'), + description: '', + checked: true + } ); } else { setSource( [ state.backend, state.recordID ] ); } @@ -600,6 +686,11 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr var value = Preferences.user[pref]; switch (pref) { + case 'enabledBatches': + $.each( editable_batches, function( batch_id, batch ) { + $( '#batches-list li[data-batch-id=' + batch_id + '] input' )[0].checked = Preferences.user.enabledBatches[batch_id]; + setSaveTargetEnabled( 'batch:' + batch_id + '/', Preferences.user.enabledBatches[batch_id] || false ); + } ); case 'fieldWidgets': $( '#set-field-widgets' ).text( value ? _("Show fields verbatim") : _("Show helpers for fixed and coded fields") ); break; @@ -621,6 +712,20 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr if ( saved_val != null ) server.checked = saved_val; } ); break; + case 'selected_save_targets': + $.each( state.saveTargets, function( target_id, target ) { + var saved_val = Preferences.user.selected_save_targets[target_id]; + + if ( saved_val != null ) setSaveTargetChecked( target_id, saved_val ); + } ); + break; + case 'selected_search_targets': + $.each( z3950Servers, function( server_id, server ) { + var saved_val = Preferences.user.selected_search_targets[server_id]; + + if ( saved_val != null ) server.checked = saved_val; + } ); + break; } } @@ -634,7 +739,21 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr } ); } + function _addLiveHandler( sel, event, handler ) { + $( document ).on( event, sel, function (e) { + e.preventDefault(); + handler.call( this, e, Preferences.user[pref] ); + Preferences.Save( [% USER_INFO.borrowernumber %] ); + showPreference(pref); + } ); + } + switch (pref) { + case 'enabledBatches': + _addLiveHandler( '#batches-list input', 'change', function() { + Preferences.user.enabledBatches[ $( this ).closest('li').data('batch-id') ] = this.checked; + } ); + break; case 'fieldWidgets': _addHandler( '#set-field-widgets', 'click', function( e, oldValue ) { editor.setUseWidgets( Preferences.user.fieldWidgets = !Preferences.user.fieldWidgets ); @@ -650,6 +769,13 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr Preferences.user.fontSize = $( e.target ).css( 'font-size' ); } ); break; + case 'selected_save_targets': + $( document ).on( 'change', 'input.save-toggle-target', function() { + var target_id = $( this ).data('target-id'); + Preferences.user.selected_save_targets[target_id] = this.checked; + Preferences.Save( [% USER_INFO.borrowernumber %] ); + } ); + break; case 'selected_search_targets': $( document ).on( 'change', 'input.search-toggle-server', function() { var server_id = $( this ).closest('li').data('server-id'); @@ -743,6 +869,85 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr showSavedMacros(); } + function addImportBatch(batch) { + var backend_id = 'batch:' + batch.batch_id; + backends[backend_id] = { + titleForRecord: _("Editing record from batch: ") + batch.name, + saveLabel: _("Batch: ") + batch.name, + saveExistingLabel: batch.name + ": #%s", + get: function( id, callback ) { + KohaBackend.GetBatchRecord( batch.batch_id, id, function( data ) { + if ( !data.error ) { + setSaveTargetChecked( backend_id + '/', false ); + addSaveTarget( { + label: batch.name + ": #" + id, + id: backend_id + '/' + id, + description: '', + checked: true + } ); + } + + callback(data); + } ); + }, + save: function( id, record, done, options ) { + function finishCb( data ) { + done( { + error: data.message || data.error, + newRecord: data.updated_record, + newId: data.import_record_id && [ backend_id, data.import_record_id ] + } ); + } + + if ( id ) { + KohaBackend.SaveBatchRecord( batch.batch_id, id, record, finishCb, { allow_control_number_conflict: options.override_warnings } ); + } else { + KohaBackend.CreateBatchRecord( record, batch.batch_id, finishCb, { allow_control_number_conflict: options.override_warnings } ); + } + }, + }; + + // Build batch UI + var $batch_entry = $( '
  • ' + batch.name + '
  • ' ); + + var $batch_buttons = $('').appendTo($batch_entry); + var $export_button = $( '' ).appendTo($batch_buttons).click( function() { + $('#batches-list .batch-export').hide(); + $export_screen.show(); + } ); + + var $export_screen = $( + ' -