From b799be3cb72cef7a2b0dda749cf0d296157e3543 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Mon, 13 Apr 2026 10:24:50 +0200 Subject: [PATCH 1/6] =?UTF-8?q?N=C2=B09177=20-=20Blockquote=20in=20HTML=20?= =?UTF-8?q?field=20are=20unreadable=20in=20darkmoon=20(again)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/backoffice/_shame.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/css/backoffice/_shame.scss b/css/backoffice/_shame.scss index 758b06689..5859d7eb7 100644 --- a/css/backoffice/_shame.scss +++ b/css/backoffice/_shame.scss @@ -39,6 +39,8 @@ // // .site-nav a { color:#BADA55!important; } +$ibo-blockquote--color: $ibo-body-text-color !default; + // N°2847 - Recolor svg illustrations with iTop's primary color .ibo-svg-illustration--container > svg *[fill="#6c63ff"]{ fill: $ibo-svg-illustration--fill; @@ -109,3 +111,11 @@ input:checked + .slider:before { .slider.round:before { border-radius: 7px; } + +/* + Bulma sets blockquote background color through a variable, it affects ckeditor and html display. + This rule is needed harmonize the blockquote text color in both contexts. + */ +.ibo-is-html-content blockquote { + color: $ibo-blockquote--color; +} \ No newline at end of file From a96e1c286dfb8a23efada7e6536dfd560c83d1b5 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:04:00 +0200 Subject: [PATCH 2/6] =?UTF-8?q?N=C2=B09379=20PHP=20unserialize=20encapsula?= =?UTF-8?q?tion=20(#878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/menunode.class.inc.php | 21 ++++++--- application/utils.inc.php | 46 +++++++++++++++++++ core/attributedef.class.inc.php | 2 +- .../Component/DataTable/DataTableSettings.php | 24 +++++++++- .../unitary-tests/application/utilsTest.php | 18 ++++++++ 5 files changed, 102 insertions(+), 9 deletions(-) diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 0071b5532..bbd1341c9 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -1546,12 +1546,21 @@ class ShortcutMenuNode extends MenuNode public function GetHyperlink($aExtraParams) { $sContext = $this->oShortcut->Get('context'); - $aContext = unserialize($sContext); - if (isset($aContext['menu'])) { - unset($aContext['menu']); - } - foreach ($aContext as $sArgName => $sArgValue) { - $aExtraParams[$sArgName] = $sArgValue; + try { + $aContext = utils::Unserialize($sContext); + if (isset($aContext['menu'])) { + unset($aContext['menu']); + } + foreach ($aContext as $sArgName => $sArgValue) { + $aExtraParams[$sArgName] = $sArgValue; + } + } catch (Exception $e) { + IssueLog::Warning("User shortcut corrupted, delete the shortcut", LogChannels::CONSOLE, [ + 'shortcut_name' => $this->oShortcut->GetName(), + 'root_cause' => $e->getMessage(), + ]); + // delete the shortcut + $this->oShortcut->DBDelete(); } return parent::GetHyperlink($aExtraParams); } diff --git a/application/utils.inc.php b/application/utils.inc.php index 3e0e6ddb3..84ee1b28f 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -3252,4 +3252,50 @@ TXT return $aTrace; } + + /** + * PHP unserialize encapsulation, allow throwing exception when not allowed object class is detected (for security hardening) + * + * @param string $data data to unserialize + * @param array $aOptions PHP @unserialise options + * @param bool $bThrowNotAllowedObjectClassException flag to throw exception + * + * @return mixed PHP @unserialise return + * @throws Exception + */ + public static function Unserialize(string $data, array $aOptions = ['allowed_classes' => false], bool $bThrowNotAllowedObjectClassException = true): mixed + { + $data = unserialize($data, $aOptions); + + if ($bThrowNotAllowedObjectClassException) { + try { + self::AssertNoIncompleteClassDetected($data); + } catch (Exception $e) { + throw new CoreException('Unserialization failed because an incomplete class was detected.', [], '', $e); + } + } + + return $data; + } + + /** + * Assert that data provided doesn't contain any incomplete class. + * + * @throws Exception + */ + public static function AssertNoIncompleteClassDetected(mixed $data): void + { + if (is_object($data)) { + if ($data instanceof __PHP_Incomplete_Class) { + throw new Exception('__PHP_Incomplete_Class_Name object detected'); + } + foreach (get_object_vars($data) as $property) { + self::AssertNoIncompleteClassDetected($property); + } + } elseif (is_array($data)) { + foreach ($data as $value) { + self::AssertNoIncompleteClassDetected($value); + } + } + } } diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index a7554f29c..afc52329b 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -4829,7 +4829,7 @@ class AttributeCaseLog extends AttributeLongText } if (strlen($sIndex) > 0) { - $aIndex = unserialize($sIndex); + $aIndex = utils::Unserialize($sIndex, ['allowed_classes' => false], false); $value = new ormCaseLog($sLog, $aIndex); } else { $value = new ormCaseLog($sLog); diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableSettings.php b/sources/Application/UI/Base/Component/DataTable/DataTableSettings.php index 2a4496b11..03ceb31f1 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTableSettings.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTableSettings.php @@ -8,8 +8,13 @@ use AttributeFriendlyName; use AttributeLinkedSet; use cmdbAbstract; use cmdbAbstractObject; +use CoreException; use Dict; +use Exception; +use IssueLog; +use LogChannels; use Metamodel; +use utils; /** * Class DataTableSettings @@ -130,7 +135,10 @@ class DataTableSettings */ public function unserialize($sData) { - $aData = unserialize($sData); + $aData = utils::Unserialize($sData); + if (!is_array($aData)) { + throw new CoreException('Wrong data table settings format, expected an array', ['datatable_settings_data' => $aData]); + } $this->iDefaultPageSize = $aData['iDefaultPageSize']; $this->aColumns = $aData['aColumns']; foreach ($this->aClassAliases as $sAlias => $sClass) { @@ -269,7 +277,19 @@ class DataTableSettings return null; } } - $oSettings->unserialize($pref); + + try { + $oSettings->unserialize($pref); + } catch (Exception $e) { + IssueLog::Warning("User table settings corrupted, back to the default values provided by the data model", LogChannels::CONSOLE, [ + 'table_id' => $sTableId, + 'root_cause' => $e->getMessage(), + ]); + // unset the preference + appUserPreferences::UnsetPref($oSettings->GetPrefsKey($sTableId)); + // use the default values provided by the data model + return null; + } return $oSettings; } diff --git a/tests/php-unit-tests/unitary-tests/application/utilsTest.php b/tests/php-unit-tests/unitary-tests/application/utilsTest.php index c0ca39975..5d357e846 100644 --- a/tests/php-unit-tests/unitary-tests/application/utilsTest.php +++ b/tests/php-unit-tests/unitary-tests/application/utilsTest.php @@ -23,6 +23,7 @@ namespace Combodo\iTop\Test\UnitTest\Application; use Combodo\iTop\Test\UnitTest\ItopTestCase; +use CoreException; use ormDocument; use utils; @@ -1043,4 +1044,21 @@ INI; unlink($sTmpFileOutsideItop); } + + public function testUnserialize() + { + // data to unserialize containing an object + $sData = 'a:2:{s:6:"string";s:9:"My string";s:6:"object";O:8:"DateTime":3:{s:4:"date";s:26:"2026-04-13 09:09:23.033175";s:13:"timezone_type";i:3;s:8:"timezone";s:16:"Europe/Amsterdam";}}'; + + // allow the DateTime object (no exception triggered) + utils::Unserialize($sData, ['allowed_classes' => ['DateTime']]); + + // flag to avoid throwing an exception + utils::Unserialize($sData, ['allowed_classes' => false], false); + + // flag to require throwing an exception + $this->expectException(CoreException::class); + utils::Unserialize($sData); + + } } From 68d14c4de6f96a828f61c31c95a202a99a28de95 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Mon, 13 Apr 2026 16:28:56 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=20N=C2=B09468=20-=20Fix=20double=20scroll?= =?UTF-8?q?=20down=20bars=20in=20input=20set=20(#876)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/input/_input-select.scss | 3 +- js/extkeywidget.js | 57 +++++++++++++++---- js/selectize/plugin_combodo_auto_position.js | 46 ++++++++++----- .../components/input/set/layout.ready.js.twig | 2 +- 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/css/backoffice/components/input/_input-select.scss b/css/backoffice/components/input/_input-select.scss index 0a5be44f3..d42ac2150 100644 --- a/css/backoffice/components/input/_input-select.scss +++ b/css/backoffice/components/input/_input-select.scss @@ -201,8 +201,9 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60 } // N°7982 Default selectize stylesheet override +// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar .selectize-dropdown-content{ - max-height: $ibo-input-select-selectize--dropdown--max-height; + max-height: calc(#{$ibo-input-select-selectize--dropdown--max-height} - 4px); } .selectize-dropdown.ui-menu .ui-state-active { diff --git a/js/extkeywidget.js b/js/extkeywidget.js index ab8c3f5fe..57960698e 100644 --- a/js/extkeywidget.js +++ b/js/extkeywidget.js @@ -120,6 +120,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper this.sFormAttCode = sFormAttCode; var me = this; + const iDropdownContentHeightDifference = 4; this.Init = function () { // make sure that the form is clean @@ -171,7 +172,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper // To avoid dropdown to be cut by the container's overflow hidden rule dropdownParent: 'body', onDropdownOpen: function (oDropdownElem) { - me.UpdateDropdownPosition(this.$control, oDropdownElem); + me.UpdateDropdownPosition(this.$control, oDropdownElem, this.$dropdown_content); }, }); let $selectize = $select[0].selectize; // This stores the selectize object to a variable (with name 'selectize') @@ -314,13 +315,14 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper }; /** - * Update the dropdown's position so it always fits in the screen - * - * @param {object} oControlElem jQuery object representing the "control" input (= where the user types) of the external key - * @param {object} oDropdownElem jQuery object representing the results dropdown - * @return {void} - */ - this.UpdateDropdownPosition = function (oControlElem, oDropdownElem) { + * Update the dropdown's position so it always fits in the screen + * + * @param {object} oControlElem jQuery object representing the "control" input (= where the user types) of the external key + * @param {object} oDropdownElem jQuery object representing the results dropdown + * @param {object|undefined} oDropdownContentElem + * @return {void} + */ + this.UpdateDropdownPosition = function (oControlElem, oDropdownElem, oDropdownContentElem) { // First fix width to ensure it's not too long const fControlWidth = oControlElem.outerWidth(); oDropdownElem.css('width', fControlWidth); @@ -328,6 +330,13 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper // Then, fix height / position to ensure it's within the viewport const fWindowHeight = window.innerHeight; + // Clear previously set rule so the comparison is done with dropdown real height + oDropdownElem.css('max-height', ''); + + if(oDropdownContentElem) { + oDropdownContentElem.css('max-height', ''); + } + const fControlTopY = oControlElem.offset().top; const fControlHeight = oControlElem.outerHeight(); @@ -338,14 +347,38 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper if (fDropdownBottomY > fWindowHeight) { // Set dropdown max-height to 1/3 of the screen, this way we are sure the dropdown will fit in either the top / bottom half of the screen - oDropdownElem.css('max-height', '30vh'); + oDropdownElem.css({ + maxHeight: '30vh', + }); fDropdownHeight = oDropdownElem.outerHeight(); - // Position dropdown above input if not enough space on the bottom part of the screen + // N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar + if(oDropdownContentElem) { + oDropdownContentElem.css('max-height', `calc(30vh - ${iDropdownContentHeightDifference}px)`); + } + + /* Position dropdown above input if not enough space on the bottom part of the screen + Doesn't seem to work with selectize as an internal plugin "auto_position" refreshes the top position after + this method is called, input set use a custom plugin to avoid fix this issue "plugin_combodo_auto_position" + This would need to take the potential 4px difference (iDropdownContentHeightDifference) into account if this is fixed. + */ if ((fDropdownTopY / fWindowHeight) > 0.6) { - oDropdownElem.css('top', fDropdownTopY - fDropdownHeight - fControlHeight); - } + oDropdownElem.css({ + top: fDropdownTopY - fDropdownHeight - fControlHeight, + borderTop: oDropdownElem.css('border-bottom') + }); + } + else { + oDropdownElem.css({ + borderTop: 'none' + }) + } } + else { + oDropdownElem.css({ + borderTop: 'none' + }) + } }; this.ManageScroll = function () { if ($('#label_'+me.id).scrollParent()[0].tagName != 'HTML') { diff --git a/js/selectize/plugin_combodo_auto_position.js b/js/selectize/plugin_combodo_auto_position.js index 7ec695c14..74d073046 100644 --- a/js/selectize/plugin_combodo_auto_position.js +++ b/js/selectize/plugin_combodo_auto_position.js @@ -19,10 +19,11 @@ Selectize.define("combodo_auto_position", function (aOptions) { // Selectize instance let oSelf = this; + const iDropdownContentHeightDifference = 4; // Plugin options aOptions = $.extend({ - maxDropDownHeight: 200, + maxDropDownHeight: '200px', }, aOptions ); @@ -33,28 +34,47 @@ Selectize.define("combodo_auto_position", function (aOptions) { // Override position dropdown function oSelf.positionDropdown = (function () { return function () { - let iRefHeight = oSelf.$dropdown.outerHeight() < aOptions.maxDropDownHeight ? - oSelf.$dropdown.outerHeight() : aOptions.maxDropDownHeight; + // Clear previously set rules so the comparison is done with dropdown real height + oSelf.$dropdown.css({ + 'max-height': '', + }); - if(oSelf.$control.offset().top + oSelf.$control.outerHeight() + iRefHeight > window.innerHeight){ + oSelf.$dropdown_content.css({ + 'max-height': '', + }); - oSelf.$dropdown.css({ - top: oSelf.$control.offset().top - iRefHeight, - left: oSelf.$control.offset().left, + let iDropdownHeight = oSelf.$dropdown.outerHeight(); + if(oSelf.$control.offset().top + oSelf.$control.outerHeight() + iDropdownHeight > window.innerHeight){ + + // Apply max-height as we are overflowing, that'll allow us to calculate where we should place ourselves later + oSelf.$dropdown.css({ + maxHeight: `${aOptions.maxDropDownHeight}`, + }) + + iDropdownHeight = oSelf.$dropdown.outerHeight(); + + oSelf.$dropdown.css({ + top: oSelf.$control.offset().top - iDropdownHeight + iDropdownContentHeightDifference, // Content will be shorter, so our real height too + left: oSelf.$control.offset().left, width: oSelf.$wrapper.outerWidth(), - 'max-height': `${aOptions.maxDropDownHeight}px`, - 'overflow-y': 'auto', - 'border-top': '1px solid #d0d0d0', + overflowY: 'auto', + borderTop : oSelf.$dropdown.css('border-bottom') }); + + // N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar + oSelf.$dropdown_content.css({ + 'max-height': `calc(${aOptions.maxDropDownHeight} - ${iDropdownContentHeightDifference}px)` + }); + } else{ oSelf.$dropdown.css({ top: oSelf.$control.offset().top + oSelf.$control.outerHeight(), left: oSelf.$control.offset().left, width: oSelf.$wrapper.outerWidth(), - 'max-height': `${aOptions.maxDropDownHeight}px`, - 'overflow-y': 'auto' - }); + overflowY: 'auto', + borderTop: 'none' + }); } }; }()); diff --git a/templates/base/components/input/set/layout.ready.js.twig b/templates/base/components/input/set/layout.ready.js.twig index 60c132bb3..ea07bbc34 100644 --- a/templates/base/components/input/set/layout.ready.js.twig +++ b/templates/base/components/input/set/layout.ready.js.twig @@ -23,7 +23,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ }, {# PLUGIN combodo auto position #} 'combodo_auto_position' : { - maxDropDownHeight: 300, {# in px #} + maxDropDownHeight: '30vh', {# same value as external key widget #} }, {# PLUGIN combodo add button #} {% if oUIBlock.HasAddOptionButton() %} From ab1290dfd0313c821f05220f5682a88f63909995 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Mon, 13 Apr 2026 21:14:03 +0200 Subject: [PATCH 4/6] =?UTF-8?q?N=C2=B08766=20-=20Fix=20user's=20login=20di?= =?UTF-8?q?splayed=20in=20backoffice=20log=20entry=20instead=20of=20user's?= =?UTF-8?q?=20contact=20friendlyname=20when=20user=20is=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/userrights.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index bb930630f..86b6898de 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -1157,7 +1157,7 @@ class UserRights return self::$m_oUser->GetKey(); } else { // find the id out of the login string - $oUser = self::FindUser($sLogin); + $oUser = self::FindUser($sLogin, bAllowDisabledUsers: true); if (is_null($oUser)) { return null; } From af01ff9e62673458695cba921a64bde288a3b780 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com> Date: Tue, 14 Apr 2026 08:02:00 +0200 Subject: [PATCH 5/6] =?UTF-8?q?N=C2=B09121=20-=20CSV=20Import=20:=20The=20?= =?UTF-8?q?advanced=20mode=20option=20no=20longer=20works=20as=20bef?= =?UTF-8?q?=E2=80=A6=20(#863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/composer/autoload_classmap.php | 1 + lib/composer/autoload_static.php | 1 + pages/ajax.csvimport.php | 10 ++ pages/csvimport.php | 113 ++++++-------------- sources/Application/Helper/ImportHelper.php | 65 +++++++++++ 5 files changed, 107 insertions(+), 83 deletions(-) create mode 100644 sources/Application/Helper/ImportHelper.php diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 4755653f8..a7180508e 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -193,6 +193,7 @@ return array( 'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php', 'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php', 'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php', + 'Combodo\\iTop\\Application\\Helper\\ImportHelper' => $baseDir . '/sources/Application/Helper/ImportHelper.php', 'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php', 'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php', 'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 64da644bb..aea2e9b27 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -548,6 +548,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php', 'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php', 'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php', + 'Combodo\\iTop\\Application\\Helper\\ImportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ImportHelper.php', 'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php', 'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php', 'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php', diff --git a/pages/ajax.csvimport.php b/pages/ajax.csvimport.php index 2622652e0..a89c9a253 100644 --- a/pages/ajax.csvimport.php +++ b/pages/ajax.csvimport.php @@ -5,9 +5,11 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ +use Combodo\iTop\Application\Helper\ImportHelper; use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Input\Select\Select; use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\TextArea; @@ -387,6 +389,14 @@ EOF } break; + case 'display_classes_select': + $oPage = new AjaxPage(""); + $sClassName = utils::ReadPostedParam('class_name', '', utils::ENUM_SANITIZATION_FILTER_CLASS); + $bAdvanced = utils::ReadPostedParam('advanced', 'false'); + $oClassesSelect = ImportHelper::GetClassesSelectUIBlock('class_name', $sClassName, UR_ACTION_BULK_MODIFY, $bAdvanced === 'true'); + $oPage->AddSubBlock($oClassesSelect); + break; + case 'get_csv_template': $sClassName = utils::ReadParam('class_name'); $sFormat = utils::ReadParam('format', 'csv'); diff --git a/pages/csvimport.php b/pages/csvimport.php index 926e3f4c0..da117ec53 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -5,6 +5,7 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ +use Combodo\iTop\Application\Helper\ImportHelper; use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSectionUIBlockFactory; @@ -14,7 +15,6 @@ use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Html\Html; use Combodo\iTop\Application\UI\Base\Component\Input\FileSelect\FileSelectUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Component\Input\Select\Select; use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\TextArea; @@ -30,7 +30,6 @@ use Combodo\iTop\Application\WebPage\AjaxPage; use Combodo\iTop\Application\WebPage\ErrorPage; use Combodo\iTop\Application\WebPage\iTopWebPage; use Combodo\iTop\Application\WebPage\WebPage; -use Combodo\iTop\Renderer\BlockRenderer; use Combodo\iTop\Service\Import\CSVImportPageProcessor; try { @@ -52,67 +51,6 @@ try { $oPage = new iTopWebPage(Dict::S('UI:Title:BulkImport')); $oPage->SetBreadCrumbEntry('ui-tool-bulkimport', Dict::S('Menu:CSVImportMenu'), Dict::S('UI:Title:BulkImport+'), '', 'fas fa-file-import', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES); - /** - * Helper function to build a select from the list of valid classes for a given action - * - * @deprecated 3.0.0 use GetClassesSelectUIBlock - * - * @param $sDefaultValue - * @param integer $iWidthPx The width (in pixels) of the drop-down list - * @param integer $iActionCode The ActionCode (from UserRights) to check for authorization for the classes - * - * @param string $sName The name of the select in the HTML form - * - * @return string The HTML fragment corresponding to the select tag - */ - function GetClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null) - { - DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetClassesSelectUIBlock'); - $oSelectBlock = GetClassesSelectUIBlock($sName, $sDefaultValue, $iActionCode); - - return BlockRenderer::RenderBlockTemplates($oSelectBlock); - } - - /** - * Helper function to build a select from the list of valid classes for a given action - * - * @param string $sName The name of the select in the HTML form - * @param $sDefaultValue - * @param integer $iWidthPx The width (in pixels) of the drop-down list - * @param integer $iActionCode The ActionCode (from UserRights) to check for authorization for the classes - * - * @return \Combodo\iTop\Application\UI\Base\Component\Input\Select\ - */ - function GetClassesSelectUIBlock(string $sName, $sDefaultValue, int $iActionCode, bool $bAdvanced = false): Select - { - $oSelectBlock = SelectUIBlockFactory::MakeForSelect($sName, 'select_'.$sName); - $oOption = SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:CSVImport:ClassesSelectOne'), false); - $oSelectBlock->AddSubBlock($oOption); - $aValidClasses = []; - $aClassCategories = ['bizmodel', 'addon/authentication']; - if ($bAdvanced) { - $aClassCategories[] = 'grant_by_profile'; - } - if (UserRights::IsAdministrator()) { - $aClassCategories[] = 'application'; - } - foreach ($aClassCategories as $sClassCategory) { - foreach (MetaModel::GetClasses($sClassCategory) as $sClassName) { - if ((is_null($iActionCode) || UserRights::IsActionAllowed($sClassName, $iActionCode)) && - (!MetaModel::IsAbstract($sClassName))) { - $sDisplayName = ($bAdvanced) ? MetaModel::GetName($sClassName)." ($sClassName)" : MetaModel::GetName($sClassName); - $aValidClasses[$sDisplayName] = SelectOptionUIBlockFactory::MakeForSelectOption($sClassName, $sDisplayName, ($sClassName == $sDefaultValue)); - } - } - } - ksort($aValidClasses); - foreach ($aValidClasses as $sValue => $oBlock) { - $oSelectBlock->AddSubBlock($oBlock); - } - - return $oSelectBlock; - } - /** * Helper to 'check' an input in an HTML form if the current value equals the value given * @@ -351,7 +289,7 @@ try { $oClassesSelect->AddSubBlock($oDefaultSelect); $aSynchroUpdate = utils::ReadParam('synchro_update', []); } else { - $oClassesSelect = GetClassesSelectUIBlock('class_name', $sClassName, UR_ACTION_BULK_MODIFY, (bool)$bAdvanced); + $oClassesSelect = ImportHelper::GetClassesSelectUIBlock('class_name', $sClassName, UR_ACTION_BULK_MODIFY, (bool)$bAdvanced); } $oPanel = TitleUIBlockFactory::MakeForPage(Dict::S('UI:Title:CSVImportStep3')); $oPage->AddSubBlock($oPanel); @@ -375,11 +313,9 @@ try { $oAdvancedMode->GetInput()->SetIsChecked(($bAdvanced == 1)); $oAdvancedMode->SetBeforeInput(false); $oAdvancedMode->GetInput()->AddCSSClass('ibo-input-checkbox'); + $oAdvancedMode->SetDescription(utils::EscapeHtml(Dict::S('UI:CSVImport:AdvancedMode+'))); $oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oAdvancedMode)); - $oDivAdvancedHelp = UIContentBlockUIBlockFactory::MakeStandard("advanced_help")->AddCSSClass('ibo-is-hidden'); - $oForm->AddSubBlock($oDivAdvancedHelp); - $oDivMapping = UIContentBlockUIBlockFactory::MakeStandard("mapping")->AddCSSClass('mt-5'); $oMessage = AlertUIBlockFactory::MakeForInformation(Dict::S('UI:CSVImport:SelectAClassFirst'))->SetIsClosable(false)->SetIsCollapsible(false); $oDivMapping->AddSubBlock($oMessage); @@ -416,7 +352,7 @@ try { $oPage->add_ready_script( <<add_script( - <<AddTab('tabsTemplate', Dict::S('UI:CSVImport:Tab:Templates')); - $oFieldTemplate = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:CSVImport:PickClassForTemplate'), GetClassesSelectUIBlock('template_class', '', UR_ACTION_BULK_MODIFY)); + $oFieldTemplate = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:CSVImport:PickClassForTemplate'), ImportHelper::GetClassesSelectUIBlock('template_class', '', UR_ACTION_BULK_MODIFY)); $oTabTemplate->AddSubBlock($oFieldTemplate); $oDivTemplate = UIContentBlockUIBlockFactory::MakeStandard("template")->AddCSSClass("ibo-is-visible"); $oTabTemplate->AddSubBlock($oDivTemplate); diff --git a/sources/Application/Helper/ImportHelper.php b/sources/Application/Helper/ImportHelper.php new file mode 100644 index 000000000..c6ffd43df --- /dev/null +++ b/sources/Application/Helper/ImportHelper.php @@ -0,0 +1,65 @@ +AddSubBlock($oOption); + $aValidClasses = []; + $aClassCategories = ['bizmodel', 'addon/authentication']; + if ($bAdvanced) { + $aClassCategories[] = 'grant_by_profile'; + } + if (UserRights::IsAdministrator()) { + $aClassCategories[] = 'application'; + } + foreach ($aClassCategories as $sClassCategory) { + foreach (MetaModel::GetClasses($sClassCategory) as $sClassName) { + if ((is_null($iActionCode) || UserRights::IsActionAllowed($sClassName, $iActionCode)) && + (!MetaModel::IsAbstract($sClassName))) { + $sDisplayName = ($bAdvanced) ? MetaModel::GetName($sClassName)." ($sClassName)" : MetaModel::GetName($sClassName); + $aValidClasses[$sDisplayName] = SelectOptionUIBlockFactory::MakeForSelectOption($sClassName, $sDisplayName, ($sClassName == $sDefaultValue)); + } + } + } + ksort($aValidClasses); + foreach ($aValidClasses as $sValue => $oBlock) { + $oSelectBlock->AddSubBlock($oBlock); + } + + return $oSelectBlock; + } +} From 7201bef8db633e1702c2191a01d97b22b2566f9c Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com> Date: Tue, 14 Apr 2026 08:08:17 +0200 Subject: [PATCH 6/6] =?UTF-8?q?N=C2=B08638=20-=20Adapt=20mysqldump=20calls?= =?UTF-8?q?=20to=20follow=20iTop=20SSL=20configuration=20(#883)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/cmdbsource.class.inc.php | 2 ++ setup/backup.class.inc.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 4ea759ae9..fa62040cf 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -1592,6 +1592,8 @@ class CMDBSource if (static::GetDBVendor() === static::ENUM_DB_VENDOR_MYSQL) { //Mysql 5.7.0 and upper deprecated --ssl and uses --ssl-mode instead return version_compare(static::GetDBVersion(), '5.7.11', '>='); + } elseif (static::GetDBVendor() === static::ENUM_DB_VENDOR_MARIADB) { + return version_compare(static::GetDBVersion(), '10.2.6', '>='); } return false; } diff --git a/setup/backup.class.inc.php b/setup/backup.class.inc.php index f1609791b..c02c27ef6 100644 --- a/setup/backup.class.inc.php +++ b/setup/backup.class.inc.php @@ -511,7 +511,7 @@ EOF; { $bDbTlsEnabled = $oConfig->Get('db_tls.enabled'); if (!$bDbTlsEnabled) { - return ''; + return CMDBSource::IsSslModeDBVersion() ? ' --skip-ssl' : ''; } $sTlsOptions = ''; // Mysql 5.7.11 and upper deprecated --ssl and uses --ssl-mode instead