mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
- stopper bug in case of reinstallation/upgrade: the file keyscache.tmp could be left in case of issue in the previous execution of the setup ; the file is now deleted prior to running a new session - if a fatal error (e.g. call to undefined function) occured during the load of a file (ajax), then we had no clue that this happened ; a log has been added after the load so that we can get faster to the issue - reordered data files (not the same order in MS-Explorer) to maximize ext key resolution on the first pass - resolving external keys afterwards should work again (though the file order is avoiding this currently), as the objects are declared "dirty" and no automatic reload will be attempted during further manipulations - added a log when an external key cannot be resolved during the final round SVN:trunk[191]
293 lines
8.2 KiB
PHP
293 lines
8.2 KiB
PHP
<?php
|
|
define ('KEYS_CACHE_FILE', '../keyscache.tmp');
|
|
/**
|
|
* Class to load sets of objects from XML files into the database
|
|
* XML files can be produced by the 'export' web service or by any other means
|
|
* Here is a simple example:
|
|
* $oLoader = new XMLDataLoader('../itop-config.php');
|
|
* $oLoader->StartSession();
|
|
* $oLoader->LoadFile('./organizations.xml');
|
|
* $oLoader->LoadFile('./locations.xml');
|
|
* $oLoader->EndSession();
|
|
*/
|
|
class XMLDataLoader
|
|
{
|
|
protected $m_aKeys;
|
|
protected $m_aObjectsCache;
|
|
protected $m_bSessionActive;
|
|
protected $m_oChange;
|
|
protected $m_sCacheFileName;
|
|
|
|
public function __construct($sConfigFileName)
|
|
{
|
|
$this->m_aKeys = array();
|
|
$this->m_aObjectsCache = array();
|
|
$this->m_oChange = null;
|
|
$this->m_sCacheFileName = dirname(__FILE__).'/'.KEYS_CACHE_FILE;
|
|
$this->InitDataModel($sConfigFileName);
|
|
$this->LoadKeysCache();
|
|
$this->m_bSessionActive = true;
|
|
|
|
}
|
|
|
|
public function StartSession($oChange)
|
|
{
|
|
// Do cleanup any existing cache file (shall not be necessary unless a setup was interrupted abruptely)
|
|
$this->ClearKeysCache();
|
|
|
|
$this->m_oChange = $oChange;
|
|
$this->m_bSessionActive = true;
|
|
}
|
|
|
|
public function EndSession()
|
|
{
|
|
$this->ResolveExternalKeys();
|
|
$this->m_bSessionActive = false;
|
|
}
|
|
|
|
public function __destruct()
|
|
{
|
|
// Stopping in the middle of a session, let's save the context information
|
|
if ($this->m_bSessionActive)
|
|
{
|
|
$this->SaveKeysCache();
|
|
}
|
|
else
|
|
{
|
|
$this->ClearKeysCache();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the ORM (MetaModel)
|
|
*/
|
|
protected function InitDataModel($sConfigFileName, $bAllowMissingDatabase = true)
|
|
{
|
|
require_once('../core/coreexception.class.inc.php');
|
|
require_once('../core/attributedef.class.inc.php');
|
|
require_once('../core/filterdef.class.inc.php');
|
|
require_once('../core/stimulus.class.inc.php');
|
|
require_once('../core/MyHelpers.class.inc.php');
|
|
require_once('../core/expression.class.inc.php');
|
|
require_once('../core/cmdbsource.class.inc.php');
|
|
require_once('../core/sqlquery.class.inc.php');
|
|
require_once('../core/dbobject.class.php');
|
|
require_once('../core/dbobjectsearch.class.php');
|
|
require_once('../core/dbobjectset.class.php');
|
|
require_once('../core/userrights.class.inc.php');
|
|
MetaModel::Startup($sConfigFileName, $bAllowMissingDatabase);
|
|
}
|
|
|
|
/**
|
|
* Stores the keys & object cache in a file
|
|
*/
|
|
protected function SaveKeysCache()
|
|
{
|
|
$hFile = @fopen($this->m_sCacheFileName, 'w');
|
|
if ($hFile !== false)
|
|
{
|
|
$sData = serialize( array('keys' => $this->m_aKeys,
|
|
'objects' => $this->m_aObjectsCache,
|
|
'change' => $this->m_oChange));
|
|
fwrite($hFile, $sData);
|
|
fclose($hFile);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Cannot write to file: '{$this->m_sCacheFileName}'");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the keys & object cache from the tmp file
|
|
*/
|
|
protected function LoadKeysCache()
|
|
{
|
|
$sFileContent = @file_get_contents($this->m_sCacheFileName);
|
|
if (!empty($sFileContent))
|
|
{
|
|
$aCache = unserialize($sFileContent);
|
|
$this->m_aKeys = $aCache['keys'];
|
|
$this->m_aObjectsCache = $aCache['objects'];
|
|
$this->m_oChange = $aCache['change'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the tmp file used to store the keys cache
|
|
*/
|
|
protected function ClearKeysCache()
|
|
{
|
|
if(is_file($this->m_sCacheFileName))
|
|
{
|
|
unlink($this->m_sCacheFileName);
|
|
}
|
|
else
|
|
{
|
|
//echo "<p>Hm, it looks like the file does not exist!!!</p>";
|
|
}
|
|
$this->m_aKeys = array();
|
|
$this->m_aObjectsCache = array();
|
|
}
|
|
|
|
/**
|
|
* Helper function to load the objects from a standard XML file into the database
|
|
*/
|
|
function LoadFile($sFilePath)
|
|
{
|
|
global $aKeys;
|
|
|
|
$oXml = simplexml_load_file($sFilePath);
|
|
|
|
$aReplicas = array();
|
|
foreach($oXml as $sClass => $oXmlObj)
|
|
{
|
|
$iSrcId = (integer)$oXmlObj['id']; // Mandatory to cast
|
|
|
|
// Import algorithm
|
|
// Here enumerate all the attributes of the object
|
|
// for all attribute that is neither an external field
|
|
// not an external key, assign it
|
|
// Store all external keys for further reference
|
|
// Create the object an store the correspondence between its newly created Id
|
|
// and its original Id
|
|
// Once all the objects have been created re-assign all the external keys to
|
|
// their actual Ids
|
|
$oTargetObj = MetaModel::NewObject($sClass);
|
|
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
|
|
{
|
|
if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()) && ($sAttCode != 'finalclass') )
|
|
{
|
|
if ($oAttDef->IsExternalKey())
|
|
{
|
|
$iDstObj = (integer)($oXmlObj->$sAttCode);
|
|
// Attempt to find the object in the list of loaded objects
|
|
$iExtKey = $this->GetObjectKey($oAttDef->GetTargetClass(), $iDstObj);
|
|
if ($iExtKey == 0)
|
|
{
|
|
$iExtKey = -$iDstObj; // Convention: Unresolved keys are stored as negative !
|
|
$oTargetObj->RegisterAsDirty();
|
|
}
|
|
// tested by Romain, little impact on perf (not significant on the intial setup)
|
|
//$oTargetObj->CheckValue($sAttCode, $iExtKey);
|
|
$oTargetObj->Set($sAttCode, $iExtKey);
|
|
}
|
|
else
|
|
{
|
|
// tested by Romain, little impact on perf (not significant on the intial setup)
|
|
//$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode);
|
|
$oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode);
|
|
}
|
|
}
|
|
}
|
|
$this->StoreObject($sClass, $oTargetObj, $iSrcId);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the new ID of an object in the database given its original ID
|
|
* This may fail (return 0) if the object has not yet been created in the database
|
|
* This is why the order of the import may be important
|
|
*/
|
|
protected function GetObjectKey($sClass, $iSrcId)
|
|
{
|
|
if (isset($this->m_aKeys[$sClass]) && isset($this->m_aKeys[$sClass][$iSrcId]))
|
|
{
|
|
return $this->m_aKeys[$sClass][$iSrcId];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Store an object in the database and remember the mapping
|
|
* between its original ID and the newly created ID in the database
|
|
*/
|
|
protected function StoreObject($sClass, $oTargetObj, $iSrcId)
|
|
{
|
|
try
|
|
{
|
|
if (is_subclass_of($oTargetObj, 'CMDBObject'))
|
|
{
|
|
$iObjId = $oTargetObj->DBInsertTrackedNoReload($this->m_oChange);
|
|
}
|
|
else
|
|
{
|
|
$iObjId = $oTargetObj->DBInsertNoReload();
|
|
}
|
|
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
echo $e->GetHtmlDesc();
|
|
}
|
|
$aParentClasses = MetaModel::EnumParentClasses($sClass);
|
|
$aParentClasses[] = $sClass;
|
|
foreach($aParentClasses as $sObjClass)
|
|
{
|
|
$this->m_aKeys[$sObjClass][$iSrcId] = $iObjId;
|
|
}
|
|
$this->m_aObjectsCache[$sClass][$iObjId] = $oTargetObj;
|
|
}
|
|
|
|
/**
|
|
* Maps an external key to its (newly created) value
|
|
*/
|
|
protected function ResolveExternalKeys()
|
|
{
|
|
foreach($this->m_aObjectsCache as $sClass => $oObjList)
|
|
{
|
|
foreach($oObjList as $oTargetObj)
|
|
{
|
|
$bChanged = false;
|
|
$sClass = get_class($oTargetObj);
|
|
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
|
|
{
|
|
if ( ($oAttDef->IsExternalKey()) && ($oTargetObj->Get($sAttCode) < 0) ) // Convention unresolved key = negative
|
|
{
|
|
$sTargetClass = $oAttDef->GetTargetClass();
|
|
$iTempKey = $oTargetObj->Get($sAttCode);
|
|
|
|
$iExtKey = $this->GetObjectKey($sTargetClass, -$iTempKey);
|
|
if ($iExtKey == 0)
|
|
{
|
|
$sMsg = "unresolved extkey in $sClass::".$oTargetObj->GetName()."(".$oTargetObj->GetKey().")::$sAttCode=$sTargetClass[$iTempKey]";
|
|
setup_web_page::log("Warning - $sMsg");
|
|
echo "Warning: $sMsg<br/>\n";
|
|
echo "<pre>aKeys[".$sTargetClass."]:\n";
|
|
print_r($this->m_aKeys[$sTargetClass]);
|
|
echo "</pre>\n";
|
|
}
|
|
else
|
|
{
|
|
$bChanged = true;
|
|
$oTargetObj->Set($sAttCode, $iExtKey);
|
|
}
|
|
}
|
|
}
|
|
if ($bChanged)
|
|
{
|
|
try
|
|
{
|
|
if (is_subclass_of($oTargetObj, 'CMDBObject'))
|
|
{
|
|
$oTargetObj->DBUpdateTracked($this->m_oChange);
|
|
}
|
|
else
|
|
{
|
|
$oTargetObj->DBUpdate();
|
|
}
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
echo $e->GetHtmlDesc();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
?>
|