/**
  * Helper to check wether the table has been created into the DB 
  * (this table did not exist in 1.0.1 and older versions)
  */
 public static function IsInstalled()
 {
     $sTable = MetaModel::DBGetTable(__CLASS__);
     if (CMDBSource::IsTable($sTable)) {
         return true;
     } else {
         return false;
     }
     return false;
 }
 /**
  * Handler called after the creation/update of the database schema
  * @param $oConfiguration Config The new configuration of the application
  * @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
  * @param $sCurrentVersion string Current version number of the module
  */
 public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
 {
     // Bug #464 - start_date was both in Ticket and Change tables
     //
     $sSourceTable = 'change';
     $sSourceKeyField = 'id';
     $sTargetTable = 'ticket';
     $sTargetKeyField = 'id';
     $sField = 'start_date';
     if (CMDBSource::IsField($sSourceTable, $sField) && CMDBSource::IsField($sTargetTable, $sField) && CMDBSource::IsField($sSourceTable, $sSourceKeyField) && CMDBSource::IsField($sTargetTable, $sTargetKeyField)) {
         SetupPage::log_info("Issue #464 - Copying change/start_date into ticket/start_date");
         $sRepair = "UPDATE `{$sTargetTable}`, `{$sSourceTable}` SET `{$sTargetTable}`.`{$sField}` = `{$sSourceTable}`.`{$sField}` WHERE `{$sTargetTable}`.`{$sField}` IS NULL AND`{$sTargetTable}`.`{$sTargetKeyField}` = `{$sSourceTable}`.`{$sSourceKeyField}`";
         CMDBSource::Query($sRepair);
     }
 }
 /**
  * Handler called after the creation/update of the database schema
  * @param $oConfiguration Config The new configuration of the application
  * @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
  * @param $sCurrentVersion string Current version number of the module
  */
 public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
 {
     // For each record having item_org_id unset,
     //    get the org_id from the container object
     //
     // Prerequisite: change null into 0 (workaround to the fact that we cannot use IS NULL in OQL)
     SetupPage::log_info("Initializing attachment/item_org_id - null to zero");
     $sTableName = MetaModel::DBGetTable('Attachment');
     $sRepair = "UPDATE `{$sTableName}` SET `item_org_id` = 0 WHERE `item_org_id` IS NULL";
     CMDBSource::Query($sRepair);
     SetupPage::log_info("Initializing attachment/item_org_id - zero to the container");
     $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_org_id = 0");
     $oSet = new DBObjectSet($oSearch);
     $iUpdated = 0;
     while ($oAttachment = $oSet->Fetch()) {
         $oContainer = MetaModel::GetObject($oAttachment->Get('item_class'), $oAttachment->Get('item_id'), false, true);
         if ($oContainer) {
             $oAttachment->SetItem($oContainer, true);
             $iUpdated++;
         }
     }
     SetupPage::log_info("Initializing attachment/item_org_id - {$iUpdated} records have been adjusted");
 }
Example #4
0
    protected function DoExecute()
    {
        // Note: relying on eval() - after upgrading to PHP 5.3 we can move to closure (aka anonymous functions)
        $aQueries = array('Basic (validate the test)' => array('search' => '
$oSearch = DBObjectSearch::FromOQL("SELECT P FROM Organization AS O JOIN Person AS P ON P.org_id = O.id WHERE org_id = 2");
				', 'oql' => 'SELECT P FROM Organization AS O JOIN Person AS P ON P.org_id = O.id WHERE P.org_id = 2'), 'Double constraint' => array('search' => '
$oSearch = DBObjectSearch::FromOQL("SELECT Contact AS c");
$sClass = $oSearch->GetClass();
$sFilterCode = "org_id";

$oAttDef = MetaModel::GetAttributeDef($sClass, $sFilterCode);

if ($oAttDef->IsExternalKey())
{
	$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass());
	
	if ($sHierarchicalKeyCode !== false)
	{
		$oFilter = new DBObjectSearch($oAttDef->GetTargetClass(), "ORGA");
		$oFilter->AddCondition("id", 2);
		$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass(), "ORGA");
		$oHKFilter->AddCondition_PointingTo(clone $oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);

		$oSearch->AddCondition_PointingTo(clone $oHKFilter, $sFilterCode);

		$oFilter = new DBObjectSearch($oAttDef->GetTargetClass(), "ORGA");
		$oFilter->AddCondition("id", 2);
		$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass(), "ORGA");
		$oHKFilter->AddCondition_PointingTo(clone $oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);

		$oSearch->AddCondition_PointingTo(clone $oHKFilter, $sFilterCode);
	}
}
				', 'oql' => 'SELECT Contact AS C JOIN Organization ???'), 'Simplified issue' => array('search' => '
$oSearch = DBObjectSearch::FromOQL("SELECT P FROM Organization AS O JOIN Person AS P ON P.org_id = O.id WHERE O.id = 2");
$oOrgSearch = new DBObjectSearch("Organization", "O2");
$oOrgSearch->AddCondition("id", 2);
$oSearch->AddCondition_PointingTo($oOrgSearch, "org_id");
				', 'oql' => 'SELECT P FROM Organization AS O JOIN Person AS P ON P.org_id = O.id JOIN Organization AS O2 ON P.org_id = O2.id WHERE O.id = 2 AND O2.id = 2'));
        foreach ($aQueries as $sQueryDesc => $aQuerySpec) {
            echo "<h2>Query {$sQueryDesc}</h2>\n";
            echo "<p>Using code: " . highlight_string("<?php\n" . trim($aQuerySpec['search']) . "\n?" . '>', true) . "</p>\n";
            echo "<p>Expected OQL: " . $aQuerySpec['oql'] . "</p>\n";
            if (isset($oSearch)) {
                unset($oSearch);
            }
            eval($aQuerySpec['search']);
            $sResOQL = $oSearch->ToOQL();
            echo "<p>Resulting OQL: " . $sResOQL . "</p>\n";
            echo "<pre>";
            print_r($oSearch);
            echo "</pre>";
            $sSQL = $oSearch->MakeSelectQuery();
            $res = CMDBSource::Query($sSQL);
            foreach (CMDBSource::ExplainQuery($sSQL) as $aRow) {
            }
        }
        //		throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'");
    }
 /**
  * Helper to modify an enum value	
  * The change is made in the datamodel definition, but the value has to be changed in the DB as well	 	 
  * Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
  * This helper does change ONE value at a time	 
  * 	 
  * @param string $sClass A valid class name
  * @param string $sAttCode The enum attribute code
  * @param string $sFrom Original value (already INVALID in the current datamodel)	 	
  * @param string $sTo New value (valid in the current datamodel)
  * @return void	 	 	
  */
 public static function RenameEnumValueInDB($sClass, $sAttCode, $sFrom, $sTo)
 {
     try {
         $sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
         $sTableName = MetaModel::DBGetTable($sOriginClass);
         $oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sAttCode);
         if ($oAttDef instanceof AttributeEnum) {
             $oValDef = $oAttDef->GetValuesDef();
             if ($oValDef) {
                 $aNewValues = array_keys($oValDef->GetValues(array(), ""));
                 if (in_array($sTo, $aNewValues)) {
                     $sEnumCol = $oAttDef->Get("sql");
                     $aFields = CMDBSource::QueryToArray("SHOW COLUMNS FROM `{$sTableName}` WHERE Field = '{$sEnumCol}'");
                     if (isset($aFields[0]['Type'])) {
                         $sColType = $aFields[0]['Type'];
                         // Note: the parsing should rely on str_getcsv (requires PHP 5.3) to cope with escaped string
                         if (preg_match("/^enum\\(\\'(.*)\\'\\)\$/", $sColType, $aMatches)) {
                             $aCurrentValues = explode("','", $aMatches[1]);
                         }
                     }
                     if (!in_array($sFrom, $aNewValues)) {
                         if (!in_array($sTo, $aCurrentValues)) {
                             $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
                             if (strtolower($sTo) == strtolower($sFrom)) {
                                 SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}' (just a change in the case)");
                                 $aTargetValues = array();
                                 foreach ($aCurrentValues as $sValue) {
                                     if ($sValue == $sFrom) {
                                         $sValue = $sTo;
                                     }
                                     $aTargetValues[] = $sValue;
                                 }
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aTargetValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                             } else {
                                 // 1st - Allow both values in the column definition
                                 //
                                 SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}'");
                                 $aAllValues = $aCurrentValues;
                                 $aAllValues[] = $sTo;
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aAllValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                                 // 2nd - Change the old value into the new value
                                 //
                                 $sRepair = "UPDATE `{$sTableName}` SET `{$sEnumCol}` = '{$sTo}' WHERE `{$sEnumCol}` = BINARY '{$sFrom}'";
                                 CMDBSource::Query($sRepair);
                                 $iAffectedRows = CMDBSource::AffectedRows();
                                 SetupPage::log_info("Changing enum in DB - {$iAffectedRows} rows updated");
                                 // 3rd - Remove the useless value from the column definition
                                 //
                                 $aTargetValues = array();
                                 foreach ($aCurrentValues as $sValue) {
                                     if ($sValue == $sFrom) {
                                         $sValue = $sTo;
                                     }
                                     $aTargetValues[] = $sValue;
                                 }
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aTargetValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                                 SetupPage::log_info("Changing enum in DB - removed useless value '{$sFrom}'");
                             }
                         }
                     } else {
                         SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sFrom}' is still a valid value (" . implode(', ', $aNewValues) . ")");
                     }
                 } else {
                     SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' is not a known value (" . implode(', ', $aNewValues) . ")");
                 }
             }
         }
     } catch (Exception $e) {
         SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' failed. Reason " . $e->getMessage());
     }
 }
    if ($oDataLoader->EndSession(true)) {
        $iCountCreated = $oDataLoader->GetCountCreated();
        CMDBSource::Query('COMMIT');
        $oP->p("Data successfully written into the DB: {$iCountCreated} objects created");
    } else {
        CMDBSource::Query('ROLLBACK');
        $oP->p("Some issues have been encountered, changes will not be recorded, please review the source data");
        $aErrors = $oDataLoader->GetErrors();
        if (count($aErrors) > 0) {
            $oP->p('Errors (' . count($aErrors) . ')');
            foreach ($aErrors as $sMsg) {
                $oP->p(' * ' . $sMsg);
            }
        }
        $aWarnings = $oDataLoader->GetWarnings();
        if (count($aWarnings) > 0) {
            $oP->p('Warnings (' . count($aWarnings) . ')');
            foreach ($aWarnings as $sMsg) {
                $oP->p(' * ' . $sMsg);
            }
        }
    }
} catch (Exception $e) {
    $oP->p("An error happened while loading the data: " . $e->getMessage());
    $oP->p("Aborting (no data written)...");
    CMDBSource::Query('ROLLBACK');
}
if (function_exists('memory_get_peak_usage')) {
    $oP->p("Information: memory peak usage: " . memory_get_peak_usage());
}
$oP->Output();
 /**
  * Describe (as a text string) the modifications corresponding to this change
  */
 public function GetDescription()
 {
     $sResult = '';
     $oTargetObjectClass = $this->Get('objclass');
     $oTargetObjectKey = $this->Get('objkey');
     $oTargetSearch = new DBObjectSearch($oTargetObjectClass);
     $oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
     $oMonoObjectSet = new DBObjectSet($oTargetSearch);
     if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES) {
         if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) {
             return '';
         }
         // Protects against renamed attributes...
         $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
         $sAttName = $oAttDef->GetLabel();
         $sLinkClass = $oAttDef->GetLinkedClass();
         $aLinkClasses = MetaModel::EnumChildClasses($sLinkClass, ENUM_CHILD_CLASSES_ALL);
         // Search for changes on the corresponding link
         //
         $oSearch = new DBObjectSearch('CMDBChangeOpSetAttribute');
         $oSearch->AddCondition('change', $this->Get('change'), '=');
         $oSearch->AddCondition('objkey', $this->Get('link_id'), '=');
         if (count($aLinkClasses) == 1) {
             // Faster than the whole building of the expression below for just one value ??
             $oSearch->AddCondition('objclass', $sLinkClass, '=');
         } else {
             $oField = new FieldExpression('objclass', $oSearch->GetClassAlias());
             $sListExpr = '(' . implode(', ', CMDBSource::Quote($aLinkClasses)) . ')';
             $sOQLCondition = $oField->Render() . " IN {$sListExpr}";
             $oNewCondition = Expression::FromOQL($sOQLCondition);
             $oSearch->AddConditionExpression($oNewCondition);
         }
         $oSet = new DBObjectSet($oSearch);
         $aChanges = array();
         while ($oChangeOp = $oSet->Fetch()) {
             $aChanges[] = $oChangeOp->GetDescription();
         }
         if (count($aChanges) == 0) {
             return '';
         }
         $sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
         $sResult = $sAttName . ' - ';
         $sResult .= Dict::Format('Change:LinkSet:Modified', $sItemDesc);
         $sResult .= ' : ' . implode(', ', $aChanges);
     }
     return $sResult;
 }
 public static function GetMySQLVersion($sDBServer, $sDBUser, $sDBPwd)
 {
     $oDBSource = new CMDBSource();
     $oDBSource->Init($sDBServer, $sDBUser, $sDBPwd);
     $sDBVersion = $oDBSource->GetDBVersion();
     return $sDBVersion;
 }
 public function LoadExtendedDataFromTable($sSQLTable)
 {
     $sSQL = "SELECT * FROM {$sSQLTable} WHERE id=" . $this->GetKey();
     $rQuery = CMDBSource::Query($sSQL);
     return CMDBSource::FetchArray($rQuery);
 }
Example #10
0
 /**
  * Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
  * 
  * @return hash A hash with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
  */
 public function FetchAssoc()
 {
     if (!$this->m_bLoaded) {
         $this->Load();
     }
     if ($this->m_iCurrRow >= $this->CountLoaded()) {
         return null;
     }
     if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
         // Pick the row from the database
         $aRow = CMDBSource::FetchArray($this->m_oSQLResult);
         $aRetObjects = array();
         foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
             if (is_null($aRow[$sClassAlias . 'id'])) {
                 $oObj = null;
             } else {
                 $oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
             }
             $aRetObjects[$sClassAlias] = $oObj;
         }
     } else {
         // Pick the row from the objects added *in memory*
         $aRetObjects = array();
         foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
             $aRetObjects[$sClassAlias] = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sClassAlias];
         }
     }
     $this->m_iCurrRow++;
     return $aRetObjects;
 }
 public function GetApplicationVersion(Config $oConfig)
 {
     $aResult = false;
     try {
         require_once APPROOT . '/core/cmdbsource.class.inc.php';
         CMDBSource::Init($oConfig->GetDBHost(), $oConfig->GetDBUser(), $oConfig->GetDBPwd(), $oConfig->GetDBName());
         CMDBSource::SetCharacterSet($oConfig->GetDBCharacterSet(), $oConfig->GetDBCollation());
         $sSQLQuery = "SELECT * FROM " . $oConfig->GetDBSubname() . "priv_module_install";
         $aSelectInstall = CMDBSource::QueryToArray($sSQLQuery);
     } catch (MySQLException $e) {
         // No database or erroneous information
         $this->log_error('Can not connect to the database: host: ' . $oConfig->GetDBHost() . ', user:'******', pwd:' . $oConfig->GetDBPwd() . ', db name:' . $oConfig->GetDBName());
         $this->log_error('Exception ' . $e->getMessage());
         return false;
     }
     // Scan the list of installed modules to get the version of the 'ROOT' module which holds the main application version
     foreach ($aSelectInstall as $aInstall) {
         $sModuleVersion = $aInstall['version'];
         if ($sModuleVersion == '') {
             // Though the version cannot be empty in iTop 2.0, it used to be possible
             // therefore we have to put something here or the module will not be considered
             // as being installed
             $sModuleVersion = '0.0.0';
         }
         if ($aInstall['parent_id'] == 0) {
             if ($aInstall['name'] == DATAMODEL_MODULE) {
                 $aResult['datamodel_version'] = $sModuleVersion;
                 $aComments = json_decode($aInstall['comment'], true);
                 if (is_array($aComments)) {
                     $aResult = array_merge($aResult, $aComments);
                 }
             } else {
                 $aResult['product_name'] = $aInstall['name'];
                 $aResult['product_version'] = $sModuleVersion;
             }
         }
     }
     if (!array_key_exists('datamodel_version', $aResult)) {
         // Versions prior to 2.0 did not record the version of the datamodel
         // so assume that the datamodel version is equal to the application version
         $aResult['datamodel_version'] = $aResult['product_version'];
     }
     $this->log_info("GetApplicationVersion returns: product_name: " . $aResult['product_name'] . ', product_version: ' . $aResult['product_version']);
     return $aResult;
 }
 public function Exec()
 {
     if ($this->sSql != '') {
         $iRepeat = utils::ReadParam('repeat', 3);
         try {
             $fRefTime = MyHelpers::getmicrotime();
             for ($i = 0; $i < $iRepeat; $i++) {
                 $resQuery = CMDBSource::Query($this->sSql);
             }
             $this->fExecDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
             // This is not relevant...
             if (preg_match_all('|\\s*JOIN\\s*\\(\\s*`|', $this->sSql, $aMatches)) {
                 $this->iTableCount = 1 + count($aMatches[0]);
             } else {
                 $this->iTableCount = 1;
             }
         } catch (Exception $e) {
             $this->aErrors[] = "Failed to execute the SQL:" . $e->getMessage();
             $resQuery = null;
         }
         if ($resQuery) {
             while ($aRow = CMDBSource::FetchArray($resQuery)) {
                 $this->aRows[] = $aRow;
             }
             CMDBSource::FreeResult($resQuery);
         }
     }
 }
Example #13
0
 protected function DoExecute()
 {
     CMDBSource::Query('START TRANSACTION');
     //CMDBSource::Query('ROLLBACK'); automatique !
     ////////////////////////////////////////////////////////////////////////////////
     // Set the stage
     //
     $oProvider = new Organization();
     $oProvider->Set('name', 'Test-Provider1');
     $oProvider->DBInsert();
     $iProvider = $oProvider->GetKey();
     $oDM1 = new DeliveryModel();
     $oDM1->Set('name', 'Test-DM-1');
     $oDM1->Set('org_id', $iProvider);
     $oDM1->DBInsert();
     $iDM1 = $oDM1->GetKey();
     $oDM2 = new DeliveryModel();
     $oDM2->Set('name', 'Test-DM-2');
     $oDM2->Set('org_id', $iProvider);
     $oDM2->DBInsert();
     $iDM2 = $oDM2->GetKey();
     ////////////////////////////////////////////////////////////////////////////////
     // Scenarii
     //
     $aScenarii = array(array('description' => 'Add the first customer', 'organizations' => array(array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Remove the customer by loading an empty set', 'organizations' => array(), 'expected-res' => array(), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Create two customers at once', 'organizations' => array(array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1'), array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1", "Test-Customer-2, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Move Customer-1 to the second Delivery Model', 'organizations' => array(array('id' => "SELECT Organization WHERE name='Test-Customer-1'", 'deliverymodel_id' => $iDM2, 'name' => 'Test-Customer-1'), array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-2, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Move Customer-1 back to the first Delivery Model and reset Customer-2 (no Delivery Model)', 'organizations' => array(array('id' => "SELECT Organization WHERE name='Test-Customer-1'", 'deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1'), array('id' => "SELECT Organization WHERE name='Test-Customer-2'", 'deliverymodel_id' => 0, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0));
     foreach ($aScenarii as $aScenario) {
         echo "<h4>" . $aScenario['description'] . "</h4>\n";
         $oChange = MetaModel::NewObject("CMDBChange");
         $oChange->Set("date", time());
         $oChange->Set("userinfo", CMDBChange::GetCurrentUserName());
         $oChange->Set("origin", 'custom-extension');
         $oChange->DBInsert();
         CMDBObject::SetCurrentChange($oChange);
         $iChange = $oChange->GetKey();
         // Prepare set
         $oLinkset = DBObjectSet::FromScratch('Organization');
         foreach ($aScenario['organizations'] as $aOrgData) {
             if (array_key_exists('id', $aOrgData)) {
                 $sOQL = $aOrgData['id'];
                 $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
                 $oOrg = $oSet->Fetch();
                 if (!is_object($oOrg)) {
                     throw new Exception('Failed to find the Organization: ' . $sOQL);
                 }
             } else {
                 $oOrg = MetaModel::NewObject('Organization');
             }
             foreach ($aOrgData as $sAttCode => $value) {
                 if ($sAttCode == 'id') {
                     continue;
                 }
                 $oOrg->Set($sAttCode, $value);
             }
             $oLinkset->AddObject($oOrg);
         }
         // Write
         $oDM = MetaModel::GetObject('DeliveryModel', $iDM1);
         $oDM->Set('customers_list', $oLinkset);
         $oDM->DBWrite();
         // Check Results
         $bFoundIssue = false;
         $oDM = MetaModel::GetObject('DeliveryModel', $iDM1);
         $oLinkset = $oDM->Get('customers_list');
         $aRes = $this->StandardizedDump($oLinkset, 'zzz');
         $sRes = var_export($aRes, true);
         echo "Found: <pre>" . $sRes . "</pre>\n";
         $sExpectedRes = var_export($aScenario['expected-res'], true);
         if ($sRes != $sExpectedRes) {
             $bFoundIssue = true;
             echo "NOT COMPLIANT!!! Expecting: <pre>" . $sExpectedRes . "</pre>\n";
         }
         // Check History
         $aQueryParams = array('change' => $iChange, 'objclass' => get_class($oDM), 'objkey' => $oDM->GetKey());
         $oAdded = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksAddRemove WHERE objclass = :objclass AND objkey = :objkey AND change = :change AND type = 'added'"), array(), $aQueryParams);
         echo "added: " . $oAdded->Count() . "<br/>\n";
         if ($aScenario['history_added'] != $oAdded->Count()) {
             $bFoundIssue = true;
             echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_added'] . "<br/>\n";
         }
         $oRemoved = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksAddRemove WHERE objclass = :objclass AND objkey = :objkey AND change = :change AND type = 'removed'"), array(), $aQueryParams);
         echo "removed: " . $oRemoved->Count() . "<br/>\n";
         if ($aScenario['history_removed'] != $oRemoved->Count()) {
             $bFoundIssue = true;
             echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_removed'] . "<br/>\n";
         }
         $oModified = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksTune WHERE objclass = :objclass AND objkey = :objkey AND change = :change"), array(), $aQueryParams);
         echo "modified: " . $oModified->Count() . "<br/>\n";
         if ($aScenario['history_modified'] != $oModified->Count()) {
             $bFoundIssue = true;
             echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_modified'] . "<br/>\n";
         }
         if ($bFoundIssue) {
             throw new Exception('Stopping on failed scenario');
         }
     }
 }
 /**
  * Helper to modify an enum value	
  * The change is made in the datamodel definition, but the value has to be changed in the DB as well	 	 
  * Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
  * 	 
  * @param string $sClass A valid class name
  * @param string $sAttCode The enum attribute code
  * @param string $sFrom Original value (already INVALID in the current datamodel)	 	
  * @param string $sTo New value (valid in the current datamodel)
  * @return void	 	 	
  */
 public static function RenameEnumValueInDB($sClass, $sAttCode, $sFrom, $sTo)
 {
     $sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
     $sTableName = MetaModel::DBGetTable($sOriginClass);
     $oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sAttCode);
     if ($oAttDef instanceof AttributeEnum) {
         $oValDef = $oAttDef->GetValuesDef();
         if ($oValDef) {
             $aNewValues = array_keys($oValDef->GetValues(array(), ""));
             if (in_array($sTo, $aNewValues)) {
                 $aAllValues = $aNewValues;
                 $aAllValues[] = $sFrom;
                 if (!in_array($sFrom, $aNewValues)) {
                     $sEnumCol = $oAttDef->Get("sql");
                     $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
                     if (strtolower($sTo) == strtolower($sFrom)) {
                         SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}' (just a change in the case)");
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aNewValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                     } else {
                         // 1st - Allow both values in the column definition
                         //
                         SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}'");
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aAllValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                         // 2nd - Change the old value into the new value
                         //
                         $sRepair = "UPDATE `{$sTableName}` SET `{$sEnumCol}` = '{$sTo}' WHERE `{$sEnumCol}` = BINARY '{$sFrom}'";
                         CMDBSource::Query($sRepair);
                         $iAffectedRows = CMDBSource::AffectedRows();
                         SetupPage::log_info("Changing enum in DB - {$iAffectedRows} rows updated");
                         // 3rd - Remove the useless value from the column definition
                         //
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aNewValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                         SetupPage::log_info("Changing enum in DB - removed useless value '{$sFrom}'");
                     }
                 } else {
                     SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sFrom}' is still a valid value (" . implode(', ', $aNewValues) . ")");
                 }
             } else {
                 SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' is not a known value (" . implode(', ', $aNewValues) . ")");
             }
         }
     }
 }
Example #15
0
 protected function DoPrepare()
 {
     $sDBHost = $this->GetDBHost();
     $sDBUser = $this->GetDBUser();
     $sDBPwd = $this->GetDBPwd();
     $sDBName = $this->GetDBName();
     CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd);
     CMDBSource::SetCharacterSet();
     if (CMDBSource::IsDB($sDBName)) {
         CMDBSource::DropDB($sDBName);
     }
     CMDBSource::CreateDB($sDBName);
 }
 public function RenderContent(WebPage $oPage, $aExtraParams = array())
 {
     if (empty($aExtraParams['currentId'])) {
         $sId = 'sqlblock_' . $oPage->GetUniqueId();
         // Works only if the page is not an Ajax one !
     } else {
         $sId = $aExtraParams['currentId'];
     }
     //		$oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
     $sQuery = $this->BuildQuery();
     $res = CMDBSource::Query($sQuery);
     $aQueryCols = CMDBSource::GetColumns($res);
     // Prepare column definitions (check + give default values)
     //
     foreach ($this->m_aColumns as $sName => $aColumnData) {
         if (!in_array($sName, $aQueryCols)) {
             throw new Exception("Unknown column name '{$sName}' in sqlblock column");
         }
         if (!isset($aColumnData['label'])) {
             $this->m_aColumns[$sName]['label'] = $sName;
         }
         if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown'])) {
             // Check if the OQL is valid
             try {
                 $this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
             } catch (OQLException $e) {
                 unset($aColumnData['drilldown']);
             }
         }
     }
     if (strlen($this->m_sTitle) > 0) {
         $oPage->add("<h2>" . Dict::S($this->m_sTitle) . "</h2>\n");
     }
     switch ($this->m_sType) {
         case 'bars':
         case 'pie':
             $aColNames = array_keys($this->m_aColumns);
             $sXColName = $aColNames[0];
             $sYColName = $aColNames[1];
             $aData = array();
             $aRows = array();
             while ($aRow = CMDBSource::FetchArray($res)) {
                 $aData[$aRow[$sXColName]] = $aRow[$sYColName];
                 $aRows[$aRow[$sXColName]] = $aRow;
             }
             $this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
             break;
         default:
         case 'table':
             $oAppContext = new ApplicationContext();
             $sContext = $oAppContext->GetForLink();
             if (!empty($sContext)) {
                 $sContext = '&' . $sContext;
             }
             $aDisplayConfig = array();
             foreach ($this->m_aColumns as $sName => $aColumnData) {
                 $aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
             }
             $aDisplayData = array();
             while ($aRow = CMDBSource::FetchArray($res)) {
                 $aSQLColNames = array_keys($aRow);
                 $aDisplayRow = array();
                 foreach ($this->m_aColumns as $sName => $aColumnData) {
                     if (isset($aColumnData['filter'])) {
                         $sFilter = $aColumnData['drilldown'];
                         $sClass = $aColumnData['filter']->GetClass();
                         $sFilter = str_replace('SELECT ' . $sClass, '', $sFilter);
                         foreach ($aSQLColNames as $sColName) {
                             $sFilter = str_replace(':' . $sColName, "'" . addslashes($aRow[$sColName]) . "'", $sFilter);
                         }
                         $sURL = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=search_oql&search_form=0&oql_class=' . $sClass . '&oql_clause=' . urlencode($sFilter) . '&format=html' . $sContext;
                         $aDisplayRow[$sName] = '<a href="' . $sURL . '">' . $aRow[$sName] . "</a>";
                     } else {
                         $aDisplayRow[$sName] = $aRow[$sName];
                     }
                 }
                 $aDisplayData[] = $aDisplayRow;
             }
             $oPage->table($aDisplayConfig, $aDisplayData);
             break;
     }
 }
 public function AddCondition($sFilterCode, $value, $sOpCode = null)
 {
     MyHelpers::CheckKeyInArray('filter code in class: ' . $this->GetClass(), $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
     $oFilterDef = MetaModel::GetClassFilterDef($this->GetClass(), $sFilterCode);
     $oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
     if (empty($sOpCode)) {
         if ($sFilterCode == 'id') {
             $sOpCode = '=';
         } else {
             $oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
             $oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams);
             $this->AddConditionExpression($oNewCondition);
             return;
         }
     }
     MyHelpers::CheckKeyInArray('operator', $sOpCode, $oFilterDef->GetOperators());
     // Preserve backward compatibility - quick n'dirty way to change that API semantic
     //
     switch ($sOpCode) {
         case 'SameDay':
         case 'SameMonth':
         case 'SameYear':
         case 'Today':
         case '>|':
         case '<|':
         case '=|':
             throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
             break;
         case "IN":
             if (!is_array($value)) {
                 $value = array($value);
             }
             $sListExpr = '(' . implode(', ', CMDBSource::Quote($value)) . ')';
             $sOQLCondition = $oField->Render() . " IN {$sListExpr}";
             break;
         case "NOTIN":
             if (!is_array($value)) {
                 $value = array($value);
             }
             $sListExpr = '(' . implode(', ', CMDBSource::Quote($value)) . ')';
             $sOQLCondition = $oField->Render() . " NOT IN {$sListExpr}";
             break;
         case 'Contains':
             $this->m_aParams[$sFilterCode] = "%{$value}%";
             $sOperator = 'LIKE';
             break;
         case 'Begins with':
             $this->m_aParams[$sFilterCode] = "{$value}%";
             $sOperator = 'LIKE';
             break;
         case 'Finishes with':
             $this->m_aParams[$sFilterCode] = "%{$value}";
             $sOperator = 'LIKE';
             break;
         default:
             $this->m_aParams[$sFilterCode] = $value;
             $sOperator = $sOpCode;
     }
     switch ($sOpCode) {
         case "IN":
         case "NOTIN":
             $oNewCondition = Expression::FromOQL($sOQLCondition);
             break;
         case 'Contains':
         case 'Begins with':
         case 'Finishes with':
         default:
             $oRightExpr = new VariableExpression($sFilterCode);
             $oNewCondition = new BinaryExpression($oField, $sOperator, $oRightExpr);
     }
     $this->AddConditionExpression($oNewCondition);
 }
 public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
 {
     $sSQL = MetaModel::MakeSelectQuery($this, $aOrderBy, $aArgs);
     $resQuery = CMDBSource::Query($sSQL);
     if (!$resQuery) {
         return;
     }
     if (count($aColumns) == 0) {
         $aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
         // Add the standard id (as first column)
         array_unshift($aColumns, 'id');
     }
     $aQueryCols = CMDBSource::GetColumns($resQuery);
     $sClassAlias = $this->GetClassAlias();
     $aColMap = array();
     foreach ($aColumns as $sAttCode) {
         $sColName = $sClassAlias . $sAttCode;
         if (in_array($sColName, $aQueryCols)) {
             $aColMap[$sAttCode] = $sColName;
         }
     }
     $aRes = array();
     while ($aRow = CMDBSource::FetchArray($resQuery)) {
         $aMappedRow = array();
         foreach ($aColMap as $sAttCode => $sColName) {
             $aMappedRow[$sAttCode] = $aRow[$sColName];
         }
         $aRes[] = $aMappedRow;
     }
     CMDBSource::FreeResult($resQuery);
     return $aRes;
 }
 public function GetBasicFilterSQLExpr($sOpCode, $value)
 {
     $sQValue = CMDBSource::Quote($value);
     switch ($sOpCode) {
         case '=':
         case '!=':
             return $this->GetSQLExpr() . " {$sOpCode} {$sQValue}";
         case 'Contains':
             return $this->GetSQLExpr() . " LIKE " . CMDBSource::Quote("%{$value}%");
         case 'NotLike':
             return $this->GetSQLExpr() . " NOT LIKE {$sQValue}";
         case 'Like':
         default:
             return $this->GetSQLExpr() . " LIKE {$sQValue}";
     }
 }
Example #20
0
         try {
             foreach ($aAnalysis as $sClass => $aData) {
                 if (isset($aData['table_fixes'])) {
                     foreach ($aData['table_fixes'] as $sAttCode => $aIssues) {
                         foreach ($aIssues as $sSQL) {
                             CMDBSource::Query($sSQL);
                             echo "<p class=\"sql_ok\">{$sSQL};</p>\n";
                         }
                     }
                 }
             }
             foreach ($aAnalysis as $sClass => $aData) {
                 if (isset($aData['view_fixes'])) {
                     foreach ($aData['view_fixes'] as $sAttCode => $aIssues) {
                         foreach ($aIssues as $sSQL) {
                             CMDBSource::Query($sSQL);
                             echo "<p class=\"sql_ok\">{$sSQL};</p>\n";
                         }
                     }
                 }
             }
             echo "<p>Done.</p>";
         } catch (MySQLException $e) {
             echo "<p class=\"sql_error\">{$sSQL};</p>\n";
             echo "<p class=\"sql_error\">" . $e->getHtmlDesc() . "</p>\n";
             echo "<p class=\"sql_error\">Operation aborted.</p>\n";
         }
         break;
     default:
         echo "The operation {$sOperation} is not supported";
 }
Example #21
0
 protected static function AddQueryTrace($aQueryData, $sOql, $sSql)
 {
     if (self::$m_bTraceQueries) {
         $sQueryId = md5(serialize($aQueryData));
         $sMySQLQueryId = md5($sSql);
         if (!isset(self::$m_aQueriesLog[$sQueryId])) {
             self::$m_aQueriesLog[$sQueryId]['data'] = serialize($aQueryData);
             self::$m_aQueriesLog[$sQueryId]['oql'] = $sOql;
             self::$m_aQueriesLog[$sQueryId]['hits'] = 1;
         } else {
             self::$m_aQueriesLog[$sQueryId]['hits']++;
         }
         if (!isset(self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId])) {
             self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['sql'] = $sSql;
             self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count'] = 1;
             $iTableCount = count(CMDBSource::ExplainQuery($sSql));
             self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['table_count'] = $iTableCount;
         } else {
             self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count']++;
         }
     }
 }
 public function Render(&$aArgs = null, $bRetrofitParams = false)
 {
     $aRes = array();
     foreach ($this->m_aExpressions as $oExpr) {
         $sCol = $oExpr->Render($aArgs, $bRetrofitParams);
         // Concat will be globally NULL if one single argument is null !
         $aRes[] = "COALESCE({$sCol}, '')";
     }
     $sSep = CMDBSource::Quote($this->m_separator);
     return "CAST(CONCAT_WS({$sSep}, " . implode(', ', $aRes) . ") AS CHAR)";
 }
 /**
  * Renders the "Actions" popup menu for the given set of objects
  * 
  * Note that the menu links containing (or ending) with a hash (#) will have their fragment
  * part (whatever is after the hash) dynamically replaced (by javascript) when the menu is
  * displayed, to correspond to the current hash/fragment in the page. This allows modifying
  * an object in with the same tab active by default as the tab that was active when selecting
  * the "Modify..." action.
  */
 public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
 {
     if ($this->m_sStyle == 'popup') {
         $this->m_sStyle = 'list';
     }
     $sHtml = '';
     $oAppContext = new ApplicationContext();
     $sContext = $oAppContext->GetForLink();
     if (!empty($sContext)) {
         $sContext = '&' . $sContext;
     }
     $sClass = $this->m_oFilter->GetClass();
     $oReflectionClass = new ReflectionClass($sClass);
     $oSet = new CMDBObjectSet($this->m_oFilter);
     $sFilter = $this->m_oFilter->serialize();
     $sFilterDesc = $this->m_oFilter->ToOql(true);
     $aActions = array();
     $sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
     $sRootUrl = utils::GetAbsoluteUrlAppRoot();
     // 1:n links, populate the target object as a default value when creating a new linked object
     if (isset($aExtraParams['target_attr'])) {
         $aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
     }
     $sDefault = '';
     if (!empty($aExtraParams['default'])) {
         foreach ($aExtraParams['default'] as $sKey => $sValue) {
             $sDefault .= "&default[{$sKey}]={$sValue}";
         }
     }
     $bIsCreationAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES && $oReflectionClass->IsSubclassOf('cmdbAbstractObject');
     switch ($oSet->Count()) {
         case 0:
             // No object in the set, the only possible action is "new"
             if ($bIsCreationAllowed) {
                 $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}");
             }
             break;
         case 1:
             $oObj = $oSet->Fetch();
             $id = $oObj->GetKey();
             $bLocked = false;
             if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) {
                 $aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
                 if ($aLockInfo['locked']) {
                     $bLocked = true;
                     //$this->AddMenuSeparator($aActions);
                     //$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:ReleaseConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}");
                 }
             }
             $bRawModifiedAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES && $oReflectionClass->IsSubclassOf('cmdbAbstractObject');
             $bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
             $bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
             // Just one object in the set, possible actions are "new / clone / modify and delete"
             if (!isset($aExtraParams['link_attr'])) {
                 if ($bIsModifyAllowed) {
                     $aActions['UI:Menu:Modify'] = array('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify&class={$sClass}&id={$id}{$sContext}#");
                 }
                 if ($bIsCreationAllowed) {
                     $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}");
                 }
                 if ($bIsDeleteAllowed) {
                     $aActions['UI:Menu:Delete'] = array('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=delete&class={$sClass}&id={$id}{$sContext}");
                 }
                 // Transitions / Stimuli
                 if (!$bLocked) {
                     $aTransitions = $oObj->EnumTransitions();
                     if (count($aTransitions)) {
                         $this->AddMenuSeparator($aActions);
                         $aStimuli = Metamodel::EnumStimuli(get_class($oObj));
                         foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
                             $iActionAllowed = get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction' ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;
                             switch ($iActionAllowed) {
                                 case UR_ALLOWED_YES:
                                     $aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus={$sStimulusCode}&class={$sClass}&id={$id}{$sContext}");
                                     break;
                                 default:
                                     // Do nothing
                             }
                         }
                     }
                 }
                 // Relations...
                 $aRelations = MetaModel::EnumRelationsEx($sClass);
                 if (count($aRelations)) {
                     $this->AddMenuSeparator($aActions);
                     foreach ($aRelations as $sRelationCode => $aRelationInfo) {
                         if (array_key_exists('down', $aRelationInfo)) {
                             $aActions[$sRelationCode . '_down'] = array('label' => $aRelationInfo['down'], 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=swf_navigator&relation={$sRelationCode}&direction=down&class={$sClass}&id={$id}{$sContext}");
                         }
                         if (array_key_exists('up', $aRelationInfo)) {
                             $aActions[$sRelationCode . '_up'] = array('label' => $aRelationInfo['up'], 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=swf_navigator&relation={$sRelationCode}&direction=up&class={$sClass}&id={$id}{$sContext}");
                         }
                     }
                 }
                 if ($bLocked && $bRawModifiedAllowed) {
                     // Add a special menu to kill the lock, but only to allowed users who can also modify this object
                     $aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
                     $bCanKill = false;
                     $oUser = UserRights::GetUserObject();
                     $aUserProfiles = array();
                     if (!is_null($oUser)) {
                         $oProfileSet = $oUser->Get('profile_list');
                         while ($oProfile = $oProfileSet->Fetch()) {
                             $aUserProfiles[$oProfile->Get('profile')] = true;
                         }
                     }
                     foreach ($aAllowedProfiles as $sProfile) {
                         if (array_key_exists($sProfile, $aUserProfiles)) {
                             $bCanKill = true;
                             break;
                         }
                     }
                     if ($bCanKill) {
                         $this->AddMenuSeparator($aActions);
                         $aActions['concurrent_lock_unlock'] = array('label' => Dict::S('UI:Menu:KillConcurrentLock'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=kill_lock&class={$sClass}&id={$id}{$sContext}");
                     }
                 }
                 /*
                 $this->AddMenuSeparator($aActions);
                 // Static menus: Email this page & CSV Export
                 $sUrl = ApplicationContext::MakeObjectUrl($sClass, $id);
                 $aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl));
                 $aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}");
                 // The style tells us whether the menu is displayed on a list of one object, or on the details of the given object 
                 if ($this->m_sStyle == 'list')
                 {
                 	// Actions specific to the list
                 	$sOQL = addslashes($sFilterDesc);
                 	$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
                 }
                 */
             }
             $this->AddMenuSeparator($aActions);
             foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
                 $oSet->Rewind();
                 foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) {
                     $aActions[$sLabel] = array('label' => $sLabel, 'url' => $sUrl);
                 }
             }
             break;
         default:
             // Check rights
             // New / Modify
             $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) && $oReflectionClass->IsSubclassOf('cmdbAbstractObject');
             $bIsBulkModifyAllowed = !MetaModel::IsAbstract($sClass) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet) && $oReflectionClass->IsSubclassOf('cmdbAbstractObject');
             $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet);
             if (isset($aExtraParams['link_attr'])) {
                 $id = $aExtraParams['object_id'];
                 $sTargetAttr = $aExtraParams['target_attr'];
                 $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr);
                 $sTargetClass = $oAttDef->GetTargetClass();
                 $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
                 if ($bIsModifyAllowed) {
                     $aActions['UI:Menu:Add'] = array('label' => Dict::S('UI:Menu:Add'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify_links&class={$sClass}&link_attr=" . $aExtraParams['link_attr'] . "&target_class={$sTargetClass}&id={$id}&addObjects=true{$sContext}");
                 }
                 if ($bIsBulkModifyAllowed) {
                     $aActions['UI:Menu:Manage'] = array('label' => Dict::S('UI:Menu:Manage'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify_links&class={$sClass}&link_attr=" . $aExtraParams['link_attr'] . "&target_class={$sTargetClass}&id={$id}{$sContext}");
                 }
                 //if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); }
             } else {
                 // many objects in the set, possible actions are: new / modify all / delete all
                 if ($bIsCreationAllowed) {
                     $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}");
                 }
                 if ($bIsBulkModifyAllowed) {
                     $aActions['UI:Menu:ModifyAll'] = array('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=select_for_modify_all&class={$sClass}&filter=" . urlencode($sFilter) . "{$sContext}");
                 }
                 if ($bIsBulkDeleteAllowed) {
                     $aActions['UI:Menu:BulkDelete'] = array('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=select_for_deletion&filter=" . urlencode($sFilter) . "{$sContext}");
                 }
                 // Stimuli
                 $aStates = MetaModel::EnumStates($sClass);
                 // Do not perform time consuming computations if there are too may objects in the list
                 $iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
                 if (count($aStates) > 0 && ($iLimit == 0 || $oSet->Count() < $iLimit)) {
                     // Life cycle actions may be available... if all objects are in the same state
                     //
                     // Group by <state>
                     $oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias());
                     $aGroupBy = array('__state__' => $oGroupByExp);
                     $aQueryParams = array();
                     if (isset($aExtraParams['query_params'])) {
                         $aQueryParams = $aExtraParams['query_params'];
                     }
                     $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
                     $aRes = CMDBSource::QueryToArray($sSql);
                     if (count($aRes) == 1) {
                         // All objects are in the same state...
                         $sState = $aRes[0]['__state__'];
                         $aTransitions = Metamodel::EnumTransitions($sClass, $sState);
                         if (count($aTransitions)) {
                             $this->AddMenuSeparator($aActions);
                             $aStimuli = Metamodel::EnumStimuli($sClass);
                             foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
                                 $oSet->Rewind();
                                 // As soon as the user rights implementation will browse the object set,
                                 // then we might consider using OptimizeColumnLoad() here
                                 $iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
                                 $iActionAllowed = get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction' ? $iActionAllowed : UR_ALLOWED_NO;
                                 switch ($iActionAllowed) {
                                     case UR_ALLOWED_YES:
                                     case UR_ALLOWED_DEPENDS:
                                         $aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus={$sStimulusCode}&state={$sState}&class={$sClass}&filter=" . urlencode($sFilter) . "{$sContext}");
                                         break;
                                     default:
                                         // Do nothing
                                 }
                             }
                         }
                     }
                 }
                 /*
                 $this->AddMenuSeparator($aActions);
                 $sUrl = utils::GetAbsoluteUrlAppRoot();
                 $aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=$sFilterDesc&body=".urlencode("{$sUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."{$sContext}"));
                 $aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}");
                 $sOQL = addslashes($sFilterDesc);
                 $aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
                 */
             }
     }
     $this->AddMenuSeparator($aActions);
     foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
         $oSet->Rewind();
         foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data) {
             if (is_array($data)) {
                 // New plugins can provide javascript handlers via the 'onclick' property
                 //TODO: enable extension of different menus by checking the 'target' property ??
                 $aActions[$sLabel] = array('label' => $sLabel, 'url' => isset($data['url']) ? $data['url'] : '#', 'onclick' => isset($data['onclick']) ? $data['onclick'] : '');
             } else {
                 // Backward compatibility with old plugins
                 $aActions[$sLabel] = array('label' => $sLabel, 'url' => $data);
             }
         }
     }
     // New extensions based on iPopupMenuItem interface
     switch ($this->m_sStyle) {
         case 'list':
             $oSet->Rewind();
             $param = $oSet;
             $iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS;
             break;
         case 'details':
             $oSet->Rewind();
             $param = $oSet->Fetch();
             $iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS;
             break;
     }
     utils::GetPopupMenuItems($oPage, $iMenuId, $param, $aActions);
     $aFavoriteActions = array();
     $aCallSpec = array($sClass, 'GetShortcutActions');
     if (is_callable($aCallSpec)) {
         $aShortcutActions = call_user_func($aCallSpec, $sClass);
         foreach ($aActions as $key => $aAction) {
             if (in_array($key, $aShortcutActions)) {
                 $aFavoriteActions[] = $aAction;
                 unset($aActions[$key]);
             }
         }
     } else {
         $aShortcutActions = array();
     }
     if (count($aFavoriteActions) > 0) {
         $sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>" . Dict::S('UI:Menu:OtherActions') . "\n<ul>\n";
     } else {
         $sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>" . Dict::S('UI:Menu:Actions') . "\n<ul>\n";
     }
     $sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
     static $bPopupScript = false;
     if (!$bPopupScript) {
         // Output this once per page...
         $oPage->add_ready_script("\$(\"div.itop_popup>ul\").popupmenu();\n");
         $bPopupScript = true;
     }
     return $sHtml;
 }
Example #24
0
 /**
  * Helper to remove selected objects without calling any handler
  * Surpasses BulkDelete as it can handle abstract classes, but has the other limitation as it bypasses standard objects handlers 
  * 	 
  * @param string $oFilter Scope of objects to wipe out
  * @return The count of deleted objects
  */
 public static function PurgeData($oFilter)
 {
     $sTargetClass = $oFilter->GetClass();
     $oSet = new DBObjectSet($oFilter);
     $oSet->OptimizeColumnLoad(array($sTargetClass => array('finalclass')));
     $aIdToClass = $oSet->GetColumnAsArray('finalclass', true);
     $aIds = array_keys($aIdToClass);
     if (count($aIds) > 0) {
         $aQuotedIds = CMDBSource::Quote($aIds);
         $sIdList = implode(',', $aQuotedIds);
         $aTargetClasses = array_merge(self::EnumChildClasses($sTargetClass, ENUM_CHILD_CLASSES_ALL), self::EnumParentClasses($sTargetClass, ENUM_PARENT_CLASSES_EXCLUDELEAF));
         foreach ($aTargetClasses as $sSomeClass) {
             $sTable = MetaModel::DBGetTable($sSomeClass);
             $sPKField = MetaModel::DBGetKey($sSomeClass);
             $sDeleteSQL = "DELETE FROM `{$sTable}` WHERE `{$sPKField}` IN ({$sIdList})";
             CMDBSource::DeleteFrom($sDeleteSQL);
         }
     }
     return count($aIds);
 }
Example #25
0
 require_once APPROOT . 'setup/setuputils.class.inc.php';
 $aLicenses = SetupUtils::GetLicenses();
 $aItopSettings = array('cron_max_execution_time', 'timezone');
 $aPHPSettings = array('memory_limit', 'max_execution_time', 'upload_max_filesize', 'post_max_size');
 $aMySQLSettings = array('max_allowed_packet', 'key_buffer_size', 'query_cache_size');
 $aMySQLStatuses = array('Key_read_requests', 'Key_reads');
 if (extension_loaded('suhosin')) {
     $aPHPSettings[] = 'suhosin.post.max_vars';
     $aPHPSettings[] = 'suhosin.get.max_value_length';
 }
 $aMySQLVars = array();
 foreach (CMDBSource::QueryToArray('SHOW VARIABLES') as $aRow) {
     $aMySQLVars[$aRow['Variable_name']] = $aRow['Value'];
 }
 $aMySQLStats = array();
 foreach (CMDBSource::QueryToArray('SHOW GLOBAL STATUS') as $aRow) {
     $aMySQLStats[$aRow['Variable_name']] = $aRow['Value'];
 }
 // Display
 //
 $oPage->add("<div id=\"about_box\">");
 $oPage->add('<div style="margin-left: 120px;">');
 $oPage->add('<table>');
 $oPage->add('<tr>');
 $oPage->add('<td><a href="http://www.combodo.com" title="www.combodo.com" target="_blank" style="background: none;"><img src="../images/logo-combodo.png" style="float: right;"/></a></td>');
 $oPage->add('<td style="padding-left: 20px;">');
 $oPage->add($sVersionString . '<br/>');
 $oPage->add(Dict::S('UI:About:DataModel') . ': ' . $sDataModelVersion . '<br/>');
 $oPage->add('MySQL: ' . $sMySQLVersion . '<br/>');
 $oPage->add('PHP: ' . $sPHPVersion . '<br/>');
 $oPage->add('</td>');
 protected static function ClauseValues($aValues)
 {
     $aSetValues = array();
     foreach ($aValues as $sFieldSpec => $value) {
         $aSetValues[] = "{$sFieldSpec} = " . CMDBSource::Quote($value);
     }
     $sSetValues = implode(', ', $aSetValues);
     return $sSetValues;
 }
 /**
  * Updates the object form POSTED arguments, and writes it into the DB (applies a stimuli if requested)
  * @param DBObject $oObj The object to update
  * $param array $aAttList If set, this will limit the list of updated attributes	 
  * @return void
  */
 public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null)
 {
     $sTransactionId = utils::ReadPostedParam('transaction_id', '');
     if (!utils::IsTransactionValid($sTransactionId)) {
         throw new TransactionException();
     }
     $sClass = get_class($oObj);
     $sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
     $sTargetState = '';
     if (!empty($sStimulus)) {
         // Compute the target state
         $aTransitions = $oObj->EnumTransitions();
         if (!isset($aTransitions[$sStimulus])) {
             throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
         }
         $sTargetState = $aTransitions[$sStimulus]['target_state'];
     }
     $oObj->UpdateObjectFromPostedForm('', $aAttList, $sTargetState);
     // Optional: apply a stimulus
     //
     if (!empty($sStimulus)) {
         if (!$oObj->ApplyStimulus($sStimulus)) {
             throw new Exception("Cannot apply stimulus '{$sStimulus}' to {$oObj->GetName()}");
         }
     }
     if ($oObj->IsModified()) {
         // Record the change
         //
         $oObj->DBUpdate();
         // Trigger ?
         //
         $aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
         $sClassList = implode(", ", CMDBSource::Quote($aClasses));
         $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ({$sClassList})"));
         while ($oTrigger = $oSet->Fetch()) {
             $oTrigger->DoActivate($oObj->ToArgs('this'));
         }
         $this->p("<h1>" . Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()) . "</h1>\n");
     }
     $bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
     if ($bLockEnabled) {
         // Release the concurrent lock, if any
         $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
         if ($sOwnershipToken !== null) {
             // We're done, let's release the lock
             iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken);
         }
     }
 }
Example #28
0
 protected function DBDeleteSingleObject()
 {
     if (!MetaModel::DBIsReadOnly()) {
         $this->OnDelete();
         $this->RecordObjDeletion($this->m_iKey);
         // May cause a reload for storing history information
         foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef) {
             if ($oAttDef->IsHierarchicalKey()) {
                 // Update the left & right indexes for each hierarchical key
                 $sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
                 $sSQL = "SELECT `" . $oAttDef->GetSQLRight() . "` AS `right`, `" . $oAttDef->GetSQLLeft() . "` AS `left` FROM `{$sTable}` WHERE id=" . CMDBSource::Quote($this->m_iKey);
                 $aRes = CMDBSource::QueryToArray($sSQL);
                 $iMyLeft = $aRes[0]['left'];
                 $iMyRight = $aRes[0]['right'];
                 $iDelta = $iMyRight - $iMyLeft + 1;
                 MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
                 // No new parent for now, insert completely at the right of the tree
                 $sSQL = "SELECT max(`" . $oAttDef->GetSQLRight() . "`) AS max FROM `{$sTable}`";
                 $aRes = CMDBSource::QueryToArray($sSQL);
                 if (count($aRes) == 0) {
                     $iNewLeft = 1;
                 } else {
                     $iNewLeft = $aRes[0]['max'] + 1;
                 }
                 MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable);
             }
         }
         foreach (MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL) as $sParentClass) {
             $this->DBDeleteSingleTable($sParentClass);
         }
         $this->AfterDelete();
         $this->m_bIsInDB = false;
         // Fix for #926: do NOT reset m_iKey as it can be used to have it for reporting purposes (see the REST service to delete objects, reported as bug #926)
         // Thought the key is not reset, using DBInsert or DBWrite will create an object having the same characteristics and a new ID. DBUpdate is protected
     }
 }
Example #29
0
 private static function _TablesInfoCacheReset()
 {
     self::$m_aTablesInfo = array();
 }
 protected static function DoUpdateDBSchema($sMode, $aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false)
 {
     SetupPage::log_info("Update Database Schema for environment '{$sTargetEnvironment}'.");
     $oConfig = new Config();
     $aParamValues = array('mode' => $sMode, 'db_server' => $sDBServer, 'db_user' => $sDBUser, 'db_pwd' => $sDBPwd, 'db_name' => $sDBName, 'db_prefix' => $sDBPrefix);
     $oConfig->UpdateFromParams($aParamValues, $sModulesDir);
     if ($bOldAddon) {
         // Old version of the add-on for backward compatibility with pre-2.0 data models
         $oConfig->SetAddons(array('user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php'));
     }
     $oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
     $oProductionEnv->InitDataModel($oConfig, true);
     // load data model only
     // Migrate application data format
     //
     // priv_internalUser caused troubles because MySQL transforms table names to lower case under Windows
     // This becomes an issue when moving your installation data to/from Windows
     // Starting 2.0, all table names must be lowercase
     if ($sMode != 'install') {
         SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' into '{$sDBPrefix}priv_internaluser' (lowercase)");
         // This command will have no effect under Windows...
         // and it has been written in two steps so as to make it work under windows!
         CMDBSource::SelectDB($sDBName);
         try {
             $sRepair = "RENAME TABLE `{$sDBPrefix}priv_internalUser` TO `{$sDBPrefix}priv_internaluser_other`, `{$sDBPrefix}priv_internaluser_other` TO `{$sDBPrefix}priv_internaluser`";
             CMDBSource::Query($sRepair);
         } catch (Exception $e) {
             SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' failed (already done in a previous upgrade?)");
         }
         // let's remove the records in priv_change which have no counterpart in priv_changeop
         SetupPage::log_info("Cleanup of '{$sDBPrefix}priv_change' to remove orphan records");
         CMDBSource::SelectDB($sDBName);
         try {
             $sTotalCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_change`";
             $iTotalCount = (int) CMDBSource::QueryToScalar($sTotalCount);
             SetupPage::log_info("There is a total of {$iTotalCount} records in {$sDBPrefix}priv_change.");
             $sOrphanCount = "SELECT COUNT(c.id) FROM `{$sDBPrefix}priv_change` AS c left join `{$sDBPrefix}priv_changeop` AS o ON c.id = o.changeid WHERE o.id IS NULL";
             $iOrphanCount = (int) CMDBSource::QueryToScalar($sOrphanCount);
             SetupPage::log_info("There are {$iOrphanCount} useless records in {$sDBPrefix}priv_change (" . sprintf('%.2f', 100.0 * $iOrphanCount / $iTotalCount) . "%)");
             if ($iOrphanCount > 0) {
                 SetupPage::log_info("Removing the orphan records...");
                 $sCleanup = "DELETE FROM `{$sDBPrefix}priv_change` USING `{$sDBPrefix}priv_change` LEFT JOIN `{$sDBPrefix}priv_changeop` ON `{$sDBPrefix}priv_change`.id = `{$sDBPrefix}priv_changeop`.changeid WHERE `{$sDBPrefix}priv_changeop`.id IS NULL;";
                 CMDBSource::Query($sCleanup);
                 SetupPage::log_info("Cleanup completed successfully.");
             } else {
                 SetupPage::log_info("Ok, nothing to cleanup.");
             }
         } catch (Exception $e) {
             SetupPage::log_info("Cleanup of orphan records in `{$sDBPrefix}priv_change` failed: " . $e->getMessage());
         }
     }
     // Module specific actions (migrate the data)
     //
     $aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT . $sModulesDir);
     foreach ($aAvailableModules as $sModuleId => $aModule) {
         if ($sModuleId != ROOT_MODULE && in_array($sModuleId, $aSelectedModules) && isset($aAvailableModules[$sModuleId]['installer'])) {
             $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
             SetupPage::log_info("Calling Module Handler: {$sModuleInstallerClass}::BeforeDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
             $aCallSpec = array($sModuleInstallerClass, 'BeforeDatabaseCreation');
             call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
         }
     }
     if (!$oProductionEnv->CreateDatabaseStructure(MetaModel::GetConfig(), $sMode)) {
         throw new Exception("Failed to create/upgrade the database structure for environment '{$sTargetEnvironment}'");
     }
     // priv_change now has an 'origin' field to distinguish between the various input sources
     // Let's initialize the field with 'interactive' for all records were it's null
     // Then check if some records should hold a different value, based on a pattern matching in the userinfo field
     CMDBSource::SelectDB($sDBName);
     try {
         $sCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_change` WHERE `origin` IS NULL";
         $iCount = (int) CMDBSource::QueryToScalar($sCount);
         if ($iCount > 0) {
             SetupPage::log_info("Initializing '{$sDBPrefix}priv_change.origin' ({$iCount} records to update)");
             // By default all uninitialized values are considered as 'interactive'
             $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'interactive' WHERE `origin` IS NULL";
             CMDBSource::Query($sInit);
             // CSV Import was identified by the comment at the end
             $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-import.php' WHERE `userinfo` LIKE '%Web Service (CSV)'";
             CMDBSource::Query($sInit);
             // CSV Import was identified by the comment at the end
             $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-interactive' WHERE `userinfo` LIKE '%(CSV)' AND origin = 'interactive'";
             CMDBSource::Query($sInit);
             // Syncho data sources were identified by the comment at the end
             // Unfortunately the comment is localized, so we have to search for all possible patterns
             $sCurrentLanguage = Dict::GetUserLanguage();
             foreach (Dict::GetLanguages() as $sLangCode => $aLang) {
                 Dict::SetUserLanguage($sLangCode);
                 $sSuffix = CMDBSource::Quote('%' . Dict::S('Core:SyncDataExchangeComment'));
                 $aSuffixes[$sSuffix] = true;
             }
             Dict::SetUserLanguage($sCurrentLanguage);
             $sCondition = "`userinfo` LIKE " . implode(" OR `userinfo` LIKE ", array_keys($aSuffixes));
             $sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'synchro-data-source' WHERE ({$sCondition})";
             CMDBSource::Query($sInit);
             SetupPage::log_info("Initialization of '{$sDBPrefix}priv_change.origin' completed.");
         } else {
             SetupPage::log_info("'{$sDBPrefix}priv_change.origin' already initialized, nothing to do.");
         }
     } catch (Exception $e) {
         SetupPage::log_error("Initializing '{$sDBPrefix}priv_change.origin' failed: " . $e->getMessage());
     }
     // priv_async_task now has a 'status' field to distinguish between the various statuses rather than just relying on the date columns
     // Let's initialize the field with 'planned' or 'error' for all records were it's null
     CMDBSource::SelectDB($sDBName);
     try {
         $sCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_async_task` WHERE `status` IS NULL";
         $iCount = (int) CMDBSource::QueryToScalar($sCount);
         if ($iCount > 0) {
             SetupPage::log_info("Initializing '{$sDBPrefix}priv_async_task.status' ({$iCount} records to update)");
             $sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'planned' WHERE (`status` IS NULL) AND (`started` IS NULL)";
             CMDBSource::Query($sInit);
             $sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'error' WHERE (`status` IS NULL) AND (`started` IS NOT NULL)";
             CMDBSource::Query($sInit);
             SetupPage::log_info("Initialization of '{$sDBPrefix}priv_async_task.status' completed.");
         } else {
             SetupPage::log_info("'{$sDBPrefix}priv_async_task.status' already initialized, nothing to do.");
         }
     } catch (Exception $e) {
         SetupPage::log_error("Initializing '{$sDBPrefix}priv_async_task.status' failed: " . $e->getMessage());
     }
     SetupPage::log_info("Database Schema Successfully Updated for environment '{$sTargetEnvironment}'.");
 }