diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 4d6788eb0..d75bf25a9 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -523,7 +523,6 @@ abstract class CMDBObject extends DBObject public function DBUpdate() { if (count($this->ListChanges()) === 0) { - $this->InitPreviousValuesForUpdatedAttributes(); return $this->GetKey(); } return parent::DBUpdate(); // TODO: Change the autogenerated stub diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 8d80ff618..7e3b8cdc8 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2927,52 +2927,7 @@ abstract class MetaModel } self::$m_sTablePrefix = $sTablePrefix; - - // Build the list of available extensions - // - $aInterfaces = [ - 'iApplicationUIExtension', - 'iPreferencesExtension', - 'iApplicationObjectExtension', - 'iLoginFSMExtension', - 'iLoginUIExtension', - 'iLogoutExtension', - 'iQueryModifier', - 'iOnClassInitialization', - 'iPopupMenuExtension', - 'iPageUIExtension', - 'iPageUIBlockExtension', - 'iBackofficeLinkedScriptsExtension', - 'iBackofficeEarlyScriptExtension', - 'iBackofficeScriptExtension', - 'iBackofficeInitScriptExtension', - 'iBackofficeReadyScriptExtension', - 'iBackofficeLinkedStylesheetsExtension', - 'iBackofficeStyleExtension', - 'iBackofficeDictEntriesExtension', - 'iBackofficeDictEntriesPrefixesExtension', - 'iPortalUIExtension', - 'ModuleHandlerApiInterface', - 'iNewsroomProvider', - 'iModuleExtension', - ]; - foreach($aInterfaces as $sInterface) - { - self::$m_aExtensionClassNames[$sInterface] = array(); - } - - foreach(get_declared_classes() as $sPHPClass) - { - $oRefClass = new ReflectionClass($sPHPClass); - $oExtensionInstance = null; - foreach($aInterfaces as $sInterface) - { - if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) - { - self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass; - } - } - } + self::InitExtensions(); // Initialize the classes (declared attributes, etc.) // @@ -7642,6 +7597,56 @@ abstract class MetaModel unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]); } } + + /** + * For test purpose + * @throws \ReflectionException + * @since 3.1.0 + */ + public static function InitExtensions() + { + // Build the list of available extensions + // + $aInterfaces = [ + 'iApplicationUIExtension', + 'iPreferencesExtension', + 'iApplicationObjectExtension', + 'iLoginFSMExtension', + 'iLoginUIExtension', + 'iLogoutExtension', + 'iQueryModifier', + 'iOnClassInitialization', + 'iPopupMenuExtension', + 'iPageUIExtension', + 'iPageUIBlockExtension', + 'iBackofficeLinkedScriptsExtension', + 'iBackofficeEarlyScriptExtension', + 'iBackofficeScriptExtension', + 'iBackofficeInitScriptExtension', + 'iBackofficeReadyScriptExtension', + 'iBackofficeLinkedStylesheetsExtension', + 'iBackofficeStyleExtension', + 'iBackofficeDictEntriesExtension', + 'iBackofficeDictEntriesPrefixesExtension', + 'iPortalUIExtension', + 'ModuleHandlerApiInterface', + 'iNewsroomProvider', + 'iModuleExtension', + ]; + foreach ($aInterfaces as $sInterface) { + self::$m_aExtensionClassNames[$sInterface] = array(); + } + + foreach (get_declared_classes() as $sPHPClass) { + $oRefClass = new ReflectionClass($sPHPClass); + $oExtensionInstance = null; + foreach ($aInterfaces as $sInterface) { + if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) { + self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass; + } + } + } + } } diff --git a/core/pluginmanager.class.inc.php b/core/pluginmanager.class.inc.php index 3e7c2c711..b6f7e5805 100644 --- a/core/pluginmanager.class.inc.php +++ b/core/pluginmanager.class.inc.php @@ -98,4 +98,14 @@ class PluginManager return $oInstance; } + + /** + * For test purpose + * @return void + * @since 3.1.0 + */ + protected static function ResetPlugins() + { + self::$m_aExtensionClasses = null; + } } diff --git a/tests/php-unit-tests/unitary-tests/application/ApplicationObjectExtensionTest.php b/tests/php-unit-tests/unitary-tests/application/ApplicationObjectExtensionTest.php new file mode 100644 index 000000000..292107f74 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/application/ApplicationObjectExtensionTest.php @@ -0,0 +1,83 @@ +InvokeNonPublicStaticMethod(MetaModel::class, 'InitExtensions', []); + // Instantiate the new object + $this->InvokeNonPublicStaticMethod(PluginManager::class, 'ResetPlugins', []); + ObjectModifyExtension::SetCallBack([ApplicationObjectExtensionTest::class, 'IncrementCallCount']); + } + + public function tearDown(): void + { + ObjectModifyExtension::SetModifications('Person', 'name', 0); + ObjectModifyExtension::SetAlwaysChanged(false); + ObjectModifyExtension::SetCallBack(null); + parent::tearDown(); + } + + public static function IncrementCallCount(string $sOrigin) + { + self::$aCalls[$sOrigin] = (self::$aCalls[$sOrigin] ?? 0) + 1; + self::$iCalls++; + } + + public static function ResetCallCount() + { + self::$aCalls = []; + self::$iCalls = 0; + } + + public function testUpdateReentranceProtection() + { + // Check that extension is called + $oPerson = $this->CreatePerson(1); + $oPerson->Set('first_name', 'testUpdateReentranceProtection'); + ObjectModifyExtension::SetModifications('Person', 'name', 1); + self::ResetCallCount(); + $oPerson->DBUpdate(); + $this->assertEquals(1, self::$iCalls); + + // Check that loop limit is 10 + $i = 15; + self::ResetCallCount(); + ObjectModifyExtension::SetModifications('Person', 'name', $i); + $oPerson->Set('first_name', 'testUpdateReentranceProtection'); + $oPerson->DBUpdate(); + $this->assertEquals(10, self::$iCalls); + } + + public function testModificationsLost() + { + self::ResetCallCount(); + $oPerson = $this->CreatePerson(1); + $oPerson->Set('first_name', 'testUpdateReentranceProtection'); + + ObjectModifyExtension::SetModifications('Person', 'name', 1); + ObjectModifyExtension::SetAlwaysChanged(true); + $oPerson->DBUpdate(); + $this->assertEquals(1, self::$iCalls); + } + +} \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/application/iApplicationObjectExtension/ObjectModifyExtension.php b/tests/php-unit-tests/unitary-tests/application/iApplicationObjectExtension/ObjectModifyExtension.php new file mode 100644 index 000000000..98764e8ba --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/application/iApplicationObjectExtension/ObjectModifyExtension.php @@ -0,0 +1,67 @@ + 0) { + if (!empty($oObject->ListPreviousValuesForUpdatedAttributes())) { + if (!is_null(self::$callBack)) { + call_user_func(self::$callBack, 'OnDBUpdate'); + } + self::$iCountModify--; + $oObject->Set(self::$sAttCodeToModify, 'Value_'.rand()); + $oObject->DBUpdate(); + } else { + return; + } + } + } + + public function OnIsModified($oObject) + { + return self::$bAlwaysChanged; + } +} \ No newline at end of file