Bugzilla – Attachment 80869 Details for
Bug 18823
Advanced editor - Rancor - add ability to edit records in import batches
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 18823: Rancor - add support for editing/searching records
Bug-18823-Rancor---add-support-for-editingsearchin.patch (text/plain), 42.47 KB, created by
Cori Lynn Arnold
on 2018-10-18 17:49:08 UTC
(
hide
)
Description:
Bug 18823: Rancor - add support for editing/searching records
Filename:
MIME Type:
Creator:
Cori Lynn Arnold
Created:
2018-10-18 17:49:08 UTC
Size:
42.47 KB
patch
obsolete
>From 452ad2765cd62e678363997e9ad40b3199c5472c Mon Sep 17 00:00:00 2001 >From: Cori Lynn Arnold <carnold@dgiinc.com> >Date: Thu, 18 Oct 2018 17:30:50 +0000 >Subject: [PATCH] Bug 18823: Rancor - add support for editing/searching records > >rebase. >--- > 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 50c7c4cc10..00bd95bee0 100644 >--- a/C4/Biblio.pm >+++ b/C4/Biblio.pm >@@ -3221,6 +3221,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); >@@ -3273,12 +3294,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 99bc428701..852c8ba550 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 42f35b3f47..656eb2a68c 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 eeca779440..373f3850b4 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 5cf81dace6..d6849343ad 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 989e8dde2a..53e2002d66 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 5bf7472f79..91306ea3d3 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 = $( >+ '<li><input type="checkbox" class="save-toggle-target" data-target-id="' + target.id + '" id="save-target-' + i + '"' + ( target.checked ? ' checked="checked"' : '' ) + '> <label for="save-target-' + i + '">' + target.label + '</label></li>' >+ ); >+ >+ $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(' <a target="_blank" href="' + link.href.replace( '{ID}', parts[1] ) + '">(' + link.title + ')</a>' ); >+ $('#title').append(' <a target="_blank" href="' + link.href.format( parts[1] ) + '">(' + link.title + ')</a>' ); > } ); >- $( '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 = $( '<li data-batch-id="' + batch.batch_id + '"><input type="checkbox" />' + batch.name + '</li>' ); >+ >+ var $batch_buttons = $('<span class="batch-buttons"></span>').appendTo($batch_entry); >+ var $export_button = $( '<button>' + _("Export...") + '</button>' ).appendTo($batch_buttons).click( function() { >+ $('#batches-list .batch-export').hide(); >+ $export_screen.show(); >+ } ); >+ >+ var $export_screen = $( >+ '<form class="batch-export form-horizontal" style="display: none">' >+ + '<div class="control-group">' >+ + '<label class="control-label">' + _("Control number range:") + '</label>' >+ + '<div class="controls"><input class="batch-control-number-start" type="text"> - <input class="batch-control-number-end" type="text"></div>' >+ + '</div>' >+ + '<label class="control-label">' + _("Timestamp range (YYYYMMDD or YYYMMDDHHMMSS):") + '</label>' >+ + '<div class="controls"><input class="batch-timestamp-start" type="text"> - <input class="batch-timestamp-end" type="text"></div>' >+ + '</div>' >+ + '<div class="control-group">' >+ + '<div class="controls"><button class="batch-export-start">' + _("Start export") + '</div>' >+ + '</form>' >+ ).appendTo($batch_entry); >+ >+ $export_screen.find('.batch-export-start').click( function() { >+ function getFormVal(name) { >+ return $export_screen.find( '.batch-' + name ).val(); >+ } >+ >+ var options = { >+ start_control_number: getFormVal('control-number-start'), >+ end_control_number: getFormVal('control-number-end'), >+ start_timestamp: getFormVal('timestamp-start'), >+ end_timestamp: getFormVal('timestamp-end'), >+ }; >+ >+ KohaBackend.StartBatchExport( batch.batch_id, options ); >+ } ); >+ >+ $('#batches-list').append( $batch_entry ); >+ } >+ > $(document).ready( function() { > // Editor setup > editor = new MARCEditor( { >@@ -792,6 +997,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > $(this).height( $(window).height() * .8 - $(this).prevAll('.modal-header').height() ); > } ); > }, 100); >+ > } > > $( '#macro-ui' ).on( 'shown.bs.modal', function() { >@@ -832,9 +1038,17 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > $.each( backends, function( id, backend ) { > if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] ); > } ); >- saveableBackends.sort(); >- $.each( saveableBackends, function( undef, backend ) { >- $( '#save-dropdown' ).append( '<li><a href="#" data-backend="' + backend[1] + '">' + backend[0] + '</a></li>' ); >+ >+ var batch_list = []; >+ >+ $.each( editable_batches, function( batch_id, batch ) { >+ batch_list.push( $.extend( { batch_id: batch_id }, batch ) ); >+ } ); >+ batch_list.sort( function( a, b ) { >+ return a.name.localeCompare(b.name); >+ } ); >+ $.each( batch_list, function() { >+ addImportBatch( this ); > } ); > > var macro_format_list = $.map( Macros.formats, function( format, name ) { >@@ -850,14 +1064,37 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > // Click bindings > $( '#save-record, #save-dropdown a' ).click( function() { > $( '#save-record' ).find('i').attr( 'class', 'fa fa-spinner fa-spin' ).siblings( 'span' ).text( _("Saving...") ); >+ var enabledTargets = []; >+ var targetNames = []; >+ $.each( state.saveTargets, function() { >+ if ( this.checked ) { >+ enabledTargets.push(this); >+ targetNames.push(this.label); >+ } >+ } ); >+ if ( enabledTargets.length == 0 ) { >+ humanMsg.displayAlert( _("Please select a save target"), { className: 'humanError' } ); >+ return false; >+ } >+ >+ $( '#save-record' ).find('i').attr( 'class', 'icon-loading' ).siblings( 'span' ).text( _("Saving...") ); >+ >+ var targets_left = enabledTargets.length; >+ var errors = false; > > function finishCb(result) { >- if ( result.error == 'syntax' ) { >- 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 ) { >- humanMsg.displayAlert( _("Record saved "), { className: 'humanSuccess' } ); >+ targets_left--; >+ if ( result.error ) { >+ if ( result.error == 'syntax' ) { >+ 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.type == 'control_number_match' ) { >+ humanMsg.displayAlert( _("Control number conflict, cannot save"), { className: 'humanError' } ); >+ } else { >+ humanMsg.displayAlert( _("Unknown error, record not saved to one or more targets"), { className: 'humanError' } ); >+ } >+ errors = true; > } > > $.each( result.errors || [], function( undef, error ) { >@@ -893,20 +1130,19 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > } > } ); > >- $( '#save-record' ).find('i').attr( 'class', 'fa fa-hdd-o' ); > >- if ( result.error ) { >- // Reset backend info >- setSource( [ state.backend, state.recordID ] ); >+ if ( targets_left == 0 ) { >+ if ( !errors ) { >+ humanMsg.displayMsg( "<h3>" + _("Record saved to:</h3>") + "</h3>" + '<ul><li>' + targetNames.join('</li><li>') + '</li></ul>', { className: 'humanSuccess' } ); >+ } >+ >+ $( '#save-record' ).find('i').attr( 'class', 'icon-hdd' ).end().find('span').text( _("Save") ); > } > } > >- var backend = $( this ).data( 'backend' ) || ( state.saveBackend ); >- if ( state.backend == backend ) { >- saveRecord( backend + '/' + state.recordID, editor, finishCb ); >- } else { >- saveRecord( backend + '/', editor, finishCb ); >- } >+ $.each( enabledTargets, function() { >+ saveRecord( this.id, editor, finishCb, options ); >+ } ); > > return false; > } ); >@@ -1047,11 +1283,48 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > } > } ); > >+ $('#open-batches').click( function() { >+ $('#batches-ui').modal(); >+ >+ return false; >+ } ); >+ >+ $('#create-batch').click( function() { >+ var batch_name = prompt( _("Name of new import batch:") ); >+ if (batch_name == null) return false; >+ >+ KohaBackend.CreateBatch( batch_name, function( data ) { >+ if ( data.error ) { >+ humanMsg.displayAlert( _("Could not create import batch"), { className: 'humanError' } ); >+ } else { >+ humanMsg.displayAlert( _("Import batch created"), { className: 'humanSuccess' } ); >+ >+ addImportBatch( editable_batches[data.batch_id] = { batch_id: data.batch_id, name: batch_name } ); >+ >+ var backend_id = 'batch:' + data.batch_id; >+ addSaveTarget( { >+ label: backends[backend_id].saveLabel, >+ id: backend_id + '/', >+ description: '', >+ enabled: false, >+ } ); >+ } >+ } ); >+ >+ return false; >+ } ); >+ >+ $('#manage-batches').click( function() { >+ window.open('/cgi-bin/koha/tools/manage-marc-import.pl'); >+ >+ return false; >+ } ); >+ > // Key bindings > bindGlobalKeys(); > > // Setup UI >- $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() { >+ $(".ui-modal").each( function() { > $(this).modal({ show: false }); > } ); > >@@ -1113,6 +1386,16 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr > $('#loading').hide(); > } > ); >+ }; ); >+ >+ $.each( backends, function( name ) { >+ if ( !this.save ) return; // Not a saving backend >+ >+ addSaveTarget( { >+ label: this.saveLabel, >+ id: name + '/', >+ description: '', >+ } ); > } ); > > // Start editor >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt >index c80943d587..300b7e0b27 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt >@@ -35,11 +35,10 @@ > <div id="toolbar" class="btn-toolbar"> > <button class="btn btn-default btn-sm" id="new-record" title="Open fresh record"><i class="fa fa-plus"></i> <span>New record</span></button> > <div class="btn-group"> >- <button class="btn btn-default btn-sm" id="save-record" title="Save current record (Ctrl-S)"><i class="fa fa-hdd-o"></i> <span>Save</span></button> >- <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"> >- <span class="caret"></span> >- </button> >- <ul class="dropdown-menu" id="save-dropdown"> >+ <button class="btn btn-default btn-sm" id="save-record" title="Save current record (Ctrl-S)"><i class="fa fa-save"></i> <span>Save </span></button> >+ <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> >+ <ul id="save-dropdown" class="dropdown-menu"> >+ <li><a id="save-with-override" href="#">Save overriding warnings</a></li> > </ul> > </div> > <button class="btn btn-default btn-sm" id="import-records" title="Import an MARC (ISO 2709) or MARCXML record"><i class="fa fa-download"></i> <span>Import record...</span></button> >@@ -66,6 +65,7 @@ > </a> > </li> > [% END %] >+ <li><a id="open-batches" href="#">Import batches...</a></li> > <li class="divider"></li> > <li><a class="set-fontSize" style="font-size: .92em" href="#">Small text</a></li> > <li><a class="set-fontSize" style="font-size: 1em" href="#">Normal text</a></li> >@@ -112,12 +112,19 @@ > </fieldset> > </form> > >+<h3>Save to:</h3> >+<form id="save-targets"> >+ <fieldset class="brief"> >+ <ol></ol> >+ </fieldset> >+</form> >+ > </div> > > </div> > </div> > >-<div id="advanced-search-ui" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="advanced-search-title" aria-hidden="true"> >+<div id="advanced-search-ui" class="ui-modal modal fade" tabindex="-1" role="dialog" aria-labelledby="advanced-search-title" aria-hidden="true"> > <div class="modal-dialog modal-lg"> > <div class="modal-content"> > >@@ -198,7 +205,7 @@ > </div> > </div> > >-<div id="search-results-ui" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="search-results-title" aria-hidden="true"> >+<div id="search-results-ui" class="ui-modal modal fade" tabindex="-1" role="dialog" aria-labelledby="search-results-title" aria-hidden="true"> > <div class="modal-dialog modal-lg"> > <div class="modal-content"> > >@@ -245,7 +252,7 @@ > > </div> > >-<div id="macro-ui" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="macro-title" aria-hidden="true"> >+<div id="macro-ui" class="ui-modal modal fade" tabindex="-1" role="dialog" aria-labelledby="macro-title" aria-hidden="true"> > <div class="modal-dialog modal-lg"> > <div class="modal-content"> > >@@ -271,6 +278,27 @@ > </div> > </div> > >+<div id="batches-ui" class="ui-modal modal fade" tabindex="-1" role="dialog" aria-labelledby="batches-title" aria-hidden="true"> >+ >+<div class="modal-header"> >+ <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">Ã</button> >+ <h3 id="batches-title">Import batch settings</h3> >+</div> >+ >+<div class="modal-body row-fluid"> >+ <div class="span9"> >+ <div id="toolbar" class="btn-toolbar"> >+ <button class="btn btn-small" type="submit" id="create-batch"><i class="icon-plus"></i> <span>Create new batch...</span></button> >+ <button class="btn btn-small" type="submit" id="manage-batches"><i class="icon-list"></i> <span>Manage import batches...</span></button> >+ </div> >+ <ul id="batches-list"></ul> >+ </div> >+ <div class="span3"> >+ </div> >+</div> >+ >+</div> >+ > <div id="shortcuts-contents" style="display: none"> > <table class="table table-condensed"> > <thead> >diff --git a/svc/cataloguing/import_batches b/svc/cataloguing/import_batches >new file mode 100755 >index 0000000000..883f3a5e7d >--- /dev/null >+++ b/svc/cataloguing/import_batches >@@ -0,0 +1,212 @@ >+#!/usr/bin/perl >+# >+# Copyright 2014 ByWater Solutions >+# >+# 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 Modern::Perl; >+ >+use C4::Biblio qw(); >+use C4::Context; >+use C4::ImportBatch qw( AddBiblioToBatch AddImportBatch GetImportRecordMarcXML ModBiblioInBatch ); >+use C4::Service; >+use DateTime; >+use DateTime::Format::Strptime; >+use Encode qw( encode_utf8 ); >+use Koha::Database; >+use MARC::Record; >+ >+our ( $query, $response ) = C4::Service->init( tools => 'manage_staged_marc' ); >+ >+sub _parse_005_timestamp { >+ my ( $timestamp, $default_to_end ) = @_; >+ >+ # YYYYMMDD >+ if ( $timestamp =~ /^\d{8}$/ ) { >+ my $parser = DateTime::Format::Strptime->new( pattern => '%Y%m%d' ); >+ my $timestamp = $parser->parse_datetime($timestamp); >+ if ( $timestamp && $default_to_end ) { >+ $timestamp->set( >+ hour => 23, >+ minute => 59, >+ second => 59 >+ ); >+ } >+ >+ return $timestamp; >+ # YYYYMMDDHHMMSS >+ } elsif ( $timestamp =~ /^\d{8}\d{6}$/ ) { >+ my $parser = DateTime::Format::Strptime->new( pattern => '%Y%m%d%H%M%S' ); >+ return $parser->parse_datetime($timestamp); >+ } >+} >+ >+sub download_batch { >+ my ( $batch_id ) = @_; >+ >+ my $schema = Koha::Database->new()->schema(); >+ >+ my %extra_conditions; >+ >+ my $start_control_number = $query->param( 'start_control_number' ); >+ my $end_control_number = $query->param( 'end_control_number' ); >+ >+ if ( $start_control_number && $end_control_number ) { >+ $extra_conditions{control_number} = { -between => [ $start_control_number, $end_control_number ] }; >+ } >+ >+ my $start_timestamp = $query->param( 'start_timestamp' ); >+ my $end_timestamp = $query->param( 'end_timestamp' ); >+ >+ if ( $start_timestamp && $end_timestamp ) { >+ my $start_timestamp = _parse_005_timestamp( $start_timestamp, 0 ); >+ my $end_timestamp = _parse_005_timestamp( $end_timestamp, 1 ); >+ >+ my $dtf = $schema->storage->datetime_parser; >+ >+ $extra_conditions{'import_record.upload_timestamp'} = { >+ -between => [ >+ $dtf->format_datetime($start_timestamp), >+ $dtf->format_datetime($end_timestamp) >+ ] >+ }; >+ } >+ >+ my $records = $schema->resultset('ImportBiblio')->search( >+ { >+ 'import_record.import_batch_id' => $batch_id, >+ %extra_conditions >+ }, >+ { >+ join => 'import_record', >+ } >+ ); >+ >+ my $mimetype = 'application/octet-stream'; >+ my $charset = 'utf-8'; >+ my $filename = DateTime->now->strftime("batch-$batch_id-%Y-%m-%d-%H-%M-%S.mrc"); >+ my $content_length = $records->get_column( { length => 'import_record.marc' } )->sum || 0; >+ >+ print $query->header( >+ -type => $mimetype, >+ -charset => $charset, >+ -attachment => $filename, >+ -Content_length => $content_length >+ ); >+ >+ for my $marc ( $records->get_column( 'import_record.marc' )->all ) { >+ print $marc; >+ } >+} >+ >+sub get_record { >+ my ( $batch_id, $import_record_id ) = @_; >+ >+ my $marcxml = GetImportRecordMarcXML( $import_record_id ); >+ >+ if ( !$marcxml ) { >+ C4::Service->return_error( 'not_found' ); >+ } >+ >+ $response->param( record => $marcxml ); >+ >+ C4::Service->return_success( $response ); >+} >+ >+sub _control_number_already_used { >+ my ( $marc_record, $batch_id, $import_record_id ) = @_; >+ >+ return unless ( $marc_record->field('001') ); >+ >+ my %extra_conditions; >+ >+ $extra_conditions{'import_record.import_record_id'} = { '!=', $import_record_id } if ( $import_record_id ); >+ >+ my $control_number = $marc_record->field('001')->data; >+ >+ my $schema = Koha::Database->new()->schema(); >+ return $schema->resultset('ImportBiblio')->count( >+ { >+ control_number => $control_number, >+ 'import_record.import_batch_id' => $batch_id, >+ %extra_conditions >+ }, >+ { >+ join => 'import_record', >+ } >+ ); >+} >+ >+sub create_record { >+ my ( $batch_id ) = @_; >+ >+ my $marcflavour = C4::Context->preference('marcflavour') || 'marc21'; >+ my $marc_record = eval { MARC::Record::new_from_xml( $query->param('record'), "utf8", $marcflavour) }; >+ if ($@) { >+ C4::Service->return_error( 'failed', $@ ); >+ } >+ >+ if ( !$query->param('allow_control_number_conflict') && _control_number_already_used( $marc_record, $batch_id ) ) { >+ C4::Service->return_error( 'control_number_match', '' ); >+ } >+ >+ my $import_record_id = AddBiblioToBatch( $batch_id, 0, $marc_record, "utf8", int( rand(99999) ) ); >+ $response->param( import_record_id => $import_record_id ); >+ >+ C4::Service->return_success( $response ); >+} >+ >+sub update_record { >+ my ( $batch_id, $import_record_id ) = @_; >+ >+ my $marcflavour = C4::Context->preference('marcflavour') || 'MARC21'; >+ my $marc_record = eval { MARC::Record::new_from_xml( $query->param('record'), "utf8", $marcflavour) }; >+ if ($@) { >+ C4::Service->return_error( 'failed', $@ ); >+ } >+ >+ if ( !$query->param('allow_control_number_conflict') && _control_number_already_used( $marc_record, $batch_id, $import_record_id ) ) { >+ C4::Service->return_error( 'control_number_match', '' ); >+ } >+ >+ C4::Biblio::UpdateMarcTimestamp( $marc_record ); >+ ModBiblioInBatch( $import_record_id, $marc_record ); >+ >+ $response->param( updated_record => $marc_record->as_xml_record( C4::Context->preference('marcflavour') ) ); >+ >+ C4::Service->return_success( $response ); >+} >+ >+sub create_batch { >+ my ( $batch_name ) = C4::Service->require_params( 'batch_name' ); >+ >+ my $batch_id = AddImportBatch({ >+ file_name => $batch_name, >+ record_type => 'biblio' >+ }); >+ >+ $response->param( batch_id => $batch_id ); >+ >+ C4::Service->return_success( $response ); >+} >+ >+C4::Service->dispatch( >+ [ 'GET /(\d+)', [ 'download' ], \&download_batch ], >+ [ 'GET /(\d+)/(\d+)', [], \&get_record ], >+ [ 'POST /(\d+)/', [], \&create_record ], >+ [ 'POST /(\d+)/(\d+)', [], \&update_record ], >+ [ 'POST /', [], \&create_batch ], >+); >-- >2.11.0
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 18823
:
64434
|
66882
|
66883
|
66884
|
66885
|
66919
|
77240
|
77241
|
77242
|
77243
|
77244
|
79240
|
79241
|
79242
|
79243
|
79244
|
80519
|
80536
|
80604
|
80606
|
80608
|
80613
|
80626
|
80627
|
80628
|
80637
|
80639
|
80651
|
80861
|
80866
|
80868
|
80869
|
80870
|
80871
|
80872
|
80873
|
80947
|
85355
|
85356
|
85357
|
85358
|
85359
|
85376
|
85719
|
85720
|
85785
|
85828
|
86087
|
86173
|
86174
|
86175
|
86176
|
86177
|
86178
|
86179
|
86180
|
86181
|
86182
|
86183
|
86791
|
86792
|
86793
|
86794
|
86795
|
86796
|
86797
|
86798
|
86799
|
88417
|
88418
|
88419
|
88420
|
88421
|
88422
|
88423
|
88424
|
88425
|
88426
|
88427
|
88428
|
88429
|
88430
|
88431
|
88432
|
88493
|
88494
|
88495
|
88496
|
88497
|
88498
|
88499
|
88500
|
88501
|
88502
|
88503
|
88504
|
88505
|
88506
|
88507
|
88508
|
91205
|
91206
|
91207
|
91208
|
91209
|
91210
|
91211
|
91212
|
91213
|
91214
|
91215
|
91216
|
91217
|
91218
|
91219
|
92051
|
92120
|
92124
|
94436
|
94437
|
94438
|
94439
|
94440
|
94441
|
94442
|
94443
|
94444
|
94445
|
94446
|
94447
|
94448
|
94449
|
94450
|
94451
|
94452
|
99092
|
99093
|
99094
|
99095
|
99096
|
99097
|
99098
|
99099
|
99100
|
99101
|
99102
|
99103
|
99104
|
99105
|
99106
|
99107
|
99108
|
99109