N°5909 - Fix iApplicationExtension not called when attachment is added

This commit is contained in:
Eric Espie
2023-06-21 14:39:04 +02:00
parent 3d3a751072
commit 99a4e5e861
5 changed files with 211 additions and 47 deletions

View File

@@ -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

View File

@@ -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;
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,83 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class ApplicationObjectExtensionTest extends \Combodo\iTop\Test\UnitTest\ItopDataTestCase
{
const CREATE_TEST_ORG = true;
// Count the calls by name
private static array $aCalls = [];
private static int $iCalls = 0;
protected function setUp(): void
{
parent::setUp();
require_once 'iApplicationObjectExtension/ObjectModifyExtension.php';
// Add ObjectModifyExtension to the plugin list
$this->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);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Test object for AbstractApplicationObjectExtension API
*/
class ObjectModifyExtension extends AbstractApplicationObjectExtension
{
private static $iCountModify;
private static $bAlwaysChanged;
private static $sClass;
private static $sAttCodeToModify;
private static $callBack;
public function __construct()
{
}
public static function SetCallBack($callBack)
{
self::$callBack = $callBack;
}
public static function SetModifications($sClass, $sAttCodeToModify, $iCountModify)
{
self::$sClass = $sClass;
self::$sAttCodeToModify = $sAttCodeToModify;
if (!MetaModel::IsValidClass($sClass) || !MetaModel::IsValidAttCode($sClass, $sAttCodeToModify)) {
throw new Exception("Invalid class $sClass or attcode $sAttCodeToModify");
}
self::$iCountModify = $iCountModify;
}
public static function SetAlwaysChanged($bAlwaysChanged)
{
self::$bAlwaysChanged = $bAlwaysChanged;
}
public function OnDBUpdate($oObject, $oChange = null)
{
if (get_class($oObject) !== self::$sClass) {
return;
}
if (self::$iCountModify > 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;
}
}