7 [% PROCESS 'i18n.inc' %]
8 [% INCLUDE 'doc-head-open.inc' %]
9 <title>MARC overlay rules › Koha › Administration</title>
10 [% INCLUDE 'doc-head-close.inc' %]
14 background-color: #C00;
19 <body id="admin_marc-overlay-rules" class="admin">
20 [% WRAPPER 'header.inc' %]
21 [% INCLUDE 'cat-search.inc' %]
24 [% WRAPPER 'sub-header.inc' %]
25 [% WRAPPER breadcrumbs %]
26 [% WRAPPER breadcrumb_item %]
27 <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
29 [% WRAPPER breadcrumb_item %]
30 <a href="#" aria-current="page">
31 <span>MARC overlay rules</span>
34 [% END #/ WRAPPER breadcrumbs %]
35 [% END #/ WRAPPER sub-header.inc %]
37 <div class="main container-fluid">
39 <div class="col-sm-10 col-sm-push-2">
41 <h1>Manage MARC overlay rules</h1>
43 [% FOR m IN messages %]
44 <div class="dialog [% m.type | html %]">
46 [% CASE 'invalid_tag_regexp' %]
47 <span>Invalid regular expression "[% m.tag | html %]".</span>
48 [% CASE 'invalid_control_field_actions' %]
49 <span>Invalid combination of actions for tag [% m.tag | html %]. Control field rules do not allow "Appended: Append" and "Removed: Skip".</span>
51 <span>[% m.code | html %]</span>
56 [% UNLESS Koha.Preference( 'MARCOverlayRules' ) %]
57 [% SET pref_MARCOverlayRules_link = '<a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=MARCOverlayRules">MARCOverlayRules</a>' %]
58 <div class="dialog message">
59 The [% pref_MARCOverlayRules_link | $raw | $KohaSpan %] preference is not set, don't forget to enable it for rules to take effect.
62 [% IF removeConfirm %]
63 <div class="dialog alert">
65 <p>Are you sure you want to remove the selected rule(s)?</p>
67 <form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="GET">
68 <button type="submit" class="deny"><i class="fa fa-fw fa-times"></i> No, do not remove</button>
70 <button type="button" class="approve" id="doremove"><i class="fa fa-fw fa-check"></i> Yes, remove</button>
74 <div class="page-section">
75 <form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="POST" id="marc-overlay-rules-form">
76 <table id="marc-overlay-rules">
80 <th>[% tp('noun', 'Filter') | html %]</th>
83 <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>
84 <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>
85 <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>
86 <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>
95 <select name="module">
96 <option value="source">Source</option>
97 <option value="categorycode">User category</option>
98 <option value="userid">Username</option>
101 <th id="filter-container"></th>
102 <th><input type="text" size="5" name="tag"/></th>
104 <select name="preset">
105 <option value="" selected>Custom</option>
106 <option value="Protect">Protect</option>
107 <option value="Overwrite">Overwrite</option>
108 <option value="Add new">Add new</option>
109 <option value="Add and append">Add and append</option>
110 <option value="Protect from deletion">Protect from deletion</option>
113 <th class="rule-operation-action-edit">
115 <option value="0">Skip</option>
116 <option value="1">Add</option>
119 <th class="rule-operation-action-edit">
120 <select name="append">
121 <option value="0">Skip</option>
122 <option value="1">Append</option>
125 <th class="rule-operation-action-edit">
126 <select name="remove">
127 <option value="0">Skip</option>
128 <option value="1">Remove</option>
131 <th class="rule-operation-action-edit">
132 <select name="delete">
133 <option value="0">Skip</option>
134 <option value="1">Delete</option>
137 <th><button class="btn btn-default btn-xs" title="Add" id="add"><i class="fa fa-plus"></i> Add rule</button></th>
138 <th><button id="btn_batchremove" disabled="disabled" class="btn btn-default btn-xs" title="Batch remove"><i class="fa fa-trash-can"></i> Delete selected</button></th>
143 [% FOREACH rule IN rules %]
144 <tr id="[% rule.id | html %]" class="rule[% IF rule.edit %]-edit[% END %]">
146 <td>[% rule.id | html %]</td>
148 <select name="module">
149 [% IF rule.module == "source" %]
150 <option value="source" selected="selected">Source</option>
152 <option value="source">Source</option>
154 [% IF rule.module == "categorycode" %]
155 <option value="categorycode" selected="selected">User category</option>
157 <option value="categorycode">User category</option>
159 [% IF rule.module == "userid" %]
160 <option value="userid" selected="selected">Username</option>
162 <option value="userid">Username</option>
166 <td id="filter-container" data-filter="[% rule.filter | html %]"></td>
167 <td><input type="text" size="3" name="tag" value="[% rule.tag | html %]"/></td>
169 <select name="preset">
170 <option value="" selected>Custom</option>
171 <option value="Protect">Protect</option>
172 <option value="Overwrite">Overwrite</option>
173 <option value="Add new">Add new</option>
174 <option value="Add and append">Add and append</option>
175 <option value="Protect from deletion">Protect from deletion</option>
178 <td class="rule-operation-action-edit">
181 <option value="0">Skip</option>
182 <option value="1" selected="selected">Add</option>
184 <option value="0" selected="selected">Skip</option>
185 <option value="1">Add</option>
189 <td class="rule-operation-action-edit">
190 <select name="append">
192 <option value="0">Skip</option>
193 <option value="1" selected="selected">Append</option>
195 <option value="0" selected="selected">Skip</option>
196 <option value="1">Append</option>
200 <td class="rule-operation-action-edit">
201 <select name="remove">
203 <option value="0">Skip</option>
204 <option value="1" selected="selected">Remove</option>
206 <option value="0" selected="selected">Skip</option>
207 <option value="1">Remove</option>
211 <td class="rule-operation-action-edit">
212 <select name="delete">
214 <option value="0">Skip</option>
215 <option value="1" selected="selected">Delete</option>
217 <option value="0" selected="selected">Skip</option>
218 <option value="1">Delete</option>
223 <button class="btn btn-default btn-xs" title="Save" id="doedit" value="[% rule.id | html %]"><i class="fa fa-check"></i> Save</button>
224 <button type="submit" class="btn btn-default btn-xs" title="Cancel" ><i class="fa fa-times"></i> Cancel</button>
228 <td>[% rule.id | html %]</td>
229 <td class="rule-module">[% rule.module | html %]</td>
230 <td class="rule-filter">[% rule.filter | html %]</td>
231 <td>[% rule.tag | html %]</td>
232 <td class="rule-preset"></td>
233 <td class="rule-operation-action" data-operation="add">[% IF rule.add %]Add[% ELSE %]Skip[% END %]</td>
234 <td class="rule-operation-action" data-operation="append">[% IF rule.append %]Append[% ELSE %]Skip[% END %]</td>
235 <td class="rule-operation-action" data-operation="remove">[% IF rule.remove %]Remove[% ELSE %]Skip[% END %]</td>
236 <td class="rule-operation-action" data-operation="delete">[% IF rule.delete %]Delete[% ELSE %]Skip[% END %]</td>
238 <a href="?op=remove&id=[% rule.id | uri %]" title="Delete" class="btn btn-default btn-xs"><i class="fa fa-trash-can"></i> Delete</a>
239 <a href="?op=edit&id=[% rule.id | uri %]" title="Edit" class="btn btn-default btn-xs"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit</a>
242 [% IF rule.removemarked %]
243 <input type="checkbox" name="batchremove" value="[% rule.id | html %]" checked="checked"/>
245 <input type="checkbox" name="batchremove" value="[% rule.id | html %]"/>
254 </div> <!-- /.page-section -->
256 <form action="/cgi-bin/koha/admin/marc-overlay-rules.pl" method="post">
257 <input type="hidden" name="op" value="redo-matching" />
260 </div><!-- /.col-sm-10.col-sm-push-2 -->
262 <div class="col-sm-2 col-sm-pull-10">
264 [% INCLUDE 'admin-menu.inc' %]
269 </div><!-- /main container-fluid -->
271 [% MACRO jsinclude BLOCK %]
272 [% Asset.js("js/admin-menu.js") | $raw %]
273 [% INCLUDE 'datatables.inc' %]
276 $(document).ready(function(){
277 function doSubmit(op, id) {
278 $('<input type="hidden"/>')
281 .appendTo('#marc-overlay-rules-form');
284 $('<input type="hidden"/>')
287 .appendTo('#marc-overlay-rules-form');
291 if (op == 'add' || op == 'edit') {
293 $('#marc-overlay-rules-form input[name="filter"]'),
294 $('#marc-overlay-rules-form input[name="tag"]')
296 for(var i = 0; i < validate.length; i++) {
297 if (validate[i].length) {
298 if(validate[i].val().length == 0) {
299 validate[i].addClass('required');
302 validate[i].removeClass('required');
309 $('#marc-overlay-rules-form').submit();
315 $('#doremove').on('click', function(){
316 doSubmit('doremove');
318 $('#doedit').on('click', function(){
319 doSubmit('doedit', $("#doedit").attr('value'));
321 $('#add').on('click', function(){
325 $('#btn_batchremove').on('click', function(){
329 /* Disable batch remove unless one or more checkboxes are checked */
330 $('input[name="batchremove"]').change(function() {
331 if($('input[name="batchremove"]:checked').length > 0) {
332 $('#btn_batchremove').removeAttr('disabled');
334 $('#btn_batchremove').attr('disabled', 'disabled');
338 $.fn.dataTable.ext.order['dom-input'] = function (settings, col) {
339 return this.api().column(col, { order: 'index' }).nodes()
340 .map(function (td, i) {
341 if($('input', td).val() != undefined) {
342 return $('input', td).val();
343 } else if($('select', td).val() != undefined) {
344 return $('option[selected="selected"]', td).val();
351 $('#marc-overlay-rules').dataTable($.extend(true, {}, dataTablesDefaults, {
353 {"bSearchable": false, "bSortable": false},
354 {"sSortDataType": "dom-input"},
355 {"sSortDataType": "dom-input"},
356 {"bSearchable": false, "sSortDataType": "dom-input"},
357 {"bSearchable": false, "sSortDataType": "dom-input"},
358 {"bSearchable": false, "sSortDataType": "dom-input"},
359 {"bSearchable": false, "sSortDataType": "dom-input"},
360 {"bSearchable": false, "sSortDataType": "dom-input"},
361 {"bSearchable": false, "sSortDataType": "dom-input"},
362 {"bSearchable": false, "bSortable": false},
363 {"bSearchable": false, "bSortable": false}
365 "pagingType": "simple"
368 var overlay_rules_presets = {};
369 overlay_rules_presets[_("Protect")] = {
375 overlay_rules_presets[_("Overwrite")] = {
381 overlay_rules_presets[_("Add new")] = {
387 overlay_rules_presets[_("Add and append")] = {
393 overlay_rules_presets[_("Protect from deletion")] = {
400 var overlay_rules_label_to_value = {};
401 overlay_rules_label_to_value[_("Add")] = 1;
402 overlay_rules_label_to_value[_("Append")] = 1;
403 overlay_rules_label_to_value[_("Remove")] = 1;
404 overlay_rules_label_to_value[_("Delete")] = 1;
405 overlay_rules_label_to_value[_("Skip")] = 0;
407 function hash_config(config) {
408 return JSON.stringify(config, Object.keys(config).sort());
411 var overlay_rules_preset_map = {};
412 $.each(overlay_rules_presets, function(preset, config) {
413 overlay_rules_preset_map[hash_config(config)] = preset;
416 function operations_config_overlay_rule_preset(config) {
417 return overlay_rules_preset_map[hash_config(config)] || '';
420 /* Set preset values according to operation config */
421 $('.rule').each(function() {
423 var operations_config = {};
424 $('.rule-operation-action', $this).each(function() {
425 var $operation = $(this);
426 operations_config[$operation.data('operation')] = overlay_rules_label_to_value[$operation.text()];
428 $('.rule-preset', $this).text(
429 operations_config_overlay_rule_preset(operations_config) || _("Custom")
433 /* Listen to operations config changes and set presets accordingly */
434 $('.rule-operation-action-edit select').change(function() {
435 var operations_config = {};
436 var $parent_row = $(this).closest('tr');
437 $('.rule-operation-action-edit select', $parent_row).each(function() {
439 operations_config[$this.attr('name')] = parseInt($this.val());
441 $('select[name="preset"]', $parent_row).val(
442 operations_config_overlay_rule_preset(operations_config)
446 /* Listen to preset changes and set operations config accordingly */
447 $('select[name="preset"]').change(function() {
449 var $parent_row = $this.closest('tr');
450 var preset = $this.val();
452 $.each(overlay_rules_presets[preset], function(operation, action) {
453 $('select[name="' + operation + '"]', $parent_row).val(action);
458 var categories = [% To.json( categories ) | $raw %];
459 // Add * => * to this array of objects, and *reduce* it to one object
460 // Note: This allows displaying a sorted list of categories later, but we still depend on an object!
461 categories.unshift({ categorycode: '*', description: '*'});
462 categories = categories.reduce( ( a,c ) => ( { ...a, [c.categorycode]: c.description } ), {} );
464 var module_filter_options = {
467 batchmod: _("Batch record modification"),
468 intranet: _("Staff interface MARC editor"),
469 batchimport: _("Staged MARC import"),
471 /* bulkmarcimport: _("bulkmarcimport.pl"), */
472 import_lexile: _("import_lexile.pl")
475 categorycode: categories,
478 //Kind of hack: Replace filter value with label when one exist
479 $('.rule-module').each(function() {
481 var module = $this.text();
482 if (module in module_filter_options) {
483 let $filter = $this.siblings('.rule-filter');
484 if ($filter.text() in module_filter_options[module]) {
485 $filter.text(module_filter_options[module][$filter.text()]);
490 var $filter_container = $('#filter-container');
492 /* Listen to module changes and set filter input accordingly */
493 $('select[name="module"]').change(function() {
495 var module_name = $this.val();
497 /* Remove current element if any */
498 $filter_container.empty();
500 var filter_elem = null;
501 if (module_name in module_filter_options) {
502 // Create select element
503 filter_elem = document.createElement('select');
504 for (var filter_value in module_filter_options[module_name]) {
505 var option = document.createElement('option');
506 option.value = filter_value;
507 option.text = module_filter_options[module_name][filter_value];
508 filter_elem.appendChild(option);
512 // Create text input element
513 filter_elem = document.createElement('input');
514 filter_elem.type = 'text';
515 filter_elem.setAttribute('size', 5);
517 filter_elem.name = 'filter';
518 filter_elem.id = 'filter';
519 $filter_container.append(filter_elem);
520 }).change(); // Trigger change
522 // Hack: set value if editing rule
523 if ($filter_container.data('filter')) {
524 $('#filter').val($filter_container.data('filter'));
530 [% INCLUDE 'intranet-bottom.inc' %]