From c8754725b076db3cee8125d18372909ef7efa598 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 29 Jan 2025 16:42:43 +0100 Subject: [PATCH 1/6] =?UTF-8?q?N=C2=B08143=20-=20Setup=20page=20in=20error?= =?UTF-8?q?=20in=20support.combodo.com?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sources/Application/TwigBase/UI/UIBlockExtension.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sources/Application/TwigBase/UI/UIBlockExtension.php b/sources/Application/TwigBase/UI/UIBlockExtension.php index b8d5f7fe4..91aaf79f3 100644 --- a/sources/Application/TwigBase/UI/UIBlockExtension.php +++ b/sources/Application/TwigBase/UI/UIBlockExtension.php @@ -30,7 +30,9 @@ class UIBlockExtension extends AbstractExtension $aFactoryClasses = InterfaceDiscovery::GetInstance()->FindItopClasses(iUIBlockFactory::class); foreach ($aFactoryClasses as $sFactoryClass) { - $aParsers[] = new UIBlockParser($sFactoryClass); + if (class_exists($sFactoryClass)){ + $aParsers[] = new UIBlockParser($sFactoryClass); + } } return $aParsers; From 7bbd4b47260cbaaadb1aab709fe7b2c2c7eef11f Mon Sep 17 00:00:00 2001 From: Anne-Cath Date: Thu, 30 Jan 2025 10:10:36 +0100 Subject: [PATCH 2/6] =?UTF-8?q?N=C2=B08144=20-=20Issue=20using=20Organizat?= =?UTF-8?q?ion=20filtering=20box?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UI/Base/Layout/NavigationMenu/NavigationMenu.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/Application/UI/Base/Layout/NavigationMenu/NavigationMenu.php b/sources/Application/UI/Base/Layout/NavigationMenu/NavigationMenu.php index 085641e88..7e9a39c17 100644 --- a/sources/Application/UI/Base/Layout/NavigationMenu/NavigationMenu.php +++ b/sources/Application/UI/Base/Layout/NavigationMenu/NavigationMenu.php @@ -388,7 +388,7 @@ class NavigationMenu extends UIBlock implements iKeyboardShortcut << Date: Thu, 30 Jan 2025 11:16:07 +0100 Subject: [PATCH 3/6] =?UTF-8?q?N=C2=B08143=20-=20Setup=20page=20in=20error?= =?UTF-8?q?=20in=20support.combodo.com?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TwigBase/UI/UIBlockExtension.php | 4 +- .../InterfaceDiscovery/InterfaceDiscovery.php | 23 ++++++++++-- .../InterfaceDiscoveryTest.php | 37 ++++++++++++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/sources/Application/TwigBase/UI/UIBlockExtension.php b/sources/Application/TwigBase/UI/UIBlockExtension.php index 91aaf79f3..b8d5f7fe4 100644 --- a/sources/Application/TwigBase/UI/UIBlockExtension.php +++ b/sources/Application/TwigBase/UI/UIBlockExtension.php @@ -30,9 +30,7 @@ class UIBlockExtension extends AbstractExtension $aFactoryClasses = InterfaceDiscovery::GetInstance()->FindItopClasses(iUIBlockFactory::class); foreach ($aFactoryClasses as $sFactoryClass) { - if (class_exists($sFactoryClass)){ - $aParsers[] = new UIBlockParser($sFactoryClass); - } + $aParsers[] = new UIBlockParser($sFactoryClass); } return $aParsers; diff --git a/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php b/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php index 2d46481b5..02299bfef 100644 --- a/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php +++ b/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php @@ -22,6 +22,7 @@ class InterfaceDiscovery private static InterfaceDiscovery $oInstance; private DataModelDependantCache $oCacheService; private ?array $aForcedClassMap = null; // For testing purposes + private bool $bCheckInterfaceImplementation = true; // false only for testing purposes const CACHE_NONE = 'CACHE_NONE'; const CACHE_DYNAMIC = 'CACHE_DYNAMIC'; // rebuild cache when files changes @@ -86,9 +87,15 @@ class InterfaceDiscovery continue; } - if ($this->IsInterfaceImplementation($sPHPClass, $sInterface)) { - $aMatchingClasses[] = $sPHPClass; + if ($this->bCheckInterfaceImplementation && ! $this->IsInterfaceImplementation($sPHPClass, $sInterface)) { + continue; } + + if (! class_exists($sPHPClass)){ + continue; + } + + $aMatchingClasses[] = $sPHPClass; } if ($this->GetCacheMode() !== self::CACHE_NONE) { @@ -241,7 +248,17 @@ class InterfaceDiscovery public function ReadClassesFromCache(string $sKey): array { - return $this->oCacheService->Fetch('InterfaceDiscovery', $sKey); + $aClasses = $this->oCacheService->Fetch('InterfaceDiscovery', $sKey); + + $aRealClasses = []; + foreach ($aClasses as $sPHPClass){ + if (! class_exists($sPHPClass)){ + continue; + } + + $aRealClasses[]=$sPHPClass; + } + return $aRealClasses; } protected function SaveClassesToCache(string $sKey, array $aMatchingClasses, array $aMoreInfo): void diff --git a/tests/php-unit-tests/unitary-tests/sources/Service/InterfaceDiscovery/InterfaceDiscoveryTest.php b/tests/php-unit-tests/unitary-tests/sources/Service/InterfaceDiscovery/InterfaceDiscoveryTest.php index 5f3689072..1a3f2aab1 100644 --- a/tests/php-unit-tests/unitary-tests/sources/Service/InterfaceDiscovery/InterfaceDiscoveryTest.php +++ b/tests/php-unit-tests/unitary-tests/sources/Service/InterfaceDiscovery/InterfaceDiscoveryTest.php @@ -26,6 +26,7 @@ class InterfaceDiscoveryTest extends ItopDataTestCase protected function tearDown(): void { + $this->SetNonPublicProperty($this->oInterfaceDiscovery, 'bCheckInterfaceImplementation', true); $this->SetNonPublicProperty(InterfaceDiscovery::GetInstance(), 'aForcedClassMap', null); $this->oCacheService->SetStorageRootDir(null); self::RecurseRmdir($this->sCacheRootDir); @@ -49,6 +50,40 @@ class InterfaceDiscoveryTest extends ItopDataTestCase ); } + /** + * @covers N°8143 - Setup page in error + */ + public function testShouldSelectTheRequestedItopClassesAndExcludeUnexistingOnes() + { + $this->SetNonPublicProperty($this->oInterfaceDiscovery, 'bCheckInterfaceImplementation', false); + $this->GivenClassMap([ + 'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory2' => APPROOT . '/sources/Application/UI/Base/Component/Alert/AlertUIBlockFactory.php', + 'Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory2' => APPROOT . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroupUIBlockFactory.php', + ]); + + $this->AssertArraysHaveSameItems([], $this->oInterfaceDiscovery->FindItopClasses(iUIBlockFactory::class)); + } + + /** + * @covers N°8143 - Setup page in error + */ + public function testReadClassesFromCache_ShouldExcludeUnexistingClasses() + { + $oCacheService = $this->createMock(DataModelDependantCache::class); + $aCachedRealClasses = [ + 'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory', + 'Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory2', + ]; + $oCacheService->expects($this->once()) + ->method('Fetch') + ->with('InterfaceDiscovery', '123') + ->willReturn($aCachedRealClasses); + + $this->oInterfaceDiscovery->SetCacheService($oCacheService); + + $this->AssertArraysHaveSameItems([ 'Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory'], $this->oInterfaceDiscovery->ReadClassesFromCache('123')); + } + public function testShouldExcludeAliases() { $this->GivenClassMap([ @@ -87,7 +122,7 @@ class InterfaceDiscoveryTest extends ItopDataTestCase $this->assertGreaterThan(0, count($this->oInterfaceDiscovery->FindItopClasses(iUIBlockFactory::class))); $this->AssertDirectoryListingEquals([ 'autoload_classmaps.php', - '1ab1e62be3e9984a8176deeb20f049b1_iUIBlockFactory.php' + '1ab1e62be3e9984a8176deeb20f049b1_iUIBlockFactory.php', ], $this->sCacheRootDir.'/InterfaceDiscovery'); } From 803ebfbe332de99ea445a5a58317328fc433227c Mon Sep 17 00:00:00 2001 From: XGUI Date: Thu, 30 Jan 2025 14:14:31 +0100 Subject: [PATCH 4/6] =?UTF-8?q?N=C2=B08129=20-=20Dont=20crash=20if=20date/?= =?UTF-8?q?time=20default=20value=20has=20a=20bad=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/attributedef.class.inc.php | 16 ++- .../src/BaseTestCase/ItopDataTestCase.php | 8 ++ .../src/BaseTestCase/ItopTestCase.php | 54 +++++++++ .../core/AttributeDefinitionTest.php | 104 +++++++++--------- 4 files changed, 129 insertions(+), 53 deletions(-) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 4c146e058..8b2915e1a 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -6433,9 +6433,21 @@ class AttributeDateTime extends AttributeDBField $sDefaultValue = $this->Get('default_value'); if (utils::IsNotNullOrEmptyString($sDefaultValue)) { try { - $oDate = new DateTimeImmutable($sDefaultValue); + $sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]); } catch (Exception $e) { - $oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([])); + try { + $sDefaultDate = Expression::FromOQL('"'.$sDefaultValue.'"')->Evaluate([]); + } catch (Exception $e) { + IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null"); + + return $this->GetNullValue(); + } + } + try { + $oDate = new DateTimeImmutable($sDefaultDate); + } catch (Exception $e) { + IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null"); + return $this->GetNullValue(); } return $oDate->format($this->GetInternalFormat()); } diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php index feff443ca..aa09aed74 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php @@ -987,6 +987,14 @@ abstract class ItopDataTestCase extends ItopTestCase } } + protected function AssertLastErrorLogEntryContains(string $sNeedle, string $sMessage = ''): void + { + $aLastLines = self::ReadTail(APPROOT.'/log/error.log'); + $this->assertStringContainsString($sNeedle, $aLastLines[0], $sMessage); + } + + + /** * Import a set of XML files describing a consistent set of iTop objects * @param string[] $aFiles diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php index 52cbe3436..af2daf509 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php @@ -550,6 +550,27 @@ abstract class ItopTestCase extends TestCase $this->AssertArraysHaveSameItems($aExpected, $aFiles, $sMessage); } + /** + * @since 3.2.1 + */ + static protected function AssertDateEqualsNow($sActualDate, $sMessage = ''): void + { + $oActualDate = \DateTime::createFromFormat(\AttributeDate::GetInternalFormat(), $sActualDate); + $oNow = new DateTime(); + $iTimeInterval = $oNow->diff($oActualDate)->s; + self::assertLessThan(2, $iTimeInterval, $sMessage); + } + /** + * @since 3.2.1 + */ + static protected function AssertDateTimeEqualsNow($sActualDate, $sMessage = ''): void + { + $oActualDateTime = \DateTime::createFromFormat(\AttributeDateTime::GetInternalFormat(), $sActualDate); + $oNow = new DateTime(); + $iTimeInterval = $oNow->diff($oActualDateTime)->s; + self::assertLessThan(2, $iTimeInterval, $sMessage); + } + /** * Control which Kernel will be loaded when invoking the bootKernel method * @@ -572,4 +593,37 @@ abstract class ItopTestCase extends TestCase } return parent::bootKernel($options); } + + /** + * @author Ain Tohvri + * + * @since 3.2.1 + */ + static protected function ReadTail($sFilename, $iLines = 1) + { + $handle = fopen($sFilename, "r"); + $iLineCounter = $iLines; + $iPos = -2; + $bBeginning = false; + $aLines = array(); + while ($iLineCounter > 0) { + $sChar = " "; + while ($sChar != "\n") { + if(fseek($handle, $iPos, SEEK_END) == -1) { + $bBeginning = true; + break; + } + $sChar = fgetc($handle); + $iPos --; + } + $iLineCounter --; + if ($bBeginning) { + rewind($handle); + } + $aLines[$iLines - $iLineCounter - 1] = fgets($handle); + if ($bBeginning) break; + } + fclose ($handle); + return array_reverse($aLines); + } } diff --git a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php index 5b25b455b..bfa979553 100644 --- a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php +++ b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php @@ -243,84 +243,76 @@ PHP public function testDateTimeEmptyDefaultReturnsNullAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, '', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then self::assertNull($defaultValue, 'Empty default value for DateTime attribute should give null default value'); } + public function testDateTimeInvalidDefaultReturnsNullAsDefaultValue() + { + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'zabugomeuh', false); + + $defaultValue = $oDateAttribute->GetDefaultValue(); + + self::assertNull($defaultValue, 'Invalid default value for DateTime attribute should give null default value'); + self::AssertLastErrorLogEntryContains("Invalid default value 'zabugomeuh' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); + } + public function testDateEmptyDefaultReturnsNullAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, '', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then self::assertNull($defaultValue, 'Empty default value for Date attribute should give null default value'); } - - public function testDateTimeNowAsDefaultGivesCurrentDateAsDefaultValue() + public function testDateInvalidDefaultReturnsNullAsDefaultValue_Case1() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', [ - 'sql' => 'start_date', - 'is_null_allowed' => false, - 'default_value' => 'NOW()', - 'allowed_values' => null, - 'depends_on' => [], - 'always_load_in_tables' => false - ]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'zabugomeuh', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); + self::AssertLastErrorLogEntryContains("Invalid default value 'zabugomeuh' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); - // Then - $sNow = date($oDateAttribute->GetInternalFormat()); - self::assertEquals($sNow, $defaultValue, 'Now as default value for DateTime attribute should give current date as default value'); + self::assertNull($defaultValue, 'Invalid default value for Date attribute should give null default value'); } - public function testDateNowAsDefaultGivesCurrentDateAsDefaultValue() + public function testDateInvalidDefaultReturnsNullAsDefaultValue_Case2() { - // Given - $oDateAttribute = new AttributeDate('start_date', [ - 'sql' => 'start_date', - 'is_null_allowed' => false, - 'default_value' => 'NOW()', - 'allowed_values' => null, - 'depends_on' => [], - 'always_load_in_tables' => false - ]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, '"27/01/2025"', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then - $sNow = date($oDateAttribute->GetInternalFormat()); - self::assertEquals($sNow, $defaultValue, 'Now as default value for Date attribute should give current date as default value'); + self::AssertLastErrorLogEntryContains("Invalid default value '\"27/01/2025\"' for field 'start_date' on class 'WorkOrder', defaulting to null", "Last error log entry should contain a meaningful message"); + self::assertNull($defaultValue, 'Invalid default value for Date attribute should give null default value'); + } + + public function testDateTimeNowAsDefaultGivesCurrentDateAsDefaultValue() + { + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'NOW()', false); + $sDefaultValue = $oDateAttribute->GetDefaultValue(); + + self::AssertDateTimeEqualsNow($sDefaultValue, 'NOW() should be evaluated as the current date and time'); + } + + public function testDateNowAsDefaultGivesCurrentDateAsDefaultValue() + { + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'NOW()', false); + + $defaultValue = $oDateAttribute->GetDefaultValue(); + + self::AssertDateEqualsNow($defaultValue, 'NOW() should be evaluated as the current date'); } public function testDateTimeIntervalAsDefaultGivesCorrectDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDateTime::class, 'DATE_ADD(NOW(), INTERVAL 1 DAY)', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then $oDate = new \DateTimeImmutable('+1day'); $sExpected = $oDate->format($oDateAttribute->GetInternalFormat()); self::assertEquals($sExpected, $defaultValue, 'Interval as default value for DateTime attribute should give correct date as default value'); @@ -328,17 +320,27 @@ PHP public function testDateIntervalAsDefaultGivesCorrectDateAsDefaultValue() { - // Given - $oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]); - $oDateAttribute->SetHostClass('WorkOrder'); + $oDateAttribute = $this->GivenAttribute(\WorkOrder::class, 'start_date', AttributeDate::class, 'DATE_ADD(NOW(), INTERVAL 1 DAY)', false); - //When $defaultValue = $oDateAttribute->GetDefaultValue(); - // Then $oDate = new \DateTimeImmutable('+1day'); $sExpected = $oDate->format($oDateAttribute->GetInternalFormat()); self::assertEquals($sExpected, $defaultValue, 'Interval as default value for Date attribute should give correct date as default value'); } + public function GivenAttribute(string $sHostClass, string $sAttCode, string $sAttributeClass, mixed $defaultValue, bool $bIsNullAllowed): \AttributeDefinition + { + $oAttribute = new $sAttributeClass($sAttCode, [ + 'sql' => $sAttCode, + 'is_null_allowed' => $bIsNullAllowed, + 'default_value' => $defaultValue, + 'allowed_values' => null, + 'depends_on' => [], + 'always_load_in_tables' => false + ]); + $oAttribute->SetHostClass($sHostClass); + return $oAttribute; + } + } \ No newline at end of file From 5851b2e1ff876401785deb65e21c6982442b94b6 Mon Sep 17 00:00:00 2001 From: XGUI Date: Thu, 30 Jan 2025 15:06:00 +0100 Subject: [PATCH 5/6] =?UTF-8?q?N=C2=B08129=20-=20Fix=20missing=20namespace?= =?UTF-8?q?=20backslash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php index af2daf509..0a07792e0 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php @@ -556,7 +556,7 @@ abstract class ItopTestCase extends TestCase static protected function AssertDateEqualsNow($sActualDate, $sMessage = ''): void { $oActualDate = \DateTime::createFromFormat(\AttributeDate::GetInternalFormat(), $sActualDate); - $oNow = new DateTime(); + $oNow = new \DateTime(); $iTimeInterval = $oNow->diff($oActualDate)->s; self::assertLessThan(2, $iTimeInterval, $sMessage); } @@ -566,7 +566,7 @@ abstract class ItopTestCase extends TestCase static protected function AssertDateTimeEqualsNow($sActualDate, $sMessage = ''): void { $oActualDateTime = \DateTime::createFromFormat(\AttributeDateTime::GetInternalFormat(), $sActualDate); - $oNow = new DateTime(); + $oNow = new \DateTime(); $iTimeInterval = $oNow->diff($oActualDateTime)->s; self::assertLessThan(2, $iTimeInterval, $sMessage); } From d5754fc568ef9ac6c82e330b1ed1258dbe792ac5 Mon Sep 17 00:00:00 2001 From: "denis.flaven@combodo.com" Date: Fri, 31 Jan 2025 17:04:56 +0100 Subject: [PATCH 6/6] =?UTF-8?q?N=C2=B08135=20-=20Bump=20datamodel=20versio?= =?UTF-8?q?n.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datamodels/2.x/version.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/version.xml b/datamodels/2.x/version.xml index 85fcece5e..b3a905e4b 100755 --- a/datamodels/2.x/version.xml +++ b/datamodels/2.x/version.xml @@ -1,4 +1,4 @@ - 2.7.11 + 2.7.12