From 9048e1ee22adf2fe873f1ae1091e071d8f54896b Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 2 Dec 2020 18:14:32 +0000 Subject: [PATCH] Bug 24429: Advanced editor - Add ability to generate an export file as cataloger saves Adds the ability for catalogers to *automatically* save a list of bibs to export as they are cataloging. To test, apply patch: 0/Verify that the advanced editor is enabled by going to "Administration" and searching for "EnableAdvancedCatalogingEditor" 1/Set it to "Enable" if it isn't already. 2/Go to "Cataloging", click on "Advanced editor" 3/Click on "Export" button (verify a modal window comes up) 4/Click on the link for "New Export File", name the export file 5/Close the modal window 6/Search for a record by entering "specious" in the keyword box and hitting enter 7/Click "import" on a record 8/Make some changes and hit "Save", note the bib number 9/Repeat steps 6-8 for several records, getting records from the catalog and importing through the "import" button in the UI 10/Click on "Export..." button 11/Click on the link for the name you created in step 4 12/Verify the bib numbers from step 8 are in the Export Files Editor 13/Click "Generate Export File" 14/Verify that all the records for every bib listed is in the file generated 15/Repeat steps with different file names / records --- .../lib/koha/cateditor/preferences.js | 1 + koha-tmpl/intranet-tmpl/prog/css/cateditor.css | 90 +++++++++++- .../prog/en/includes/cateditor-ui.inc | 152 ++++++++++++++++++++- .../prog/en/modules/cataloguing/editor.tt | 25 ++++ tools/export.pl | 7 + 5 files changed, 272 insertions(+), 3 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js index 5cf81dace6..2a71d463c5 100644 --- a/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js +++ b/koha-tmpl/intranet-tmpl/lib/koha/cateditor/preferences.js @@ -33,6 +33,7 @@ define( function() { font: 'monospace', fontSize: '1em', macros: {}, + exportRecords: (); 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 ba7de2e5b2..426a8f1b58 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/cateditor.css +++ b/koha-tmpl/intranet-tmpl/prog/css/cateditor.css @@ -224,7 +224,7 @@ 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, #export-ui .modal-lg { width: 90%; } @@ -372,6 +372,92 @@ body { z-index: 9001; } +/*> Export Records UI */ +#export-save-message { + color: #666; + font-size: 13px; + float: right; + line-height: 26px; +} + +#export-list > li { + border: 2px solid #F0F0F0; + border-radius: 6px; + display: block; + font-size: 115%; +} + +#export-list > li + li { + margin-top: -2px; +} + +#export-list .active { + background: #EDF4F6; + border-color: none; +} + +#export-list a { + display: block; + padding: 6px; +} + +#export-list a:focus { + outline: none; +} + +.export-info { + background-color: #F4F4F4; + display: none; + margin: 0; + padding: 10px; + text-align: right; +} + +.export-info li { + color: #666; + font-size: 75%; + list-style-type: none; +} + +.export-info .label { + clear: left; + font-weight: bold; + float: left; +} + +#export-list .active .export-info { + display: block; +} + +.btn-toolbar label, .btn-toolbar select { + font-size: 13px; + vertical-align: middle; +} + +.btn-toolbar label { + margin-left: 1em; +} + +.btn-toolbar select { + padding: 2px; +} + +#export-editor { + display: flex; + flex-direction: column; + height: 100%; +} + +#export-editor .CodeMirror { + flex: 1; + font-size: 13px; +} + +/* Hotpatch from latest CodeMirror: Fix gutter positioning */ +.CodeMirror-gutter-wrapper { + position: absolute; +} + /*> Macros */ #macro-save-message { color: #666; @@ -477,4 +563,4 @@ body { .ui-keyboard-accept { display: none; -} \ No newline at end of file +} 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 6f44f4b9da..215ff634c5 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc @@ -82,6 +82,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr var editor; var macroEditor; + var exportRecordEditor; function makeAuthorisedValueWidgets( frameworkCode ) { $.each( KohaBackend.GetAllTagsInfo( frameworkCode ), function( tag, tagInfo ) { @@ -279,6 +280,17 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr done( { error: data.error, newRecord: data.marcxml && data.marcxml[0], newId: data.biblionumber && [ 'catalog', data.biblionumber ] } ); } + //Add the record to the exportRecords list if there is one + if(Preferences.user.exportRecords) { + var export_list = $.map (Preferences.user.exportRecords, function (exportRecord, name) { + return $.extend( { name: name}, exportRecord ); + }); + $.each (export_list, function ( undef, exportRecord ) { + exportRecord.contents += "\n" + state.recordID; + storeExportRecord( exportRecord.name, { contents: exportRecord.contents} ); + } ); + } + if ( id ) { KohaBackend.SaveRecord( id, record, finishCb ); } else { @@ -643,6 +655,9 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr case 'macros': // Macros loaded on first show of modal break; + case 'exportRecords': + //Export records loaded on first show of modal + break; case 'selected_search_targets': $.each( z3950Servers, function( index, server ) { var saved_val = Preferences.user.selected_search_targets[server.server_id]; @@ -696,6 +711,88 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr } ); } + //> Export record functions + function loadExportRecord( name ) { + $( '#export-list li' ).removeClass ('active' ); + exportRecordEditor.activeExportRecord = name; + + if ( !name ) { + exportRecordEditor.setValue ( '' ); + return; + } + + $( '#export-list li[data-name="' + name + '"]' ).addClass( 'active' ); + var exportRecord = Preferences.user.exportRecords[name]; + exportRecord.contents = dedupeString( exportRecord.contents ); + exportRecordEditor.setValue( exportRecord.contents ); + exportRecordEditor.setOption( 'readOnly', false ); + if ( exportRecord.history ) exportRecordEditor.setHistory( exportRecord.history ); + } + + function dedupeString( strg ) { + var pieces = strg.split("\n"); + var output = []; //Output array + for (var i = 0; i < pieces.length; i++) { + if (output.indexOf(pieces[i]) < 0 && pieces[i].length > 0) { + output.push(pieces[i]); + } + } + + return output.join("\n"); + } + + function storeExportRecord( name, exportRecord ) { + if ( exportRecord ) { + Preferences.user.exportRecords[name] = exportRecord; + } else { + delete Preferences.user.exportRecords[name]; + } + + Preferences.Save( [% logged_in_user.borrowernumber | html %] ); + } + + function showSavedExportRecords ( exportRecords ) { + var scrollTop = $('#export-list').scrollTop(); + $( '#export-list').empty(); + var export_list = $.map (Preferences.user.exportRecords, function (exportRecord, name) { + return $.extend( { name: name }, exportRecord ); + } ); + export_list.sort (function ( a, b ) { + return a.name.localeCompare(b.name); + } ); + $.each ( export_list, function ( undef, exportRecord ) { + var $li = $( '
  • ' + exportRecord.name + '
        ' ); + $li.click ( function() { + loadExportRecord(exportRecord.name); + return false; + } ); + if ( exportRecord.name == exportRecordEditor.activeExportRecord ) $li.addClass ( 'active' ); + var modified = exportRecord.modified && new Date(exportRecord.modified); + $('#export-list').append($li); + } ); + var $new_li = $( '
      1. ' + _("New Export File...") + '
      2. ' ); + $new_li.click( function () { + var name = prompt(_("Please enter the name for the new export record:")); + if (!name) return; + + if ( !Preferences.user.exportRecords[name] ) storeExportRecord( name, {contents: "" } ); + showSavedExportRecords(); + loadExportRecord( name ); + } ); + $('#export-list').append($new_li); + $('#export-list').scrollTop(scrollTop); + } + + function saveExportRecord() { + var name = exportRecordEditor.activeExportRecord; + if (!name || exportRecordEditor.savedGeneration == exportRecordEditor.changeGeneration() ) return; + exportRecordEditor.savedGeneration = exportRecordEditor.changeGeneration(); + storeExportRecord( name, { contents: exportRecordEditor.getValue(), modified: (new Date()).valueOf(), history: exportRecordEditor.getHistory() } ); + } + + + //> End Exporter Record Functions + //> Macro functions var canCreatePublic = "[% CAN_user_editcatalogue_create_shared_macros | html %]"; var canDeletePublic = "[% CAN_user_editcatalogue_delete_shared_macros | html %]"; @@ -1034,6 +1131,33 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr showSavedMacros(); } ); + $( '#exporter-ui').on( 'show.bs.modal', function() { + if ( exportRecordEditor ) return; + + exportRecordEditor = CodeMirror( + $('#export-editor')[0], + { + mode: 'null', + lineNumbers: true, + readOnly: true, + } + ); + + var saveTimeout; + exportRecordEditor.on ( 'change', function ( cm, change ) { + $('#export-save-message').empty(); + if ( change.origin == 'setValue' ) return; + + if ( saveTimeout ) clearTimeout( saveTimeout ); + saveTimeout = setTimeout ( function() { + saveExportRecord(); + saveTimeout = null; + }, 500 ); + } ); + + showSavedExportRecords(); + } ); + var saveableBackends = []; $.each( backends, function( id, backend ) { if ( backend.save ) saveableBackends.push( [ backend.saveLabel, id ] ); @@ -1106,6 +1230,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr saveRecord( backend + '/', editor, finishCb ); } + return false; } ); @@ -1196,6 +1321,31 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr } } ); + $('#open-exporter').click( function () { + $('#exporter-ui').modal('show'); + + return false; + } ); + + $('#generate-file').click( function () { + var bibs = exportRecordEditor.getValue(); + bibs = bibs.replace(/\r?\n/g, ",") + ","; + $.post("/cgi-bin/koha/tools/export.pl", {"record_type":"bibs", "export_bibs":bibs, "filename":"test.mrc", "op":"export" }, function (data) { + var blob = new Blob([data], { 'type': 'application/octet-stream;charset=utf-8'}); + saveAs(blob, exportRecordEditor.activeExportRecord + ".mrc" ); + } ); + } ); + + $('#delete-export-file').click( function() { + if ( !exportRecordEditor.activeExportRecord || !confirm( _("Are you sure you want to delete this Export File?") ) ) return; + + storeExportRecord( exportRecordEditor.activeExportRecord, undefined ); + showSavedExportRecords(); + loadExportRecord( undefined ); + + return false; + } ); + $( '#show-advanced-search' ).click( function() { showAdvancedSearch(); @@ -1247,7 +1397,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr bindGlobalKeys(); // Setup UI - $("#advanced-search-ui, #search-results-ui, #macro-ui").each( function() { + $("#advanced-search-ui, #search-results-ui, #macro-ui", "#exporter-ui").each( function() { $(this).modal({ show: false }); } ); 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 d28822651f..68c2ac19ed 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt @@ -41,6 +41,7 @@ +
        + +