From 8056a63e82650a68fdc48def8df0b671c81e781a Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Tue, 28 Oct 2025 09:34:44 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=20N=C2=B08748=20-=20Implement=20horizontal?= =?UTF-8?q?=20scroll=20for=20LongText=20attributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/backoffice/components/_field.scss | 5 +++-- css/backoffice/components/datatable/_datatable.scss | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/css/backoffice/components/_field.scss b/css/backoffice/components/_field.scss index e1fa43b58..6bc1d458f 100644 --- a/css/backoffice/components/_field.scss +++ b/css/backoffice/components/_field.scss @@ -81,9 +81,10 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default; } } - /*N°6543 - We need the rule to keep text inside the column when width is defined */ + /* N°6543 - We need the rule to keep text inside the column when width is defined */ &[data-attribute-type="AttributeHTML"], - &[data-attribute-type="AttributeText"] { + &[data-attribute-type="AttributeText"], + &[data-attribute-type="AttributeLongText"] { &[data-attribute-flag-read-only="true"] { display: grid; diff --git a/css/backoffice/components/datatable/_datatable.scss b/css/backoffice/components/datatable/_datatable.scss index c7fe7f27f..4d77bff61 100644 --- a/css/backoffice/components/datatable/_datatable.scss +++ b/css/backoffice/components/datatable/_datatable.scss @@ -125,7 +125,8 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default; } /* N°6543 - We need the rule to keep text inside the column when width is defined */ > [data-attribute-type="AttributeHTML"], - > [data-attribute-type="AttributeText"] { + > [data-attribute-type="AttributeText"], + > [data-attribute-type="AttributeLongText"] { max-width: 100%; overflow: auto; } From 5df936c587b094765c583aa2276303b09e75e398 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Tue, 28 Oct 2025 15:24:30 +0100 Subject: [PATCH 2/4] =?UTF-8?q?N=C2=B07982=20-=20Allow=20select=20drop-dow?= =?UTF-8?q?n=20height=20to=20be=20customized=20through=20SCSS=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/backoffice/components/input/_input-select.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/css/backoffice/components/input/_input-select.scss b/css/backoffice/components/input/_input-select.scss index 4fd9ad9f8..0a5be44f3 100644 --- a/css/backoffice/components/input/_input-select.scss +++ b/css/backoffice/components/input/_input-select.scss @@ -200,6 +200,11 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60 overflow-y: auto; } +// N°7982 Default selectize stylesheet override +.selectize-dropdown-content{ + max-height: $ibo-input-select-selectize--dropdown--max-height; +} + .selectize-dropdown.ui-menu .ui-state-active { margin: unset; background-color: $ibo-input-select-selectize--item--active--background-color; From adfa8000631ea386dfb90f77d09b6392eb8e8ca0 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Thu, 30 Oct 2025 16:38:38 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=20N=C2=B07939=20-=20Improve=20activity=20p?= =?UTF-8?q?anel=20tab=20togglers=20when=20there's=20more=20than=202=20log?= =?UTF-8?q?=20attributes=20(#766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity-panel/_activity-panel.scss | 43 ++++++++++++------- js/layouts/activity-panel/activity-panel.js | 10 ++++- .../layouts/activity-panel/layout.html.twig | 6 ++- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/css/backoffice/layout/activity-panel/_activity-panel.scss b/css/backoffice/layout/activity-panel/_activity-panel.scss index 408ef4a66..96cc578a0 100644 --- a/css/backoffice/layout/activity-panel/_activity-panel.scss +++ b/css/backoffice/layout/activity-panel/_activity-panel.scss @@ -15,9 +15,10 @@ $ibo-activity-panel--padding-y: $ibo-spacing-0 !default; /* - Header */ $ibo-activity-panel--header--background-color: $ibo-color-grey-100 !default; -$ibo-activity-panel--togglers--color: $ibo-color-grey-600 !default; -$ibo-activity-panel--togglers--on-hover--color: $ibo-color-grey-800 !default; -$ibo-activity-panel--togglers--elements-spacing: 0.75rem !default; +$ibo-activity-panel--actions--color: $ibo-color-grey-600 !default; +$ibo-activity-panel--actions--on-hover--color: $ibo-color-grey-800 !default; +$ibo-activity-panel--actions--elements-spacing: 0.75rem !default; +$ibo-activity-panel--actions-padding-right: $ibo-activity-panel--padding-x !default; /* - Tabs togglers*/ $ibo-activity-panel--tabs-togglers--padding-x: $ibo-activity-panel--padding-x * 3 !default; /* We need to increase this so the size toggler which will be set in abs. pos. can overlap it nicely */ @@ -151,35 +152,45 @@ $ibo-activity-panel--open-icon--margin-left: 0.75rem !default; background-color: $ibo-activity-panel--header--background-color; /* Remove hyperlinks default color */ - > .ibo-activity-panel--tabs-togglers a{ + .ibo-activity-panel--togglers a{ color: $ibo-activity-panel--tab-toolbar--text-color; } } -/* Size/display togglers */ +/* All toggles container */ .ibo-activity-panel--togglers { - position: absolute; - right: $ibo-activity-panel--padding-x; - top: 0; - bottom: 0; - @extend %ibo-fully-centered-content; - color: $ibo-activity-panel--togglers--color; + display: flex; + align-items: center; +} + +/* Size/display togglers */ +.ibo-activity-panel--actions { + display: flex; + align-items: center; + flex-grow: 0; + position: sticky; + padding-right: $ibo-activity-panel--actions-padding-right; + + background-color: $ibo-activity-panel--header--background-color; + color: $ibo-activity-panel--actions--color; &:hover { - color: $ibo-activity-panel--togglers--on-hover--color; + color: $ibo-activity-panel--actions--on-hover--color; } > *:not(:first-child) { - margin-left: $ibo-activity-panel--togglers--elements-spacing; + margin-left: $ibo-activity-panel--actions--elements-spacing; } } /* Tabs togglers */ .ibo-activity-panel--tabs-togglers{ - position: relative; /* For size toggler */ + display: flex; + align-items: center; + justify-content: safe center; + flex-grow: 1; padding-left: $ibo-activity-panel--tabs-togglers--padding-x; - padding-right: $ibo-activity-panel--tabs-togglers--padding-x; - @extend %ibo-fully-centered-content; + overflow-x: auto; } .ibo-activity-panel--tab-toggler{ &.ibo-is-active{ diff --git a/js/layouts/activity-panel/activity-panel.js b/js/layouts/activity-panel/activity-panel.js index f31d13880..90714f1bb 100644 --- a/js/layouts/activity-panel/activity-panel.js +++ b/js/layouts/activity-panel/activity-panel.js @@ -618,11 +618,17 @@ $(function() _InitializeCurrentTab : function(){ const sTabId = $.bbq.getState(this.element.attr('id'), true); if(sTabId !== undefined){ + let oTabTogglerElem = null; if(sTabId.startsWith("caselog-")){ - this._GetTabTogglerFromCaseLogAttCode(sTabId.replace("caselog-", "")).find(this.js_selectors.tab_title).trigger('click') + oTabTogglerElem = this._GetTabTogglerFromCaseLogAttCode(sTabId.replace("caselog-", "")).find(this.js_selectors.tab_title).trigger('click') } else if(sTabId === "activity"){ - this.element.find(this.js_selectors.tab_toggler + '[data-tab-type="activity"]').find(this.js_selectors.tab_title).trigger('click') + oTabTogglerElem = this.element.find(this.js_selectors.tab_toggler + '[data-tab-type="activity"]').find(this.js_selectors.tab_title).trigger('click') + } + + // Scroll to the tab toggler if found + if(oTabTogglerElem !== null){ + oTabTogglerElem[0].scrollIntoView(); } } }, diff --git a/templates/base/layouts/activity-panel/layout.html.twig b/templates/base/layouts/activity-panel/layout.html.twig index bd547f846..e8f12b14a 100644 --- a/templates/base/layouts/activity-panel/layout.html.twig +++ b/templates/base/layouts/activity-panel/layout.html.twig @@ -5,7 +5,8 @@ data-object-id="{{ oUIBlock.GetObjectId() }}" data-object-mode="{{ oUIBlock.GetObjectMode() }}">
-
+
+
{% for sCaseLogAttCode, aCaseLogData in oUIBlock.GetCaseLogTabs() %} {% set sExtraCSSClass = (loop.index == 1) ? 'ibo-is-active' : '' %}
{{ 'UI:Layout:ActivityPanel:Tab:Activity:Title'|dict_s }}
-
+
+
Date: Tue, 4 Nov 2025 15:25:53 +0100 Subject: [PATCH 4/4] =?UTF-8?q?N=C2=B04250=20-=20Problem=20with=20unencryp?= =?UTF-8?q?tion=20when=20the=20attribute=20is=20empty=20N=C2=B04058=20-=20?= =?UTF-8?q?Setup=20failed=20when=20added=20an=20encrypted=20field=20due=20?= =?UTF-8?q?to=20default=20value=20NULL=20non=20SODIUM=20compatible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/simplecrypt.class.inc.php | 21 +++++ .../unitary-tests/core/SympleCryptTest.php | 79 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/php-unit-tests/unitary-tests/core/SympleCryptTest.php diff --git a/core/simplecrypt.class.inc.php b/core/simplecrypt.class.inc.php index b49d0c2d1..91c90738d 100644 --- a/core/simplecrypt.class.inc.php +++ b/core/simplecrypt.class.inc.php @@ -118,6 +118,7 @@ class SimpleCrypt */ function Encrypt($key, $sString) { + return $this->oEngine->Encrypt($key,$sString); } @@ -130,6 +131,10 @@ class SimpleCrypt */ function Decrypt($key, $string) { + if (is_null($string) || strlen($string) == 0) { + IssueLog::Warning("Cannot decrypt empty/null value"); + return $string; + } return $this->oEngine->Decrypt($key,$string); } @@ -234,6 +239,10 @@ class SimpleCryptSimpleEngine implements CryptEngine public function Decrypt($key, $encrypted_data) { + if (is_null($encrypted_data) || strlen($encrypted_data) == 0) { + IssueLog::Warning("Cannot decrypt empty/null value"); + return $encrypted_data; + } $result = ''; for($i=1; $i<=strlen($encrypted_data); $i++) { @@ -330,6 +339,10 @@ class SimpleCryptSodiumEngine implements CryptEngine public function Decrypt($key, $encrypted_data) { + if (is_null($encrypted_data) || strlen($encrypted_data) == 0) { + IssueLog::Warning("Cannot decrypt empty/null value"); + return $encrypted_data; + } $key = hex2bin($key); $encrypted_data = base64_decode($encrypted_data); $nonce = mb_substr($encrypted_data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); @@ -362,6 +375,10 @@ class SimpleCryptOpenSSLEngine implements CryptEngine public function Decrypt($key, $encrypted_data) { + if (is_null($encrypted_data) || strlen($encrypted_data) == 0) { + IssueLog::Warning("Cannot decrypt empty/null value"); + return $encrypted_data; + } $key = hex2bin($key); $iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("AES-256-CBC"), '8bit'); $encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("AES-256-CBC"), null, '8bit'); @@ -411,6 +428,10 @@ class SimpleCryptOpenSSLMcryptCompatibilityEngine implements CryptEngine public function Decrypt($key, $encrypted_data) { + if (is_null($encrypted_data) || strlen($encrypted_data) == 0) { + IssueLog::Warning("Cannot decrypt empty/null value"); + return $encrypted_data; + } $key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key); $iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("BF-CBC"), '8bit'); $encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("BF-CBC"), null, '8bit'); diff --git a/tests/php-unit-tests/unitary-tests/core/SympleCryptTest.php b/tests/php-unit-tests/unitary-tests/core/SympleCryptTest.php new file mode 100644 index 000000000..4c39bbd06 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/core/SympleCryptTest.php @@ -0,0 +1,79 @@ +['SimpleCrypt'], + 'SimpleCryptSimpleEngine'=>['SimpleCryptSimpleEngine']]; + if(function_exists('sodium_crypto_secretbox_open')){ + $aClassProvider['SimpleCryptSodiumEngine'] = ['SimpleCryptSodiumEngine'] ; + } + if(function_exists('openssl_decrypt')){ + $aClassProvider['SimpleCryptOpenSSLEngine'] = ['SimpleCryptOpenSSLEngine']; + $aClassProvider['SimpleCryptOpenSSLMcryptCompatibilityEngine'] = ['SimpleCryptOpenSSLMcryptCompatibilityEngine']; + } + return$aClassProvider; + } + /** + * @param $sClass + * @dataProvider DecryptClassProvider + **/ + public function testDecryptWithNullValue($sClass) + { + $oSimpleCrypt = new $sClass(); + $this->assertEquals(null, $oSimpleCrypt->Decrypt("dd", null)); + } + + /** + * @param $sClass + * @dataProvider DecryptClassProvider + **/ + public function testDecryptWithEmptyValue($sClass) + { + $oSimpleCrypt = new $sClass(); + $this->assertEquals('', $oSimpleCrypt->Decrypt("dd", "")); + } + + public function DecryptClassWithNonDecryptableValueProvider() + { + $aClassProvider = ['SimpleCrypt'=>['SimpleCrypt', '** decryption error **'], + // 'SimpleCryptSimpleEngine'=>['SimpleCryptSimpleEngine', ' '] + ]; + if(function_exists('sodium_crypto_secretbox_open')){ + $aClassProvider['SimpleCryptSodiumEngine'] = ['SimpleCryptSodiumEngine', '', 'SodiumException'] ; + } + if(function_exists('openssl_decrypt')){ + $aClassProvider['SimpleCryptOpenSSLEngine'] = ['SimpleCryptOpenSSLEngine', '** decryption error **']; + $aClassProvider['SimpleCryptOpenSSLMcryptCompatibilityEngine'] = ['SimpleCryptOpenSSLMcryptCompatibilityEngine', '** decryption error **']; + } + return$aClassProvider; + } + /** + * @param $sClass + * @param $sExpectedValue + * @dataProvider DecryptClassWithNonDecryptableValueProvider + **/ + public function testDecrypWithNonDecryptableValue($sClass, $sExpectedValue = '', $sExpectedException = null) + { + if($sExpectedException !== null) { + $this->expectException($sExpectedException); + } + $oSimpleCrypt = new $sClass(); + $result=$oSimpleCrypt->Decrypt("dd", "gabuzomeuuofteod"); + $this->assertEquals($sExpectedValue, $result,''); + } + +}