/** * 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"); }
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); }
/** * 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); } } }
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) . ")"); } } } }
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}"; } }
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"; }
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; }
/** * 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); }
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); } } }
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 } }
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}'."); }