Bug 21411: Advanced cataloging editor - rancor - Allow configuration of Keyboard
authorNick Clemens <nick@bywatersolutions.com>
Mon, 4 Mar 2019 16:23:31 +0000 (11:23 -0500)
committerNick Clemens <nick@bywatersolutions.com>
Fri, 10 May 2019 18:59:39 +0000 (18:59 +0000)
This patch allows for redfining the keyboard shortcuts used in rancor.
For now these are set globally (per instance)
Ctrl-S is a codemirror setting and cannot be altered

To test:
 1 - Apply patches
 2 - update database
 3 - Load the advanced cataloging editor
 4 - Click the "Keyboard shortcuts" button
 5 - Ensure the list looks correct
 6 - Note that 'Ctrl-S' note sit cannot be remapped
 7 - Note the 'Redefine shortcuts' link at the top of the menu
 8 - Confirm all the listed shortcuts work as expected
 9 - Click the 'Redefine' link
10 - Ensure current mappings load correctly
11 - Read explanation and verify it makes sense
12 - Remap some functions
13 - Return to the cataloging editor
14 - Confirm your mappings work

Signed-off-by: Liz Rea <liz@catalyst.net.nz>

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

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

12 files changed:
Koha/KeyboardShortcut.pm [new file with mode: 0644]
Koha/KeyboardShortcuts.pm [new file with mode: 0644]
cataloguing/editor.pl
installer/data/mysql/atomicupdate/add_keyboard_shortcuts.perl [new file with mode: 0644]
installer/data/mysql/en/mandatory/keyboard_shortcuts.sql [new file with mode: 0644]
installer/data/mysql/en/mandatory/keyboard_shortcuts.txt [new file with mode: 0644]
installer/data/mysql/kohastructure.sql
koha-tmpl/intranet-tmpl/lib/koha/cateditor/marc-editor.js
koha-tmpl/intranet-tmpl/prog/en/includes/cateditor-ui.inc
koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/editor.tt
koha-tmpl/intranet-tmpl/prog/en/modules/tools/adveditorshortcuts.tt [new file with mode: 0644]
tools/adveditorshortcuts.pl [new file with mode: 0755]

diff --git a/Koha/KeyboardShortcut.pm b/Koha/KeyboardShortcut.pm
new file mode 100644 (file)
index 0000000..4a45f44
--- /dev/null
@@ -0,0 +1,44 @@
+package Koha::KeyboardShortcut;
+
+# 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 Carp;
+
+use Koha::Database;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::KeyboardShortcut - Koha KeyboardShortcut Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'KeyboardShortcut';
+}
+
+1;
diff --git a/Koha/KeyboardShortcuts.pm b/Koha/KeyboardShortcuts.pm
new file mode 100644 (file)
index 0000000..af26745
--- /dev/null
@@ -0,0 +1,50 @@
+package Koha::KeyboardShortcuts;
+
+# 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 Carp;
+
+use Koha::Database;
+
+use Koha::KeyboardShortcut;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::KeyboardShortcuts - Koha KeyboardShortcut Object set class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'KeyboardShortcut';
+}
+
+sub object_class {
+    return 'Koha::KeyboardShortcut';
+}
+
+1;
index a0e6c83..7fb34cf 100755 (executable)
@@ -31,6 +31,7 @@ use DBIx::Class::ResultClass::HashRefInflator;
 use Koha::Database;
 use Koha::MarcSubfieldStructures;
 use Koha::BiblioFrameworks;
+use Koha::KeyboardShortcuts;
 
 my $input = CGI->new;
 
@@ -51,6 +52,13 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
 
 my $schema = Koha::Database->new->schema;
 
+my @keyboard_shortcuts = Koha::KeyboardShortcuts->search();
+
+# Keyboard shortcuts
+$template->param(
+    shortcuts => \@keyboard_shortcuts,
+);
+
 # Available import batches
 $template->{VARS}->{editable_batches} = [ $schema->resultset('ImportBatch')->search(
     {
diff --git a/installer/data/mysql/atomicupdate/add_keyboard_shortcuts.perl b/installer/data/mysql/atomicupdate/add_keyboard_shortcuts.perl
new file mode 100644 (file)
index 0000000..a90d5a8
--- /dev/null
@@ -0,0 +1,30 @@
+$DBversion = 'XXX';
+if( CheckVersion( $DBversion ) ) {
+    unless ( TableExists( 'keyboard_shortcuts' ) ) {
+        $dbh->do(q|
+            CREATE TABLE keyboard_shortcuts (
+            shortcut_name varchar(80) NOT NULL,
+            shortcut_keys varchar(80) NOT NULL,
+            shortcut_desc varchar(200) NOT NULL,
+            PRIMARY KEY (shortcut_name)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;|
+        );
+    }
+    $dbh->do(q|
+        INSERT IGNORE INTO keyboard_shortcuts (shortcut_name, shortcut_keys, shortcut_desc) VALUES
+        ("insert_copyright","Alt-C","Insert copyright symbol (©)"),
+        ("insert_copyright_sound","Alt-P","Insert copyright symbol (℗) (sound recordings)"),
+        ("insert_delimiter","Ctrl-D","Insert delimiter (‡)"),
+        ("subfield_help","Ctrl-H","Get help on current subfield"),
+        ("link_authorities","Shift-Ctrl-L","Link field to authorities"),
+        ("delete_field","Ctrl-X","Delete current field"),
+        ("delete_subfield","Shift-Ctrl-X","Delete current subfield"),
+        ("new_line","Enter","New field on next line"),
+        ("line_break","Shift-Enter","Insert line break"),
+        ("next_position","Tab","Move to next position"),
+        ("prev_position","Shift-Tab","Move to previous position")
+        ;|
+    );
+    SetVersion( $DBversion );
+    print "Upgrade to $DBversion done (Bug XXXXX - Add keyboard_shortcuts table)\n";
+}
diff --git a/installer/data/mysql/en/mandatory/keyboard_shortcuts.sql b/installer/data/mysql/en/mandatory/keyboard_shortcuts.sql
new file mode 100644 (file)
index 0000000..b7e30d3
--- /dev/null
@@ -0,0 +1,35 @@
+--
+-- Default keyboard shortcuts
+-- for Koha.
+--
+-- Copyright (C) 2007 LiblimeA
+-- Copyright 2018 Koha Development Team
+--
+-- 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 2 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.
+
+-- keyboard shortcuts
+INSERT INTO keyboard_shortcuts (shortcut_name, shortcut_keys, shortcut_desc) VALUES
+    ("insert_copyright","Alt-C","Insert copyright symbol (©)"),
+    ("insert_copyright_sound","Alt-P","Insert copyright symbol (℗) (sound recordings)"),
+    ("insert_delimiter","Ctrl-D","Insert delimiter (‡)"),
+    ("subfield_help","Ctrl-H","Get help on current subfield"),
+    ("link_authorities","Shift-Ctrl-L","Link field to authorities"),
+    ("delete_field","Ctrl-X","Delete current field"),
+    ("delete_subfield","Shift-Ctrl-X","Delete current subfield"),
+    ("new_line","Enter","New field on next line"),
+    ("line_break","Shift-Enter","Insert line break"),
+    ("next_position","Tab","Move to next position"),
+    ("prev_position","Shift-Tab","Move to previous position");
diff --git a/installer/data/mysql/en/mandatory/keyboard_shortcuts.txt b/installer/data/mysql/en/mandatory/keyboard_shortcuts.txt
new file mode 100644 (file)
index 0000000..64e5e94
--- /dev/null
@@ -0,0 +1 @@
+Default keyboard shorcuts
index f82fc52..f4c9b25 100644 (file)
@@ -4324,6 +4324,18 @@ CREATE TABLE stockrotationitems (
       ON UPDATE CASCADE ON DELETE CASCADE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
+--
+-- Table structure for table `keyboardshortcuts`
+--
+
+DROP TABLE IF EXISTS `keyboard_shortcuts`;
+CREATE TABLE keyboard_shortcuts (
+shortcut_name varchar(80) NOT NULL,
+shortcut_keys varchar(80) NOT NULL,
+shortcut_desc varchar(200) NOT NULL,
+PRIMARY KEY (shortcut_name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
 /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
 /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
index 7fe7062..af20307 100644 (file)
@@ -135,36 +135,36 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
             return [];
         }
     }
+    var _editorKeys = {};
 
-    var _editorKeys = {
-        'Alt-C': function( cm ) {
+    _editorKeys[insert_copyright] =  function( cm ) {
             cm.replaceRange( '©', cm.getCursor() );
-        },
+        }
 
-        'Alt-P': function( cm ) {
+    _editorKeys[insert_copyright_sound] = function( cm ) {
             cm.replaceRange( '℗', cm.getCursor() );
-        },
+        }
 
-        Enter: function( cm ) {
+    _editorKeys[new_line] = function( cm ) {
             var cursor = cm.getCursor();
             cm.replaceRange( '\n', { line: cursor.line }, null, 'marcAware' );
             cm.setCursor( { line: cursor.line + 1, ch: 0 } );
-        },
+        }
 
-        'Shift-Enter': function( cm ) {
+    _editorKeys[line_break] =  function( cm ) {
             var cur = cm.getCursor();
 
             cm.replaceRange( "\n", cur, null );
-        },
+        }
 
-        'Ctrl-X': function( cm ) {
+    _editorKeys[delete_field] =  function( cm ) {
             // Delete line (or cut)
             if ( cm.somethingSelected() ) return true;
 
             cm.execCommand('deleteLine');
-        },
+        }
 
-        'Shift-Ctrl-L': function( cm ) {
+    _editorKeys[link_authorities] =  function( cm ) {
             // Launch the auth search popup
             var field = cm.marceditor.getCurrentField();
 
@@ -186,18 +186,18 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
             }
             newin=window.open("../authorities/auth_finder.pl?source=biblio&authtypecode="+authtype+"&index="+index+"&value_mainstr="+encodeURI(mainmainstring)+"&value_main="+encodeURI(mainstring), "_blank",'width=700,height=550,toolbar=false,scrollbars=yes');
 
-        },
+        }
 
-        'Shift-Ctrl-X': function( cm ) {
+    _editorKeys[delete_subfield] = function( cm ) {
             // Delete subfield
             var field = cm.marceditor.getCurrentField();
             if ( !field ) return;
 
             var subfield = field.getSubfieldAt( cm.getCursor().ch );
             if ( subfield ) subfield.delete();
-        },
+        }
 
-        Tab: function( cm ) {
+     _editorKeys[next_position] =  function( cm ) {
             // Move through parts of tag/fixed fields
             var positions = getTabPositions( cm.marceditor );
             var cur = cm.getCursor();
@@ -210,9 +210,9 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
             }
 
             cm.setCursor( { line: cur.line + 1, ch: 0 } );
-        },
+        }
 
-        'Shift-Tab': function( cm ) {
+    _editorKeys[prev_position] = function( cm ) {
             // Move backwards through parts of tag/fixed fields
             var positions = getTabPositions( cm.marceditor );
             var cur = cm.getCursor();
@@ -233,15 +233,13 @@ define( [ 'marc-record', 'koha-backend', 'preferences', 'text-marc', 'widget' ],
             } else {
                 cm.setCursor( { line: cur.line - 1, ch: 0 } );
             }
-        },
+        }
 
-        'Ctrl-D': function( cm ) {
-            // Insert subfield delimiter
-            var cur = cm.getCursor();
+    _editorKeys[insert_delimiter] = function(cm){
+        var cur = cm.getCursor();
 
-            cm.replaceRange( "‡", cur, null );
-        },
-    };
+        cm.replaceRange( "‡", cur, null );
+    }
 
     // The objects below are part of a field/subfield manipulation API, accessed through the base
     // editor object.
index db6dc66..a5734ff 100644 (file)
@@ -5,6 +5,9 @@
 [% Asset.js("lib/koha/cateditor/marc-mode.js") | $raw %]
 [% Asset.js("lib/require.js") | $raw %]
 <script>
+[% FOREACH shortcut IN shortcuts -%]
+    var [% shortcut.shortcut_name | html %] = "[% shortcut.shortcut_keys | html %]";
+[% END %]
     var authInfo = {
         [%- FOREACH authtag = authtags -%]
             [% authtag.tagfield | html %]: {
index 260eff9..9c49cc8 100644 (file)
 </div>
 
 <div id="shortcuts-contents" style="display: none">
+<a id="redefine_shortcuts" href="/cgi-bin/koha/tools/adveditorshortcuts.pl">Redefine shortcuts</a>
 <table class="table table-condensed">
     <thead>
         <tr>
         </tr>
     </thead>
     <tbody>
-        <tr>
-            <td>Alt-C</td>
-            <td>Insert copyright symbol (©)</td>
-        </tr>
-        <tr>
-            <td>Alt-P</td>
-            <td>Insert copyright symbol (℗) (sound recordings)</td>
-        </tr>
-        <tr>
-            <td>Ctrl-D</td>
-            <td>Insert delimiter (‡)</td>
-        </tr>
-        <tr>
-            <td>Ctrl-H</td>
-            <td>Get help on current subfield</td>
-        </tr>
-        <tr>
-            <td>Ctrl-Shift-L</td>
-            <td>Link field to authorities</td>
-        </tr>
+        [% FOREACH shortcut IN shortcuts %]
+            <tr>
+                <td>[% shortcut.shortcut_keys %]</td>
+                <td>[% shortcut.shortcut_desc %]</td>
+            </tr>
+        [% END %]
         <tr>
             <td>Ctrl-S</td>
-            <td>Save record</td>
-        </tr>
-        <tr>
-            <td>Ctrl-X</td>
-            <td>Delete current field</td>
-        </tr>
-        <tr>
-            <td>Ctrl-Shift-X</td>
-            <td>Delete current subfield</td>
-        </tr>
-        <tr>
-            <td>Enter</td>
-            <td>New field on next line</td>
-        </tr>
-        <tr>
-            <td>Shift-Enter</td>
-            <td>Insert line break</td>
-        </tr>
-        <tr>
-            <td>Tab</td>
-            <td>Move to next position</td>
-        </tr>
-        <tr>
-            <td>Shift-Tab</td>
-            <td>Move to previous position</td>
+            <td>Save record (cannot be remapped)</td>
         </tr>
     </tbody>
 </table>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/adveditorshortcuts.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/adveditorshortcuts.tt
new file mode 100644 (file)
index 0000000..c1b98dc
--- /dev/null
@@ -0,0 +1,68 @@
+[% USE raw %]
+[% USE Asset %]
+[% SET footerjs = 1 %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Tools &rsaquo; Advanced editor shortcuts</title>
+[% INCLUDE 'doc-head-close.inc' %]
+</head>
+<body id="adveditor_shortcuts" class="tools">
+    [% INCLUDE 'header.inc' %]
+    [% INCLUDE 'cat-search.inc' %]
+
+<div id="breadcrumbs">
+    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
+    <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo;
+    <a href="/cgi-bin/koha/tools/adveditorshortcuts.pl">Advanced editor shortcuts</a>
+</div>
+
+<div class="main container-fluid">
+    <div class="row">
+        <div class="col-sm-10 col-sm-push-2">
+            <main>
+                <h1>Advanced editor keyboard shortcuts</h1>
+                <ul>
+                    <li>Separate keys using a hyphen "-"</li>
+                    <li>Control key is "Ctrl"</li>
+                    <li>Alt key is "Alt"</li>
+                    <li>Shift is "Shift"</li>
+                    <li>If combing multiple keys they must be in specified order: Shift-Cmd-Ctrl-Alt</li>
+                    <li><a href="https://codemirror.net/doc/manual.html#keymaps">More documentation on defining key maps</a></li>
+                </ul>
+
+                <form id="adveditor_shortcuts" method="post" action="/cgi-bin/koha/tools/adveditorshortcuts.pl">
+                    <input type=hidden name="op" value="save" />
+
+                    <table id="adv_editor_keyboard_shortcuts">
+                        <thead>
+                            <th>Description</th>
+                            <th>Shortcut keys</th>
+                        </thead>
+                        <tbody>
+                           [% FOREACH shortcut IN shortcuts %]
+                        <tr>
+                            <td><label for="shortcut_keys">[% shortcut.shortcut_desc %]</label></td>
+                            <td>
+                                <input type="hidden" name="shortcut_name" value="[% shortcut.shortcut_name %]">
+                                <input type="text" name="shortcut_keys" value="[% shortcut.shortcut_keys %]">
+                           </td>
+                        </tr>
+                   [% END %]
+                   </table>
+                   <input type="submit" value="Save shortcuts">
+               </form>
+
+           </main>
+        </div> <!-- /.col-sm-10.col-sm-push-2 -->
+
+        <div class="col-sm-2 col-sm-pull-10">
+            <aside>
+                [% INCLUDE 'tools-menu.inc' %]
+            </aside>
+        </div> <!-- /.col-sm-2.col-sm-pull-10 -->
+    </div> <!-- /.row -->
+
+[% MACRO jsinclude BLOCK %]
+    [% Asset.js("js/tools-menu.js") | $raw %]
+[% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/tools/adveditorshortcuts.pl b/tools/adveditorshortcuts.pl
new file mode 100755 (executable)
index 0000000..a5162b1
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+
+# Copyright 2018 Koha Development Team
+#
+# 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>.
+
+=head1 NAME
+
+adveditorshortcuts.pl : Define keyboard shortcuts for the advanced cataloging editor (rancor)
+
+=head1 SYNOPSIS
+
+=cut
+
+=head1 DESCRIPTION
+
+This script allows the user to redefine the keyboard shortcuts for the advacned cataloging editor
+
+=head1 FUNCTIONS
+
+=cut
+
+use Modern::Perl;
+use Encode;
+
+use C4::Auth;
+use C4::Context;
+use C4::Output;
+use CGI qw ( -utf8 );
+use C4::Koha;
+use Koha::KeyboardShortcuts;
+
+my $input            = new CGI;
+my $op               = $input->param('op') || 'list';
+my @messages;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {   template_name   => "tools/adveditorshortcuts.tt",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 1,
+        flagsrequired   => {},
+        debug           => 1,
+    }
+);
+
+my $shortcuts = Koha::KeyboardShortcuts->search();
+
+if ( $op eq 'save' ) {
+    my @shortcut_names = $input->multi_param('shortcut_name');
+    my @shortcut_keys  = $input->multi_param('shortcut_keys');
+    my %updated_shortcuts;
+    @updated_shortcuts{@shortcut_names} = @shortcut_keys;
+
+    while ( my $shortcut = $shortcuts->next() ){
+        $shortcut->shortcut_keys( $updated_shortcuts{$shortcut->shortcut_name} );
+        $shortcut->store();
+    }
+}
+
+
+$template->param(
+    shortcuts  => $shortcuts,
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;