diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index c14eaa9f7..87088fd41 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -455,8 +455,7 @@ abstract class cmdbAbstractObject extends CMDBObject { $aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode); } - $sHtml = '#'.$oSet->GetFilter()->ToOQL()."\n"; - $sHtml .= implode($sSeparator, $aHeader)."\n"; + $sHtml = implode($sSeparator, $aHeader)."\n"; $oSet->Seek(0); while ($oObj = $oSet->Fetch()) { @@ -464,7 +463,7 @@ abstract class cmdbAbstractObject extends CMDBObject $aRow[] = $oObj->GetKey(); foreach($aList as $sAttCode) { - $aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, '\\'); + $aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, $sTextQualifier); } $sHtml .= implode($sSeparator, $aRow)."\n"; } diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index e8e48e07c..c01ff2c64 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -53,7 +53,7 @@ class NiceWebPage extends WebPage foreach($aChoices as $sKey => $sValue) { $sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : ""; - $this->add(""); + $this->add(""); } $this->add(""); } diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 076453ade..b9728acae 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -61,6 +61,7 @@ abstract class AttributeDefinition private $m_aParams = array(); private $m_sHostClass = array(); protected function Get($sParamName) {return $this->m_aParams[$sParamName];} + protected function IsParam($sParamName) {return (array_key_exists($sParamName, $this->m_aParams));} public function __construct($sCode, $aParams) { @@ -194,9 +195,9 @@ abstract class AttributeDefinition return Str::pure2xml((string)$sValue); } - public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"') { - return str_replace($sSeparator, $sSepEscape, (string)$sValue); + return (string)$sValue; } public function GetAllowedValues($aArgs = array(), $sBeginsWith = '') @@ -233,7 +234,34 @@ class AttributeLinkedSet extends AttributeDefinition public function GetValuesDef() {return $this->Get("allowed_values");} public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} - public function GetDefaultValue() {return DBObjectSet::FromScratch($this->Get('linked_class'));} + public function GetDefaultValue($aArgs = array()) + { + // Note: so far, this feature is a prototype, + // later, the argument 'this' should always be present in the arguments + // + if (($this->IsParam('default_value')) && array_key_exists('this', $aArgs)) + { + $oSet = $this->Get('default_value'); + return $oSet->GetValues($aArgs); + } + else + { + return DBObjectSet::FromScratch($this->Get('linked_class')); + } + } + + public function GetSupportedRelations() + { + if (array_key_exists('supported_relations', $this->m_aParams)) + { + $aSupportedRelations = $this->Get('supported_relations'); + return $aSupportedRelations; + } + else + { + return array(); + } + } public function GetLinkedClass() {return $this->Get('linked_class');} public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');} @@ -252,7 +280,7 @@ class AttributeLinkedSet extends AttributeDefinition return "ERROR: LIST OF OBJECTS"; } - public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"') { return "ERROR: LIST OF OBJECTS"; } @@ -598,6 +626,14 @@ class AttributeString extends AttributeDBField { return $value; } + + public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"') + { + $sFrom = array("\r\n", $sTextQualifier); + $sTo = array("\n", $sTextQualifier.$sTextQualifier); + $sEscaped = str_replace($sFrom, $sTo, (string)$sValue); + return '"'.$sEscaped.'"'; + } } /** @@ -678,11 +714,6 @@ class AttributeText extends AttributeString { return Str::pure2xml($value); } - - public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') - { - return str_replace("\n", "[newline]", parent::GetAsCSV($sValue, $sSeparator, $sSepEscape)); - } } /** @@ -982,9 +1013,12 @@ class AttributeDate extends AttributeDBField return Str::pure2xml($value); } - public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"') { - return str_replace($sSeparator, $sSepEscape, $value); + $sFrom = array("\r\n", $sTextQualifier); + $sTo = array("\n", $sTextQualifier.$sTextQualifier); + $sEscaped = str_replace($sFrom, $sTo, (string)$sValue); + return '"'.$sEscaped.'"'; } } @@ -1231,10 +1265,10 @@ class AttributeExternalField extends AttributeDefinition $oExtAttDef = $this->GetExtAttDef(); return $oExtAttDef->GetAsXML($value); } - public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"') { $oExtAttDef = $this->GetExtAttDef(); - return $oExtAttDef->GetAsCSV($value); + return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier); } } @@ -1394,7 +1428,7 @@ class AttributeBlob extends AttributeDefinition } } - public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"') { return ''; // Not exportable in CSV ! } diff --git a/core/csvparser.class.inc.php b/core/csvparser.class.inc.php index b6c6334a3..20b952427 100644 --- a/core/csvparser.class.inc.php +++ b/core/csvparser.class.inc.php @@ -1,8 +1,7 @@ @@ -19,6 +18,16 @@ class CSVParserException extends CoreException +define('stSTARTING', 1); //grey zone: the type is undetermined +define('stRAW', 2); //building a non-qualified string +define('stQUALIFIED', 3); //building qualified string +define('stESCAPED', 4); //just encountered an escape char + +define('evSEPARATOR', 1); +define('evNEWLINE', 2); +define('evTEXTQUAL', 3); // used for escaping as well +define('evOTHERCHAR', 4); + /** * CSVParser @@ -34,157 +43,148 @@ class CSVParser { private $m_sCSVData; private $m_sSep; - private $m_iSkip; + private $m_sTextQualifier; - public function __construct($sTxt) - { - $this->m_sCSVData = $sTxt; - } - - public function SetSeparator($sSep) + public function __construct($sTxt, $sSep = ',', $sTextQualifier = '"') { + $this->m_sCSVData = str_replace("\r\n", "\n", $sTxt); $this->m_sSep = $sSep; - } - public function GetSeparator() - { - return $this->m_sSep; + $this->m_sTextQualifier = $sTextQualifier; } - public function SetSkipLines($iSkip) - { - $this->m_iSkip = $iSkip; - } - public function GetSkipLines() - { - return $this->m_iSkip; - } + protected $m_sCurrCell = ''; + protected $m_aCurrRow = array(); + protected $m_iToSkip = 0; + protected $m_aDataSet = array(); - public function GuessSeparator() + protected function __AddChar($c) { - // Note: skip the first line anyway - - $aKnownSeps = array(';', ',', "\t"); // Use double quote for special chars!!! - $aStatsBySeparator = array(); - foreach ($aKnownSeps as $sSep) + $this->m_sCurrCell .= $c; + } + protected function __ClearCell() + { + $this->m_sCurrCell = ''; + } + protected function __AddCell($c = null, $aFieldMap = null) + { + if (!is_null($aFieldMap)) { - $aStatsBySeparator[$sSep] = array(); + $iNextCol = count($this->m_aCurrRow); + $iNextName = $aFieldMap[$iNextCol]; + $this->m_aCurrRow[$iNextName] = $this->m_sCurrCell; } - - foreach(explode("\n", $this->m_sCSVData) as $sLine) + else { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - $aLineCharsCount = count_chars($sLine, 0); - foreach ($aKnownSeps as $sSep) + $this->m_aCurrRow[] = $this->m_sCurrCell; + } + $this->m_sCurrCell = ''; + } + protected function __AddRow($c = null, $aFieldMap = null) + { + $this->__AddCell($c, $aFieldMap); + + if ($this->m_iToSkip > 0) + { + $this->m_iToSkip--; + } + elseif (count($this->m_aCurrRow) > 1) + { + $this->m_aDataSet[] = $this->m_aCurrRow; + } + elseif ((count($this->m_aCurrRow) == 1) && (strlen($this->m_aCurrRow[0]) > 0)) + { + $this->m_aDataSet[] = $this->m_aCurrRow; + } + else + { + // blank line, skip silently + } + $this->m_aCurrRow = array(); + } + + function ToArray($iToSkip = 1, $aFieldMap = null, $iMax = 0) + { + $aTransitions = array(); + + $aTransitions[stSTARTING][evSEPARATOR] = array('__AddCell', stSTARTING); + $aTransitions[stSTARTING][evNEWLINE] = array('__AddRow', stSTARTING); + $aTransitions[stSTARTING][evTEXTQUAL] = array('', stQUALIFIED); + $aTransitions[stSTARTING][evOTHERCHAR] = array('__AddChar', stRAW); + + $aTransitions[stRAW][evSEPARATOR] = array('__AddCell', stSTARTING); + $aTransitions[stRAW][evNEWLINE] = array('__AddRow', stSTARTING); + $aTransitions[stRAW][evTEXTQUAL] = array('__AddChar', stRAW); + $aTransitions[stRAW][evOTHERCHAR] = array('__AddChar', stRAW); + + $aTransitions[stQUALIFIED][evSEPARATOR] = array('__AddChar', stQUALIFIED); + $aTransitions[stQUALIFIED][evNEWLINE] = array('__AddChar', stQUALIFIED); + $aTransitions[stQUALIFIED][evTEXTQUAL] = array('', stESCAPED); + $aTransitions[stQUALIFIED][evOTHERCHAR] = array('__AddChar', stQUALIFIED); + + $aTransitions[stESCAPED][evSEPARATOR] = array('__AddCell', stSTARTING); + $aTransitions[stESCAPED][evNEWLINE] = array('__AddRow', stSTARTING); + $aTransitions[stESCAPED][evTEXTQUAL] = array('__AddChar', stQUALIFIED); + $aTransitions[stESCAPED][evOTHERCHAR] = array('__AddChar', stSTARTING); + + // Reset parser variables + $this->m_sCurrCell = ''; + $this->m_aCurrRow = array(); + $this->m_iToSkip = $iToSkip; + $this->m_aDataSet = array(); + + $iState = stSTARTING; + for($i = 0; $i < strlen($this->m_sCSVData) ; $i++) + { + $c = $this->m_sCSVData[$i]; + +// // Note: I did that because the unit test was not working fine (file edited with notepad: \n chars padded :-( +// if (ord($c) == 0) continue; + + if ($c == $this->m_sSep) { - $aStatsBySeparator[$sSep][] = $aLineCharsCount[ord($sSep)]; + $iEvent = evSEPARATOR; } - } - - // Default to ',' - $this->SetSeparator(","); - - foreach ($aKnownSeps as $sSep) - { - // Note: this function is NOT available :-( - // stats_variance($aStatsBySeparator[$sSep]); - $iMin = min($aStatsBySeparator[$sSep]); - $iMax = max($aStatsBySeparator[$sSep]); - if (($iMin == $iMax) && ($iMax > 0)) + elseif ($c == "\n") { - $this->SetSeparator($sSep); - break; + $iEvent = evNEWLINE; } - } - return $this->GetSeparator(); - } - - public function GuessSkipLines() - { - // Take the FIRST -valuable- LINE ONLY - // If there is a number, then for sure this is not a header line - // Otherwise, we may consider that there is one line to skip - foreach(explode("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - foreach (explode($this->m_sSep, $sLine) as $value) + elseif ($c == $this->m_sTextQualifier) { - if (is_numeric($value)) + $iEvent = evTEXTQUAL; + } + else + { + $iEvent = evOTHERCHAR; + } + + $sAction = $aTransitions[$iState][$iEvent][0]; + $iState = $aTransitions[$iState][$iEvent][1]; + + if (!empty($sAction)) + { + $aCallSpec = array($this, $sAction); + if (is_callable($aCallSpec)) { - $this->SetSkipLines(0); - return 0; + call_user_func($aCallSpec, $c, $aFieldMap); + } + else + { + throw new CSVParserException("CSVParser: unknown verb '$sAction'"); } } - $this->SetSkipLines(1); - return 1; - } - } - function ToArray($aFieldMap = null, $iMax = 0) - { - // $aFieldMap is an array of col_index=>col_name - // $iMax is to limit the count of rows computed - $aRes = array(); - - $iCount = 0; - $iSkipped = 0; - foreach(explode("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - if ($iSkipped < $this->m_iSkip) - { - $iSkipped++; - continue; - } - - foreach (explode($this->m_sSep, $sLine) as $iCol=>$sValue) - { - if (is_array($aFieldMap)) $sColRef = $aFieldMap[$iCol]; - else $sColRef = $iCol; - $aRes[$iCount][$sColRef] = $sValue; - } - - $iCount++; - if (($iMax > 0) && ($iCount >= $iMax)) break; + $iLineCount = count($this->m_aDataSet); + if (($iMax > 0) && ($iLineCount >= $iMax)) break; } - return $aRes; + // Close the final line + $this->__AddRow(null, $aFieldMap); + return $this->m_aDataSet; } public function ListFields() { - // Take the first valuable line - foreach(explode("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - // We've got the first valuable line, that's it! - break; - } - - $aRet = array(); - foreach (explode($this->m_sSep, $sLine) as $iCol=>$value) - { - if ($this->m_iSkip == 0) - { - // No header to help us - $sLabel = "field $iCol"; - } - else - { - $sLabel = "$value"; - } - $aRet[] = $sLabel; - } - return $aRet; + $aHeader = $this->ToArray(0, null, 1); + return $aHeader[0]; } } diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 7d18d74fa..f0ddffe8d 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -381,10 +381,10 @@ abstract class DBObject return $oAtt->GetAsXML($this->Get($sAttCode)); } - public function GetAsCSV($sAttCode, $sSeparator = ';', $sSepEscape = ',') + public function GetAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"') { $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); - return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sSepEscape); + return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier); } protected static function MakeHyperLink($sObjClass, $sObjKey, $aAvailableFields) @@ -883,6 +883,7 @@ abstract class DBObject $aScalarArgs = array(); $aScalarArgs[$sArgName] = $this->GetKey(); $aScalarArgs[$sArgName.'->id'] = $this->GetKey(); + $aScalarArgs[$sArgName.'->object()'] = $this; $aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink(); $aScalarArgs[$sArgName.'->name()'] = $this->GetName(); @@ -903,6 +904,38 @@ abstract class DBObject public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) { + foreach (MetaModel::GetLinkedSets($sClass) as $sAttCode => $oAttDef) + { + $aSupportedRelations = $oAttDef->GetSupportedRelations(); + if (!array_key_exists($sRelCode, $aSupportedRelations)) continue; //skip + + $bPropagate = true; // #@# Todo: discuss that setting + $iDepth = $bPropagate ? $iMaxDepth - 1 : 0; + + $oNeighbors = $this->Get($sAttCode); + while ($oObj = $oObjSet->Fetch()) + { + $sRootClass = MetaModel::GetRootClass(get_class($oObj)); + $sObjKey = $oObj->GetKey(); + if (array_key_exists($sRootClass, $aResults)) + { + if (array_key_exists($sObjKey, $aResults[$sRootClass])) + { + continue; // already visited, skip + } + } + + $aResults[$sRootClass][$sObjKey] = $oObj; + if ($iDepth > 0) + { + $oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults); + } + } + } + + return; + + // #@# todo : Discuss the Relations and the way they are defined (do we deprecate the queries ? what are the properties -e.g. depth- and where do we set them ?) foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo) { MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]); diff --git a/core/metamodel.class.php b/core/metamodel.class.php index acd8003c4..a1ec24097 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -458,6 +458,19 @@ abstract class MetaModel return $aExtKeys; } + final static public function GetLinkedSets($sClass) + { + $aLinkedSets = array(); + foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt) + { + if (is_subclass_of($oAtt, 'AttributeLinkedSet')) + { + $aLinkedSets[$sAttCode] = $oAtt; + } + } + return $aLinkedSets; + } + final static public function GetExternalFields($sClass, $sKeyAttCode) { $aExtFields = array(); @@ -2039,13 +2052,16 @@ abstract class MetaModel list($aErrors, $aSugFix) = self::DBCheckFormat(); $aSQL = array(); - foreach ($aSugFix as $sClass => $aQueries) + foreach ($aSugFix as $sClass => $aTarget) { - foreach ($aQueries as $sQuery) + foreach ($aTarget as $aQueries) { - //$aSQL[] = $sQuery; - // forces a refresh of cached information - CMDBSource::CreateTable($sQuery); + foreach ($aQueries as $sQuery) + { + //$aSQL[] = $sQuery; + // forces a refresh of cached information + CMDBSource::CreateTable($sQuery); + } } } // does not work -how to have multiple statements in a single query? @@ -2078,15 +2094,15 @@ abstract class MetaModel $sAutoIncrement = (self::IsAutoIncrementKey($sClass) ? "AUTO_INCREMENT" : ""); if (!CMDBSource::IsTable($sTable)) { - $aErrors[$sClass][] = "table '$sTable' could not be found into the DB"; - $aSugFix[$sClass][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = innodb CHARACTER SET utf8 COLLATE utf8_unicode_ci"; + $aErrors[$sClass]['*'][] = "table '$sTable' could not be found into the DB"; + $aSugFix[$sClass]['*'][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = innodb CHARACTER SET utf8 COLLATE utf8_unicode_ci"; } // Check that the key field exists // elseif (!CMDBSource::IsField($sTable, $sKeyField)) { - $aErrors[$sClass][] = "key '$sKeyField' (table $sTable) could not be found"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY"; + $aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) could not be found"; + $aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY"; } else { @@ -2094,13 +2110,13 @@ abstract class MetaModel // if (!CMDBSource::IsKey($sTable, $sKeyField)) { - $aErrors[$sClass][] = "key '$sKeyField' is not a key for table '$sTable'"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)"; + $aErrors[$sClass]['id'][] = "key '$sKeyField' is not a key for table '$sTable'"; + $aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)"; } if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField)) { - $aErrors[$sClass][] = "key '$sKeyField' (table $sTable) is not automatically incremented"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` `$sKeyField` INT(11) NOT NULL AUTO_INCREMENT"; + $aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) is not automatically incremented"; + $aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` `$sKeyField` INT(11) NOT NULL AUTO_INCREMENT"; } } @@ -2118,11 +2134,11 @@ abstract class MetaModel $sFieldSpecs = $oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL"; if (!CMDBSource::IsField($sTable, $sField)) { - $aErrors[$sClass][] = "field '$sField' could not be found in table '$sTable'"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs"; + $aErrors[$sClass][$sAttCode][] = "field '$sField' could not be found in table '$sTable'"; + $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs"; if ($oAttDef->IsExternalKey()) { - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; + $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; } } else @@ -2135,30 +2151,30 @@ abstract class MetaModel $bToBeChanged = true; if ($oAttDef->IsNullAllowed()) { - $aErrors[$sClass][] = "field '$sField' in table '$sTable' could be NULL"; + $aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' could be NULL"; } else { - $aErrors[$sClass][] = "field '$sField' in table '$sTable' could NOT be NULL"; + $aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' could NOT be NULL"; } } $sActualFieldType = CMDBSource::GetFieldType($sTable, $sField); if (strcasecmp($sDBFieldType, $sActualFieldType) != 0) { $bToBeChanged = true; - $aErrors[$sClass][] = "field '$sField' in table '$sTable' has a wrong type: found '$sActualFieldType' while expecting '$sDBFieldType'"; + $aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found '$sActualFieldType' while expecting '$sDBFieldType'"; } if ($bToBeChanged) { - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; } // Create indexes (external keys only... so far) // if ($oAttDef->IsExternalKey() && !CMDBSource::HasIndex($sTable, $sField)) { - $aErrors[$sClass][] = "Foreign key '$sField' in table '$sTable' should have an index"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; + $aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index"; + $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; } } } diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index e84b0c2b5..1d9e315c7 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -35,7 +35,7 @@ abstract class ValueSetDefinition } - public function GetValues($aArgs, $sBeginsWith) + public function GetValues($aArgs, $sBeginsWith = '') { if (!$this->m_bIsLoaded) { @@ -116,6 +116,55 @@ class ValueSetObjects extends ValueSetDefinition } +/** + * Set of existing values for a link set attribute, given a relation code + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetRelatedObjects extends ValueSetDefinition +{ + protected $m_sRelationCode; + protected $m_iMaxDepth; +// protected $m_aOrderBy; + + public function __construct($sRelationCode, $iMaxDepth = 99) + { + $this->m_sRelationCode = $sRelationCode; + $this->m_iMaxDepth = $iMaxDepth; +// $this->m_aOrderBy = $aOrderBy; + } + + protected function LoadValues($aArgs) + { + $this->m_aValues = array(); + + if (!array_key_exists('this', $aArgs)) + { + throw new CoreException("Missing 'this' in arguments", array('args' => $aArgs)); + } + + $oTarget = $aArgs['this->object()']; + + $oTargetNeighbors = $oTarget->GetRelatedObjects($this->m_sRelationCode, $this->m_iMaxDepth); + while ($oObject = $oTargetNeighbors->Fetch()) + { + $this->m_aValues[$oObject->GetKey()] = $oObject->GetAsHTML($oObject->GetName()); + } + return true; + } + + public function GetValuesDescription() + { + return 'Filter: '.$this->m_sFilterExpr; + } +} + + /** * Fixed set values (could be hardcoded in the business model) * diff --git a/pages/ITopConsultant.php b/pages/ITopConsultant.php index 022788c8c..3efbd6890 100644 --- a/pages/ITopConsultant.php +++ b/pages/ITopConsultant.php @@ -254,7 +254,7 @@ function DumpDatabase() function printMenu($sConfigFile) { $sClassCount = count(MetaModel::GetClasses()); - $bHasDB = MetaModel::DBExists(); + $bHasDB = MetaModel::DBExists(false); // no need to be complete to consider that something already exists $sUrl = "?config=".urlencode($sConfigFile); echo "
\n"; @@ -345,22 +345,25 @@ function DisplayDBFormatIssues($aErrors, $aSugFix, $sRepairUrl = "", $sSQLStatem echo "
"; echo "

Wrong Database format

\n"; echo "

The current database is not consistent with the given business model. Please investigate.

\n"; - foreach ($aErrors as $sClass => $aMessages) + foreach ($aErrors as $sClass => $aTarget) { echo "

Wrong declaration (or DB format ?) for class $sClass

\n"; echo "
    \n"; $i = 0; - foreach ($aMessages as $sMsg) + foreach ($aTarget as $sTarget => $aMessages) { + echo "

    Wrong declaration for attribute $sTarget

    \n"; + $sMsg = implode(' AND ', $aMessages); if (!empty($sRepairUrl)) { - $aSQLFixes[] = $aSugFix[$sClass][$i]; - $sUrl = "$sRepairUrl&$sSQLStatementArgName=".urlencode($aSugFix[$sClass][$i]); - echo "
  • $sMsg (fix it now!)
  • \n"; + $aSQLFixes = array_merge($aSQLFixes, $aSugFix[$sClass][$sTarget]); + $sSQLFixes = implode('; ', $aSugFix[$sClass][$sTarget]); + $sUrl = "$sRepairUrl&$sSQLStatementArgName=".urlencode($sSQLFixes); + echo "
  • $sMsg (fix it now!)
  • \n"; } else { - echo "
  • $sMsg ({$aSugFix[$sClass][$i]})
  • \n"; + echo "
  • $sMsg (".htmlentities($sSQLFixes).")
  • \n"; } $i++; } diff --git a/pages/advanced_search.php b/pages/advanced_search.php index aa503c610..5629973d8 100644 --- a/pages/advanced_search.php +++ b/pages/advanced_search.php @@ -226,8 +226,8 @@ function Page3_ViewResults($oPage, $oFilter) $oSet = new CMDBObjectSet($oFilter); $oPage->p("Found ".$oSet->Count()." items"); - $sFilterPhrase = $oFilter->serialize(); - $oPage->p("See detailed results"); + $sFilterPhrase = urlencode($oFilter->serialize()); + $oPage->p("See detailed results"); } } diff --git a/pages/csvimport.php b/pages/csvimport.php index 8ba2e60f4..7fb11789b 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -62,7 +62,7 @@ function MakeExtFieldSelectValue($sAttCode, $sExtAttCode) function ShowTableForm($oPage, $oCSVParser, $sClass) { - $aData = $oCSVParser->ToArray(null, 3); + $aData = $oCSVParser->ToArray(1, null, 3); $aColToRow = array(); foreach($aData as $aRow) { @@ -193,6 +193,10 @@ function ShowTableForm($oPage, $oCSVParser, $sClass) { $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; } + else + { + // Houston... + } } $oPage->details($aFields); } @@ -354,12 +358,24 @@ function Do_Welcome($oPage, $sClass) $sWiztep = "1_welcome"; $oPage->p("

    Bulk load from CSV data / step 1

    "); + // Reload values (in case we are reaching this page from the next one $sCSVData = utils::ReadPostedParam('csvdata'); + $sSep = utils::ReadPostedParam('separator', ','); + $sTQualif = utils::ReadPostedParam('textqualifier', '"'); + + $aSeparators = array(',' => ', (coma)', ';' => ';', ';' => ';', '|' => '|', '#' => '#', '@' => '@', ':' => ':'); + $aTextQualifiers = array('"' => '"', "'" => "'", '`' => '`', '/' => '/'); $oPage->add("
    "); $oPage->MakeClassesSelect("class", $sClass, 50, UR_ACTION_BULK_MODIFY); $oPage->add("
    "); - $oPage->add(""); + $oPage->add(""); + $oPage->add("
    "); + $oPage->add("Separator: "); + $oPage->add_select($aSeparators, 'separator', $sSep, 50); + $oPage->add("
    "); + $oPage->add("Text qualifier: "); + $oPage->add_select($aTextQualifiers, 'textqualifier', $sTQualif, 50); $oPage->add("
    "); $oPage->add(""); $oPage->add("
    \n"); @@ -415,12 +431,13 @@ function Do_Format($oPage, $sClass) $sWiztep = "2_format"; $sCSVData = utils::ReadPostedParam('csvdata'); - $oCSVParser = new CSVParser($sCSVData); - $sSep = $oCSVParser->GuessSeparator(); - $iSkip = $oCSVParser->GuessSkipLines(); + $sSep = utils::ReadPostedParam('separator'); + $sTQualif = utils::ReadPostedParam('textqualifier'); + $oCSVParser = new CSVParser($sCSVData, $sSep, $sTQualif); + $iSkip = 1; // No data ? - $aData = $oCSVParser->ToArray(null); + $aData = $oCSVParser->ToArray(); $iTarget = count($aData); if ($iTarget == 0) { @@ -429,29 +446,32 @@ function Do_Format($oPage, $sClass) return; } - // Guess the format : - $oPage->p("Guessed separator: '$sSep' (ASCII=".ord($sSep).")"); - $oPage->p("Guessed # of lines to skip: $iSkip"); + // Expected format - to be improved + $oPage->p("Separator: '$sSep'"); + $oPage->p("Text qualifier: '$sTQualif'"); + $oPage->p("The first line will be skipped (considered as being the list of fields)"); $oPage->p("Target: $iTarget rows"); - $oPage->Add(""); + $oPage->add(""); ShowTableForm($oPage, $oCSVParser, $sClass); - $oPage->Add(""); - $oPage->Add(""); - $oPage->Add(""); - $oPage->Add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); - $oPage->Add(""); - $oPage->add(""); - $oPage->Add(""); - $oPage->Add("
    "); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); } function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) { $sCSVData = utils::ReadPostedParam('csvdata'); $sSep = utils::ReadPostedParam('separator'); + $sTQualif = utils::ReadPostedParam('textqualifier'); $iSkip = utils::ReadPostedParam('skiplines'); $aFieldMap = utils::ReadPostedParam('fmap'); $aIsReconcKey = utils::ReadPostedParam('iskey'); @@ -465,16 +485,15 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) return; } - $oCSVParser = new CSVParser($sCSVData); - $oCSVParser->SetSeparator($sSep); - $oCSVParser->SetSkipLines($iSkip); - $aData = $oCSVParser->ToArray(null); + $oCSVParser = new CSVParser($sCSVData, $sSep, $sTQualif); + $aData = $oCSVParser->ToArray($iSkip, null); $iTarget = count($aData); $oPage->p("

    Goal summary

    "); $oPage->p("Target: $iTarget rows"); - $aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); + $aSampleData = $oCSVParser->ToArray($iSkip, array_keys($aFieldMap), 5); + $aDisplayConfig = array(); $aExtKeys = array(); foreach ($aFieldMap as $sFieldId=>$sColDesc) @@ -519,6 +538,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $aDisplayConfig[$sFieldId] = array("label"=>"-?-?-$sColDesc-?-?-", "description"=>""); } } + $oPage->table($aDisplayConfig, $aSampleData); if ($oChange) @@ -558,8 +578,9 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $oPage->add("
    "); $oPage->add(""); - $oPage->add(""); - $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); $oPage->add(""); $oPage->add_input_hidden("fmap", $aFieldMap); $oPage->add_input_hidden("iskey", $aIsReconcKey); diff --git a/pages/index.php b/pages/index.php index 4cbac19c2..01297ed74 100644 --- a/pages/index.php +++ b/pages/index.php @@ -149,36 +149,6 @@ function DisplayChangesLog(WebPage $oPage, $sClassName, $sKey) $oPage->p("Delete this object (no confirmation!)"); } -function DumpObjectsAsCSV(WebPage $oPage, $sClassName, $oSearchFilter = null, $sSeparator = ",") -{ - global $oContext; - - $aHeader = array(); - $aHeader[] = 'pkey'; - foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef) - { - $aHeader[] = $oAttDef->GetLabel(); - } - $oPage->Add(join($sSeparator, $aHeader)."\n"); - - if ($oSearchFilter == null) - { - $oSearchFilter = $oContext->NewFilter($sClassName); - } - $oObjectSet = new CMDBObjectSet($oSearchFilter); - - while ($oObj = $oObjectSet->Fetch()) - { - $aRow = array(); - $aRow[] = $oObj->GetKey(); - foreach($oObj->GetAttributesList($sClassName) as $sAttCode) - { - $aRow[] = $oObj->GetAsCSV($sAttCode); - } - $oPage->Add(join($sSeparator, $aRow)."\n"); - } -} - function DumpObjects(WebPage $oPage, $sClassName, CMDBSearchFilter $oSearchFilter = null) { global $oContext; @@ -554,32 +524,6 @@ switch($operation) DeleteObject($oPage, $sClass, $sKey); break; - case 'direct': - $sFilter = ReadParam('filter'); - $sFormat = ReadParam('format', 'html'); - $oSearchFilter = CMDBSearchFilter::unserialize($sFilter); - switch($sFormat) - { - case 'csv': - $oPage->small_p($oSearchFilter->__DescribeHTML()); - $oPage->Add(""); - break; - - case 'xls': - $oPage->add_header('Content-disposition: attachment;filename=served.xls'); // Will fool Excel - $oPage->add_header('Content-Type: application/vnd.ms-excel'); // Will fool Excel - DumpObjects($oPage, $oSearchFilter->GetClass(), $oSearchFilter); - break; - - case 'html': - default: - $oSet = new CMDBObjectSet($oSearchFilter); - cmdbAbstractObject::DisplaySet($oPage, $oSet); - } - break; - case 'addlinks': $sClass = ReadParam('class'); $sKey = ReadParam('key'); diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 341d313e5..137fad97d 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -1,1590 +1,1682 @@ -new FieldExpression('column1', 'myTableAlias'), 'column2'=>new FieldExpression('column2', 'myTableAlias')), - $oCondition = new BinaryExpression(new FieldExpression('column1', 'myTableAlias'), 'LIKE', new ScalarExpression('trash')), - $aFullTextNeedles = array('column1'), - $bToDelete = false, - $aValues = array() - ); - $oQuery->AddCondition(Expression::FromOQL('DATE(NOW() - 1200 * 2) > \'2008-07-31\'')); - - $oSubQuery1 = new SQLQuery( - $sTable = 'myTable1', - $sTableAlias = 'myTable1Alias', - $aFields = array('column1_1'=>new FieldExpression('column1', 'myTableAlias'), 'column1_2'=>new FieldExpression('column1', 'myTableAlias')), - $oCondition = new TrueSQLExpression, - $aFullTextNeedles = array(), - $bToDelete = false, - $aValues = array() - ); - - $oSubQuery2 = new SQLQuery( - $sTable = 'myTable2', - $sTableAlias = 'myTable2Alias', - $aFields = array('column2_1'=>new FieldExpression('column2', 'myTableAlias'), 'column2_2'=>new FieldExpression('column2', 'myTableAlias')), - $oCondition = new TrueSQLExpression, - $aFullTextNeedles = array(), - $bToDelete = false, - $aValues = array() - ); - - $oQuery->AddInnerJoin($oSubQuery1, 'column1', 'column1_1'); - $oQuery->AddLeftJoin($oSubQuery2, 'column2', 'column2_2'); - - $oQuery->DisplayHtml(); - $oQuery->RenderDelete(); - $oQuery->RenderUpdate(); - echo '

    '.$oQuery->RenderSelect().'

    '; - $oQuery->RenderSelect(array('column1')); - $oQuery->RenderSelect(array('column1', 'column2')); - } -} - -class TestOQLParser extends TestFunction -{ - static public function GetName() {return 'Check OQL parsing';} - static public function GetDescription() {return 'Attempts a series of queries, and in particular those with a bad syntax';} - - protected function CheckQuery($sQuery, $bIsCorrectQuery) - { - $oOql = new OqlInterpreter($sQuery); - try - { - $oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery - MyHelpers::var_dump_html($oTrash, true); - } - catch (OQLException $OqlException) - { - if ($bIsCorrectQuery) - { - echo "

    More info on this unexpected failure:
    ".$OqlException->getHtmlDesc()."

    \n"; - throw $OqlException; - return false; - } - else - { - // Everything is fine :-) - echo "

    More info on this expected failure:
    ".$OqlException->getHtmlDesc()."

    \n"; - return true; - } - } - // The query was correctly parsed, was it expected to be correct ? - if ($bIsCorrectQuery) - { - return true; - } - else - { - throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); - return false; - } - } - - protected function TestQuery($sQuery, $bIsCorrectQuery) - { - if (!$this->CheckQuery($sQuery, $bIsCorrectQuery)) - { - return false; - } - return true; - } - - public function DoExecute() - { - $aQueries = array( - 'SELECT toto' => true, - 'SELECT toto WHERE toto.a = 1' => true, - 'SELECT toto WHERE toto.a=1' => true, - 'SELECT toto WHERE toto.a = "1"' => true, - 'SELECT toto WHHHERE toto.a = "1"' => false, - 'SELECT toto WHERE toto.a == "1"' => false, - 'SELECT toto WHERE toto.a % 1' => false, - //'SELECT toto WHERE toto.a LIKE 1' => false, - 'SELECT toto WHERE toto.a like \'arg\'' => false, - 'SELECT toto WHERE toto.a NOT LIKE "That\'s it"' => true, - 'SELECT toto WHERE toto.a NOT LIKE "That\'s "it""' => false, - 'SELECT toto WHERE toto.a NOT LIKE "That\'s \\"it\\""' => true, - 'SELECT toto WHERE toto.a NOT LIKE \'That"s it\'' => true, - 'SELECT toto WHERE toto.a NOT LIKE \'That\'s it\'' => false, - 'SELECT toto WHERE toto.a NOT LIKE \'That\\\'s it\'' => true, - 'SELECT toto WHERE toto.a NOT LIKE "blah \\ truc"' => false, - 'SELECT toto WHERE toto.a NOT LIKE "blah \\\\ truc"' => true, - 'SELECT toto WHERE toto.a NOT LIKE \'blah \\ truc\'' => false, - 'SELECT toto WHERE toto.a NOT LIKE \'blah \\\\ truc\'' => true, - - 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, - 'SELECT toto WHERE toto.a NOT LIKE "\\""' => true, - 'SELECT toto WHERE toto.a NOT LIKE "\\"\\\\"' => true, - 'SELECT toto WHERE toto.a NOT LIKE "\\\\\\""' => true, - 'SELECT toto WHERE toto.a NOT LIKE ""' => true, - 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, - "SELECT UserRightsMatrixClassGrant WHERE UserRightsMatrixClassGrant.class = 'lnkContactRealObject' AND UserRightsMatrixClassGrant.action = 'modify' AND UserRightsMatrixClassGrant.login = 'Denis'" => true, - "SELECT A WHERE A.col1 = 'lit1' AND A.col2 = 'lit2' AND A.col3 = 'lit3'" => true, - - 'SELECT toto WHERE toto.a NOT LIKE "blah" AND toto.b LIKE "foo"' => true, - - //'SELECT toto WHERE toto.a > \'asd\'' => false, - 'SELECT toto WHERE toto.a = 1 AND toto.b LIKE "x" AND toto.f >= 12345' => true, - 'SELECT Device JOIN Site ON Device.site = Site.id' => true, - 'SELECT Device JOIN Site ON Device.site = Site.id JOIN Country ON Site.location = Country.id' => true, - - "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = 123 AND B.col1 = 'aa') OR (A.col3 = 'zzz' AND B.col4 > 100)" => true, - "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = B.col2 AND B.col1 = A.col2) OR (A.col3 = '' AND B.col4 > 100)" => true, - "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + B.col2 * B.col1 = A.col2" => true, - "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + (B.col2 * B.col1) = A.col2" => true, - "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true, - - 'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true, - ); - - $iErrors = 0; - - foreach($aQueries as $sQuery => $bIsCorrectQuery) - { - $sIsOk = $bIsCorrectQuery ? 'good' : 'bad'; - echo "

    Testing query: $sQuery ($sIsOk)

    \n"; - $bRet = $this->TestQuery($sQuery, $bIsCorrectQuery); - if (!$bRet) $iErrors++; - } - - return ($iErrors == 0); - } -} - - -class TestGenericItoMyModel extends TestBizModelGeneric -{ - static public function GetName() - { - return 'Generic RO test on '.self::GetConfigFile(); - } - - static public function GetConfigFile() {return '../config-test-mymodel.php';} -} - -class TestGenericItopBigModel extends TestBizModelGeneric -{ - static public function GetName() - { - return 'Generic RO test on '.self::GetConfigFile(); - } - - static public function GetConfigFile() {return '../config-test-itopv06.php';} -} - -class TestUserRightsMatrixItop extends TestUserRights -{ - static public function GetName() - { - return 'User rights test on user rights matrix'; - } - - static public function GetDescription() - { - return 'blah blah blah'; - } - - public function DoPrepare() - { - parent::DoPrepare(); - MetaModel::Startup('../config-test-itopv06.php'); - } - - protected function DoExecute() - { - $sUser = 'Romain'; - echo "

    Totor: ".(UserRights::Login('Totor', 'toto') ? 'ok' : 'NO')."

    \n"; - echo "

    Romain: ".(UserRights::Login('Romain', 'toto') ? 'ok' : 'NO')."

    \n"; - echo "

    User: ".UserRights::GetUser()."

    \n"; - echo "

    On behalf of...".UserRights::GetRealUser()."

    \n"; - - echo "

    Denis (impersonate) : ".(UserRights::Impersonate('Denis', 'tutu') ? 'ok' : 'NO')."

    \n"; - echo "

    User: ".UserRights::GetUser()."

    \n"; - echo "

    On behalf of...".UserRights::GetRealUser()."

    \n"; - - UserRights::GetFilter('bizOrganization'); // returns a filter object - - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT bizOrganization")); - echo "

    IsActionAllowed...".(UserRights::IsActionAllowed('bizOrganization', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; - echo "

    IsStimulusAllowed...".(UserRights::IsStimulusAllowed('bizOrganization', 'myStimulus', $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; - echo "

    IsActionAllowedOnAttribute...".(UserRights::IsActionAllowedOnAttribute('bizOrganization', 'myattribute', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; - return true; - } -} - -/////////////////////////////////////////////////////////////////////////// -// Test a complex biz model on the fly -/////////////////////////////////////////////////////////////////////////// - -class TestMyBizModel extends TestBizModel -{ - static public function GetName() - { - return 'A series of tests on a weird business model'; - } - - static public function GetDescription() - { - return 'Attempts various operations and build complex queries'; - } - - static public function GetConfigFile() {return '../config-test-mymodel.php';} - - function test_linksinfo() - { - echo "

    Enum links

    "; - MyHelpers::var_dump_html(MetaModel::EnumReferencedClasses("cmdbTeam")); - MyHelpers::var_dump_html(MetaModel::EnumReferencingClasses("Organization")); - - MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses()); - MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdbContact")); - MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdWorkshop")); - MyHelpers::var_dump_html(MetaModel::GetLinkLabel("Liens_entre_contacts_et_workshop", "toworkshop")); - } - - function test_list_attributes() - { - echo "

    List attributes

    "; - foreach(MetaModel::ListAttributeDefs("cmdbTeam") as $sAttCode=>$oAttDef) - { - echo $oAttDef->GetLabel()." / ".$oAttDef->GetDescription()." / ".$oAttDef->GetType()."
    \n"; - } - } - - function test_search() - { - echo "

    Two searches

    "; - $oFilterAllDevs = new DBObjectSearch("cmdbTeam"); - $oAllDevs = new DBObjectSet($oFilterAllDevs); - - echo "Found ".$oAllDevs->Count()." items.
    \n"; - while ($oDev = $oAllDevs->Fetch()) - { - $aValues = array(); - foreach(MetaModel::GetAttributesList($oAllDevs->GetClass()) as $sAttCode) - { - $aValues[] = MetaModel::GetLabel(get_class($oDev), $sAttCode)." (".MetaModel::GetDescription(get_class($oDev), $sAttCode).") = ".$oDev->GetAsHTML($sAttCode); - } - echo $oDev->GetKey()." => ".implode(", ", $aValues)."
    \n"; - } - - // a second one - $oMyFilter = new DBObjectSearch("cmdbContact"); - //$oMyFilter->AddCondition("name", "aii", "Finishes with"); - $oMyFilter->AddCondition("name", "aii"); - $this->search_and_show_list($oMyFilter); - - } - - function test_reload() - { - echo "

    Reload

    "; - $team = MetaModel::GetObject("cmdbContact", "2"); - echo "Chargement de l'attribut headcount: {$team->Get("headcount")}
    \n"; - MyHelpers::var_dump_html($team); - } - - function test_setattribute() - { - echo "

    Set attribute and update

    "; - $team = MetaModel::GetObject("cmdbTeam", "2"); - $team->Set("headcount", rand(1,1000)); - $team->Set("email", "Luis ".rand(9,250)); - MyHelpers::var_dump_html($team->ListChanges()); - echo "New headcount = {$team->Get("headcount")}
    \n"; - echo "Computed name = {$team->Get("name")}
    \n"; - - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "test_setattribute / Made by robot #".rand(1,100)); - $iChangeId = $oMyChange->DBInsert(); - - //MetaModel::StartDebugQuery(); - $team->DBUpdateTracked($oMyChange); - //MetaModel::StopDebugQuery(); - - echo "

    Check the modified team

    "; - $oTeam = MetaModel::GetObject("cmdbTeam", "2"); - MyHelpers::var_dump_html($oTeam); - } - function test_newobject() - { - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "test_newobject / Made by robot #".rand(1,100)); - $iChangeId = $oMyChange->DBInsert(); - - echo "

    Create a new object (team)

    "; - $oNewTeam = MetaModel::NewObject("cmdbTeam"); - $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); - $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); - $oNewTeam->Set("email", null); - $oNewTeam->Set("owner", "ITOP"); - $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value - $iId = $oNewTeam->DBInsertTracked($oMyChange); - echo "Created new team: $iId
    "; - echo "

    Delete team #$iId

    "; - $oTeam = MetaModel::GetObject("cmdbTeam", $iId); - $oTeam->DBDeleteTracked($oMyChange); - echo "Deleted team: $iId
    "; - MyHelpers::var_dump_html($oTeam); - } - - - function test_updatecolumn() - { - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "test_updatecolumn / Made by robot #".rand(1,100)); - $iChangeId = $oMyChange->DBInsert(); - - $sNewEmail = "updatecol".rand(9,250)."@quedlaballe.com"; - echo "

    Update a the email: set to '$sNewEmail'

    "; - $oMyFilter = new DBObjectSearch("cmdbContact"); - $oMyFilter->AddCondition("name", "o", "Contains"); - - echo "Candidates before:
    "; - $this->search_and_show_list($oMyFilter); - - MetaModel::BulkUpdateTracked($oMyChange, $oMyFilter, array("email" => $sNewEmail)); - - echo "Candidates after:
    "; - $this->search_and_show_list($oMyFilter); - } - - function test_error() - { - trigger_error("Stop requested", E_USER_ERROR); - } - - function test_changetracking() - { - echo "

    Create a change

    "; - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "Made by robot #".rand(1,100)); - $iChangeId = $oMyChange->DBInsert(); - echo "Created new change: $iChangeId
    "; - MyHelpers::var_dump_html($oMyChange); - - echo "

    Create a new object (team)

    "; - $oNewTeam = MetaModel::NewObject("cmdbTeam"); - $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); - $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); - $oNewTeam->Set("email", null); - $oNewTeam->Set("owner", "ITOP"); - $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value - $iId = $oNewTeam->DBInsertTracked($oMyChange); - echo "Created new team: $iId
    "; - echo "

    Delete team #$iId

    "; - $oTeam = MetaModel::GetObject("cmdbTeam", $iId); - $oTeam->DBDeleteTracked($oMyChange); - echo "Deleted team: $iId
    "; - MyHelpers::var_dump_html($oTeam); - } - - function test_zlist() - { - echo "

    Test ZLists

    "; - $aZLists = MetaModel::EnumZLists(); - foreach ($aZLists as $sListCode) - { - $aListInfos = MetaModel::GetZListInfo($sListCode); - echo "

    List '".$sListCode."' (".$aListInfos["description"].") of type '".$aListInfos["type"]."'

    \n"; - - foreach (MetaModel::GetSubclasses("cmdbObjectHomeMade") as $sKlass) - { - $aItems = MetaModel::GetZListItems($sKlass, $sListCode); - if (count($aItems) == 0) continue; - - echo "$sKlass - $sListCode : {".implode(", ", $aItems)."}
    \n"; - } - } - - echo "

    IsAttributeInZList()...

    "; - echo "Liens_entre_contacts_et_workshop::ws_info in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "ws_info") ? "yes" : "no")."
    \n"; - echo "Liens_entre_contacts_et_workshop::toworkshop in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "toworkshop") ? "yes" : "no")."
    \n"; - - } - - function test_SibuSQL() - { - echo "

    Simple But Structured Query Language

    "; - - $oMyFilter = new DBObjectSearch("cmdbContact"); - echo "Tous les contacts: ".$oMyFilter->ToSibuSQL()."
    \n"; - $oNewFilter = DBObjectSearch::FromSibuSQL($oMyFilter->ToSibuSQL()); - echo "En passant par un filtre, ca revient en : ".$oNewFilter->ToSibuSQL()."
    \n"; - $this->search_and_show_list($oNewFilter); - - $sFilterDesc = "cmdbContact: name Begins with '$[debutnom:as:debut du nom]' AND ownername NotLike $[ddd::]"; - echo "Construction d'un filtre a partir de sa description en SibuSQL: $sFilterDesc
    \n"; - - MyHelpers::var_dump_html(DBObjectSearch::ListSibusQLParams($sFilterDesc)); - $oNewFilter = DBObjectSearch::FromSibuSQL($sFilterDesc, array('ddd'=>123)); - echo "Ca revient en: ".$oNewFilter->ToSibuSQL(); - } - - function test_pkey() - { - echo "

    Test search on pkey

    "; - $sExpr1 = "cmdbContact: pkey IN {40, 42}"; - $sExpr2 = "cmdbContact: pkey NOTIN {40, 42}"; - $this->search_and_show_list_from_sibusql($sExpr1); - $this->search_and_show_list_from_sibusql($sExpr2); - - echo "Et maintenant, on fusionne....
    \n"; - $oSet1 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr1)); - $oSet2 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr2)); - $oIntersect = $oSet1->CreateIntersect($oSet2); - $oDelta = $oSet1->CreateDelta($oSet2); - - $oMerge = clone $oSet1; - $oMerge->Merge($oSet2); - $oMerge->Merge($oSet2); - - echo "Set1 - Found ".$oSet1->Count()." items.
    \n"; - echo "Set2 - Found ".$oSet2->Count()." items.
    \n"; - echo "Intersect - Found ".$oIntersect->Count()." items.
    \n"; - echo "Delta - Found ".$oDelta->Count()." items.
    \n"; - echo "Merge - Found ".$oMerge->Count()." items.
    \n"; - //$this->show_list($oObjSet); - } - - function test_relations() - { - echo "

    Test relations

    "; - - //MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbObjectHomeMade", "Potes")); - MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbContact", "Potes")); - - $iMaxDepth = 9; - echo "Max depth = $iMaxDepth
    \n"; - - $oObj = MetaModel::GetObject("cmdbContact", 18); - $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth); - echo $oObj->Get('name')." has some 'Potes'...
    \n"; - foreach ($aRels as $sClass => $aObjs) - { - echo "$sClass, count = ".count($aObjs)." => ".implode(', ', array_keys($aObjs))."
    \n"; - $oObjectSet = CMDBObjectSet::FromArray($sClass, $aObjs); - $this->show_list($oObjectSet); - } - - echo "

    Test relations - same results, by the mean of a SibuSQL

    "; - $this->search_and_show_list_from_sibusql("cmdbContact: RELATED (Potes, $iMaxDepth) TO (cmdbContact: pkey = 18)"); - - } - - function test_linkedset() - { - echo "

    Linked set attributes

    \n"; - $oObj = MetaModel::GetObject("cmdbContact", 18); - - echo "
    Current workshops
    \n"; - $oSetWorkshopsCurr = $oObj->Get("myworkshops"); - $this->show_list($oSetWorkshopsCurr); - - echo "
    Setting workshops
    \n"; - $oNewLink = new cmdbLiens(); - $oNewLink->Set('toworkshop', 2); - $oNewLink->Set('function', 'mafonctioooon'); - $oNewLink->Set('a1', 'tralala1'); - $oNewLink->Set('a2', 'F7M'); - $oSetWorkshops = CMDBObjectSet::FromArray("cmdbLiens", array($oNewLink)); - $oObj->Set("myworkshops", $oSetWorkshops); - $this->show_list($oSetWorkshops); - - echo "
    New workshops
    \n"; - $oSetWorkshopsCurr = $oObj->Get("myworkshops"); - $this->show_list($oSetWorkshopsCurr); - - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "test_linkedset / Made by robot #".rand(1,100)); - $iChangeId = $oMyChange->DBInsert(); - $oObj->DBUpdateTracked($oMyChange); - $oObj = MetaModel::GetObject("cmdbContact", 18); - - echo "
    After the write
    \n"; - $oSetWorkshopsCurr = $oObj->Get("myworkshops"); - $this->show_list($oSetWorkshopsCurr); - } - - function test_object_lifecycle() - { - echo "

    Test object lifecycle

    "; - - - MyHelpers::var_dump_html(MetaModel::GetStateAttributeCode("cmdbContact")); - MyHelpers::var_dump_html(MetaModel::EnumStates("cmdbContact")); - MyHelpers::var_dump_html(MetaModel::EnumStimuli("cmdbContact")); - foreach(MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef) - { - echo "

    Transition from $sStateCode

    \n"; - MyHelpers::var_dump_html(MetaModel::EnumTransitions("cmdbContact", $sStateCode)); - } - - $oObj = MetaModel::GetObject("cmdbContact", 18); - echo "Current state: ".$oObj->GetState()."... let's go to school..."; - MyHelpers::var_dump_html($oObj->EnumTransitions()); - $oObj->ApplyStimulus("toschool"); - echo "New state: ".$oObj->GetState()."... let's get older..."; - MyHelpers::var_dump_html($oObj->EnumTransitions()); - $oObj->ApplyStimulus("raise"); - echo "New state: ".$oObj->GetState()."... let's try to go further... (should give an error)"; - MyHelpers::var_dump_html($oObj->EnumTransitions()); - $oObj->ApplyStimulus("raise"); // should give an error - } - - - protected function DoExecute() - { -// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); -// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); - //$this->test_linksinfo(); - //$this->test_list_attributes(); - //$this->test_search(); - //$this->test_reload(); - //$this->test_newobject(); - $this->test_setattribute(); - //$this->test_updatecolumn(); - //$this->test_error(); - //$this->test_changetracking(); - $this->test_zlist(); - $this->test_SibuSQL(); - //$this->test_pkey(); - $this->test_relations(); - $this->test_linkedset(); - $this->test_object_lifecycle(); - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Test a complex biz model on the fly -/////////////////////////////////////////////////////////////////////////// - -abstract class MyFarm extends TestBizModel -{ - static public function GetConfigFile() {return '../config-test-farm.php';} - - protected function DoPrepare() - { - parent::DoPrepare(); - $this->ResetDB(); - MetaModel::DBCheckIntegrity(); - } - - protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) - { - $oNew = MetaModel::NewObject('Mammal'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - $oNew->Set('name', $sName); - $oNew->Set('height', $iHeight); - $oNew->Set('birth', $sBirth); - return $this->ObjectToDB($oNew); - } - - protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) - { - $oNew = MetaModel::NewObject('Bird'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - return $this->ObjectToDB($oNew); - } - - protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) - { - $oNew = MetaModel::NewObject('FlyingBird'); - $oNew->Set('species', $sSpecies); - $oNew->Set('sex', $sSex); - $oNew->Set('speed', $iSpeed); - $oNew->Set('mother', $iMotherid); - $oNew->Set('father', $iFatherId); - $oNew->Set('flyingspeed', $iFlyingSpeed); - return $this->ObjectToDB($oNew); - } - - private function InsertGroup($sName, $iLeaderId) - { - $oNew = MetaModel::NewObject('Group'); - $oNew->Set('name', $sName); - $oNew->Set('leader', $iLeaderId); - $iId = $oNew->DBInsertNoReload(); - return $iId; - } -} - - -class TestQueriesOnFarm extends MyFarm -{ - static public function GetName() - { - return 'Farm test'; - } - - static public function GetDescription() - { - return 'A series of tests on the farm business model (SQL generation)'; - } - - protected function CheckQuery($sQuery, $bIsCorrectQuery) - { - if ($bIsCorrectQuery) - { - echo "

    $sQuery

    \n"; - } - else - { - echo "

    $sQuery

    \n"; - } - try - { - //$oOql = new OqlInterpreter($sQuery); - //$oTrash = $oOql->ParseObjectQuery(); - //MyHelpers::var_dump_html($oTrash, true); - $oMyFilter = DBObjectSearch::FromOQL($sQuery); - } - catch (OQLException $oOqlException) - { - if ($bIsCorrectQuery) - { - echo "

    More info on this unexpected failure:
    ".$oOqlException->getHtmlDesc()."

    \n"; - throw $oOqlException; - return false; - } - else - { - // Everything is fine :-) - echo "

    More info on this expected failure:\n"; - echo "

      \n"; - echo "
    • ".get_class($oOqlException)."
    • \n"; - echo "
    • ".$oOqlException->getMessage()."
    • \n"; - echo "
    • ".$oOqlException->getHtmlDesc()."
    • \n"; - echo "
    \n"; - echo "

    \n"; - return true; - } - } - // The query was correctly parsed, was it expected to be correct ? - if (!$bIsCorrectQuery) - { - throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); - return false; - } - echo "

    To OQL: ".$oMyFilter->ToOQL()."

    "; - - $this->search_and_show_list($oMyFilter); - - //echo "

    first pass

    \n"; - //MyHelpers::var_dump_html($oMyFilter, true); - $sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); - //echo "

    second pass

    \n"; - //MyHelpers::var_dump_html($oMyFilter, true); - //$sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); - - $sSerialize = $oMyFilter->serialize(); - echo "

    Serialized:$sSerialize

    \n"; - $oFilter2 = DBObjectSearch::unserialize($sSerialize); - try - { - $sQuery2 = MetaModel::MakeSelectQuery($oFilter2); - } - catch (Exception $e) - { - echo "

    Could not compute the query after unserialize

    \n"; - echo "

    Query 1: $sQuery1

    \n"; - MyHelpers::var_cmp_html($oMyFilter, $oFilter2); - throw $e; - } - //if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same! - if ($sQuery1 != $sQuery2) - { - echo "

    serialize/unserialize mismatch :-(

    \n"; - MyHelpers::var_cmp_html($sQuery1, $sQuery2); - MyHelpers::var_cmp_html($oMyFilter, $oFilter2); - return false; - } - return true; - } - - protected function DoExecute() - { -// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); -// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); - echo "

    Create protagonists...

    "; - - $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); - $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); - $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); - $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); - $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); - - $this->InsertBird('rooster', 'male', 12, 0, 0); - $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); - - // Benchmarking - // - if (false) - { - define ('COUNT_BENCHMARK', 10); - echo "

    Parsing a long query, ".COUNT_BENCHMARK." times

    "; - $sQuery = "SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR) AND Dad.height * 2 <= ROUND(TO_DAYS(Dad.birth) / (3 + 1) * 5 - 3)"; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $oMyFilter = DBObjectSearch::FromOQL($sQuery); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fParsingDuration = $fDuration / COUNT_BENCHMARK; - echo "

    Mean time by op: $fParsingDuration

    "; - } - - echo "

    Test queries...

    "; - - $aQueries = array( - 'SELECT Animal' => true, - 'SELECT Animal WHERE Animal.pkey = 1' => false, - 'SELECT Animal WHERE Animal.id = 1' => true, - 'SELECT Aniiimal' => false, - 'SELECTe Animal' => false, - 'SELECT * FROM Animal' => false, - 'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true, - 'SELECT Animal AS zoo WHERE species = \'human\'' => true, - 'SELECT Animal AS zoo WHERE espece = \'human\'' => false, - 'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true, - 'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false, - 'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true, - 'SELECT Animal AS zoo WHERE zoo.species NOT IN (\'human\', "pig")' => true, - 'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false, - 'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true, - 'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true, - 'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true, - 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, - 'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true, - 'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true, - 'SELECT Mammal AS m WHERE m.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR)' => true, - 'SELECT Mammal AS m WHERE m.birth > DATE_SUB(NOW(), INTERVAL 2000 DAY)' => true, - 'SELECT Mammal AS m WHERE (TO_DAYS(NOW()) - TO_DAYS(m.birth)) > 2000' => true, - 'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true, - 'SELECT Mammal AS m WHERE (1 + 2' => false, - 'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true, - 'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true, - 'SELECT Mammal AS m WHERE 1/0' => true, - 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, - 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true, - 'SELECT Group JOIN Animal ON Group.leader = Animal.id' => true, - 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true, - 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.masterchief = A.id' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.pkey' => false, - 'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true, - 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false, - 'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true, - 'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false, - 'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false, - 'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false, - 'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true, - 'SELECT Group AS G WHERE G.leader_speed < 100000' => true, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, - 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true, - 'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true, - 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, - 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true, - 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, - 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true, - 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true, - ); - //$aQueries = array( - // 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, - //); - foreach($aQueries as $sQuery => $bIsCorrect) - { - $this->CheckQuery($sQuery, $bIsCorrect); - } - return true; - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Test data load -/////////////////////////////////////////////////////////////////////////// - -class TestBulkChangeOnFarm extends TestBizModel -{ - static public function GetName() - { - return 'Farm test - data load'; - } - - static public function GetDescription() - { - return 'Bulk load'; - } - - static public function GetConfigFile() {return '../config-test-farm.php';} - - protected function DoPrepare() - { - parent::DoPrepare(); - $this->ResetDB(); - MetaModel::DBCheckIntegrity(); - } - - protected function DoExecute() - { -// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); -// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); - - $oParser = new CSVParser("#denomination,hauteur,age - suzy,123,2009-01-01 - chita,456, - "); - $oParser->SetSeparator(','); - $aData = $oParser->ToArray(array('_name', '_height', '_birth')); - MyHelpers::var_dump_html($aData); - - $oBulk = new BulkChange( - 'Mammal', - $aData, - array('name' => '_name', 'height' => '_height', 'birth' => '_birth'), - array('name'), - array() - ); - - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "Testor"); - $iChangeId = $oMyChange->DBInsert(); -// echo "Created new change: $iChangeId
    "; - - echo "

    Planned for loading...

    "; - $aRes = $oBulk->Process(); - print_r($aRes); - echo "

    Go for loading...

    "; - $aRes = $oBulk->Process($oMyChange); - print_r($aRes); - - return; - - $oRawData = array( - 'Mammal', - array('species', 'sex', 'speed', 'mother', 'father', 'name', 'height', 'birth'), - "human,male,23,0,0,romulus,192,1971 - human,male,23,0,0,remus,154,-50 - human,male,23,0,0,julius,160,-49 - human,female,23,0,0,cleopatra,142,-50 - pig,female,23,0,0,confucius,50,2003" - ); - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Test data load -/////////////////////////////////////////////////////////////////////////// - -class TestFullTextSearchOnFarm extends MyFarm -{ - static public function GetName() - { - return 'Farm test - full text search'; - } - - static public function GetDescription() - { - return 'Focus on the full text search feature'; - } - - protected function DoExecute() - { - echo "

    Create protagonists...

    "; - - $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); - $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); - $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); - $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); - $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); - - $this->InsertBird('rooster', 'male', 12, 0, 0); - $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); - - echo "

    Search...

    "; - $oSearch = new DBObjectSearch('Mammal'); - $oSearch->AddCondition_FullText('manof'); - //$oResultSet = new DBObjectSet($oSearch); - $this->search_and_show_list($oSearch); - } -} - - -/////////////////////////////////////////////////////////////////////////// -// Benchmark queries -/////////////////////////////////////////////////////////////////////////// - -class TestItopEfficiency extends TestBizModel -{ - static public function GetName() - { - return 'Itop - benchmark'; - } - - static public function GetDescription() - { - return 'Measure time to perform the queries'; - } - - static public function GetConfigFile() {return '../config-itop.php';} - - protected function DoBenchmark($sOqlQuery) - { - echo "

    Testing query: $sOqlQuery

    "; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $oFilter = DBObjectSearch::FromOQL($sOqlQuery); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fParsingDuration = $fDuration / COUNT_BENCHMARK; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $sSQL = MetaModel::MakeSelectQuery($oFilter); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fBuildDuration = $fDuration / COUNT_BENCHMARK; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $res = CMDBSource::Query($sSQL); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fQueryDuration = $fDuration / COUNT_BENCHMARK; - - // The fetch could not be repeated with the same results - // But we've seen so far that is was very very quick to exec - // So it makes sense to benchmark it a single time - $fStart = MyHelpers::getmicrotime(); - $aRow = CMDBSource::FetchArray($res); - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fFetchDuration = $fDuration; - - $fStart = MyHelpers::getmicrotime(); - for($i=0 ; $i < COUNT_BENCHMARK ; $i++) - { - $sOql = $oFilter->ToOQL(); - } - $fDuration = MyHelpers::getmicrotime() - $fStart; - $fToOqlDuration = $fDuration / COUNT_BENCHMARK; - - echo "
      \n"; - echo "
    • Parsing: $fParsingDuration
    • \n"; - echo "
    • Build: $fBuildDuration
    • \n"; - echo "
    • Query: $fQueryDuration
    • \n"; - echo "
    • Fetch: $fFetchDuration
    • \n"; - echo "
    • ToOql: $fToOqlDuration
    • \n"; - echo "
    \n"; - - // Everything but the ToOQL (wich is interesting, anyhow) - $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; - - return array( - 'rows' => CMDBSource::NbRows($res), - 'duration (s)' => round($fTotal, 4), - 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1), - 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1), - 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1), - 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1), - 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1), - 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1), - ); - } - - protected function DoExecute() - { - define ('COUNT_BENCHMARK', 3); - echo "

    The test will be repeated ".COUNT_BENCHMARK." times

    "; - - $aQueries = array( - 'SELECT CMDBChangeOpSetAttribute', - 'SELECT CMDBChangeOpSetAttribute WHERE id=10', - 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789', - 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10', - 'SELECT bizIncidentTicket', - 'SELECT bizIncidentTicket WHERE id=1', - 'SELECT bizPerson', - 'SELECT bizPerson WHERE id=1', - 'SELECT bizIncidentTicket JOIN bizPerson ON bizIncidentTicket.agent_id = bizPerson.id WHERE bizPerson.id = 5', - ); - $aStats = array(); - foreach ($aQueries as $sOQL) - { - $aStats[$sOQL] = $this->DoBenchmark($sOQL); - } - - $aData = array(); - foreach ($aStats as $sOQL => $aResults) - { - $aValues = array(); - $aValues['OQL'] = htmlentities($sOQL); - - foreach($aResults as $sDesc => $sInfo) - { - $aValues[$sDesc] = htmlentities($sInfo); - } - $aData[] = $aValues; - } - echo MyHelpers::make_table_from_assoc_array($aData); - } -} - -/////////////////////////////////////////////////////////////////////////// -// Test data load -/////////////////////////////////////////////////////////////////////////// - -class TestItopWebServices extends TestWebServices -{ - static public function GetName() - { - return 'Itop - web services'; - } - - static public function GetDescription() - { - return 'Bulk load and ???'; - } - - protected function DoExecSingleLoad($aLoadSpec) - { - $sTitle = 'Load: '.$aLoadSpec['class']; - $sClass = $aLoadSpec['class']; - $sCsvData = $aLoadSpec['csvdata']; - - $aPostData = array('class' => $sClass, 'csvdata' => $sCsvData); - $sRes = self::DoPostRequestAuth('../webservices/import.php', $aPostData); - - echo "

    $sTitle

    $sCsvData
    $sRes
    "; - } - - protected function DoExecute() - { - - $aLoads = array( - array( - 'class' => 'bizOrganization', - 'csvdata' => "name;code\nWorldCompany;WCY" - ), - array( - 'class' => 'bizLocation', - 'csvdata' => "name;org_id;address\nParis;1;Centre de la Franca" - ), - array( - 'class' => 'bizPerson', - 'csvdata' => "email;name;first_name;org_id;phone\njohn.foo@starac.com;Foo;John;1;+33(1)23456789" - ), - array( - 'class' => 'bizTeam', - 'csvdata' => "name;org_id;location_name\nSquadra Azzura2;1;Paris" - ), - array( - 'class' => 'bizWorkgroup', - 'csvdata' => "name;org_id;team_id\ntravailleurs alpins;1;6" - ), - array( - 'class' => 'bizIncidentTicket', - 'csvdata' => "name;title;type;org_id;initial_situation;start_date;next_update;caller_id;workgroup_id;agent_id\nOVSD-12345;server down;Network;1;server was found down;2009-04-10 12:00;2009-04-10 15:00;3;317;5" - ), - ); - - foreach ($aLoads as $aLoadSpec) - { - $this->DoExecSingleLoad($aLoadSpec); - } - } -} - - -$aWebServices = array( - array( - 'verb' => 'GetVersion', - 'expected result' => '0.8', - 'explain result' => 'n/a', - 'args' => array(), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => true, - 'explain result' => 'link attribute unknown + a CI not found', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Server', /* sType */ - 'desc of ticket', /* sDescription */ - 'initial situation blah blah blah', /* sInitialSituation */ - 'very grave', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - new SOAPLinkCreationSpec( - 'logInfra', - array(new SOAPSearchCondition('id', 108)), - array(new SOAPAttributeValue('impacting', 'very critical')) - ), - new SOAPLinkCreationSpec( - 'bizDevice', - array(new SOAPSearchCondition('name', 'Router03')), - array(new SOAPAttributeValue('impact', 'who cares')) - ), - new SOAPLinkCreationSpec( - 'bizDevice', - array(new SOAPSearchCondition('name', 'thisone')), - array(new SOAPAttributeValue('impact', 'our lives')) - ), - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'caller not specified', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Desktop', /* sType */ - 'PC burning', /* sDescription */ - 'The power supply suddenly started to warm up', /* sInitialSituation */ - 'The agent could not do his job', /* sImpact */ - null, /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'wrong condition on CI to attach', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Desktop', /* sType */ - 'PC burning', /* sDescription */ - 'The power supply suddenly started to warm up', /* sInitialSituation */ - 'The agent could not do his job', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - new SOAPLinkCreationSpec( - 'logInfra', - array(new SOAPSearchCondition('dummyfiltercode', 2)), - array(new SOAPAttributeValue('impact', 'very critical')) - ), - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => true, - 'explain result' => 'no CI to attach (empty array)', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Network', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => true, - 'explain result' => 'no CI to attach (null)', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Network', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - null, /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'caller unknown', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'Network', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1000))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'wrong values for type and severity', - 'args' => array( - 'admin', /* sLogin */ - 'admin', /* sPassword */ - 'my type', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'my severity' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'wrong password', - 'args' => array( - 'admin', /* sLogin */ - 'xxxxx', /* sPassword */ - 'Network', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), - array( - 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'wrong login', - 'args' => array( - 'xxxxx', /* sLogin */ - 'yyyyy', /* sPassword */ - 'Network', /* sType */ - 'Houston not reachable', /* sDescription */ - 'Tried to join the shuttle', /* sInitialSituation */ - 'Could not talk to my wife', /* sImpact */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ - array( - ), /* aImpact */ - 'low' /* sSeverity */ - ), - ), -); - - -class TestSoap extends TestSoapWebService -{ - static public function GetName() {return 'Test SOAP';} - static public function GetDescription() {return 'Do basic stuff to test the SOAP capability';} - - protected function DoExecute() - { - echo "

    Note: You may also want to try the sample SOAP client itopsoap.examples.php

    \n"; - - global $aSOAPMapping; - - // this file is generated dynamically with location = here - $sWsdlUri = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php'; - - ini_set("soap.wsdl_cache_enabled","0"); - $this->m_SoapClient = new SoapClient - ( - $sWsdlUri, - array( - 'classmap' => $aSOAPMapping, - 'trace' => 1, - ) - ); - - if (false) - { - print "
    \n"; 
    -			print_r($this->m_SoapClient->__getTypes());
    -			print "
    \n"; - } - - global $aWebServices; - foreach ($aWebServices as $iPos => $aWebService) - { - echo "

    SOAP call #$iPos ".$aWebService['explain result']."

    \n"; - - try - { - $oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']); - } - catch(SoapFault $e) - { - print "
    \n"; 
    -				print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
    -				print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
    -				print "
    "; - print "Response in HTML:

    ".$this->m_SoapClient->__getLastResponse()."

    "; - throw $e; - } - - echo "
    \n";
    -			print_r($oRes);
    -			echo "
    \n"; - - print "
    \n"; 
    -			print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
    -			print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
    -			print "
    "; - - if ($oRes instanceof SOAPResult) - { - $res = $oRes->status; - } - else - { - $res = $oRes; - } - if ($res != $aWebService['expected result']) - { - throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'"); - } - } - } -} - -class TestWebServicesDirect extends TestBizModel -{ - static public function GetName() {return 'Test web services locally';} - static public function GetDescription() {return 'Invoke the service directly (troubleshooting)';} - - static public function GetConfigFile() {return '../config-itop.php';} - - protected function DoExecute() - { - $oWebServices = new WebServices(); - - global $aWebServices; - foreach ($aWebServices as $aWebService) - { - $oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']); - echo "
    \n";
    -			print_r($oRes);
    -			echo "
    \n"; - } - return true; - } -} - -class TestTriggerAndEmail extends TestBizModel -{ - static public function GetName() {return 'Test trigger and email';} - static public function GetDescription() {return 'Create a trigger and an email, then activates the trigger';} - - static public function GetConfigFile() {return '../config-itop.php';} - - protected function CreateEmailSpec($oTrigger, $sStatus, $sTo, $sCC, $sTesterEmail) - { - $oAction = MetaModel::NewObject("ActionEmail"); - $oAction->Set("status", $sStatus); - $oAction->Set("name", "New server"); - $oAction->Set("test_recipient", $sTesterEmail); - $oAction->Set("from", $sTesterEmail); - $oAction->Set("reply_to", $sTesterEmail); - $oAction->Set("to", $sTo); - $oAction->Set("cc", $sCC); - $oAction->Set("bcc", ""); - $oAction->Set("subject", "New server: '\$this->name()$'"); - $oAction->Set("body", "

    Dear customer,

    We have created the server \$this->hyperlink()$ in the IT infrastructure database.

    You will be further notified when it is in Production.

    The IT infrastructure management team.

    Here are some accentuated characters for french people: 'ééà'

    "); - $oAction->Set("importance", "low"); - $iActionId = $this->ObjectToDB($oAction, true); - - $oLink = MetaModel::NewObject("lnkTriggerAction"); - $oLink->Set("trigger_id", $oTrigger->GetKey()); - $oLink->Set("action_id", $iActionId); - $oLink->Set("order", "1"); - $iLink = $this->ObjectToDB($oLink, true); - } - - protected function DoExecute() - { - $oMyPerson = MetaModel::NewObject("bizPerson"); - $oMyPerson->Set("name", "testemail1"); - $oMyPerson->Set("org_id", "1"); - $oMyPerson->Set("email", "romain.quetiez@hp.com"); - $iPersonId = $this->ObjectToDB($oMyPerson, true); - - $oMyPerson = MetaModel::NewObject("bizPerson"); - $oMyPerson->Set("name", "testemail2"); - $oMyPerson->Set("org_id", "1"); - $oMyPerson->Set("email", "denis.flaven@hp.com"); - $iPersonId = $this->ObjectToDB($oMyPerson, true); - - $oMyPerson = MetaModel::NewObject("bizPerson"); - $oMyPerson->Set("name", "testemail3"); - $oMyPerson->Set("org_id", "1"); - $oMyPerson->Set("email", "erwan.taloc@hp.com"); - $iPersonId = $this->ObjectToDB($oMyPerson, true); - - $oMyServer = MetaModel::NewObject("bizServer"); - $oMyServer->Set("name", "wfr.terminator.com"); - $oMyServer->Set("severity", "low"); - $oMyServer->Set("status", "production"); - $oMyServer->Set("org_id", 2); - $oMyServer->Set("location_id", 2); - $iServerId = $this->ObjectToDB($oMyServer, true); - - $oMyTrigger = MetaModel::NewObject("TriggerOnStateEnter"); - $oMyTrigger->Set("description", "Testor"); - $oMyTrigger->Set("target_class", "bizServer"); - $oMyTrigger->Set("state", "Shipped"); - $iTriggerId = $this->ObjectToDB($oMyTrigger, true); - - // Error in OQL field(s) - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'test', - "SELECT bizPerson WHERE naime = 'Dali'", - "SELECT bizServer", - 'romain.quetiez@hp.com' - ); - - // Error: no recipient - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'test', - "", - "", - 'romain.quetiez@hp.com' - ); - - // Test - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'test', - "SELECT bizPerson WHERE name LIKE 'testemail%'", - "SELECT bizPerson", - 'romain.quetiez@hp.com' - ); - - // Test failing because of a wrong test recipient address - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'test', - "SELECT bizPerson WHERE name LIKE 'testemail%'", - "", - 'toto@walibi.bg' - ); - - // Normal behavior - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'enabled', - "SELECT bizPerson WHERE name LIKE 'testemail%'", - "", - 'romain.quetiez@hp.com' - ); - - // Does nothing, because it is disabled - // - $this->CreateEmailSpec - ( - $oMyTrigger, - 'disabled', - "SELECT bizPerson WHERE name = 'testemail%'", - "", - 'romain.quetiez@hp.com' - ); - - $oMyTrigger->DoActivate($oMyServer->ToArgs('this')); - - return true; - } -} -?> +new FieldExpression('column1', 'myTableAlias'), 'column2'=>new FieldExpression('column2', 'myTableAlias')), + $oCondition = new BinaryExpression(new FieldExpression('column1', 'myTableAlias'), 'LIKE', new ScalarExpression('trash')), + $aFullTextNeedles = array('column1'), + $bToDelete = false, + $aValues = array() + ); + $oQuery->AddCondition(Expression::FromOQL('DATE(NOW() - 1200 * 2) > \'2008-07-31\'')); + + $oSubQuery1 = new SQLQuery( + $sTable = 'myTable1', + $sTableAlias = 'myTable1Alias', + $aFields = array('column1_1'=>new FieldExpression('column1', 'myTableAlias'), 'column1_2'=>new FieldExpression('column1', 'myTableAlias')), + $oCondition = new TrueSQLExpression, + $aFullTextNeedles = array(), + $bToDelete = false, + $aValues = array() + ); + + $oSubQuery2 = new SQLQuery( + $sTable = 'myTable2', + $sTableAlias = 'myTable2Alias', + $aFields = array('column2_1'=>new FieldExpression('column2', 'myTableAlias'), 'column2_2'=>new FieldExpression('column2', 'myTableAlias')), + $oCondition = new TrueSQLExpression, + $aFullTextNeedles = array(), + $bToDelete = false, + $aValues = array() + ); + + $oQuery->AddInnerJoin($oSubQuery1, 'column1', 'column1_1'); + $oQuery->AddLeftJoin($oSubQuery2, 'column2', 'column2_2'); + + $oQuery->DisplayHtml(); + $oQuery->RenderDelete(); + $oQuery->RenderUpdate(); + echo '

    '.$oQuery->RenderSelect().'

    '; + $oQuery->RenderSelect(array('column1')); + $oQuery->RenderSelect(array('column1', 'column2')); + } +} + +class TestOQLParser extends TestFunction +{ + static public function GetName() {return 'Check OQL parsing';} + static public function GetDescription() {return 'Attempts a series of queries, and in particular those with a bad syntax';} + + protected function CheckQuery($sQuery, $bIsCorrectQuery) + { + $oOql = new OqlInterpreter($sQuery); + try + { + $oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery + MyHelpers::var_dump_html($oTrash, true); + } + catch (OQLException $OqlException) + { + if ($bIsCorrectQuery) + { + echo "

    More info on this unexpected failure:
    ".$OqlException->getHtmlDesc()."

    \n"; + throw $OqlException; + return false; + } + else + { + // Everything is fine :-) + echo "

    More info on this expected failure:
    ".$OqlException->getHtmlDesc()."

    \n"; + return true; + } + } + // The query was correctly parsed, was it expected to be correct ? + if ($bIsCorrectQuery) + { + return true; + } + else + { + throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); + return false; + } + } + + protected function TestQuery($sQuery, $bIsCorrectQuery) + { + if (!$this->CheckQuery($sQuery, $bIsCorrectQuery)) + { + return false; + } + return true; + } + + public function DoExecute() + { + $aQueries = array( + 'SELECT toto' => true, + 'SELECT toto WHERE toto.a = 1' => true, + 'SELECT toto WHERE toto.a=1' => true, + 'SELECT toto WHERE toto.a = "1"' => true, + 'SELECT toto WHHHERE toto.a = "1"' => false, + 'SELECT toto WHERE toto.a == "1"' => false, + 'SELECT toto WHERE toto.a % 1' => false, + //'SELECT toto WHERE toto.a LIKE 1' => false, + 'SELECT toto WHERE toto.a like \'arg\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s it"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s "it""' => false, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s \\"it\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'That"s it\'' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'That\'s it\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE \'That\\\'s it\'' => true, + 'SELECT toto WHERE toto.a NOT LIKE "blah \\ truc"' => false, + 'SELECT toto WHERE toto.a NOT LIKE "blah \\\\ truc"' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'blah \\ truc\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE \'blah \\\\ truc\'' => true, + + 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\"\\\\"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\\\\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE ""' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, + "SELECT UserRightsMatrixClassGrant WHERE UserRightsMatrixClassGrant.class = 'lnkContactRealObject' AND UserRightsMatrixClassGrant.action = 'modify' AND UserRightsMatrixClassGrant.login = 'Denis'" => true, + "SELECT A WHERE A.col1 = 'lit1' AND A.col2 = 'lit2' AND A.col3 = 'lit3'" => true, + + 'SELECT toto WHERE toto.a NOT LIKE "blah" AND toto.b LIKE "foo"' => true, + + //'SELECT toto WHERE toto.a > \'asd\'' => false, + 'SELECT toto WHERE toto.a = 1 AND toto.b LIKE "x" AND toto.f >= 12345' => true, + 'SELECT Device JOIN Site ON Device.site = Site.id' => true, + 'SELECT Device JOIN Site ON Device.site = Site.id JOIN Country ON Site.location = Country.id' => true, + + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = 123 AND B.col1 = 'aa') OR (A.col3 = 'zzz' AND B.col4 > 100)" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = B.col2 AND B.col1 = A.col2) OR (A.col3 = '' AND B.col4 > 100)" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + B.col2 * B.col1 = A.col2" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + (B.col2 * B.col1) = A.col2" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true, + + 'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true, + ); + + $iErrors = 0; + + foreach($aQueries as $sQuery => $bIsCorrectQuery) + { + $sIsOk = $bIsCorrectQuery ? 'good' : 'bad'; + echo "

    Testing query: $sQuery ($sIsOk)

    \n"; + $bRet = $this->TestQuery($sQuery, $bIsCorrectQuery); + if (!$bRet) $iErrors++; + } + + return ($iErrors == 0); + } +} + + +class TestCSVParser extends TestFunction +{ + static public function GetName() {return 'Check CSV parsing';} + static public function GetDescription() {return 'Loads a set of CSV data';} + + public function DoExecute() + { + $sDataFile = '"field1","field2","field3" +"a","b","c" +a,b,c +"","","" +,, +"a""","b","c" +"a1 +a2","b","c" +"a1,a2","b","c" +"a","b","c1,"",c2 +,c3" +"a","b","ouf !" +'; + + $sDataFile = '?field1?;?field2?;?field3? +?a?;?b?;?c? +a;b;c +??;??;?? +;; +?a"?;?b?;?c? +?a1 +a2?;?b?;?c? +?a1,a2?;?b?;?c? +?a?;?b?;?c1,",c2 +,c3? +?a?;?b?;?ouf !? +'; + + echo "
    \n";
    +		print_r($sDataFile);
    +		echo "
    \n"; + + $aExpectedResult = array( + //array('field1', 'field2', 'field3'), + array('a', 'b', 'c'), + array('a', 'b', 'c'), + array('', '', ''), + array('', '', ''), + array('a"', 'b', 'c'), + array("a1\na2", 'b', 'c'), + array('a1,a2', 'b', 'c'), + array('a', 'b', "c1,\",c2\n,c3"), + array('a', 'b', 'ouf !'), + array('a', 'b', 'a'), + ); + + $oCSVParser = new CSVParser($sDataFile, ';', '?'); + $aData = $oCSVParser->ToArray(1, null, 0); + + $iIssues = 0; + + echo "\n"; + foreach ($aData as $iRow => $aRow) + { + echo "\n"; + foreach ($aRow as $iCol => $sCell) + { + if (empty($sCell)) + { + $sCellValue = ' '; + } + else + { + $sCellValue = htmlentities($sCell); + } + + if (!isset($aExpectedResult[$iRow][$iCol])) + { + $iIssues++; + $sCellValue = "$sCellValue"; + } + elseif ($aExpectedResult[$iRow][$iCol] != $sCell) + { + $iIssues++; + $sCellValue = "$sCellValue, expecting '".$aExpectedResult[$iRow][$iCol]."'"; + } + + echo ""; + } + echo "\n"; + } + echo "
    $sCellValue
    \n"; + return ($iIssues > 0); + } +} + +class TestGenericItoMyModel extends TestBizModelGeneric +{ + static public function GetName() + { + return 'Generic RO test on '.self::GetConfigFile(); + } + + static public function GetConfigFile() {return '../config-test-mymodel.php';} +} + +class TestGenericItopBigModel extends TestBizModelGeneric +{ + static public function GetName() + { + return 'Generic RO test on '.self::GetConfigFile(); + } + + static public function GetConfigFile() {return '../config-test-itopv06.php';} +} + +class TestUserRightsMatrixItop extends TestUserRights +{ + static public function GetName() + { + return 'User rights test on user rights matrix'; + } + + static public function GetDescription() + { + return 'blah blah blah'; + } + + public function DoPrepare() + { + parent::DoPrepare(); + MetaModel::Startup('../config-test-itopv06.php'); + } + + protected function DoExecute() + { + $sUser = 'Romain'; + echo "

    Totor: ".(UserRights::Login('Totor', 'toto') ? 'ok' : 'NO')."

    \n"; + echo "

    Romain: ".(UserRights::Login('Romain', 'toto') ? 'ok' : 'NO')."

    \n"; + echo "

    User: ".UserRights::GetUser()."

    \n"; + echo "

    On behalf of...".UserRights::GetRealUser()."

    \n"; + + echo "

    Denis (impersonate) : ".(UserRights::Impersonate('Denis', 'tutu') ? 'ok' : 'NO')."

    \n"; + echo "

    User: ".UserRights::GetUser()."

    \n"; + echo "

    On behalf of...".UserRights::GetRealUser()."

    \n"; + + UserRights::GetFilter('bizOrganization'); // returns a filter object + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT bizOrganization")); + echo "

    IsActionAllowed...".(UserRights::IsActionAllowed('bizOrganization', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; + echo "

    IsStimulusAllowed...".(UserRights::IsStimulusAllowed('bizOrganization', 'myStimulus', $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; + echo "

    IsActionAllowedOnAttribute...".(UserRights::IsActionAllowedOnAttribute('bizOrganization', 'myattribute', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

    \n"; + return true; + } +} + +/////////////////////////////////////////////////////////////////////////// +// Test a complex biz model on the fly +/////////////////////////////////////////////////////////////////////////// + +class TestMyBizModel extends TestBizModel +{ + static public function GetName() + { + return 'A series of tests on a weird business model'; + } + + static public function GetDescription() + { + return 'Attempts various operations and build complex queries'; + } + + static public function GetConfigFile() {return '../config-test-mymodel.php';} + + function test_linksinfo() + { + echo "

    Enum links

    "; + MyHelpers::var_dump_html(MetaModel::EnumReferencedClasses("cmdbTeam")); + MyHelpers::var_dump_html(MetaModel::EnumReferencingClasses("Organization")); + + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses()); + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdWorkshop")); + MyHelpers::var_dump_html(MetaModel::GetLinkLabel("Liens_entre_contacts_et_workshop", "toworkshop")); + } + + function test_list_attributes() + { + echo "

    List attributes

    "; + foreach(MetaModel::ListAttributeDefs("cmdbTeam") as $sAttCode=>$oAttDef) + { + echo $oAttDef->GetLabel()." / ".$oAttDef->GetDescription()." / ".$oAttDef->GetType()."
    \n"; + } + } + + function test_search() + { + echo "

    Two searches

    "; + $oFilterAllDevs = new DBObjectSearch("cmdbTeam"); + $oAllDevs = new DBObjectSet($oFilterAllDevs); + + echo "Found ".$oAllDevs->Count()." items.
    \n"; + while ($oDev = $oAllDevs->Fetch()) + { + $aValues = array(); + foreach(MetaModel::GetAttributesList($oAllDevs->GetClass()) as $sAttCode) + { + $aValues[] = MetaModel::GetLabel(get_class($oDev), $sAttCode)." (".MetaModel::GetDescription(get_class($oDev), $sAttCode).") = ".$oDev->GetAsHTML($sAttCode); + } + echo $oDev->GetKey()." => ".implode(", ", $aValues)."
    \n"; + } + + // a second one + $oMyFilter = new DBObjectSearch("cmdbContact"); + //$oMyFilter->AddCondition("name", "aii", "Finishes with"); + $oMyFilter->AddCondition("name", "aii"); + $this->search_and_show_list($oMyFilter); + + } + + function test_reload() + { + echo "

    Reload

    "; + $team = MetaModel::GetObject("cmdbContact", "2"); + echo "Chargement de l'attribut headcount: {$team->Get("headcount")}
    \n"; + MyHelpers::var_dump_html($team); + } + + function test_setattribute() + { + echo "

    Set attribute and update

    "; + $team = MetaModel::GetObject("cmdbTeam", "2"); + $team->Set("headcount", rand(1,1000)); + $team->Set("email", "Luis ".rand(9,250)); + MyHelpers::var_dump_html($team->ListChanges()); + echo "New headcount = {$team->Get("headcount")}
    \n"; + echo "Computed name = {$team->Get("name")}
    \n"; + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_setattribute / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + //MetaModel::StartDebugQuery(); + $team->DBUpdateTracked($oMyChange); + //MetaModel::StopDebugQuery(); + + echo "

    Check the modified team

    "; + $oTeam = MetaModel::GetObject("cmdbTeam", "2"); + MyHelpers::var_dump_html($oTeam); + } + function test_newobject() + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_newobject / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + echo "

    Create a new object (team)

    "; + $oNewTeam = MetaModel::NewObject("cmdbTeam"); + $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); + $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); + $oNewTeam->Set("email", null); + $oNewTeam->Set("owner", "ITOP"); + $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value + $iId = $oNewTeam->DBInsertTracked($oMyChange); + echo "Created new team: $iId
    "; + echo "

    Delete team #$iId

    "; + $oTeam = MetaModel::GetObject("cmdbTeam", $iId); + $oTeam->DBDeleteTracked($oMyChange); + echo "Deleted team: $iId
    "; + MyHelpers::var_dump_html($oTeam); + } + + + function test_updatecolumn() + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_updatecolumn / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + $sNewEmail = "updatecol".rand(9,250)."@quedlaballe.com"; + echo "

    Update a the email: set to '$sNewEmail'

    "; + $oMyFilter = new DBObjectSearch("cmdbContact"); + $oMyFilter->AddCondition("name", "o", "Contains"); + + echo "Candidates before:
    "; + $this->search_and_show_list($oMyFilter); + + MetaModel::BulkUpdateTracked($oMyChange, $oMyFilter, array("email" => $sNewEmail)); + + echo "Candidates after:
    "; + $this->search_and_show_list($oMyFilter); + } + + function test_error() + { + trigger_error("Stop requested", E_USER_ERROR); + } + + function test_changetracking() + { + echo "

    Create a change

    "; + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + echo "Created new change: $iChangeId
    "; + MyHelpers::var_dump_html($oMyChange); + + echo "

    Create a new object (team)

    "; + $oNewTeam = MetaModel::NewObject("cmdbTeam"); + $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); + $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); + $oNewTeam->Set("email", null); + $oNewTeam->Set("owner", "ITOP"); + $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value + $iId = $oNewTeam->DBInsertTracked($oMyChange); + echo "Created new team: $iId
    "; + echo "

    Delete team #$iId

    "; + $oTeam = MetaModel::GetObject("cmdbTeam", $iId); + $oTeam->DBDeleteTracked($oMyChange); + echo "Deleted team: $iId
    "; + MyHelpers::var_dump_html($oTeam); + } + + function test_zlist() + { + echo "

    Test ZLists

    "; + $aZLists = MetaModel::EnumZLists(); + foreach ($aZLists as $sListCode) + { + $aListInfos = MetaModel::GetZListInfo($sListCode); + echo "

    List '".$sListCode."' (".$aListInfos["description"].") of type '".$aListInfos["type"]."'

    \n"; + + foreach (MetaModel::GetSubclasses("cmdbObjectHomeMade") as $sKlass) + { + $aItems = MetaModel::GetZListItems($sKlass, $sListCode); + if (count($aItems) == 0) continue; + + echo "$sKlass - $sListCode : {".implode(", ", $aItems)."}
    \n"; + } + } + + echo "

    IsAttributeInZList()...

    "; + echo "Liens_entre_contacts_et_workshop::ws_info in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "ws_info") ? "yes" : "no")."
    \n"; + echo "Liens_entre_contacts_et_workshop::toworkshop in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "toworkshop") ? "yes" : "no")."
    \n"; + + } + + function test_SibuSQL() + { + echo "

    Simple But Structured Query Language

    "; + + $oMyFilter = new DBObjectSearch("cmdbContact"); + echo "Tous les contacts: ".$oMyFilter->ToSibuSQL()."
    \n"; + $oNewFilter = DBObjectSearch::FromSibuSQL($oMyFilter->ToSibuSQL()); + echo "En passant par un filtre, ca revient en : ".$oNewFilter->ToSibuSQL()."
    \n"; + $this->search_and_show_list($oNewFilter); + + $sFilterDesc = "cmdbContact: name Begins with '$[debutnom:as:debut du nom]' AND ownername NotLike $[ddd::]"; + echo "Construction d'un filtre a partir de sa description en SibuSQL: $sFilterDesc
    \n"; + + MyHelpers::var_dump_html(DBObjectSearch::ListSibusQLParams($sFilterDesc)); + $oNewFilter = DBObjectSearch::FromSibuSQL($sFilterDesc, array('ddd'=>123)); + echo "Ca revient en: ".$oNewFilter->ToSibuSQL(); + } + + function test_pkey() + { + echo "

    Test search on pkey

    "; + $sExpr1 = "cmdbContact: pkey IN {40, 42}"; + $sExpr2 = "cmdbContact: pkey NOTIN {40, 42}"; + $this->search_and_show_list_from_sibusql($sExpr1); + $this->search_and_show_list_from_sibusql($sExpr2); + + echo "Et maintenant, on fusionne....
    \n"; + $oSet1 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr1)); + $oSet2 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr2)); + $oIntersect = $oSet1->CreateIntersect($oSet2); + $oDelta = $oSet1->CreateDelta($oSet2); + + $oMerge = clone $oSet1; + $oMerge->Merge($oSet2); + $oMerge->Merge($oSet2); + + echo "Set1 - Found ".$oSet1->Count()." items.
    \n"; + echo "Set2 - Found ".$oSet2->Count()." items.
    \n"; + echo "Intersect - Found ".$oIntersect->Count()." items.
    \n"; + echo "Delta - Found ".$oDelta->Count()." items.
    \n"; + echo "Merge - Found ".$oMerge->Count()." items.
    \n"; + //$this->show_list($oObjSet); + } + + function test_relations() + { + echo "

    Test relations

    "; + + //MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbObjectHomeMade", "Potes")); + MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbContact", "Potes")); + + $iMaxDepth = 9; + echo "Max depth = $iMaxDepth
    \n"; + + $oObj = MetaModel::GetObject("cmdbContact", 18); + $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth); + echo $oObj->Get('name')." has some 'Potes'...
    \n"; + foreach ($aRels as $sClass => $aObjs) + { + echo "$sClass, count = ".count($aObjs)." => ".implode(', ', array_keys($aObjs))."
    \n"; + $oObjectSet = CMDBObjectSet::FromArray($sClass, $aObjs); + $this->show_list($oObjectSet); + } + + echo "

    Test relations - same results, by the mean of a SibuSQL

    "; + $this->search_and_show_list_from_sibusql("cmdbContact: RELATED (Potes, $iMaxDepth) TO (cmdbContact: pkey = 18)"); + + } + + function test_linkedset() + { + echo "

    Linked set attributes

    \n"; + $oObj = MetaModel::GetObject("cmdbContact", 18); + + echo "
    Current workshops
    \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + + echo "
    Setting workshops
    \n"; + $oNewLink = new cmdbLiens(); + $oNewLink->Set('toworkshop', 2); + $oNewLink->Set('function', 'mafonctioooon'); + $oNewLink->Set('a1', 'tralala1'); + $oNewLink->Set('a2', 'F7M'); + $oSetWorkshops = CMDBObjectSet::FromArray("cmdbLiens", array($oNewLink)); + $oObj->Set("myworkshops", $oSetWorkshops); + $this->show_list($oSetWorkshops); + + echo "
    New workshops
    \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_linkedset / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + $oObj = MetaModel::GetObject("cmdbContact", 18); + + echo "
    After the write
    \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + } + + function test_object_lifecycle() + { + echo "

    Test object lifecycle

    "; + + + MyHelpers::var_dump_html(MetaModel::GetStateAttributeCode("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumStates("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumStimuli("cmdbContact")); + foreach(MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef) + { + echo "

    Transition from $sStateCode

    \n"; + MyHelpers::var_dump_html(MetaModel::EnumTransitions("cmdbContact", $sStateCode)); + } + + $oObj = MetaModel::GetObject("cmdbContact", 18); + echo "Current state: ".$oObj->GetState()."... let's go to school..."; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("toschool"); + echo "New state: ".$oObj->GetState()."... let's get older..."; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("raise"); + echo "New state: ".$oObj->GetState()."... let's try to go further... (should give an error)"; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("raise"); // should give an error + } + + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + //$this->test_linksinfo(); + //$this->test_list_attributes(); + //$this->test_search(); + //$this->test_reload(); + //$this->test_newobject(); + $this->test_setattribute(); + //$this->test_updatecolumn(); + //$this->test_error(); + //$this->test_changetracking(); + $this->test_zlist(); + $this->test_SibuSQL(); + //$this->test_pkey(); + $this->test_relations(); + $this->test_linkedset(); + $this->test_object_lifecycle(); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Test a complex biz model on the fly +/////////////////////////////////////////////////////////////////////////// + +abstract class MyFarm extends TestBizModel +{ + static public function GetConfigFile() {return '../config-test-farm.php';} + + protected function DoPrepare() + { + parent::DoPrepare(); + $this->ResetDB(); + MetaModel::DBCheckIntegrity(); + } + + protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) + { + $oNew = MetaModel::NewObject('Mammal'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + $oNew->Set('name', $sName); + $oNew->Set('height', $iHeight); + $oNew->Set('birth', $sBirth); + return $this->ObjectToDB($oNew); + } + + protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) + { + $oNew = MetaModel::NewObject('Bird'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + return $this->ObjectToDB($oNew); + } + + protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) + { + $oNew = MetaModel::NewObject('FlyingBird'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + $oNew->Set('flyingspeed', $iFlyingSpeed); + return $this->ObjectToDB($oNew); + } + + private function InsertGroup($sName, $iLeaderId) + { + $oNew = MetaModel::NewObject('Group'); + $oNew->Set('name', $sName); + $oNew->Set('leader', $iLeaderId); + $iId = $oNew->DBInsertNoReload(); + return $iId; + } +} + + +class TestQueriesOnFarm extends MyFarm +{ + static public function GetName() + { + return 'Farm test'; + } + + static public function GetDescription() + { + return 'A series of tests on the farm business model (SQL generation)'; + } + + protected function CheckQuery($sQuery, $bIsCorrectQuery) + { + if ($bIsCorrectQuery) + { + echo "

    $sQuery

    \n"; + } + else + { + echo "

    $sQuery

    \n"; + } + try + { + //$oOql = new OqlInterpreter($sQuery); + //$oTrash = $oOql->ParseObjectQuery(); + //MyHelpers::var_dump_html($oTrash, true); + $oMyFilter = DBObjectSearch::FromOQL($sQuery); + } + catch (OQLException $oOqlException) + { + if ($bIsCorrectQuery) + { + echo "

    More info on this unexpected failure:
    ".$oOqlException->getHtmlDesc()."

    \n"; + throw $oOqlException; + return false; + } + else + { + // Everything is fine :-) + echo "

    More info on this expected failure:\n"; + echo "

      \n"; + echo "
    • ".get_class($oOqlException)."
    • \n"; + echo "
    • ".$oOqlException->getMessage()."
    • \n"; + echo "
    • ".$oOqlException->getHtmlDesc()."
    • \n"; + echo "
    \n"; + echo "

    \n"; + return true; + } + } + // The query was correctly parsed, was it expected to be correct ? + if (!$bIsCorrectQuery) + { + throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); + return false; + } + echo "

    To OQL: ".$oMyFilter->ToOQL()."

    "; + + $this->search_and_show_list($oMyFilter); + + //echo "

    first pass

    \n"; + //MyHelpers::var_dump_html($oMyFilter, true); + $sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); + //echo "

    second pass

    \n"; + //MyHelpers::var_dump_html($oMyFilter, true); + //$sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); + + $sSerialize = $oMyFilter->serialize(); + echo "

    Serialized:$sSerialize

    \n"; + $oFilter2 = DBObjectSearch::unserialize($sSerialize); + try + { + $sQuery2 = MetaModel::MakeSelectQuery($oFilter2); + } + catch (Exception $e) + { + echo "

    Could not compute the query after unserialize

    \n"; + echo "

    Query 1: $sQuery1

    \n"; + MyHelpers::var_cmp_html($oMyFilter, $oFilter2); + throw $e; + } + //if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same! + if ($sQuery1 != $sQuery2) + { + echo "

    serialize/unserialize mismatch :-(

    \n"; + MyHelpers::var_cmp_html($sQuery1, $sQuery2); + MyHelpers::var_cmp_html($oMyFilter, $oFilter2); + return false; + } + return true; + } + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + echo "

    Create protagonists...

    "; + + $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); + $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); + $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); + $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); + $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); + + $this->InsertBird('rooster', 'male', 12, 0, 0); + $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); + + // Benchmarking + // + if (false) + { + define ('COUNT_BENCHMARK', 10); + echo "

    Parsing a long query, ".COUNT_BENCHMARK." times

    "; + $sQuery = "SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR) AND Dad.height * 2 <= ROUND(TO_DAYS(Dad.birth) / (3 + 1) * 5 - 3)"; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oMyFilter = DBObjectSearch::FromOQL($sQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + echo "

    Mean time by op: $fParsingDuration

    "; + } + + echo "

    Test queries...

    "; + + $aQueries = array( + 'SELECT Animal' => true, + 'SELECT Animal WHERE Animal.pkey = 1' => false, + 'SELECT Animal WHERE Animal.id = 1' => true, + 'SELECT Aniiimal' => false, + 'SELECTe Animal' => false, + 'SELECT * FROM Animal' => false, + 'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true, + 'SELECT Animal AS zoo WHERE species = \'human\'' => true, + 'SELECT Animal AS zoo WHERE espece = \'human\'' => false, + 'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true, + 'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false, + 'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true, + 'SELECT Animal AS zoo WHERE zoo.species NOT IN (\'human\', "pig")' => true, + 'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false, + 'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true, + 'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true, + 'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true, + 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, + 'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true, + 'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true, + 'SELECT Mammal AS m WHERE m.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR)' => true, + 'SELECT Mammal AS m WHERE m.birth > DATE_SUB(NOW(), INTERVAL 2000 DAY)' => true, + 'SELECT Mammal AS m WHERE (TO_DAYS(NOW()) - TO_DAYS(m.birth)) > 2000' => true, + 'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true, + 'SELECT Mammal AS m WHERE (1 + 2' => false, + 'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true, + 'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true, + 'SELECT Mammal AS m WHERE 1/0' => true, + 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, + 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true, + 'SELECT Group JOIN Animal ON Group.leader = Animal.id' => true, + 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true, + 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.masterchief = A.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.pkey' => false, + 'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false, + 'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true, + 'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false, + 'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false, + 'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false, + 'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true, + 'SELECT Group AS G WHERE G.leader_speed < 100000' => true, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true, + 'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true, + 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, + 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true, + 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true, + ); + //$aQueries = array( + // 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, + //); + foreach($aQueries as $sQuery => $bIsCorrect) + { + $this->CheckQuery($sQuery, $bIsCorrect); + } + return true; + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestBulkChangeOnFarm extends TestBizModel +{ + static public function GetName() + { + return 'Farm test - data load'; + } + + static public function GetDescription() + { + return 'Bulk load'; + } + + static public function GetConfigFile() {return '../config-test-farm.php';} + + protected function DoPrepare() + { + parent::DoPrepare(); + $this->ResetDB(); + MetaModel::DBCheckIntegrity(); + } + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + + $oParser = new CSVParser("denomination,hauteur,age + suzy,123,2009-01-01 + chita,456, + "); + $aData = $oParser->ToArray(array('_name', '_height', '_birth'), ','); + MyHelpers::var_dump_html($aData); + + $oBulk = new BulkChange( + 'Mammal', + $aData, + array('name' => '_name', 'height' => '_height', 'birth' => '_birth'), + array('name'), + array() + ); + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Testor"); + $iChangeId = $oMyChange->DBInsert(); +// echo "Created new change: $iChangeId
    "; + + echo "

    Planned for loading...

    "; + $aRes = $oBulk->Process(); + print_r($aRes); + echo "

    Go for loading...

    "; + $aRes = $oBulk->Process($oMyChange); + print_r($aRes); + + return; + + $oRawData = array( + 'Mammal', + array('species', 'sex', 'speed', 'mother', 'father', 'name', 'height', 'birth'), + "human,male,23,0,0,romulus,192,1971 + human,male,23,0,0,remus,154,-50 + human,male,23,0,0,julius,160,-49 + human,female,23,0,0,cleopatra,142,-50 + pig,female,23,0,0,confucius,50,2003" + ); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestFullTextSearchOnFarm extends MyFarm +{ + static public function GetName() + { + return 'Farm test - full text search'; + } + + static public function GetDescription() + { + return 'Focus on the full text search feature'; + } + + protected function DoExecute() + { + echo "

    Create protagonists...

    "; + + $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); + $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); + $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); + $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); + $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); + + $this->InsertBird('rooster', 'male', 12, 0, 0); + $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); + + echo "

    Search...

    "; + $oSearch = new DBObjectSearch('Mammal'); + $oSearch->AddCondition_FullText('manof'); + //$oResultSet = new DBObjectSet($oSearch); + $this->search_and_show_list($oSearch); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Benchmark queries +/////////////////////////////////////////////////////////////////////////// + +class TestItopEfficiency extends TestBizModel +{ + static public function GetName() + { + return 'Itop - benchmark'; + } + + static public function GetDescription() + { + return 'Measure time to perform the queries'; + } + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoBenchmark($sOqlQuery) + { + echo "

    Testing query: $sOqlQuery

    "; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oFilter = DBObjectSearch::FromOQL($sOqlQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sSQL = MetaModel::MakeSelectQuery($oFilter); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fBuildDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $res = CMDBSource::Query($sSQL); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fQueryDuration = $fDuration / COUNT_BENCHMARK; + + // The fetch could not be repeated with the same results + // But we've seen so far that is was very very quick to exec + // So it makes sense to benchmark it a single time + $fStart = MyHelpers::getmicrotime(); + $aRow = CMDBSource::FetchArray($res); + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fFetchDuration = $fDuration; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sOql = $oFilter->ToOQL(); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fToOqlDuration = $fDuration / COUNT_BENCHMARK; + + echo "
      \n"; + echo "
    • Parsing: $fParsingDuration
    • \n"; + echo "
    • Build: $fBuildDuration
    • \n"; + echo "
    • Query: $fQueryDuration
    • \n"; + echo "
    • Fetch: $fFetchDuration
    • \n"; + echo "
    • ToOql: $fToOqlDuration
    • \n"; + echo "
    \n"; + + // Everything but the ToOQL (wich is interesting, anyhow) + $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; + + return array( + 'rows' => CMDBSource::NbRows($res), + 'duration (s)' => round($fTotal, 4), + 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1), + 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1), + 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1), + 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1), + 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1), + 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1), + ); + } + + protected function DoExecute() + { + define ('COUNT_BENCHMARK', 3); + echo "

    The test will be repeated ".COUNT_BENCHMARK." times

    "; + + $aQueries = array( + 'SELECT CMDBChangeOpSetAttribute', + 'SELECT CMDBChangeOpSetAttribute WHERE id=10', + 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789', + 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10', + 'SELECT bizIncidentTicket', + 'SELECT bizIncidentTicket WHERE id=1', + 'SELECT bizPerson', + 'SELECT bizPerson WHERE id=1', + 'SELECT bizIncidentTicket JOIN bizPerson ON bizIncidentTicket.agent_id = bizPerson.id WHERE bizPerson.id = 5', + ); + $aStats = array(); + foreach ($aQueries as $sOQL) + { + $aStats[$sOQL] = $this->DoBenchmark($sOQL); + } + + $aData = array(); + foreach ($aStats as $sOQL => $aResults) + { + $aValues = array(); + $aValues['OQL'] = htmlentities($sOQL); + + foreach($aResults as $sDesc => $sInfo) + { + $aValues[$sDesc] = htmlentities($sInfo); + } + $aData[] = $aValues; + } + echo MyHelpers::make_table_from_assoc_array($aData); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestItopWebServices extends TestWebServices +{ + static public function GetName() + { + return 'Itop - web services'; + } + + static public function GetDescription() + { + return 'Bulk load and ???'; + } + + protected function DoExecSingleLoad($aLoadSpec) + { + $sTitle = 'Load: '.$aLoadSpec['class']; + $sClass = $aLoadSpec['class']; + $sCsvData = $aLoadSpec['csvdata']; + + $aPostData = array('class' => $sClass, 'csvdata' => $sCsvData); + $sRes = self::DoPostRequestAuth('../webservices/import.php', $aPostData); + + echo "

    $sTitle

    $sCsvData
    $sRes
    "; + } + + protected function DoExecute() + { + + $aLoads = array( + array( + 'class' => 'bizOrganization', + 'csvdata' => "name;code\nWorldCompany;WCY" + ), + array( + 'class' => 'bizLocation', + 'csvdata' => "name;org_id;address\nParis;1;Centre de la Franca" + ), + array( + 'class' => 'bizPerson', + 'csvdata' => "email;name;first_name;org_id;phone\njohn.foo@starac.com;Foo;John;1;+33(1)23456789" + ), + array( + 'class' => 'bizTeam', + 'csvdata' => "name;org_id;location_name\nSquadra Azzura2;1;Paris" + ), + array( + 'class' => 'bizWorkgroup', + 'csvdata' => "name;org_id;team_id\ntravailleurs alpins;1;6" + ), + array( + 'class' => 'bizIncidentTicket', + 'csvdata' => "name;title;type;org_id;initial_situation;start_date;next_update;caller_id;workgroup_id;agent_id\nOVSD-12345;server down;Network;1;server was found down;2009-04-10 12:00;2009-04-10 15:00;3;317;5" + ), + ); + + foreach ($aLoads as $aLoadSpec) + { + $this->DoExecSingleLoad($aLoadSpec); + } + } +} + + +$aWebServices = array( + array( + 'verb' => 'GetVersion', + 'expected result' => '0.8', + 'explain result' => 'n/a', + 'args' => array(), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => true, + 'explain result' => 'link attribute unknown + a CI not found', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Server', /* sType */ + 'desc of ticket', /* sDescription */ + 'initial situation blah blah blah', /* sInitialSituation */ + 'very grave', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + new SOAPLinkCreationSpec( + 'logInfra', + array(new SOAPSearchCondition('id', 108)), + array(new SOAPAttributeValue('impacting', 'very critical')) + ), + new SOAPLinkCreationSpec( + 'bizDevice', + array(new SOAPSearchCondition('name', 'Router03')), + array(new SOAPAttributeValue('impact', 'who cares')) + ), + new SOAPLinkCreationSpec( + 'bizDevice', + array(new SOAPSearchCondition('name', 'thisone')), + array(new SOAPAttributeValue('impact', 'our lives')) + ), + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'caller not specified', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Desktop', /* sType */ + 'PC burning', /* sDescription */ + 'The power supply suddenly started to warm up', /* sInitialSituation */ + 'The agent could not do his job', /* sImpact */ + null, /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong condition on CI to attach', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Desktop', /* sType */ + 'PC burning', /* sDescription */ + 'The power supply suddenly started to warm up', /* sInitialSituation */ + 'The agent could not do his job', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + new SOAPLinkCreationSpec( + 'logInfra', + array(new SOAPSearchCondition('dummyfiltercode', 2)), + array(new SOAPAttributeValue('impact', 'very critical')) + ), + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => true, + 'explain result' => 'no CI to attach (empty array)', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => true, + 'explain result' => 'no CI to attach (null)', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + null, /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'caller unknown', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1000))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong values for type and severity', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'my type', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'my severity' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong password', + 'args' => array( + 'admin', /* sLogin */ + 'xxxxx', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong login', + 'args' => array( + 'xxxxx', /* sLogin */ + 'yyyyy', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), +); + + +class TestSoap extends TestSoapWebService +{ + static public function GetName() {return 'Test SOAP';} + static public function GetDescription() {return 'Do basic stuff to test the SOAP capability';} + + protected function DoExecute() + { + echo "

    Note: You may also want to try the sample SOAP client itopsoap.examples.php

    \n"; + + global $aSOAPMapping; + + // this file is generated dynamically with location = here + $sWsdlUri = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php'; + + ini_set("soap.wsdl_cache_enabled","0"); + $this->m_SoapClient = new SoapClient + ( + $sWsdlUri, + array( + 'classmap' => $aSOAPMapping, + 'trace' => 1, + ) + ); + + if (false) + { + print "
    \n"; 
    +			print_r($this->m_SoapClient->__getTypes());
    +			print "
    \n"; + } + + global $aWebServices; + foreach ($aWebServices as $iPos => $aWebService) + { + echo "

    SOAP call #$iPos ".$aWebService['explain result']."

    \n"; + + try + { + $oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']); + } + catch(SoapFault $e) + { + print "
    \n"; 
    +				print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
    +				print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
    +				print "
    "; + print "Response in HTML:

    ".$this->m_SoapClient->__getLastResponse()."

    "; + throw $e; + } + + echo "
    \n";
    +			print_r($oRes);
    +			echo "
    \n"; + + print "
    \n"; 
    +			print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
    +			print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
    +			print "
    "; + + if ($oRes instanceof SOAPResult) + { + $res = $oRes->status; + } + else + { + $res = $oRes; + } + if ($res != $aWebService['expected result']) + { + throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'"); + } + } + } +} + +class TestWebServicesDirect extends TestBizModel +{ + static public function GetName() {return 'Test web services locally';} + static public function GetDescription() {return 'Invoke the service directly (troubleshooting)';} + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoExecute() + { + $oWebServices = new WebServices(); + + global $aWebServices; + foreach ($aWebServices as $aWebService) + { + $oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']); + echo "
    \n";
    +			print_r($oRes);
    +			echo "
    \n"; + } + return true; + } +} + +class TestTriggerAndEmail extends TestBizModel +{ + static public function GetName() {return 'Test trigger and email';} + static public function GetDescription() {return 'Create a trigger and an email, then activates the trigger';} + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function CreateEmailSpec($oTrigger, $sStatus, $sTo, $sCC, $sTesterEmail) + { + $oAction = MetaModel::NewObject("ActionEmail"); + $oAction->Set("status", $sStatus); + $oAction->Set("name", "New server"); + $oAction->Set("test_recipient", $sTesterEmail); + $oAction->Set("from", $sTesterEmail); + $oAction->Set("reply_to", $sTesterEmail); + $oAction->Set("to", $sTo); + $oAction->Set("cc", $sCC); + $oAction->Set("bcc", ""); + $oAction->Set("subject", "New server: '\$this->name()$'"); + $oAction->Set("body", "

    Dear customer,

    We have created the server \$this->hyperlink()$ in the IT infrastructure database.

    You will be further notified when it is in Production.

    The IT infrastructure management team.

    Here are some accentuated characters for french people: 'ééà'

    "); + $oAction->Set("importance", "low"); + $iActionId = $this->ObjectToDB($oAction, true); + + $oLink = MetaModel::NewObject("lnkTriggerAction"); + $oLink->Set("trigger_id", $oTrigger->GetKey()); + $oLink->Set("action_id", $iActionId); + $oLink->Set("order", "1"); + $iLink = $this->ObjectToDB($oLink, true); + } + + protected function DoExecute() + { + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail1"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "romain.quetiez@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail2"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "denis.flaven@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail3"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "erwan.taloc@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyServer = MetaModel::NewObject("bizServer"); + $oMyServer->Set("name", "wfr.terminator.com"); + $oMyServer->Set("severity", "low"); + $oMyServer->Set("status", "production"); + $oMyServer->Set("org_id", 2); + $oMyServer->Set("location_id", 2); + $iServerId = $this->ObjectToDB($oMyServer, true); + + $oMyTrigger = MetaModel::NewObject("TriggerOnStateEnter"); + $oMyTrigger->Set("description", "Testor"); + $oMyTrigger->Set("target_class", "bizServer"); + $oMyTrigger->Set("state", "Shipped"); + $iTriggerId = $this->ObjectToDB($oMyTrigger, true); + + // Error in OQL field(s) + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE naime = 'Dali'", + "SELECT bizServer", + 'romain.quetiez@hp.com' + ); + + // Error: no recipient + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "", + "", + 'romain.quetiez@hp.com' + ); + + // Test + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "SELECT bizPerson", + 'romain.quetiez@hp.com' + ); + + // Test failing because of a wrong test recipient address + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "", + 'toto@walibi.bg' + ); + + // Normal behavior + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'enabled', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "", + 'romain.quetiez@hp.com' + ); + + // Does nothing, because it is disabled + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'disabled', + "SELECT bizPerson WHERE name = 'testemail%'", + "", + 'romain.quetiez@hp.com' + ); + + $oMyTrigger->DoActivate($oMyServer->ToArgs('this')); + + return true; + } +} +?> diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index fab829719..4aa1b790e 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -5,49 +5,64 @@ * 'file' string Name of the file to load * 'session_status' string 'start', 'continue' or 'end' * 'percent' integer 0..100 the percentage of completion once the file has been loaded - */ + */ define('SAFE_MINIMUM_MEMORY', 32*1024*1024); require_once('../application/utils.inc.php'); require_once('./setuppage.class.inc.php'); - -$iMemoryLimit = utils::ConvertToBytes(ini_get('memory_limit')); -if ($iMemoryLimit < SAFE_MINIMUM_MEMORY) -{ - if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE) - { - SetupWebPage::error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself."); - } - else - { - SetupWebPage::log("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY."."); - } -} - -function FatalErrorCatcher($sOutput) -{ - if ( preg_match('|.*|s', $sOutput, &$aMatches) ) - { + +$sMemoryLimit = trim(ini_get('memory_limit')); +if (empty($sMemoryLimit)) +{ + // On some PHP installations, memory_limit does not exist as a PHP setting! + // (encountered on a 5.2.0 under Windows) + // In that case, ini_set will not work, let's keep track of this and proceed with the data load + SetupWebPage::log_info("No memory limit has been defined in this instance of PHP"); +} +else +{ + // Check that the limit will allow us to load the data + // + $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit); + if ($iMemoryLimit < SAFE_MINIMUM_MEMORY) + { + if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE) + { + SetupWebPage::log_error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself."); + } + else + { + SetupWebPage::log_info("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY."."); + } + } + +} + + +function FatalErrorCatcher($sOutput) +{ + if ( preg_match('|.*|s', $sOutput, $aMatches) ) + { header("HTTP/1.0 500 Internal server error."); foreach ($aMatches as $sMatch) { $errors .= strip_tags($sMatch)."\n"; } - $sOutput = "$errors\n"; - // Logging to a file does not work if the whole memory is exhausted... - //SetupWebPage::error("Fatal error - in $__FILE__ , $errors"); - } + $sOutput = "$errors\n"; + // Logging to a file does not work if the whole memory is exhausted... + //SetupWebPage::log_error("Fatal error - in $__FILE__ , $errors"); + } return $sOutput; -} +} -//Define some bogus, invalid HTML tags that no sane -//person would ever put in an actual document and tell -//PHP to delimit fatal error warnings with them. +//Define some bogus, invalid HTML tags that no sane +//person would ever put in an actual document and tell +//PHP to delimit fatal error warnings with them. ini_set('error_prepend_string', ''); ini_set('error_append_string', ''); - -// Starts the capture of the ouput, and sets a filter to capture the fatal errors. -ob_start('FatalErrorCatcher'); // Start capturing the output, and pass it through the fatal error catcher - + +// Starts the capture of the ouput, and sets a filter to capture the fatal errors. +ob_start('FatalErrorCatcher'); // Start capturing the output, and pass it through the fatal error catcher + require_once('../core/config.class.inc.php'); require_once('../core/cmdbsource.class.inc.php'); require_once('./xmldataloader.class.inc.php'); @@ -58,14 +73,14 @@ define('TMP_CONFIG_FILE', '../tmp-config-itop.php'); // Never cache this page header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past - + /** * Main program */ $sFileName = Utils::ReadParam('file', ''); $sSessionStatus = Utils::ReadParam('session_status', ''); $iPercent = (integer)Utils::ReadParam('percent', 0); -SetupWebPage::log("Info - Loading file: $sFileName"); +SetupWebPage::log_info("Loading file: $sFileName"); try { @@ -81,30 +96,30 @@ try $oChange->Set("date", time()); $oChange->Set("userinfo", "Initialization"); $iChangeId = $oChange->DBInsert(); - SetupWebPage::log("Info - starting data load session"); + SetupWebPage::log_info("starting data load session"); $oDataLoader->StartSession($oChange); } $oDataLoader->LoadFile($sFileName); - $sResult = sprintf("Info - loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent); + $sResult = sprintf("loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent); echo $sResult; - SetupWebPage::log($sResult); + SetupWebPage::log_info($sResult); if ($sSessionStatus == 'end') { $oDataLoader->EndSession(); - SetupWebPage::log("Info - ending data load session"); + SetupWebPage::log_info("ending data load session"); } } catch(Exception $e) { echo "

    An error happened while loading the data

    \n"; echo '

    '.$e."

    \n"; - SetupWebPage::log("Error - An error happened while loading the data. ".$e); -} - -if (function_exists('memory_get_peak_usage')) -{ - SetupWebPage::log("Info - loading file '$sFileName', peak memory usage. ".memory_get_peak_usage()); + SetupWebPage::log_error("An error happened while loading the data. ".$e); +} + +if (function_exists('memory_get_peak_usage')) +{ + SetupWebPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage()); } ?> diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php index b375004a7..37785327f 100644 --- a/setup/setuppage.class.inc.php +++ b/setup/setuppage.class.inc.php @@ -101,25 +101,25 @@ table.formTable { public function info($sText) { $this->add("

    $sText

    \n"); - $this->log("Info - ".$sText); + $this->log_info($sText); } public function ok($sText) { $this->add("

    $sText

    \n"); - $this->log("Ok - ".$sText); + $this->log_ok($sText); } public function warning($sText) { $this->add("

    $sText

    \n"); - $this->log("Warning - ".$sText); + $this->log_warning($sText); } public function error($sText) { $this->add("

    $sText

    \n"); - $this->log("Error - ".$sText); + $this->log_error($sText); } public function form($aData) @@ -159,6 +159,26 @@ table.formTable { return parent::output(); } + public static function log_error($sText) + { + self::log("Error - ".$sText); + } + + public static function log_warning($sText) + { + self::log("Warning - ".$sText); + } + + public static function log_info($sText) + { + self::log("Info - ".$sText); + } + + public static function log_ok($sText) + { + self::log("Ok - ".$sText); + } + public static function log($sText) { $hLogFile = @fopen(INSTALL_LOG_FILE, 'a'); diff --git a/setup/xmldataloader.class.inc.php b/setup/xmldataloader.class.inc.php index 46ee3acec..aff8fa18a 100644 --- a/setup/xmldataloader.class.inc.php +++ b/setup/xmldataloader.class.inc.php @@ -177,7 +177,7 @@ class XMLDataLoader // tested by Romain, little impact on perf (not significant on the intial setup) if (!$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode)) { - SetupWebPage::log("Error - Value not allowed - $sClass/$iSrcId - $sAttCode: '".$oXmlObj->$sAttCode."'"); + SetupWebPage::log_error("Value not allowed - $sClass/$iSrcId - $sAttCode: '".$oXmlObj->$sAttCode."'"); echo "Wrong value for attribute $sAttCode: '".$oXmlObj->$sAttCode."'"; } $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); @@ -223,7 +223,7 @@ class XMLDataLoader } catch(Exception $e) { - SetupWebPage::log("Error - An object could not be loaded - $sClass/$iSrcId - ".$e->getMessage()); + SetupWebPage::log_error("An object could not be loaded - $sClass/$iSrcId - ".$e->getMessage()); echo $e->GetHtmlDesc(); } $aParentClasses = MetaModel::EnumParentClasses($sClass); @@ -257,7 +257,7 @@ class XMLDataLoader if ($iExtKey == 0) { $sMsg = "unresolved extkey in $sClass::".$oTargetObj->GetKey()."(".$oTargetObj->GetName().")::$sAttCode=$sTargetClass::$iTempKey"; - SetupWebPage::log("Warning - $sMsg"); + SetupWebPage::log_warning($sMsg); echo "Warning: $sMsg
    \n"; echo "
    aKeys[".$sTargetClass."]:\n";
     							print_r($this->m_aKeys[$sTargetClass]);
    diff --git a/webservices/import.php b/webservices/import.php
    index 9de93e004..eaeb1eb9f 100644
    --- a/webservices/import.php
    +++ b/webservices/import.php
    @@ -18,6 +18,7 @@
     // - only external fields attributes could be used as reconciliation keys for external keys
     // - reconciliation is made on the first column
     // - no option to force 'always create' or 'never create'
    +// - text qualifier hardcoded to "
     //
     // Known issues
     // - ALMOST impossible to troubleshoot when an externl key has a wrong value
    @@ -58,11 +59,9 @@ try
     	$sSep = utils::ReadParam('separator', ';');
     	$sCSVData = utils::ReadPostedParam('csvdata');
     
    -	$oCSVParser = new CSVParser($sCSVData); 
    -	$oCSVParser->SetSeparator($sSep);
    -	$oCSVParser->SetSkipLines(1);
    +	$oCSVParser = new CSVParser($sCSVData, $sSep, $sDelimiter = '"'); 
     
    -	// Limitation: as the attribute list is in the first line, we can not match external key by an third-party attribute
    +	// Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
     	$sRawFieldList = $oCSVParser->ListFields();
     	$aAttList = array();
     	$aExtKeys = array();
    @@ -93,9 +92,6 @@ try
     	// Limitation: the reconciliation key is the first attribute
     	$aReconcilKeys = array($sRawFieldList[0]);
     
    -//	print_r($oCSVParser->ListFields());
    -//	print_r($oCSVParser->ToArray($oCSVParser->ListFields()));
    -
     	$aData = $oCSVParser->ToArray();
     	$oBulk = new BulkChange(
     		$sClass,