Bug 16424: Add framework support to advanced MARC editor
authorEre Maijala <ere.maijala@helsinki.fi>
Sun, 16 Sep 2018 18:37:06 +0000 (21:37 +0300)
committerNick Clemens <nick@bywatersolutions.com>
Fri, 28 Sep 2018 19:01:50 +0000 (19:01 +0000)
Keeps the selected framework, allows selecting another and validates the record using the correct framework. Contains additional minor tweaks to display proper error messages.

To test:
1. Add a record with a non-default framework in the basic editor.
2. Switch to advanced editor and make sure the settings menu displays the correct framework.
3. Save the record and confirm that the framework code did not change.
4. Change the framework and save the record again.
5. Verify that the framework code changed.
6. Change one framework to make an extra field mandatory.
7. Make sure that the field is required in the editor when the framework above is selected but not when another framework is selected.

Signed-off-by: Michal Denar <black23@gmail.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

cataloguing/editor.pl
koha-tmpl/intranet-tmpl/lib/koha/cateditor/koha-backend.js
koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js
koha-tmpl/intranet-tmpl/prog/css/cateditor.css
koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc
koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt
svc/bib
svc/bib_framework [new file with mode: 0755]

index 0806233..42f35b3 100755 (executable)
@@ -30,6 +30,7 @@ use C4::Output;
 use DBIx::Class::ResultClass::HashRefInflator;
 use Koha::Database;
 use Koha::MarcSubfieldStructures;
+use Koha::BiblioFrameworks;
 
 my $input = CGI->new;
 
@@ -60,6 +61,9 @@ $template->{VARS}->{DefaultLanguageField008} = pack( 'A3', C4::Context->preferen
 my $authtags = Koha::MarcSubfieldStructures->search({ authtypecode => { '!=' => '' }, 'frameworkcode' => '' });
 $template->{VARS}->{authtags} = $authtags;
 
+my $frameworks = Koha::BiblioFrameworks->search({}, { order_by => ['frameworktext'] });
+$template->{VARS}->{frameworks} = $frameworks;
+
 # Z39.50 servers
 my $dbh = C4::Context->dbh;
 $template->{VARS}->{z3950_servers} = $dbh->selectall_arrayref( q{
index 9e393d3..49bf383 100644 (file)
@@ -66,9 +66,26 @@ define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=defin
         while ( record.removeField(bibnumTag) );
     }
 
+    function initFramework( frameworkcode, callback ) {
+        if ( typeof _frameworks[frameworkcode] === 'undefined' ) {
+            $.get(
+                '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=' + frameworkcode
+            ).done( function( framework ) {
+                _importFramework( frameworkcode, framework.framework );
+                callback();
+            } ).fail( function( data ) {
+                callback( 'Could not fetch framework settings' );
+            } );
+        } else {
+            callback();
+        }
+    }
+
     var KohaBackend = {
         NOT_EMPTY: {}, // Sentinel value
 
+        InitFramework: initFramework,
+
         GetAllTagsInfo: function( frameworkcode, tagnumber ) {
             return _framework_mappings[frameworkcode];
         },
@@ -89,12 +106,23 @@ define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=defin
         GetRecord: function( id, callback ) {
             $.get(
                 '/cgi-bin/koha/svc/bib/' + id
-            ).done( function( data ) {
-                var record = new MARC.Record();
-                record.loadMARCXML(data);
-                callback(record);
-            } ).fail( function( data ) {
-                callback( { error: data } );
+            ).done( function( metadata ) {
+                $.get(
+                    '/cgi-bin/koha/svc/bib_framework/' + id
+                ).done( function( frameworkcode ) {
+                    var record = new MARC.Record();
+                    record.loadMARCXML(metadata);
+                    record.frameworkcode = $(frameworkcode).find('frameworkcode').text();
+                    initFramework( record.frameworkcode, function( error ) {
+                        if ( typeof error === 'undefined' ) {
+                            callback( record );
+                        } else {
+                            callback( { error: error } );
+                        }
+                    });
+                } ).fail( function( data ) {
+                    callback( { error: _('Could not fetch frameworkcode for record') } );
+                } );
             } );
         },
 
@@ -110,21 +138,26 @@ define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=defin
             } ).done( function( data ) {
                 callback( _fromXMLStruct( data ) );
             } ).fail( function( data ) {
-                callback( { error: data } );
+                callback( { error: _('Could not save record') } );
             } );
         },
 
         SaveRecord: function( id, record, callback ) {
+            var frameworkcode = record.frameworkcode;
             record = record.clone();
             _removeBiblionumberFields( record );
 
             $.ajax( {
                 type: 'POST',
-                url: '/cgi-bin/koha/svc/bib/' + id,
+                url: '/cgi-bin/koha/svc/bib/' + id + '?frameworkcode=' + encodeURIComponent(frameworkcode),
                 data: record.toXML(),
                 contentType: 'text/xml'
             } ).done( function( data ) {
-                callback( _fromXMLStruct( data ) );
+                var record = _fromXMLStruct( data );
+                if ( record.marcxml ) {
+                    record.marcxml[0].frameworkcode = frameworkcode;
+                }
+                callback( record );
             } ).fail( function( data ) {
                 callback( { data: { error: data } } );
             } );
@@ -196,10 +229,10 @@ define( [ '/cgi-bin/koha/svc/cataloguing/framework?frameworkcode=&callback=defin
         ValidateRecord: function( frameworkcode, record ) {
             var errors = [];
 
-            var mandatoryTags = KohaBackend.GetTagsBy( '', 'mandatory', '1' );
-            var mandatorySubfields = KohaBackend.GetSubfieldsBy( '', 'mandatory', '1' );
-            var nonRepeatableTags = KohaBackend.GetTagsBy( '', 'repeatable', '0' );
-            var nonRepeatableSubfields = KohaBackend.GetSubfieldsBy( '', 'repeatable', '0' );
+            var mandatoryTags = KohaBackend.GetTagsBy( record.frameworkcode, 'mandatory', '1' );
+            var mandatorySubfields = KohaBackend.GetSubfieldsBy( record.frameworkcode, 'mandatory', '1' );
+            var nonRepeatableTags = KohaBackend.GetTagsBy( record.frameworkcode, 'repeatable', '0' );
+            var nonRepeatableSubfields = KohaBackend.GetSubfieldsBy( record.frameworkcode, 'repeatable', '0' );
 
             $.each( mandatoryTags, function( tag ) {
                 if ( !record.hasField( tag ) ) errors.push( { type: 'missingTag', tag: tag } );
index 1caf75d..046ff71 100644 (file)
@@ -478,6 +478,8 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
     } );
 
     function MARCEditor( options ) {
+        this.frameworkcode = '';
+
         this.cm = CodeMirror(
             options.position,
             {
@@ -535,9 +537,32 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
             this.cm.refresh();
         },
 
+        setFrameworkCode: function( code, callback ) {
+            this.frameworkcode = code;
+            $( 'a.change-framework i.selected' ).addClass( 'hidden' );
+            $( 'a.change-framework i.unselected' ).removeClass( 'hidden' );
+            $( 'a.change-framework[data-frameworkcode="' + code + '"] i.unselected' ).addClass( 'hidden' );
+            $( 'a.change-framework[data-frameworkcode="' + code + '"] i.selected' ).removeClass( 'hidden ');
+            KohaBackend.InitFramework( code, callback );
+        },
+
         displayRecord: function( record ) {
             this.cm.setValue( TextMARC.RecordToText(record) );
             this.modified = false;
+            var cm = this.cm;
+            this.setFrameworkCode(
+                typeof record.frameworkcode !== 'undefined' ? record.frameworkcode : '',
+                function ( error ) {
+                    if ( typeof error !== 'undefined' ) {
+                        humanMsg.displayAlert( _(error), { className: 'humanError' } );
+                    }
+                    cm.setOption( 'mode', {
+                        name: 'marc',
+                        nonRepeatableTags: KohaBackend.GetTagsBy( this.frameworkcode, 'repeatable', '0' ),
+                        nonRepeatableSubfields: KohaBackend.GetSubfieldsBy( this.frameworkcode, 'repeatable', '0' )
+                    });
+                }
+            );
         },
 
         getRecord: function() {
@@ -553,6 +578,7 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
 
             this.textMode = false;
 
+            record.frameworkcode = this.frameworkcode;
             return record;
         },
 
index 6840d0c..b134b66 100644 (file)
@@ -55,6 +55,12 @@ body {
     padding-bottom: 0;
 }
 
+.humanMsg.humanSuccess {
+       left: 75%;
+    width: 20%;
+    top: 160px;
+}
+
 #shortcuts-container {
     font-size: 12px;
 }
@@ -283,14 +289,6 @@ body {
     width: 14em;
 }
 
-.fa.fa-spinner {
-    display: inline-block;
-    height: 16px;
-    width: 16px;
-    background: transparent url("../img/spinner-small.gif") top left no-repeat;
-    padding: -1px;
-    vertical-align: text-top;
-}
 
 /*> Search results */
 
index 7dd6f9d..a20bbe6 100644 (file)
@@ -329,6 +329,7 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr
             if (data.newRecord) {
                 var record = new MARC.Record();
                 record.loadMARCXML(data.newRecord);
+                record.frameworkcode = data.newRecord.frameworkcode;
                 editor.displayRecord( record );
             }
 
@@ -841,7 +842,7 @@ 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' ).siblings( 'span' ).text( _("Saving...") );
+             $( '#save-record' ).find('i').attr( 'class', 'fa fa-spinner fa-spin' ).siblings( 'span' ).text( _("Saving...") );
 
             function finishCb(result) {
                 if ( result.error == 'syntax' ) {
@@ -1093,6 +1094,19 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr
                 { return undefined; }
         };
 
+        $('a.change-framework').click( function() {
+            $("#loading").show();
+            editor.setFrameworkCode(
+                $(this).data( 'frameworkcode' ),
+                function ( error ) {
+                    if ( typeof error !== 'undefined' ) {
+                        humanMsg.displayAlert( _("Failed to change framework"), { className: 'humanError' } );
+                    }
+                    $('#loading').hide();
+                }
+            );
+        } );
+
         // Start editor
         Preferences.Load( [% logged_in_user.borrowernumber || 0 | html %] );
         displayPreferences(editor);
@@ -1104,7 +1118,10 @@ require( [ 'koha-backend', 'search', 'macros', 'marc-editor', 'marc-record', 'pr
         } );
 
         function finishCb( data ) {
-            if ( data.error ) openRecord( 'new/', editor, finishCb );
+            if ( data.error ) {
+                humanMsg.displayAlert( data.error );
+                openRecord( 'new/', editor, finishCb );
+            }
 
             Resources.GetAll().done( function() {
                 $("#loading").hide();
index b282489..503552f 100644 (file)
             <ul id="prefs-menu" class="dropdown-menu">
                 <li><a id="switch-editor" href="#">Switch to basic editor</a></li>
                 <li><a id="set-field-widgets" href="#"></a></li>
+                <li class="nav-header">Change framework</li>
+                <li>
+                    <a class="change-framework" data-frameworkcode="">
+                        <i class="fa fa-fw unselected">&nbsp;</i>
+                        <i class="fa fa-fw fa-check selected">&nbsp;</i>
+                        Default
+                    </a>
+                </li>
+                [% FOREACH framework IN frameworks %]
+                    <li>
+                        <a class="change-framework" data-frameworkcode="[% framework.frameworkcode | html %]">
+                            <i class="fa fa-fw fa-check selected"></i>
+                            <i class="fa fa-fw unselected">&nbsp;</i>
+                            [% framework.frameworktext | html %]
+                        </a>
+                    </li>
+                [% END %]
                 <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>
diff --git a/svc/bib b/svc/bib
index 36fa461..5898517 100755 (executable)
--- a/svc/bib
+++ b/svc/bib
@@ -80,6 +80,7 @@ sub update_bib {
     my $query = shift;
     my $biblionumber = shift;
     my $old_record = GetMarcBiblio({ biblionumber => $biblionumber });
+    my $frameworkcode = $query->url_param('frameworkcode') // '';
     unless  (defined $old_record) {
         print $query->header(-type => 'text/xml', -status => '404 Not Found');
         return;
@@ -113,7 +114,7 @@ sub update_bib {
             }
         }
 
-        ModBiblio( $record, $biblionumber, '' );
+        ModBiblio( $record, $biblionumber, $frameworkcode );
         my $new_record = GetMarcBiblio({
             biblionumber => $biblionumber,
             embed_items  => scalar $query->url_param('items') });
diff --git a/svc/bib_framework b/svc/bib_framework
new file mode 100755 (executable)
index 0000000..4b47d6d
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+# Copyright 2007 LibLime
+# Copyright 2012 software.coop and MJ Ray
+#
+# 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, see <http://www.gnu.org/licenses>.
+#
+use strict;
+use warnings;
+use CGI qw ( -utf8 );
+use C4::Auth qw/check_api_auth/;
+use C4::Biblio;
+use C4::Items;
+use XML::Simple;
+
+binmode STDOUT, ':encoding(UTF-8)';
+
+my $query = new CGI;
+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');
+}
+if ($query->request_method eq 'GET') {
+    fetch_bib_framework($query, $biblionumber);
+} else {
+    print $query->header(-type => 'text/xml', -status => '405 Method not allowed');
+    print XMLout({ error => 'Method not allowed' }, NoAttr => 1, RootName => 'response', XMLDecl => 1);
+    exit 0;
+}
+exit 0;
+
+sub fetch_bib_framework {
+    my $query = shift;
+    my $biblionumber = shift;
+    my $frameworkcode = GetFrameworkCode( $biblionumber );
+    my $result = {
+        'frameworkcode' => $frameworkcode
+    };
+    print $query->header(-type => 'text/xml',-charset => 'utf-8',);
+    print XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => 0);
+}