Koha/koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc-overlay-rules.tt
Marius Mandrescu 351e380a14
Bug 33335: Fix JavaScript error on the MARC overlay rules page
The "MARC overlay rules" page doesn't display or work correctly
if a patron category code contains a "-".

This happens because of the JavaScript function in
"marc-overlay-rules.tt" line 469. This causes an error
"Uncaught SyntaxError: missing : after property id".

Test plan:
1. Go to Administration > Patron categories.
2. Make sure you don't have a patron category code that contains
   a "-".
3. Go to Administration > Record overlay rules.
4. The table should display correctly, and you can add, edit
   and delete rules.
5. Return to Patron categories.
6. Add a new patron category with a "-" in the patron category code.
7. Return to Record overlay rules page:
   => The page doesn't display and load correctly (see the attached
      image) - the normal DataTables header and footer aren't
      displayed, and you can't add, edit or delete overlay rules.
  => If you turn on web developer tools, an error is displayed in the
     console: "Uncaught SyntaxError: missing : after property id".
8. Apply the patch.
9. Repeat the step 7, the Record overlay rules page should now
   display correctly and you should be able to add, edit and
   delete rules.
10. Sign off.

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2023-05-12 17:50:07 -03:00

529 lines
26 KiB
Text

[% USE raw %]
[% USE Asset %]
[% SET footerjs = 1 %]
[% USE Koha %]
[% USE KohaSpan %]
[% PROCESS 'i18n.inc' %]
[% INCLUDE 'doc-head-open.inc' %]
<title>MARC overlay rules &rsaquo; Koha &rsaquo; Administration</title>
[% INCLUDE 'doc-head-close.inc' %]
<style>
.required {
background-color: #C00;
}
</style>
</head>
<body id="admin_marc-overlay-rules" class="admin">
[% WRAPPER 'header.inc' %]
[% INCLUDE 'cat-search.inc' %]
[% END %]
[% WRAPPER 'sub-header.inc' %]
[% WRAPPER breadcrumbs %]
[% WRAPPER breadcrumb_item %]
<a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
[% END %]
[% WRAPPER breadcrumb_item %]
<a href="#" aria-current="page">
<span>MARC overlay rules</span>
</a>
[% END %]
[% END #/ WRAPPER breadcrumbs %]
[% END #/ WRAPPER sub-header.inc %]
<div class="main container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-push-2">
<h1>Manage MARC overlay rules</h1>
[% FOR m IN messages %]
<div class="dialog [% m.type | html %]">
[% SWITCH m.code %]
[% CASE 'invalid_tag_regexp' %]
<span>Invalid regular expression "[% m.tag | html %]".</span>
[% CASE 'invalid_control_field_actions' %]
<span>Invalid combination of actions for tag [% m.tag | html %]. Control field rules do not allow "Appended: Append" and "Removed: Skip".</span>
[% CASE %]
<span>[% m.code | html %]</span>
[% END %]
</div>
[% END %]
[% UNLESS Koha.Preference( 'MARCOverlayRules' ) %]
[% SET pref_MARCOverlayRules_link = '<a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=MARCOverlayRules">MARCOverlayRules</a>' %]
<div class="dialog message">
The [% pref_MARCOverlayRules_link | $raw | $KohaSpan %] preference is not set, don't forget to enable it for rules to take effect.
</div>
[% END %]
[% IF removeConfirm %]
<div class="dialog alert">
<h3>Remove rule?</h3>
<p>Are you sure you want to remove the selected rule(s)?</p>
<form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="GET">
<button type="submit" class="deny"><i class="fa fa-fw fa-remove"></i> No, do not remove</button>
</form>
<button type="button" class="approve" id="doremove"><i class="fa fa-fw fa-check"></i> Yes, remove</button>
</div>
[% END %]
<div class="page-section">
<form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="POST" id="marc-overlay-rules-form">
<table id="marc-overlay-rules">
<thead><tr>
<th>Rule</th>
<th>Module</th>
<th>[% tp('noun', 'Filter') | html %]</th>
<th>Tag</th>
<th>Preset</th>
<th>Added <i id="info_added" data-toggle="tooltip" title="If a field matching the rule tag only exists in the incoming record" data-placement="right" class="fa fa-info-circle"></i></th>
<th>Appended <i id="info_appended" data-toggle="tooltip" title="If the original record has one or more fields matching with the rule tag, but one or more fields matching the rule tag differ in the incoming record" data-placement="right" class="fa fa-info-circle"></i></th>
<th>Removed <i id="info_removed" data-toggle="tooltip" title="If the original record has a field matching the rule tag, but the matching field is not in the incoming record" data-placement="right" class="fa fa-info-circle"></i></th>
<th>Deleted <i id="info_deleted" data-toggle="tooltip" title="If the original record has fields matching the rule tag, but no fields with this are found in the incoming record" data-placement="right" class="fa fa-info-circle"></i></th>
<th>Actions</th>
<th>&nbsp;</th>
</tr></thead>
[% UNLESS edit %]
<tfoot>
<tr class="rule-new">
<th>&nbsp;</th>
<th>
<select name="module">
<option value="source">Source</option>
<option value="categorycode">User category</option>
<option value="userid">Username</option>
</select>
</th>
<th id="filter-container"></th>
<th><input type="text" size="5" name="tag"/></th>
<th>
<select name="preset">
<option value="" selected>Custom</option>
<option value="Protect">Protect</option>
<option value="Overwrite">Overwrite</option>
<option value="Add new">Add new</option>
<option value="Add and append">Add and append</option>
<option value="Protect from deletion">Protect from deletion</option>
</select>
</th>
<th class="rule-operation-action-edit">
<select name="add">
<option value="0">Skip</option>
<option value="1">Add</option>
</select>
</th>
<th class="rule-operation-action-edit">
<select name="append">
<option value="0">Skip</option>
<option value="1">Append</option>
</select>
</th>
<th class="rule-operation-action-edit">
<select name="remove">
<option value="0">Skip</option>
<option value="1">Remove</option>
</select>
</th>
<th class="rule-operation-action-edit">
<select name="delete">
<option value="0">Skip</option>
<option value="1">Delete</option>
</select>
</th>
<th><button class="btn btn-default btn-xs" title="Add" id="add"><i class="fa fa-plus"></i> Add rule</button></th>
<th><button id="btn_batchremove" disabled="disabled" class="btn btn-default btn-xs" title="Batch remove"><i class="fa fa-trash"></i> Delete selected</button></th>
</tr>
</tfoot>
[% END %]
<tbody>
[% FOREACH rule IN rules %]
<tr id="[% rule.id | html %]" class="rule[% IF rule.edit %]-edit[% END %]">
[% IF rule.edit %]
<td>[% rule.id | html %]</td>
<td>
<select name="module">
[% IF rule.module == "source" %]
<option value="source" selected="selected">Source</option>
[% ELSE %]
<option value="source">Source</option>
[% END %]
[% IF rule.module == "categorycode" %]
<option value="categorycode" selected="selected">User category</option>
[% ELSE %]
<option value="categorycode">User category</option>
[% END %]
[% IF rule.module == "userid" %]
<option value="userid" selected="selected">Username</option>
[% ELSE %]
<option value="userid">Username</option>
[% END %]
</select>
</td>
<td id="filter-container" data-filter="[% rule.filter | html %]"></td>
<td><input type="text" size="3" name="tag" value="[% rule.tag | html %]"/></td>
<th>
<select name="preset">
<option value="" selected>Custom</option>
<option value="Protect">Protect</option>
<option value="Overwrite">Overwrite</option>
<option value="Add new">Add new</option>
<option value="Add and append">Add and append</option>
<option value="Protect from deletion">Protect from deletion</option>
</select>
</th>
<td class="rule-operation-action-edit">
<select name="add">
[% IF rule.add %]
<option value="0">Skip</option>
<option value="1" selected="selected">Add</option>
[% ELSE %]
<option value="0" selected="selected">Skip</option>
<option value="1">Add</option>
[% END %]
</select>
</td>
<td class="rule-operation-action-edit">
<select name="append">
[% IF rule.append %]
<option value="0">Skip</option>
<option value="1" selected="selected">Append</option>
[% ELSE %]
<option value="0" selected="selected">Skip</option>
<option value="1">Append</option>
[% END %]
</select>
</td>
<td class="rule-operation-action-edit">
<select name="remove">
[% IF rule.remove %]
<option value="0">Skip</option>
<option value="1" selected="selected">Remove</option>
[% ELSE %]
<option value="0" selected="selected">Skip</option>
<option value="1">Remove</option>
[% END %]
</select>
</td>
<td class="rule-operation-action-edit">
<select name="delete">
[% IF rule.delete %]
<option value="0">Skip</option>
<option value="1" selected="selected">Delete</option>
[% ELSE %]
<option value="0" selected="selected">Skip</option>
<option value="1">Delete</option>
[% END %]
</select>
</td>
<td class="actions">
<button class="btn btn-default btn-xs" title="Save" id="doedit" value="[% rule.id | html %]"><i class="fa fa-check"></i> Save</button>
<button type="submit" class="btn btn-default btn-xs" title="Cancel" ><i class="fa fa-times"></i> Cancel</button>
</td>
<td></td>
[% ELSE %]
<td>[% rule.id | html %]</td>
<td class="rule-module">[% rule.module | html %]</td>
<td class="rule-filter">[% rule.filter | html %]</td>
<td>[% rule.tag | html %]</td>
<td class="rule-preset"></td>
<td class="rule-operation-action" data-operation="add">[% IF rule.add %]Add[% ELSE %]Skip[% END %]</td>
<td class="rule-operation-action" data-operation="append">[% IF rule.append %]Append[% ELSE %]Skip[% END %]</td>
<td class="rule-operation-action" data-operation="remove">[% IF rule.remove %]Remove[% ELSE %]Skip[% END %]</td>
<td class="rule-operation-action" data-operation="delete">[% IF rule.delete %]Delete[% ELSE %]Skip[% END %]</td>
<td class="actions">
<a href="?op=remove&id=[% rule.id | uri %]" title="Delete" class="btn btn-default btn-xs"><i class="fa fa-trash"></i> Delete</a>
<a href="?op=edit&id=[% rule.id | uri %]" title="Edit" class="btn btn-default btn-xs"><i class="fa fa-pencil"></i> Edit</a>
</td>
<td>
[% IF rule.removemarked %]
<input type="checkbox" name="batchremove" value="[% rule.id | html %]" checked="checked"/>
[% ELSE %]
<input type="checkbox" name="batchremove" value="[% rule.id | html %]"/>
[% END %]
</td>
[% END %]
</tr>
[% END %]
</tbody>
</table>
</form>
</div> <!-- /.page-section -->
<form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="post">
<input type="hidden" name="op" value="redo-matching" />
</form>
</div><!-- /.col-sm-10.col-sm-push-2 -->
<div class="col-sm-2 col-sm-pull-10">
<aside>
[% INCLUDE 'admin-menu.inc' %]
</aside>
</div>
</div><!-- /.row -->
</div><!-- /main container-fluid -->
[% MACRO jsinclude BLOCK %]
[% Asset.js("js/admin-menu.js") | $raw %]
[% INCLUDE 'datatables.inc' %]
<script>
$(document).ready(function(){
function doSubmit(op, id) {
$('<input type="hidden"/>')
.attr('name', 'op')
.attr('value', op)
.appendTo('#marc-overlay-rules-form');
if(id) {
$('<input type="hidden"/>')
.attr('name', 'id')
.attr('value', id)
.appendTo('#marc-overlay-rules-form');
}
var valid = true;
if (op == 'add' || op == 'edit') {
var validate = [
$('#marc-overlay-rules-form input[name="filter"]'),
$('#marc-overlay-rules-form input[name="tag"]')
];
for(var i = 0; i < validate.length; i++) {
if (validate[i].length) {
if(validate[i].val().length == 0) {
validate[i].addClass('required');
valid = false;
} else {
validate[i].removeClass('required');
}
}
}
}
if (valid) {
$('#marc-overlay-rules-form').submit();
}
return valid;
}
$('#doremove').on('click', function(){
doSubmit('doremove');
});
$('#doedit').on('click', function(){
doSubmit('doedit', $("#doedit").attr('value'));
});
$('#add').on('click', function(){
doSubmit('add');
return false;
});
$('#btn_batchremove').on('click', function(){
doSubmit('remove');
});
/* Disable batch remove unless one or more checkboxes are checked */
$('input[name="batchremove"]').change(function() {
if($('input[name="batchremove"]:checked').length > 0) {
$('#btn_batchremove').removeAttr('disabled');
} else {
$('#btn_batchremove').attr('disabled', 'disabled');
}
});
$.fn.dataTable.ext.order['dom-input'] = function (settings, col) {
return this.api().column(col, { order: 'index' }).nodes()
.map(function (td, i) {
if($('input', td).val() != undefined) {
return $('input', td).val();
} else if($('select', td).val() != undefined) {
return $('option[selected="selected"]', td).val();
} else {
return $(td).html();
}
});
}
$('#marc-overlay-rules').dataTable($.extend(true, {}, dataTablesDefaults, {
"aoColumns": [
{"bSearchable": false, "bSortable": false},
{"sSortDataType": "dom-input"},
{"sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "sSortDataType": "dom-input"},
{"bSearchable": false, "bSortable": false},
{"bSearchable": false, "bSortable": false}
],
"pagingType": "simple"
}));
var overlay_rules_presets = {};
overlay_rules_presets[_("Protect")] = {
'add': 0,
'append': 0,
'remove': 0,
'delete': 0
};
overlay_rules_presets[_("Overwrite")] = {
'add': 1,
'append': 1,
'remove': 1,
'delete': 1
};
overlay_rules_presets[_("Add new")] = {
'add': 1,
'append': 0,
'remove': 0,
'delete': 0
};
overlay_rules_presets[_("Add and append")] = {
'add': 1,
'append': 1,
'remove': 0,
'delete': 0
};
overlay_rules_presets[_("Protect from deletion")] = {
'add': 1,
'append': 1,
'remove': 1,
'delete': 0
};
var overlay_rules_label_to_value = {};
overlay_rules_label_to_value[_("Add")] = 1;
overlay_rules_label_to_value[_("Append")] = 1;
overlay_rules_label_to_value[_("Remove")] = 1;
overlay_rules_label_to_value[_("Delete")] = 1;
overlay_rules_label_to_value[_("Skip")] = 0;
function hash_config(config) {
return JSON.stringify(config, Object.keys(config).sort());
}
var overlay_rules_preset_map = {};
$.each(overlay_rules_presets, function(preset, config) {
overlay_rules_preset_map[hash_config(config)] = preset;
});
function operations_config_overlay_rule_preset(config) {
return overlay_rules_preset_map[hash_config(config)] || '';
}
/* Set preset values according to operation config */
$('.rule').each(function() {
var $this = $(this);
var operations_config = {};
$('.rule-operation-action', $this).each(function() {
var $operation = $(this);
operations_config[$operation.data('operation')] = overlay_rules_label_to_value[$operation.text()];
});
$('.rule-preset', $this).text(
operations_config_overlay_rule_preset(operations_config) || _("Custom")
);
});
/* Listen to operations config changes and set presets accordingly */
$('.rule-operation-action-edit select').change(function() {
var operations_config = {};
var $parent_row = $(this).closest('tr');
$('.rule-operation-action-edit select', $parent_row).each(function() {
var $this = $(this);
operations_config[$this.attr('name')] = parseInt($this.val());
});
$('select[name="preset"]', $parent_row).val(
operations_config_overlay_rule_preset(operations_config)
);
});
/* Listen to preset changes and set operations config accordingly */
$('select[name="preset"]').change(function() {
var $this = $(this);
var $parent_row = $this.closest('tr');
var preset = $this.val();
if (preset) {
$.each(overlay_rules_presets[preset], function(operation, action) {
$('select[name="' + operation + '"]', $parent_row).val(action);
});
}
});
var $category = {};
[% FOR categorycode IN categorycodes %]
$category["[% categorycode.categorycode %]"] = "[% categorycode.description | html %]";
[% END %]
var module_filter_options = {
source: {
'*': '*',
batchmod: _("Batch record modification"),
intranet: _("Staff interface MARC editor"),
batchimport: _("Staged MARC import"),
z3950: _("Z39.50"),
/* bulkmarcimport: _("bulkmarcimport.pl"), */
import_lexile: _("import_lexile.pl")
},
categorycode: $category
};
//Kind of hack: Replace filter value with label when one exist
$('.rule-module').each(function() {
var $this = $(this);
var module = $this.text();
if (module in module_filter_options) {
let $filter = $this.siblings('.rule-filter');
if ($filter.text() in module_filter_options[module]) {
$filter.text(module_filter_options[module][$filter.text()]);
}
}
});
var $filter_container = $('#filter-container');
/* Listen to module changes and set filter input accordingly */
$('select[name="module"]').change(function() {
var $this = $(this);
var module_name = $this.val();
/* Remove current element if any */
$filter_container.empty();
var filter_elem = null;
if (module_name in module_filter_options) {
// Create select element
filter_elem = document.createElement('select');
for (var filter_value in module_filter_options[module_name]) {
var option = document.createElement('option');
option.value = filter_value;
option.text = module_filter_options[module_name][filter_value];
filter_elem.appendChild(option);
}
}
else {
// Create text input element
filter_elem = document.createElement('input');
filter_elem.type = 'text';
filter_elem.setAttribute('size', 5);
}
filter_elem.name = 'filter';
filter_elem.id = 'filter';
$filter_container.append(filter_elem);
}).change(); // Trigger change
// Hack: set value if editing rule
if ($filter_container.data('filter')) {
$('#filter').val($filter_container.data('filter'));
}
});
</script>
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]