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
320 changes: 171 additions & 149 deletions SQL/0000-00-03-ConfigTables.sql

Large diffs are not rendered by default.

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 @@ -93,6 +93,7 @@ DROP TABLE IF EXISTS `help`;
DROP TABLE IF EXISTS `ConfigI18n`;
DROP TABLE IF EXISTS `Config`;
DROP TABLE IF EXISTS `ConfigSettings`;
DROP TABLE IF EXISTS `ConfigCategories`;
DROP TABLE IF EXISTS `menu_categories`;

-- issues must be deleted before `modules` table
Expand Down
36 changes: 36 additions & 0 deletions SQL/New_patches/2026-05-31_config-categories.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CREATE TABLE `ConfigCategories` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL,
`Description` varchar(255) DEFAULT NULL,
`Visible` tinyint(1) DEFAULT '0',
`Label` varchar(255) DEFAULT NULL,
`OrderNumber` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO ConfigCategories (Name, Description, Visible, Label, OrderNumber)
SELECT Name, Description, Visible, Label, OrderNumber
FROM ConfigSettings
WHERE Parent IS NULL AND DataType IS NULL;

ALTER TABLE ConfigSettings
ADD COLUMN CategoryID int(11) DEFAULT NULL AFTER Parent;

UPDATE ConfigSettings child
JOIN ConfigSettings category_setting ON child.Parent=category_setting.ID
JOIN ConfigCategories category ON category.Name=category_setting.Name
SET child.CategoryID=category.ID,
child.Parent=NULL
WHERE category_setting.Parent IS NULL AND category_setting.DataType IS NULL;

DELETE FROM ConfigSettings
WHERE Parent IS NULL AND DataType IS NULL;

ALTER TABLE ConfigSettings
ADD KEY `fk_ConfigSettings_CategoryID_idx` (`CategoryID`),
ADD CONSTRAINT `fk_ConfigSettings_CategoryID`
FOREIGN KEY (`CategoryID`)
REFERENCES `ConfigCategories` (`ID`)
ON DELETE SET NULL
ON UPDATE CASCADE;
2 changes: 0 additions & 2 deletions modules/configuration/ajax/process.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ function getPathIDs(string $table): array
*/
$query = "SELECT c.ID FROM Config c "
. "LEFT JOIN ConfigSettings cs ON (c.ConfigID = cs.ID) "
. "JOIN ConfigSettings csp ON (cs.Parent = csp.ID) "
. "WHERE cs.DataType = 'web_path';";
break;
case 'ConfigSettings':
Expand Down Expand Up @@ -268,4 +267,3 @@ function validPath($value)
}
return true;
}

61 changes: 60 additions & 1 deletion modules/configuration/css/configuration.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,70 @@
margin-top: -3px;
}

.configuration-tree-node {
border-left: 1px solid #ddd;
margin-bottom: 10px;
padding-left: 12px;
}

.configuration-tree-header {
background: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px 12px;
text-align: left;
width: 100%;
}

.configuration-tree-header:hover,
.configuration-tree-header:focus {
background: #f2f7fc;
}

.configuration-tree-icon {
color: #31708f;
margin-right: 6px;
}

.configuration-tree-icon-expanded {
display: none;
}

.configuration-tree-header[aria-expanded="true"] .configuration-tree-icon-collapsed {
display: none;
}

.configuration-tree-header[aria-expanded="true"] .configuration-tree-icon-expanded {
display: inline-block;
}

.configuration-tree-label {
font-weight: 600;
}

.configuration-tree-name {
color: grey;
font-size: 11px;
margin-left: 8px;
}

.configuration-tree-description {
color: #555;
display: block;
margin-left: 23px;
margin-top: 4px;
}

.configuration-tree-children {
margin-left: 18px;
padding-top: 12px;
}

.submit-area {
margin-top: 30px;
}

.btn-container {
display: flex;
justify-content: center;
}
}
191 changes: 137 additions & 54 deletions modules/configuration/php/configuration.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ class Configuration extends \NDB_Form

$parentConfigItems = $DB->pselect(
"SELECT Label, Name
FROM ConfigSettings
WHERE Parent IS NULL AND Visible=1 ORDER BY OrderNumber",
FROM ConfigCategories
WHERE Visible=1 ORDER BY OrderNumber",
[]
);

Expand All @@ -135,77 +135,160 @@ class Configuration extends \NDB_Form
$DB = $this->loris->getDatabaseConnection();
$lang = \User::singleton()->getLanguageCode();

// Get the names and meta-information for the config settings in the database
$configs = iterator_to_array(
$categories = iterator_to_array(
$DB->pselect(
"SELECT * FROM ConfigSettings WHERE Visible=1 ORDER BY OrderNumber",
"SELECT ID, Name, Description, Visible, 0 AS AllowMultiple,
NULL AS DataType, NULL AS Parent, Label, OrderNumber,
0 AS Multilingual
FROM ConfigCategories
WHERE Visible=1
ORDER BY OrderNumber",
[]
)
);

// Convert the labels and descriptions to the appropriate language
foreach ($configs as &$setting) {
$setting['Label'] = dgettext('ConfigSettings', $setting['Label']);
$setting['Description'] = dgettext(
foreach ($categories as &$category) {
$category['Label'] = dgettext(
'ConfigSettings',
$category['Label']
);
$category['Description'] = dgettext(
'ConfigSettings',
$setting['Description']
$category['Description']
);
}
unset($category);

$configs = iterator_to_array(
$DB->pselect(
"SELECT *
FROM ConfigSettings
WHERE Visible=1
ORDER BY CategoryID, Parent, OrderNumber, ID",
[]
)
);

// Check whether any setting is overwritten in the config.xml
// Add this info to the array so form entries can be disabled in front end
// Update the value for the setting to the value from the config.xml
foreach ($configs as &$setting) {
try {
$setting['Disabled'] = 'Yes';
if ($setting['Parent'] != null) {
$valueFromXML = $config->getSettingFromXML($setting['Name']);
if (!is_array($valueFromXML)) {
$setting['Value'][0] = $valueFromXML;
} else {
$setting['Value'] = $valueFromXML;
}
}
} catch (\ConfigurationException $e) {
$setting['Disabled'] = 'No';
$childrenByParent = [];
$settingsByCategory = [];
foreach ($configs as $setting) {
$setting = $this->_formatConfigSetting(
$setting,
$config,
$DB,
$lang
);
if ($setting['Parent'] !== null) {
$childrenByParent[strval($setting['Parent'])][] = $setting;
continue;
}

if ($setting['CategoryID'] !== null) {
$settingsByCategory[intval($setting['CategoryID'])][] = $setting;
}
}

// Now check for config settings from the database for the fields not
// overridden in the config.xml
foreach ($configs as &$setting) {
if ($setting['Disabled'] == 'No') {
$value = $DB->pselect(
"SELECT ID, Value FROM Config WHERE ConfigID=:ID",
['ID' => $setting['ID']]
foreach ($categories as &$category) {
$category['Disabled'] = 'No';
$category['Value'] = [];
$category['Children'] = [];
$categorySettings = $settingsByCategory[intval($category['ID'])]
?? [];
foreach ($categorySettings as $setting) {
$setting['Children'] = $this->_buildConfigSettingTree(
$childrenByParent,
intval($setting['ID'])
);
if ($value) {
foreach ($value as $subvalue) {
if ($setting['Multilingual']) {
$setting['Value'][$subvalue['ID']]
= $config->getMultilingualSetting(
$lang,
$setting['Name']
);
} else {
$setting['Value'][$subvalue['ID']] = $subvalue['Value'];
}
}
}
$category['Children'][] = $setting;
}
}
unset($category);

return $categories;
}

/**
* Add value and override metadata to a config setting row.
*
* @param array $setting The config setting row
* @param \NDB_Config $config Runtime configuration object
* @param \Database $DB Database handle
* @param string $lang Current language code
*
* @return array
*/
private function _formatConfigSetting(
array $setting,
\NDB_Config $config,
\Database $DB,
string $lang
) : array {
$setting['Label'] = dgettext('ConfigSettings', $setting['Label']);
$setting['Description'] = dgettext(
'ConfigSettings',
$setting['Description']
);
$setting['Children'] = [];
$setting['Value'] = [];

if ($setting['DataType'] === null) {
$setting['Disabled'] = 'No';
return $setting;
}

// build a tree from configs array
$tree = [];
foreach ($configs as &$node) {
$node['Children'] = [];
$tree[intval($node['ID'])] = &$node;
try {
$setting['Disabled'] = 'Yes';
$valueFromXML = $config->getSettingFromXML($setting['Name']);
if (!is_array($valueFromXML)) {
$setting['Value'][0] = $valueFromXML;
} else {
$setting['Value'] = $valueFromXML;
}
return $setting;
} catch (\ConfigurationException $e) {
$setting['Disabled'] = 'No';
}
foreach ($configs as &$node) {
$tree[$node['Parent'] ?? '']['Children'][] = &$node;

$values = $DB->pselect(
"SELECT ID, Value FROM Config WHERE ConfigID=:ID",
['ID' => $setting['ID']]
);
foreach ($values as $subvalue) {
if ($setting['Multilingual']) {
$setting['Value'][$subvalue['ID']]
= $config->getMultilingualSetting(
$lang,
$setting['Name']
);
} else {
$setting['Value'][$subvalue['ID']] = $subvalue['Value'];
}
}

return $configs;
return $setting;
}

/**
* Build a recursive config setting tree from settings grouped by parent.
*
* @param array $childrenByParent Config settings grouped by parent ID
* @param int $parentID Parent ID to build from
*
* @return array
*/
private function _buildConfigSettingTree(
array $childrenByParent,
int $parentID
) : array {
$settings = [];
foreach ($childrenByParent[strval($parentID)] ?? [] as $setting) {
$setting['Children'] = $this->_buildConfigSettingTree(
$childrenByParent,
intval($setting['ID'])
);
$settings[] = $setting;
}
return $settings;
}

/**
Expand Down
Loading
Loading