Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion SQL/0000-00-03-ConfigTables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ CREATE TABLE `ConfigSettings` (
`Description` varchar(255) DEFAULT NULL,
`Visible` tinyint(1) DEFAULT '0',
`AllowMultiple` tinyint(1) DEFAULT '0',
`DataType` ENUM('text','boolean','email','instrument','textarea','scan_type','date_format','lookup_center','path','web_path', 'log_level') DEFAULT NULL,
`DataType` ENUM('text','boolean','email','instrument','textarea','scan_type','date_format','lookup_center','path','web_path', 'log_level','mapping') DEFAULT NULL,
`Parent` int(11) DEFAULT NULL,
`Label` varchar(255) DEFAULT NULL,
`OrderNumber` int(11) DEFAULT NULL,
Expand Down Expand Up @@ -41,6 +41,19 @@ CREATE TABLE `Config` (
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `ConfigMappings` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ConfigID` int(11) NOT NULL,
`Value` text,
PRIMARY KEY (`ID`),
KEY `fk_ConfigMappings_ConfigID_idx` (`ConfigID`),
CONSTRAINT `fk_ConfigMappings_ConfigID`
FOREIGN KEY (`ConfigID`)
REFERENCES `Config` (`ID`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Filling ConfigSettings table
--
Expand Down
1 change: 1 addition & 0 deletions SQL/9999-99-99-drop_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ DROP TABLE IF EXISTS `help`;

-- 0000-00-03-ConfigTables.sql
DROP TABLE IF EXISTS `ConfigI18n`;
DROP TABLE IF EXISTS `ConfigMappings`;
DROP TABLE IF EXISTS `Config`;
DROP TABLE IF EXISTS `ConfigSettings`;
DROP TABLE IF EXISTS `menu_categories`;
Expand Down
15 changes: 15 additions & 0 deletions SQL/New_patches/2026-05-31_config-mapping.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE `ConfigMappings` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ConfigID` int(11) NOT NULL,
`Value` text,
PRIMARY KEY (`ID`),
KEY `fk_ConfigMappings_ConfigID_idx` (`ConfigID`),
CONSTRAINT `fk_ConfigMappings_ConfigID`
FOREIGN KEY (`ConfigID`)
REFERENCES `Config` (`ID`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `ConfigSettings`
MODIFY COLUMN `DataType` ENUM('text','boolean','email','instrument','textarea','scan_type','date_format','lookup_center','path','web_path', 'log_level','mapping') DEFAULT NULL;
76 changes: 70 additions & 6 deletions modules/configuration/ajax/process.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,35 @@
$client->initialize();
$factory = \NDB_Factory::singleton();
$DB = $factory->database();

$mappingValues = [];
foreach ($_POST as $key => $value) {
$key = (string) $key;
if (strpos($key, 'mapping-') === 0) {
$mappingValues[substr($key, strlen('mapping-'))] = $value;
}
}

foreach ($_POST as $key => $value) {
$key = (string) $key;
if (strpos($key, 'mapping-') === 0
|| !array_key_exists($key, $mappingValues)
) {
continue;
}
$valueIsEmpty = trim((string) $value) === '';
$mappingValueIsEmpty = trim((string) $mappingValues[$key]) === '';
if ($valueIsEmpty !== $mappingValueIsEmpty) {
displayError(400, 'Mapping rows need both a value and mapped value.');
return;
}
}

foreach ($_POST as $key => $value) {
$key = (string) $key;
if (strpos($key, 'mapping-') === 0) {
continue;
}
if (is_numeric($key)) {
// When a $key is numeric, it means we are updating the entry in the
// Config table with ID == $key.
Expand Down Expand Up @@ -71,6 +99,9 @@
['Value' => $value],
['ID' => $key]
);
if (array_key_exists($key, $mappingValues)) {
saveMappingValue($key, $mappingValues[$key]);
}
}
} else {
// This branch is executed when the key is prefixed with the string
Expand All @@ -83,13 +114,15 @@
// This is different from the above is_numeric case; this makes use of
// Config.ID, not Config.ConfigID (which is a FK to ConfigSettings.ID).
// The Config table is the one that will be modified here.
$keySplit = explode("-", $key); // e.g. 'add-17-1' or 'remove'
$action = $keySplit[0];
$ConfigSettingsID = $keySplit[1];
$valueSplit = explode("-", $value); // e.g. "remove-74"
$removeID = $valueSplit[1];
$keySplit = explode("-", $key); // e.g. 'add-17-1' or 'remove'
$action = $keySplit[0];
//assert(count($keySplit) == 2);
if ($action == 'add') {
$ConfigSettingsID = $keySplit[1] ?? null;
if ($ConfigSettingsID === null) {
displayError(400, 'Invalid action');
return;
}
// This branch adds a new entry to the Config table.
if ($value === "") {
continue;
Expand Down Expand Up @@ -131,7 +164,16 @@
'Value' => $value,
]
);
if (array_key_exists($key, $mappingValues)) {
saveMappingValue($DB->lastInsertID, $mappingValues[$key]);
}
} elseif ($action == 'remove') {
$valueSplit = explode("-", $value); // e.g. "remove-74"
$removeID = $valueSplit[1] ?? null;
if ($removeID === null) {
displayError(400, 'Invalid action');
return;
}
// Delete an entry from the Config table.
$DB->delete(
'Config',
Expand All @@ -144,6 +186,29 @@
unset($pathIDs);
}

/**
* Save the mapped value for a row in the Config table.
*
* @param string $configID The Config.ID value
* @param string $value The right-hand mapped value
*
* @return void
*/
function saveMappingValue($configID, $value): void
{
$factory = \NDB_Factory::singleton();
$DB = $factory->database();

$DB->delete('ConfigMappings', ['ConfigID' => $configID]);
$DB->unsafeInsert(
'ConfigMappings',
[
'ConfigID' => $configID,
'Value' => $value,
]
);
}

/**
* Check Duplicate value
*
Expand Down Expand Up @@ -263,4 +328,3 @@ function validPath($value)
}
return true;
}

33 changes: 32 additions & 1 deletion modules/configuration/css/configuration.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,35 @@
.btn-container {
display: flex;
justify-content: center;
}
}

.configuration-mapping-row {
align-items: stretch;
display: flex;
gap: 10px;
width: 100%;
}

.configuration-mapping-fields {
display: grid;
flex: 1 1 auto;
gap: 10px;
grid-template-columns: minmax(160px, 1fr) minmax(220px, 2fr);
min-width: 0;
}

.configuration-mapping-fields .form-control {
height: 34px;
min-width: 0;
}

@media (max-width: 767px) {
.configuration-mapping-row {
flex-wrap: wrap;
}

.configuration-mapping-fields {
flex-basis: 100%;
grid-template-columns: 1fr;
}
}
62 changes: 53 additions & 9 deletions modules/configuration/jsx/configuration_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ $(function() {

// Setup the new form field
let newField = currentField.clone();
newField.find('.form-control').attr('name', name);
assignFormControlNames(newField, name);
$(newField).find('.btn-remove')
.addClass('remove-new')
.removeClass('btn-remove');
Expand All @@ -39,7 +39,7 @@ $(function() {
$('.btn-remove').on('click', function(e) {
e.preventDefault();

let selectedOption = $(this).parent().parent().children()
let selectedOption = $(this).parent().parent().find('.form-control:first')
.prop('value');

let fieldName = $(this)
Expand Down Expand Up @@ -76,9 +76,7 @@ $(function() {
let name = 'add-' + parentID;

resetForm($(button).parent().parent());
$(button)
.parent().parent().children('.form-control')
.attr('name', name);
assignFormControlNames($(button).parent().parent(), name);
$(button)
.addClass('remove-new')
.removeClass('btn-remove');
Expand All @@ -94,14 +92,22 @@ $(function() {
});

// On form submit, process the changes through an AJAX call
$('form').on('submit', function(e) {
$('body').on('submit', 'form', function(e) {
e.preventDefault();

let form = $(this).serialize();

// Clear previous feedback
$('.submit-area > label').remove();

if (hasIncompleteMappingFields()) {
$('<label>Mapping rows need both a value and mapped value.</label>')
.hide()
.appendTo('.submit-area')
.fadeIn(500).delay(1000);
return;
}

let form = $(this).serialize();

$.ajax({
type: 'post',
url: loris.BaseURL + '/configuration/ajax/process.php',
Expand All @@ -122,7 +128,7 @@ $(function() {
});

// On form reset, to delete the elements added with the "Add field" button that were not submitted.
$('form').on('reset', function(e) {
$('body').on('reset', 'form', function(e) {
$('.tab-pane.active').find('select[name^="add-"]').parent().remove();
});
});
Expand Down Expand Up @@ -150,3 +156,41 @@ function resetForm(form) {
$(form).find('input:radio, input:checkbox')
.removeAttr('checked').removeAttr('selected');
}

/**
* Assign posted field names to simple and mapping configuration controls.
*
* @param {Element} field A configuration entry element
* @param {string} name The base POST field name
*/
function assignFormControlNames(field, name) {
'use strict';

$(field).find('.form-control').attr('name', name);
$(field).find('.configuration-mapping-mapped-value')
.attr('name', 'mapping-' + name);
}

/**
* Check whether any mapping row has only one side of the pair filled.
*
* @returns {boolean} True when an incomplete mapping row exists
*/
function hasIncompleteMappingFields() {
'use strict';

let incomplete = false;
$('.configuration-mapping-fields').each(function() {
let value = $.trim($(this).find('.configuration-mapping-value').val());
let mappedValue = $.trim(
$(this).find('.configuration-mapping-mapped-value').val()
);

if ((value === '') !== (mappedValue === '')) {
incomplete = true;
return false;
}
});

return incomplete;
}
14 changes: 12 additions & 2 deletions modules/configuration/php/configuration.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,22 @@ class Configuration extends \NDB_Form
foreach ($configs as &$setting) {
if ($setting['Disabled'] == 'No') {
$value = $DB->pselect(
"SELECT ID, Value FROM Config WHERE ConfigID=:ID",
"SELECT c.ID, c.Value, cm.Value AS MappedValue
FROM Config c
LEFT JOIN ConfigMappings cm ON (c.ID=cm.ConfigID)
WHERE c.ConfigID=:ID",
['ID' => $setting['ID']]
);
if ($value) {
foreach ($value as $subvalue) {
$setting['Value'][$subvalue['ID']] = $subvalue['Value'];
if ($setting['DataType'] === 'mapping') {
$setting['Value'][$subvalue['ID']] = [
'Value' => $subvalue['Value'],
'MappedValue' => $subvalue['MappedValue'] ?? '',
];
} else {
$setting['Value'][$subvalue['ID']] = $subvalue['Value'];
}
}
}
}
Expand Down
22 changes: 20 additions & 2 deletions modules/configuration/templates/form_configuration.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@
<input type="text" class="form-control" name="{$k}" value="{$v|escape:html}" {if $d eq "Yes"}disabled{/if}>
{/function}

{function name=createMapping}
{if isset($v.Value)}
{assign var=mappingValue value=$v.Value}
{assign var=mappedValue value=$v.MappedValue|default:''}
{else}
{assign var=mappingValue value=$v|default:''}
{assign var=mappedValue value=''}
{/if}
<div class="configuration-mapping-fields">
<input type="text" class="form-control configuration-mapping-value" name="{$k}" value="{$mappingValue|escape:html}" placeholder="Value" {if $d eq "Yes"}disabled{/if}>
<input type="text" class="form-control configuration-mapping-mapped-value" name="mapping-{$k}" value="{$mappedValue|escape:html}" placeholder="Mapped value" {if $d eq "Yes"}disabled{/if}>
</div>
{/function}

{function name=createLogDropdown}
<select class="form-control" name="{$k}" {if $d eq "Yes"}disabled{/if}>
{foreach from=$log_levels key=name item=label}
Expand Down Expand Up @@ -94,7 +108,7 @@
{function name=printForm}
<div class="config-form-group" id="{$node['ID']}">
{foreach from=$node['Value']|default key=k item=v}
{if $node['AllowMultiple'] == 1}<div class="input-group entry">{/if}
{if $node['AllowMultiple'] == 1}<div class="input-group entry {if $node['DataType'] eq 'mapping'}configuration-mapping-row{/if}">{/if}

{if $k == 0}
{assign var=id value={"add-"|cat:$node['ID']} }
Expand All @@ -118,6 +132,8 @@
{call createLookUpCenterNameUsing k=$id v=$v d=$node['Disabled']}
{elseif $node['DataType'] eq 'log_level'}
{call createLogDropdown k=$id v=$v d=$node['Disabled']}
{elseif $node['DataType'] eq 'mapping'}
{call createMapping k=$id v=$v d=$node['Disabled']}
{else}
{call createText k=$id v=$v d=$node['Disabled']}
{/if}
Expand All @@ -130,7 +146,7 @@
{/if}
{if $node['AllowMultiple'] == 1}</div>{/if}
{foreachelse}
{if $node['AllowMultiple'] == 1}<div class="input-group entry">{/if}
{if $node['AllowMultiple'] == 1}<div class="input-group entry {if $node['DataType'] eq 'mapping'}configuration-mapping-row{/if}">{/if}
{assign var=id value={"add-"|cat:$node['ID']} }
{if $node['DataType'] eq 'boolean'}
{call createRadio k=$id d=$node['Disabled']}
Expand All @@ -146,6 +162,8 @@
{call createTextArea k=$id d=$node['Disabled']}
{elseif $node['DataType'] eq 'lookup_center'}
{call createLookUpCenterNameUsing k=$id d=$node['Disabled']}
{elseif $node['DataType'] eq 'mapping'}
{call createMapping k=$id d=$node['Disabled']}
{else}
{call createText k=$id d=$node['Disabled']}
{/if}
Expand Down
Loading
Loading