Bug 7882: Add ability to move and reorder fields in MARC editor

Use jQueryUI sortable to make fields and subfields moveable in the
MARC editor for both records and authorities.

This patch convert items from div's to ul's and consequently alter css
and js to match. It also replace the up arrow with a more adapted icon
(from font awesome).

Primary authorship by Elliott Davis.

To Test:

1. Add or edit a record in cataloguing module.
2. You should be able to move the fields and subfields around.
    - You can click on any part of the element to drag it, the move
    icon to the left of the item is a good place to do it.
    => You can only change the order of fields of the same tag.
3. Make sure all of the javascript driven fonctionnality still work :
    - Duplicate fields/subfields
    - Remove fields/subfields
    - Using the tag editor for control fields or to link authorities
    - etc
4. Reorder some fields/subfields and save the record.
5. Edit the record again.
6. The order in the editor should match the changes which were saved.
    - Empty subfields should appear after the ones with content.
7. Repeat steps 1-6 with the authority editor.

Signed-off-by: Séverine QUEUNE <severine.queune@bulac.fr>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Signed-off-by: Joy Nelson <joy@bywatersolutions.com>
(cherry picked from commit 23724c9c74)

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Maryse Simard 2019-10-23 15:34:39 -04:00 committed by Lucas Gass
parent 5d49c80644
commit 3dd55ca547
7 changed files with 108 additions and 59 deletions

View File

@ -511,7 +511,7 @@ sub build_tabs {
for ( my $tabloop = 0 ; $tabloop <= $max_num_tab ; $tabloop++ ) {
my @loop_data = (); #innerloop in the template.
my $i = 0;
foreach my $tag (@tab_data) {
foreach my $tag (sort @tab_data) {
$i++;
next if ! $tag;
my ($indicator1, $indicator2);

View File

@ -15,6 +15,49 @@ div#toolbar {
margin: .3em 0;
}
ul.sortable_field, ul.sortable_subfield {
margin-bottom: 0;
padding-left: 0;
}
ul li.tag, ul li.tag li.subfield_line {
list-style-type: none;
position: relative;
padding-left: 30px;
}
ul li.tag:before, ul li.tag li.subfield_line:before {
position: absolute;
font-family: 'FontAwesome';
font-size: .8em;
top: 0;
left: 10px;
content: "\f047";
}
ul li.tag:before {
padding-top: 1.7em;
}
li.ui-sortable-helper {
background-color: #e0e0e0;
max-height: 150px;
padding: 2px;
border-radius: 4px;
}
li.ui-sortable-helper ul li {
display: none !important;
}
.sortable_subfield .ui-sortable-helper input.flat {
background-color: transparent;
}
ul li.tag li.subfield_line.ui-sortable-helper::before {
top: 5px;
}
.buttonPlus {
font-weight : bold;
text-decoration : none;
@ -44,14 +87,14 @@ a.expandfield {
padding: .7em 0;
}
div.subfield_line {
li.subfield_line {
padding-bottom: .3em;
float: left;
clear: left;
width: 100%;
}
div.subfield_line label {
li.subfield_line label {
font-size:89%;
float: left;
padding-right : .4em;
@ -266,7 +309,7 @@ tbody tr.active td {
}
@media (min-width: 768px) {
div.subfield_line label {
li.subfield_line label {
width: 20em;
}
@ -280,7 +323,7 @@ tbody tr.active td {
}
@media (min-width: 1200px) {
div.subfield_line label {
li.subfield_line label {
width: 25em;
}

View File

@ -12,7 +12,9 @@
});
var Sticky;
$(document).ready(function() {
$('#authoritytabs').tabs();
var tabs = $('#authoritytabs').tabs();
$( "ul.sortable_field", tabs ).sortable();
$( "ul.sortable_subfield", tabs ).sortable();
Sticky = $("#toolbar");
Sticky.hcSticky({
stickTo: ".main",
@ -235,9 +237,17 @@ function confirmnotdup(redirect){
[% FOREACH BIG_LOO IN BIG_LOOP %]
<div id="tab[% BIG_LOO.number | html %]XX">
[% previous = "" %]
[% FOREACH innerloo IN BIG_LOO.innerloop %]
[% IF ( innerloo.tag ) %]
<div class="tag clearfix" id="tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
[% IF innerloo.tag != previous %]
[% IF previous != "" %]
</ul>
[% END %]
[% previous = innerloo.tag %]
<ul class="sortable_field">
[% END %]
<li class="tag clearfix" id="tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
<div class="tag_title" id="div_indicator_tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
[% UNLESS hide_marc %]
[% IF advancedMARCEditor %]
@ -316,9 +326,10 @@ function confirmnotdup(redirect){
</div>
<ul class="sortable_subfield">
[% FOREACH subfield_loo IN innerloo.subfield_loop %]
<!-- One line on the marc editor -->
<div class="subfield_line" style="[% subfield_loo.visibility | html %]" id="subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]">
<li class="subfield_line" style="[% subfield_loo.visibility | html %]" id="subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]">
[% UNLESS advancedMARCEditor %]
[% IF ( subfield_loo.fixedfield ) %]<label for="tag_[% subfield_loo.tag | html %]_subfield_[% subfield_loo.subfield | html %]_[% subfield_loo.index | html %]_[% subfield_loo.index_subfield | html %]" style="display:none;" class="labelsubfield">
@ -328,11 +339,6 @@ function confirmnotdup(redirect){
[% UNLESS hide_marc %]
<span class="subfieldcode">
[% IF ( subfield_loo.fixedfield ) %]
<img class="buttonUp" style="display:none;" src="[% interface | html %]/[% theme | html %]/img/up.png" onclick="upSubfield('subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]')" alt="Move Up" title="Move Up" />
[% ELSE %]
<img class="buttonUp" src="[% interface | html %]/[% theme | html %]/img/up.png" onclick="upSubfield('subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]')" alt="Move Up" title="Move Up" />
[% END %]
<input type="text"
title="[% subfield_loo.marc_lib | $raw %]"
style=" [% IF ( subfield_loo.fixedfield ) %]display:none; [% END %]border:0;"
@ -400,12 +406,14 @@ function confirmnotdup(redirect){
[% END %]
</span>
</div>
</li>
<!-- End of the line -->
[% END %]
</div>
</ul> <!-- /.sortable_subfield -->
</li>
[% END %]<!-- if innerloo.tag -->
</ul> <!-- /.sortable_field -->
[% END %]<!-- BIG_LOO.innerloop -->
</div>
[% END %]<!-- BIG_LOOP -->

View File

@ -46,7 +46,7 @@
} catch(e) {
return;
}
var field_start = whichfield.parentNode.parentNode;
var field_start = whichfield.parentNode.parentNode.parentNode;
// Sets the good number of form fields for the specified subfield
// Returns false if the cloning failed
@ -58,7 +58,7 @@
// Find the subfield we want to clone
var re = new RegExp('^subfield' + subfield_name,'g');
var subfields = $(field_start).children('div').filter( function() {
var subfields = $(field_start).children('ul').children('li').filter( function() {
return this.id.match(re);
});
@ -89,7 +89,7 @@
// Find the subfields where we will add the new values
var re = new RegExp('^subfield' + subfield_name,'g');
var subfields = $(field_start).children('div').filter( function() {
var subfields = $(field_start).children('ul').children('li').filter( function() {
return this.id.match(re);
});

View File

@ -23,9 +23,11 @@
$("#toolbar").hide();
[% END %]
$('#addbibliotabs').tabs().bind('show.ui-tabs', function(e, ui) {
var $tabs = $('#addbibliotabs').tabs().bind('show.ui-tabs', function(e, ui) {
$("#"+ui.panel.id+" input:eq(0)").focus();
});
$( "ul.sortable_field", $tabs ).sortable();
$( "ul.sortable_subfield", $tabs ).sortable();
[% IF tab %]
link = $("a[href='#[% tab | html %]']");
@ -664,9 +666,17 @@
[% IF ( BIG_LOOP.size > 1 ) %]
<h3>Section [% BIG_LOO.number | html %]</h3>
[% END %]
[% previous = "" %]
[% FOREACH innerloo IN BIG_LOO.innerloop %]
[% IF ( innerloo.tag ) %]
<div class="tag clearfix" id="tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
[% IF innerloo.tag != previous %]
[% IF previous != "" %]
</ul>
[% END %]
[% previous = innerloo.tag %]
<ul class="sortable_field">
[% END %]
<li class="tag clearfix" id="tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
<div class="tag_title" id="div_indicator_tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]">
[% IF advancedMARCEditor %]
<a href="#" tabindex="1" class="tagnum" title="[% innerloo.tag_lib | html %] - Click to Expand this Tag" onclick="ExpandField('tag_[% innerloo.tag | html %]_[% innerloo.index | html %][% innerloo.random | html %]'); return false;">[% innerloo.tag | html %]</a>
@ -725,9 +735,10 @@
</span> <!-- /.field_controls -->
</div> <!-- /div.tag_title -->
<ul class="sortable_subfield">
[% FOREACH subfield_loo IN innerloo.subfield_loop %]
<!-- One line on the marc editor -->
<div class="subfield_line" style="[% subfield_loo.visibility | html %]" id="subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]">
<li class="subfield_line" style="[% subfield_loo.visibility | html %]" id="subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]">
[% UNLESS advancedMARCEditor %]
[% IF ( subfield_loo.fixedfield ) %]
<label for="tag_[% subfield_loo.tag | html %]_subfield_[% subfield_loo.subfield | html %]_[% subfield_loo.index | html %]_[% subfield_loo.index_subfield | html %]" style="display:none;" class="labelsubfield">
@ -737,11 +748,6 @@
[% END %]
<span class="subfieldcode">
[% IF ( subfield_loo.fixedfield ) %]
<img class="buttonUp" style="display:none;" src="[% interface | html %]/[% theme | html %]/img/up.png" onclick="upSubfield('subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]')" alt="Move Up" title="Move Up" />
[% ELSE %]
<img class="buttonUp" src="[% interface | html %]/[% theme | html %]/img/up.png" onclick="upSubfield('subfield[% subfield_loo.tag | html %][% subfield_loo.subfield | html %][% subfield_loo.random | html %]')" alt="Move Up" title="Move Up" />
[% END %]
<input type="text"
title="[% subfield_loo.marc_lib | $raw %]"
style=" [% IF ( subfield_loo.fixedfield ) %]display:none; [% END %]border:0;"
@ -813,12 +819,14 @@
</a>
[% END %]
</span>
</div> <!-- /.subfield_line -->
</li> <!-- /.subfield_line -->
<!-- End of the line -->
[% END # /FOREACH subfield_loop %]
</div> <!-- /.tag.clearfix -->
</ul> <!-- /.sortable_subfield -->
</li> <!-- /.tag.clearfix -->
[% END %]<!-- if innerloo.tag -->
[% END # /FOREACH BIG_LOO.innerloop %]
</ul> <!-- /.sortable_field -->
</div> <!-- /#tabXXX -->
[% END # /FOREACH BIG_LOOP %]
</div><!-- /#addbibliotabs -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

View File

@ -56,20 +56,20 @@ function openAuth(tagsubfieldid,authtype,source) {
}
function ExpandField(index) {
var original = document.getElementById(index); //original <div>
var divs = original.getElementsByTagName('div');
for(var i=0,divslen = divs.length ; i<divslen ; i++){ // foreach div
if(divs[i].hasAttribute('id') == 0 ) {continue; } // div element is specific to Select2
if(divs[i].getAttribute('id').match(/^subfield/)){ // if it s a subfield
if (!divs[i].style.display) {
var original = document.getElementById(index); //original <li>
var lis = original.getElementsByTagName('li');
for(var i=0,lislen = lis.length ; i<lislen ; i++){ // foreach li
if(lis[i].hasAttribute('id') == 0 ) {continue; } // li element is specific to Select2
if(lis[i].getAttribute('id').match(/^subfield/)){ // if it s a subfield
if (!lis[i].style.display) {
// first time => show all subfields
divs[i].style.display = 'block';
} else if (divs[i].style.display == 'none') {
lis[i].style.display = 'block';
} else if (lis[i].style.display == 'none') {
// show
divs[i].style.display = 'block';
lis[i].style.display = 'block';
} else {
// hide
divs[i].style.display = 'none';
lis[i].style.display = 'none';
}
}
}
@ -101,16 +101,16 @@ var Select2Utils = {
* @param advancedMARCEditor '0' for false, '1' for true
*/
function CloneField(index, hideMarc, advancedMARCEditor) {
var original = document.getElementById(index); //original <div>
var original = document.getElementById(index); //original <li>
Select2Utils.removeSelect2(original);
var clone = original.cloneNode(true);
var new_key = CreateKey();
var new_id = original.getAttribute('id')+new_key;
clone.setAttribute('id',new_id); // setting a new id for the parent div
clone.setAttribute('id',new_id); // setting a new id for the parent li
var divs = clone.getElementsByTagName('div');
var divs = Array.from(clone.getElementsByTagName('li')).concat(Array.from(clone.getElementsByTagName('div')));
// if hide_marc, indicators are hidden fields
// setting a new name for the new indicator
@ -120,10 +120,10 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
}
// settings all subfields
for(var i=0,divslen = divs.length ; i<divslen ; i++){ // foreach div
for(var i=0,divslen = divs.length ; i<divslen ; i++){ // foreach div/li
if(divs[i].getAttribute("id").match(/^subfield/)){ // if it s a subfield
// set the attribute for the new 'div' subfields
// set the attribute for the new 'li' subfields
divs[i].setAttribute('id',divs[i].getAttribute('id')+new_key);
var inputs = divs[i].getElementsByTagName('input');
@ -162,7 +162,7 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
}
}
if( $(inputs[1]).hasClass('framework_plugin') ) {
var olddiv= original.getElementsByTagName('div')[i];
var olddiv= original.getElementsByTagName('li')[i];
var oldcontrol= olddiv.getElementsByTagName('input')[1];
AddEventHandlers( oldcontrol,inputs[1],id_input );
}
@ -173,12 +173,6 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
labels[0].setAttribute('for',id_input);
}
if(hideMarc == '0') {
// updating javascript parameters on button up
var imgs = divs[i].getElementsByTagName('img');
imgs[0].setAttribute('onclick',"upSubfield(\'"+divs[i].getAttribute('id')+"\');");
}
// setting its '+' and '-' buttons
try {
var anchors = divs[i].getElementsByTagName('a');
@ -209,7 +203,7 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
// 2 possibilities :
try{
if( $(buttonDot).hasClass('framework_plugin') ) {
var olddiv= original.getElementsByTagName('div')[i];
var olddiv= original.getElementsByTagName('li')[i];
var oldcontrol= olddiv.getElementsByTagName('a')[0];
AddEventHandlers(oldcontrol,buttonDot,id_input);
} else {
@ -236,10 +230,6 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
}
}
}
if(hideMarc == '0') {
var buttonUp = divs[i].getElementsByTagName('img')[0];
buttonUp.setAttribute('onclick',"upSubfield('" + divs[i].getAttribute('id') + "')");
}
} else { // it's a indicator div
if(divs[i].getAttribute('id').match(/^div_indicator/)){
@ -275,6 +265,8 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
// insert this line on the page
original.parentNode.insertBefore(clone,original.nextSibling);
$("ul.sortable_subfield", clone).sortable();
Select2Utils.initSelect2(original);
Select2Utils.initSelect2(clone);
}
@ -290,7 +282,7 @@ function CloneSubfield(index, advancedMARCEditor){
Select2Utils.removeSelect2(original);
var clone = original.cloneNode(true);
var new_key = CreateKey();
// set the attribute for the new 'div' subfields
// set the attribute for the new 'li' subfields
var inputs = clone.getElementsByTagName('input');
var selects = clone.getElementsByTagName('select');
var textareas = clone.getElementsByTagName('textarea');
@ -351,8 +343,6 @@ function CloneSubfield(index, advancedMARCEditor){
clone.setAttribute('id',new_id);
try {
var buttonUp = clone.getElementsByTagName('img')[0];
buttonUp.setAttribute('onclick',"upSubfield('" + new_id + "')");
var anchors = clone.getElementsByTagName('a');
if(anchors.length){
for(var i = 0 ,lenanchors = anchors.length ; i < lenanchors ; i++){
@ -500,7 +490,7 @@ function CloneItemSubfield(original){
var clone = original.cloneNode(true);
var new_key = CreateKey();
// set the attribute for the new 'div' subfields
// set the attribute for the new 'li' subfields
var inputs = clone.getElementsByTagName('input');
var selects = clone.getElementsByTagName('select');
var textareas = clone.getElementsByTagName('textarea');