mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-14 06:08:43 +02:00
Compare commits
1 Commits
support/3.
...
feature/86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f049a364bd |
@@ -1546,21 +1546,12 @@ class ShortcutMenuNode extends MenuNode
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sContext = $this->oShortcut->Get('context');
|
||||
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();
|
||||
$aContext = unserialize($sContext);
|
||||
if (isset($aContext['menu'])) {
|
||||
unset($aContext['menu']);
|
||||
}
|
||||
foreach ($aContext as $sArgName => $sArgValue) {
|
||||
$aExtraParams[$sArgName] = $sArgValue;
|
||||
}
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
@@ -3252,50 +3252,4 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4829,7 +4829,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
|
||||
if (strlen($sIndex) > 0) {
|
||||
$aIndex = utils::Unserialize($sIndex, ['allowed_classes' => false], false);
|
||||
$aIndex = unserialize($sIndex);
|
||||
$value = new ormCaseLog($sLog, $aIndex);
|
||||
} else {
|
||||
$value = new ormCaseLog($sLog);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1157,7 +1157,7 @@ class UserRights
|
||||
return self::$m_oUser->GetKey();
|
||||
} else {
|
||||
// find the id out of the login string
|
||||
$oUser = self::FindUser($sLogin, bAllowDisabledUsers: true);
|
||||
$oUser = self::FindUser($sLogin);
|
||||
if (is_null($oUser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -201,9 +201,8 @@ $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: calc(#{$ibo-input-select-selectize--dropdown--max-height} - 4px);
|
||||
max-height: $ibo-input-select-selectize--dropdown--max-height;
|
||||
}
|
||||
|
||||
.selectize-dropdown.ui-menu .ui-state-active {
|
||||
|
||||
@@ -120,7 +120,6 @@ 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
|
||||
@@ -172,7 +171,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, this.$dropdown_content);
|
||||
me.UpdateDropdownPosition(this.$control, oDropdownElem);
|
||||
},
|
||||
});
|
||||
let $selectize = $select[0].selectize; // This stores the selectize object to a variable (with name 'selectize')
|
||||
@@ -315,14 +314,13 @@ 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
|
||||
* @param {object|undefined} oDropdownContentElem
|
||||
* @return {void}
|
||||
*/
|
||||
this.UpdateDropdownPosition = function (oControlElem, oDropdownElem, oDropdownContentElem) {
|
||||
* 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) {
|
||||
// First fix width to ensure it's not too long
|
||||
const fControlWidth = oControlElem.outerWidth();
|
||||
oDropdownElem.css('width', fControlWidth);
|
||||
@@ -330,13 +328,6 @@ 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();
|
||||
|
||||
@@ -347,38 +338,14 @@ 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({
|
||||
maxHeight: '30vh',
|
||||
});
|
||||
oDropdownElem.css('max-height', '30vh');
|
||||
fDropdownHeight = oDropdownElem.outerHeight();
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// Position dropdown above input if not enough space on the bottom part of the screen
|
||||
if ((fDropdownTopY / fWindowHeight) > 0.6) {
|
||||
oDropdownElem.css({
|
||||
top: fDropdownTopY - fDropdownHeight - fControlHeight,
|
||||
borderTop: oDropdownElem.css('border-bottom')
|
||||
});
|
||||
}
|
||||
else {
|
||||
oDropdownElem.css({
|
||||
borderTop: 'none'
|
||||
})
|
||||
}
|
||||
oDropdownElem.css('top', fDropdownTopY - fDropdownHeight - fControlHeight);
|
||||
}
|
||||
}
|
||||
else {
|
||||
oDropdownElem.css({
|
||||
borderTop: 'none'
|
||||
})
|
||||
}
|
||||
};
|
||||
this.ManageScroll = function () {
|
||||
if ($('#label_'+me.id).scrollParent()[0].tagName != 'HTML') {
|
||||
|
||||
@@ -19,11 +19,10 @@ Selectize.define("combodo_auto_position", function (aOptions) {
|
||||
|
||||
// Selectize instance
|
||||
let oSelf = this;
|
||||
const iDropdownContentHeightDifference = 4;
|
||||
|
||||
// Plugin options
|
||||
aOptions = $.extend({
|
||||
maxDropDownHeight: '200px',
|
||||
maxDropDownHeight: 200,
|
||||
},
|
||||
aOptions
|
||||
);
|
||||
@@ -34,47 +33,28 @@ Selectize.define("combodo_auto_position", function (aOptions) {
|
||||
// Override position dropdown function
|
||||
oSelf.positionDropdown = (function () {
|
||||
return function () {
|
||||
// Clear previously set rules so the comparison is done with dropdown real height
|
||||
oSelf.$dropdown.css({
|
||||
'max-height': '',
|
||||
});
|
||||
let iRefHeight = oSelf.$dropdown.outerHeight() < aOptions.maxDropDownHeight ?
|
||||
oSelf.$dropdown.outerHeight() : aOptions.maxDropDownHeight;
|
||||
|
||||
oSelf.$dropdown_content.css({
|
||||
'max-height': '',
|
||||
});
|
||||
if(oSelf.$control.offset().top + oSelf.$control.outerHeight() + iRefHeight > window.innerHeight){
|
||||
|
||||
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,
|
||||
oSelf.$dropdown.css({
|
||||
top: oSelf.$control.offset().top - iRefHeight,
|
||||
left: oSelf.$control.offset().left,
|
||||
width: oSelf.$wrapper.outerWidth(),
|
||||
overflowY: 'auto',
|
||||
borderTop : oSelf.$dropdown.css('border-bottom')
|
||||
'max-height': `${aOptions.maxDropDownHeight}px`,
|
||||
'overflow-y': 'auto',
|
||||
'border-top': '1px solid #d0d0d0',
|
||||
});
|
||||
|
||||
// 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(),
|
||||
overflowY: 'auto',
|
||||
borderTop: 'none'
|
||||
});
|
||||
'max-height': `${aOptions.maxDropDownHeight}px`,
|
||||
'overflow-y': 'auto'
|
||||
});
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,13 +8,8 @@ use AttributeFriendlyName;
|
||||
use AttributeLinkedSet;
|
||||
use cmdbAbstract;
|
||||
use cmdbAbstractObject;
|
||||
use CoreException;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use LogChannels;
|
||||
use Metamodel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class DataTableSettings
|
||||
@@ -135,10 +130,7 @@ class DataTableSettings
|
||||
*/
|
||||
public function 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]);
|
||||
}
|
||||
$aData = unserialize($sData);
|
||||
$this->iDefaultPageSize = $aData['iDefaultPageSize'];
|
||||
$this->aColumns = $aData['aColumns'];
|
||||
foreach ($this->aClassAliases as $sAlias => $sClass) {
|
||||
@@ -277,19 +269,7 @@ class DataTableSettings
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
$oSettings->unserialize($pref);
|
||||
|
||||
return $oSettings;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({
|
||||
},
|
||||
{# PLUGIN combodo auto position #}
|
||||
'combodo_auto_position' : {
|
||||
maxDropDownHeight: '30vh', {# same value as external key widget #}
|
||||
maxDropDownHeight: 300, {# in px #}
|
||||
},
|
||||
{# PLUGIN combodo add button #}
|
||||
{% if oUIBlock.HasAddOptionButton() %}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use CoreException;
|
||||
use ormDocument;
|
||||
use utils;
|
||||
|
||||
@@ -1044,21 +1043,4 @@ 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user