Trac #86 - CSV format aligned with standard specifications

Trac #93 - Fixed issue within the setup data load (related to memory_limit)
Fixed issues with the consultant toolkit: upgrade an existing DB (add new class/attribute)
Developed core services to allow for demonstrating impact computation capability
Deprecated option operation=direct on page UI.php

SVN:trunk[313]
This commit is contained in:
Romain Quetiez
2010-03-08 09:10:16 +00:00
parent 10fa31807b
commit 5a09dc6e2b
16 changed files with 2131 additions and 1909 deletions

View File

@@ -455,8 +455,7 @@ abstract class cmdbAbstractObject extends CMDBObject
{ {
$aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode); $aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode);
} }
$sHtml = '#'.$oSet->GetFilter()->ToOQL()."\n"; $sHtml = implode($sSeparator, $aHeader)."\n";
$sHtml .= implode($sSeparator, $aHeader)."\n";
$oSet->Seek(0); $oSet->Seek(0);
while ($oObj = $oSet->Fetch()) while ($oObj = $oSet->Fetch())
{ {
@@ -464,7 +463,7 @@ abstract class cmdbAbstractObject extends CMDBObject
$aRow[] = $oObj->GetKey(); $aRow[] = $oObj->GetKey();
foreach($aList as $sAttCode) foreach($aList as $sAttCode)
{ {
$aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, '\\'); $aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, $sTextQualifier);
} }
$sHtml .= implode($sSeparator, $aRow)."\n"; $sHtml .= implode($sSeparator, $aRow)."\n";
} }

View File

@@ -53,7 +53,7 @@ class NiceWebPage extends WebPage
foreach($aChoices as $sKey => $sValue) foreach($aChoices as $sKey => $sValue)
{ {
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : ""; $sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"$sKey\"$sSelected>$sValue</option>"); $this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue)."</option>");
} }
$this->add("</select>"); $this->add("</select>");
} }

View File

@@ -61,6 +61,7 @@ abstract class AttributeDefinition
private $m_aParams = array(); private $m_aParams = array();
private $m_sHostClass = array(); private $m_sHostClass = array();
protected function Get($sParamName) {return $this->m_aParams[$sParamName];} 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) public function __construct($sCode, $aParams)
{ {
@@ -194,9 +195,9 @@ abstract class AttributeDefinition
return Str::pure2xml((string)$sValue); 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 = '') public function GetAllowedValues($aArgs = array(), $sBeginsWith = '')
@@ -233,7 +234,34 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetValuesDef() {return $this->Get("allowed_values");} public function GetValuesDef() {return $this->Get("allowed_values");}
public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} 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 GetLinkedClass() {return $this->Get('linked_class');}
public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');} public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');}
@@ -252,7 +280,7 @@ class AttributeLinkedSet extends AttributeDefinition
return "ERROR: LIST OF OBJECTS"; return "ERROR: LIST OF OBJECTS";
} }
public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"')
{ {
return "ERROR: LIST OF OBJECTS"; return "ERROR: LIST OF OBJECTS";
} }
@@ -598,6 +626,14 @@ class AttributeString extends AttributeDBField
{ {
return $value; 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); 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); 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(); $oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetAsXML($value); return $oExtAttDef->GetAsXML($value);
} }
public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"')
{ {
$oExtAttDef = $this->GetExtAttDef(); $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 ! return ''; // Not exportable in CSV !
} }

View File

@@ -1,8 +1,7 @@
<?php <?php
/** /**
* CSVParser * CSVParser
* CSV interpreter helper, optionaly tries to guess column mapping and the separator, check the consistency * CSV interpreter helper
* *
* @package iTopORM * @package iTopORM
* @author Romain Quetiez <romainquetiez@yahoo.fr> * @author Romain Quetiez <romainquetiez@yahoo.fr>
@@ -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 * CSVParser
@@ -34,157 +43,148 @@ class CSVParser
{ {
private $m_sCSVData; private $m_sCSVData;
private $m_sSep; private $m_sSep;
private $m_iSkip; private $m_sTextQualifier;
public function __construct($sTxt) public function __construct($sTxt, $sSep = ',', $sTextQualifier = '"')
{
$this->m_sCSVData = $sTxt;
}
public function SetSeparator($sSep)
{ {
$this->m_sCSVData = str_replace("\r\n", "\n", $sTxt);
$this->m_sSep = $sSep; $this->m_sSep = $sSep;
} $this->m_sTextQualifier = $sTextQualifier;
public function GetSeparator()
{
return $this->m_sSep;
} }
public function SetSkipLines($iSkip) protected $m_sCurrCell = '';
{ protected $m_aCurrRow = array();
$this->m_iSkip = $iSkip; protected $m_iToSkip = 0;
} protected $m_aDataSet = array();
public function GetSkipLines()
{
return $this->m_iSkip;
}
public function GuessSeparator() protected function __AddChar($c)
{ {
// Note: skip the first line anyway $this->m_sCurrCell .= $c;
}
$aKnownSeps = array(';', ',', "\t"); // Use double quote for special chars!!! protected function __ClearCell()
$aStatsBySeparator = array(); {
foreach ($aKnownSeps as $sSep) $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;
} }
else
foreach(explode("\n", $this->m_sCSVData) as $sLine)
{ {
$sLine = trim($sLine); $this->m_aCurrRow[] = $this->m_sCurrCell;
if (substr($sLine, 0, 1) == '#') continue; }
if (empty($sLine)) continue; $this->m_sCurrCell = '';
}
$aLineCharsCount = count_chars($sLine, 0); protected function __AddRow($c = null, $aFieldMap = null)
foreach ($aKnownSeps as $sSep) {
$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;
} }
} elseif ($c == "\n")
// 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))
{ {
$this->SetSeparator($sSep); $iEvent = evNEWLINE;
break;
} }
} elseif ($c == $this->m_sTextQualifier)
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)
{ {
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); call_user_func($aCallSpec, $c, $aFieldMap);
return 0; }
else
{
throw new CSVParserException("CSVParser: unknown verb '$sAction'");
} }
} }
$this->SetSkipLines(1);
return 1;
}
}
function ToArray($aFieldMap = null, $iMax = 0) $iLineCount = count($this->m_aDataSet);
{ if (($iMax > 0) && ($iLineCount >= $iMax)) break;
// $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;
} }
return $aRes; // Close the final line
$this->__AddRow(null, $aFieldMap);
return $this->m_aDataSet;
} }
public function ListFields() public function ListFields()
{ {
// Take the first valuable line $aHeader = $this->ToArray(0, null, 1);
foreach(explode("\n", $this->m_sCSVData) as $sLine) return $aHeader[0];
{
$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;
} }
} }

View File

@@ -381,10 +381,10 @@ abstract class DBObject
return $oAtt->GetAsXML($this->Get($sAttCode)); 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); $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) protected static function MakeHyperLink($sObjClass, $sObjKey, $aAvailableFields)
@@ -883,6 +883,7 @@ abstract class DBObject
$aScalarArgs = array(); $aScalarArgs = array();
$aScalarArgs[$sArgName] = $this->GetKey(); $aScalarArgs[$sArgName] = $this->GetKey();
$aScalarArgs[$sArgName.'->id'] = $this->GetKey(); $aScalarArgs[$sArgName.'->id'] = $this->GetKey();
$aScalarArgs[$sArgName.'->object()'] = $this;
$aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink(); $aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink();
$aScalarArgs[$sArgName.'->name()'] = $this->GetName(); $aScalarArgs[$sArgName.'->name()'] = $this->GetName();
@@ -903,6 +904,38 @@ abstract class DBObject
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) 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) foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo)
{ {
MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]); MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]);

View File

@@ -458,6 +458,19 @@ abstract class MetaModel
return $aExtKeys; 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) final static public function GetExternalFields($sClass, $sKeyAttCode)
{ {
$aExtFields = array(); $aExtFields = array();
@@ -2039,13 +2052,16 @@ abstract class MetaModel
list($aErrors, $aSugFix) = self::DBCheckFormat(); list($aErrors, $aSugFix) = self::DBCheckFormat();
$aSQL = array(); $aSQL = array();
foreach ($aSugFix as $sClass => $aQueries) foreach ($aSugFix as $sClass => $aTarget)
{ {
foreach ($aQueries as $sQuery) foreach ($aTarget as $aQueries)
{ {
//$aSQL[] = $sQuery; foreach ($aQueries as $sQuery)
// forces a refresh of cached information {
CMDBSource::CreateTable($sQuery); //$aSQL[] = $sQuery;
// forces a refresh of cached information
CMDBSource::CreateTable($sQuery);
}
} }
} }
// does not work -how to have multiple statements in a single query? // 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" : ""); $sAutoIncrement = (self::IsAutoIncrementKey($sClass) ? "AUTO_INCREMENT" : "");
if (!CMDBSource::IsTable($sTable)) if (!CMDBSource::IsTable($sTable))
{ {
$aErrors[$sClass][] = "table '$sTable' could not be found into the DB"; $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"; $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 // Check that the key field exists
// //
elseif (!CMDBSource::IsField($sTable, $sKeyField)) elseif (!CMDBSource::IsField($sTable, $sKeyField))
{ {
$aErrors[$sClass][] = "key '$sKeyField' (table $sTable) could not be found"; $aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) could not be found";
$aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY"; $aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY";
} }
else else
{ {
@@ -2094,13 +2110,13 @@ abstract class MetaModel
// //
if (!CMDBSource::IsKey($sTable, $sKeyField)) if (!CMDBSource::IsKey($sTable, $sKeyField))
{ {
$aErrors[$sClass][] = "key '$sKeyField' is not a key for table '$sTable'"; $aErrors[$sClass]['id'][] = "key '$sKeyField' is not a key for table '$sTable'";
$aSugFix[$sClass][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)"; $aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)";
} }
if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField)) if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField))
{ {
$aErrors[$sClass][] = "key '$sKeyField' (table $sTable) is not automatically incremented"; $aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) is not automatically incremented";
$aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` `$sKeyField` INT(11) NOT NULL AUTO_INCREMENT"; $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"; $sFieldSpecs = $oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL";
if (!CMDBSource::IsField($sTable, $sField)) if (!CMDBSource::IsField($sTable, $sField))
{ {
$aErrors[$sClass][] = "field '$sField' could not be found in table '$sTable'"; $aErrors[$sClass][$sAttCode][] = "field '$sField' could not be found in table '$sTable'";
$aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs"; $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs";
if ($oAttDef->IsExternalKey()) if ($oAttDef->IsExternalKey())
{ {
$aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)";
} }
} }
else else
@@ -2135,30 +2151,30 @@ abstract class MetaModel
$bToBeChanged = true; $bToBeChanged = true;
if ($oAttDef->IsNullAllowed()) 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 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); $sActualFieldType = CMDBSource::GetFieldType($sTable, $sField);
if (strcasecmp($sDBFieldType, $sActualFieldType) != 0) if (strcasecmp($sDBFieldType, $sActualFieldType) != 0)
{ {
$bToBeChanged = true; $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) 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) // Create indexes (external keys only... so far)
// //
if ($oAttDef->IsExternalKey() && !CMDBSource::HasIndex($sTable, $sField)) if ($oAttDef->IsExternalKey() && !CMDBSource::HasIndex($sTable, $sField))
{ {
$aErrors[$sClass][] = "Foreign key '$sField' in table '$sTable' should have an index"; $aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index";
$aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; $aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)";
} }
} }
} }

View File

@@ -35,7 +35,7 @@ abstract class ValueSetDefinition
} }
public function GetValues($aArgs, $sBeginsWith) public function GetValues($aArgs, $sBeginsWith = '')
{ {
if (!$this->m_bIsLoaded) 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 <romainquetiez@yahoo.fr>
* @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) * Fixed set values (could be hardcoded in the business model)
* *

View File

@@ -254,7 +254,7 @@ function DumpDatabase()
function printMenu($sConfigFile) function printMenu($sConfigFile)
{ {
$sClassCount = count(MetaModel::GetClasses()); $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); $sUrl = "?config=".urlencode($sConfigFile);
echo "<div style=\"background-color:eeeeee; padding:10px;\">\n"; echo "<div style=\"background-color:eeeeee; padding:10px;\">\n";
@@ -345,22 +345,25 @@ function DisplayDBFormatIssues($aErrors, $aSugFix, $sRepairUrl = "", $sSQLStatem
echo "<div style=\"width:100%;padding:10px;background:#FFAAAA;display:;\">"; echo "<div style=\"width:100%;padding:10px;background:#FFAAAA;display:;\">";
echo "<h1>Wrong Database format</h1>\n"; echo "<h1>Wrong Database format</h1>\n";
echo "<p>The current database is not consistent with the given business model. Please investigate.</p>\n"; echo "<p>The current database is not consistent with the given business model. Please investigate.</p>\n";
foreach ($aErrors as $sClass => $aMessages) foreach ($aErrors as $sClass => $aTarget)
{ {
echo "<p>Wrong declaration (or DB format ?) for class <b>$sClass</b></p>\n"; echo "<p>Wrong declaration (or DB format ?) for class <b>$sClass</b></p>\n";
echo "<ul class=\"treeview\">\n"; echo "<ul class=\"treeview\">\n";
$i = 0; $i = 0;
foreach ($aMessages as $sMsg) foreach ($aTarget as $sTarget => $aMessages)
{ {
echo "<p>Wrong declaration for attribute <b>$sTarget</b></p>\n";
$sMsg = implode(' AND ', $aMessages);
if (!empty($sRepairUrl)) if (!empty($sRepairUrl))
{ {
$aSQLFixes[] = $aSugFix[$sClass][$i]; $aSQLFixes = array_merge($aSQLFixes, $aSugFix[$sClass][$sTarget]);
$sUrl = "$sRepairUrl&$sSQLStatementArgName=".urlencode($aSugFix[$sClass][$i]); $sSQLFixes = implode('; ', $aSugFix[$sClass][$sTarget]);
echo "<li>$sMsg (<a href=\"$sUrl\" title=\"".$aSugFix[$sClass][$i]."\" target=\"_blank\">fix it now!</a>)</li>\n"; $sUrl = "$sRepairUrl&$sSQLStatementArgName=".urlencode($sSQLFixes);
echo "<li>$sMsg (<a href=\"$sUrl\" title=\"".htmlentities($sSQLFixes)."\" target=\"_blank\">fix it now!</a>)</li>\n";
} }
else else
{ {
echo "<li>$sMsg ({$aSugFix[$sClass][$i]})</li>\n"; echo "<li>$sMsg (".htmlentities($sSQLFixes).")</li>\n";
} }
$i++; $i++;
} }

View File

@@ -226,8 +226,8 @@ function Page3_ViewResults($oPage, $oFilter)
$oSet = new CMDBObjectSet($oFilter); $oSet = new CMDBObjectSet($oFilter);
$oPage->p("Found ".$oSet->Count()." items"); $oPage->p("Found ".$oSet->Count()." items");
$sFilterPhrase = $oFilter->serialize(); $sFilterPhrase = urlencode($oFilter->serialize());
$oPage->p("<a href=\"/pages/index.php?operation=direct&filter=$sFilterPhrase\">See detailed results</a>"); $oPage->p("<a href=\"/pages/index.php?operation=search&filter=$sFilterPhrase\">See detailed results</a>");
} }
} }

View File

@@ -62,7 +62,7 @@ function MakeExtFieldSelectValue($sAttCode, $sExtAttCode)
function ShowTableForm($oPage, $oCSVParser, $sClass) function ShowTableForm($oPage, $oCSVParser, $sClass)
{ {
$aData = $oCSVParser->ToArray(null, 3); $aData = $oCSVParser->ToArray(1, null, 3);
$aColToRow = array(); $aColToRow = array();
foreach($aData as $aRow) foreach($aData as $aRow)
{ {
@@ -193,6 +193,10 @@ function ShowTableForm($oPage, $oCSVParser, $sClass)
{ {
$aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex];
} }
else
{
// Houston...
}
} }
$oPage->details($aFields); $oPage->details($aFields);
} }
@@ -354,12 +358,24 @@ function Do_Welcome($oPage, $sClass)
$sWiztep = "1_welcome"; $sWiztep = "1_welcome";
$oPage->p("<h1>Bulk load from CSV data / step 1</h1>"); $oPage->p("<h1>Bulk load from CSV data / step 1</h1>");
// Reload values (in case we are reaching this page from the next one
$sCSVData = utils::ReadPostedParam('csvdata'); $sCSVData = utils::ReadPostedParam('csvdata');
$sSep = utils::ReadPostedParam('separator', ',');
$sTQualif = utils::ReadPostedParam('textqualifier', '"');
$aSeparators = array(',' => ', (coma)', ';' => ';', ';' => ';', '|' => '|', '#' => '#', '@' => '@', ':' => ':');
$aTextQualifiers = array('"' => '"', "'" => "'", '`' => '`', '/' => '/');
$oPage->add("<form method=\"post\" action=\"\">"); $oPage->add("<form method=\"post\" action=\"\">");
$oPage->MakeClassesSelect("class", $sClass, 50, UR_ACTION_BULK_MODIFY); $oPage->MakeClassesSelect("class", $sClass, 50, UR_ACTION_BULK_MODIFY);
$oPage->add("<br/>"); $oPage->add("<br/>");
$oPage->add("<textarea rows=\"25\" cols=\"100\" name=\"csvdata\" wrap=\"soft\">$sCSVData</textarea>"); $oPage->add("<textarea rows=\"25\" cols=\"100\" name=\"csvdata\" wrap=\"soft\">".htmlentities($sCSVData)."</textarea>");
$oPage->add("<br/>");
$oPage->add("Separator: ");
$oPage->add_select($aSeparators, 'separator', $sSep, 50);
$oPage->add("<br/>");
$oPage->add("Text qualifier: ");
$oPage->add_select($aTextQualifiers, 'textqualifier', $sTQualif, 50);
$oPage->add("<br/>"); $oPage->add("<br/>");
$oPage->add("<input type=\"hidden\" name=\"fromwiztep\" value=\"$sWiztep\">"); $oPage->add("<input type=\"hidden\" name=\"fromwiztep\" value=\"$sWiztep\">");
$oPage->add("<input type=\"submit\" name=\"todo\" value=\"Next\"><br/>\n"); $oPage->add("<input type=\"submit\" name=\"todo\" value=\"Next\"><br/>\n");
@@ -415,12 +431,13 @@ function Do_Format($oPage, $sClass)
$sWiztep = "2_format"; $sWiztep = "2_format";
$sCSVData = utils::ReadPostedParam('csvdata'); $sCSVData = utils::ReadPostedParam('csvdata');
$oCSVParser = new CSVParser($sCSVData); $sSep = utils::ReadPostedParam('separator');
$sSep = $oCSVParser->GuessSeparator(); $sTQualif = utils::ReadPostedParam('textqualifier');
$iSkip = $oCSVParser->GuessSkipLines(); $oCSVParser = new CSVParser($sCSVData, $sSep, $sTQualif);
$iSkip = 1;
// No data ? // No data ?
$aData = $oCSVParser->ToArray(null); $aData = $oCSVParser->ToArray();
$iTarget = count($aData); $iTarget = count($aData);
if ($iTarget == 0) if ($iTarget == 0)
{ {
@@ -429,29 +446,32 @@ function Do_Format($oPage, $sClass)
return; return;
} }
// Guess the format : // Expected format - to be improved
$oPage->p("Guessed separator: '<strong>$sSep</strong>' (ASCII=".ord($sSep).")"); $oPage->p("Separator: '<strong>$sSep</strong>'");
$oPage->p("Guessed # of lines to skip: $iSkip"); $oPage->p("Text qualifier: '<strong>$sTQualif</strong>'");
$oPage->p("The first line will be skipped (considered as being the list of fields)");
$oPage->p("Target: $iTarget rows"); $oPage->p("Target: $iTarget rows");
$oPage->Add("<form method=\"post\" action=\"\">"); $oPage->add("<form method=\"post\" action=\"\">");
ShowTableForm($oPage, $oCSVParser, $sClass); ShowTableForm($oPage, $oCSVParser, $sClass);
$oPage->Add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">"); $oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">");
$oPage->Add("<input type=\"hidden\" name=\"csvdata\" value=\"$sCSVData\">"); $oPage->add("<input type=\"hidden\" name=\"csvdata\" value=\"".htmlentities($sCSVData)."\">");
$oPage->Add("<input type=\"hidden\" name=\"separator\" value=\"$sSep\">"); $oPage->add("<input type=\"hidden\" name=\"separator\" value=\"".htmlentities($sSep)."\">");
$oPage->Add("<input type=\"hidden\" name=\"skiplines\" value=\"$iSkip\">"); $oPage->add("<input type=\"hidden\" name=\"textqualifier\" value=\"".htmlentities($sTQualif)."\">");
$oPage->add("<input type=\"hidden\" name=\"skiplines\" value=\"$iSkip\">");
$oPage->Add("<input type=\"hidden\" name=\"fromwiztep\" value=\"$sWiztep\">"); $oPage->add("<input type=\"hidden\" name=\"fromwiztep\" value=\"$sWiztep\">");
$oPage->add("<input type=\"submit\" name=\"todo\" value=\"Back\">"); $oPage->add("<input type=\"submit\" name=\"todo\" value=\"Back\">");
$oPage->Add("<input type=\"submit\" name=\"todo\" value=\"Next\">"); $oPage->add("<input type=\"submit\" name=\"todo\" value=\"Next\">");
$oPage->Add("</form>"); $oPage->add("</form>");
} }
function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null)
{ {
$sCSVData = utils::ReadPostedParam('csvdata'); $sCSVData = utils::ReadPostedParam('csvdata');
$sSep = utils::ReadPostedParam('separator'); $sSep = utils::ReadPostedParam('separator');
$sTQualif = utils::ReadPostedParam('textqualifier');
$iSkip = utils::ReadPostedParam('skiplines'); $iSkip = utils::ReadPostedParam('skiplines');
$aFieldMap = utils::ReadPostedParam('fmap'); $aFieldMap = utils::ReadPostedParam('fmap');
$aIsReconcKey = utils::ReadPostedParam('iskey'); $aIsReconcKey = utils::ReadPostedParam('iskey');
@@ -465,16 +485,15 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null)
return; return;
} }
$oCSVParser = new CSVParser($sCSVData); $oCSVParser = new CSVParser($sCSVData, $sSep, $sTQualif);
$oCSVParser->SetSeparator($sSep); $aData = $oCSVParser->ToArray($iSkip, null);
$oCSVParser->SetSkipLines($iSkip);
$aData = $oCSVParser->ToArray(null);
$iTarget = count($aData); $iTarget = count($aData);
$oPage->p("<h2>Goal summary</h2>"); $oPage->p("<h2>Goal summary</h2>");
$oPage->p("Target: $iTarget rows"); $oPage->p("Target: $iTarget rows");
$aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); $aSampleData = $oCSVParser->ToArray($iSkip, array_keys($aFieldMap), 5);
$aDisplayConfig = array(); $aDisplayConfig = array();
$aExtKeys = array(); $aExtKeys = array();
foreach ($aFieldMap as $sFieldId=>$sColDesc) foreach ($aFieldMap as $sFieldId=>$sColDesc)
@@ -519,6 +538,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null)
$aDisplayConfig[$sFieldId] = array("label"=>"-?-?-$sColDesc-?-?-", "description"=>""); $aDisplayConfig[$sFieldId] = array("label"=>"-?-?-$sColDesc-?-?-", "description"=>"");
} }
} }
$oPage->table($aDisplayConfig, $aSampleData); $oPage->table($aDisplayConfig, $aSampleData);
if ($oChange) if ($oChange)
@@ -558,8 +578,9 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null)
$oPage->add("<form method=\"post\" action=\"\">"); $oPage->add("<form method=\"post\" action=\"\">");
$oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">"); $oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">");
$oPage->add("<input type=\"hidden\" name=\"csvdata\" value=\"$sCSVData\">"); $oPage->add("<input type=\"hidden\" name=\"csvdata\" value=\"".htmlentities($sCSVData)."\">");
$oPage->add("<input type=\"hidden\" name=\"separator\" value=\"$sSep\">"); $oPage->add("<input type=\"hidden\" name=\"separator\" value=\"".htmlentities($sSep)."\">");
$oPage->add("<input type=\"hidden\" name=\"textqualifier\" value=\"".htmlentities($sTQualif)."\">");
$oPage->add("<input type=\"hidden\" name=\"skiplines\" value=\"$iSkip\">"); $oPage->add("<input type=\"hidden\" name=\"skiplines\" value=\"$iSkip\">");
$oPage->add_input_hidden("fmap", $aFieldMap); $oPage->add_input_hidden("fmap", $aFieldMap);
$oPage->add_input_hidden("iskey", $aIsReconcKey); $oPage->add_input_hidden("iskey", $aIsReconcKey);

View File

@@ -149,36 +149,6 @@ function DisplayChangesLog(WebPage $oPage, $sClassName, $sKey)
$oPage->p("<a href=\"?operation=delete&class=$sClassName&key=$sKey\">Delete this object (no confirmation!)</a>"); $oPage->p("<a href=\"?operation=delete&class=$sClassName&key=$sKey\">Delete this object (no confirmation!)</a>");
} }
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) function DumpObjects(WebPage $oPage, $sClassName, CMDBSearchFilter $oSearchFilter = null)
{ {
global $oContext; global $oContext;
@@ -554,32 +524,6 @@ switch($operation)
DeleteObject($oPage, $sClass, $sKey); DeleteObject($oPage, $sClass, $sKey);
break; 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("<TEXTAREA ROWS=\"30\" COLS=\"100\">");
DumpObjectsAsCSV($oPage, $oSearchFilter->GetClass(), $oSearchFilter);
$oPage->Add("</TEXTAREA>");
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': case 'addlinks':
$sClass = ReadParam('class'); $sClass = ReadParam('class');
$sKey = ReadParam('key'); $sKey = ReadParam('key');

File diff suppressed because it is too large Load Diff

View File

@@ -5,49 +5,64 @@
* 'file' string Name of the file to load * 'file' string Name of the file to load
* 'session_status' string 'start', 'continue' or 'end' * 'session_status' string 'start', 'continue' or 'end'
* 'percent' integer 0..100 the percentage of completion once the file has been loaded * 'percent' integer 0..100 the percentage of completion once the file has been loaded
*/ */
define('SAFE_MINIMUM_MEMORY', 32*1024*1024); define('SAFE_MINIMUM_MEMORY', 32*1024*1024);
require_once('../application/utils.inc.php'); require_once('../application/utils.inc.php');
require_once('./setuppage.class.inc.php'); require_once('./setuppage.class.inc.php');
$iMemoryLimit = utils::ConvertToBytes(ini_get('memory_limit')); $sMemoryLimit = trim(ini_get('memory_limit'));
if ($iMemoryLimit < SAFE_MINIMUM_MEMORY) if (empty($sMemoryLimit))
{ {
if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE) // On some PHP installations, memory_limit does not exist as a PHP setting!
{ // (encountered on a 5.2.0 under Windows)
SetupWebPage::error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself."); // 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 }
{ else
SetupWebPage::log("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY."."); {
} // Check that the limit will allow us to load the data
} //
$iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
function FatalErrorCatcher($sOutput) if ($iMemoryLimit < SAFE_MINIMUM_MEMORY)
{ {
if ( preg_match('|<phpfatalerror>.*</phpfatalerror>|s', $sOutput, &$aMatches) ) 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('|<phpfatalerror>.*</phpfatalerror>|s', $sOutput, $aMatches) )
{
header("HTTP/1.0 500 Internal server error."); header("HTTP/1.0 500 Internal server error.");
foreach ($aMatches as $sMatch) foreach ($aMatches as $sMatch)
{ {
$errors .= strip_tags($sMatch)."\n"; $errors .= strip_tags($sMatch)."\n";
} }
$sOutput = "$errors\n"; $sOutput = "$errors\n";
// Logging to a file does not work if the whole memory is exhausted... // Logging to a file does not work if the whole memory is exhausted...
//SetupWebPage::error("Fatal error - in $__FILE__ , $errors"); //SetupWebPage::log_error("Fatal error - in $__FILE__ , $errors");
} }
return $sOutput; return $sOutput;
} }
//Define some bogus, invalid HTML tags that no sane //Define some bogus, invalid HTML tags that no sane
//person would ever put in an actual document and tell //person would ever put in an actual document and tell
//PHP to delimit fatal error warnings with them. //PHP to delimit fatal error warnings with them.
ini_set('error_prepend_string', '<phpfatalerror>'); ini_set('error_prepend_string', '<phpfatalerror>');
ini_set('error_append_string', '</phpfatalerror>'); ini_set('error_append_string', '</phpfatalerror>');
// Starts the capture of the ouput, and sets a filter to capture the fatal errors. // 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 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/config.class.inc.php');
require_once('../core/cmdbsource.class.inc.php'); require_once('../core/cmdbsource.class.inc.php');
require_once('./xmldataloader.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 // Never cache this page
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
/** /**
* Main program * Main program
*/ */
$sFileName = Utils::ReadParam('file', ''); $sFileName = Utils::ReadParam('file', '');
$sSessionStatus = Utils::ReadParam('session_status', ''); $sSessionStatus = Utils::ReadParam('session_status', '');
$iPercent = (integer)Utils::ReadParam('percent', 0); $iPercent = (integer)Utils::ReadParam('percent', 0);
SetupWebPage::log("Info - Loading file: $sFileName"); SetupWebPage::log_info("Loading file: $sFileName");
try try
{ {
@@ -81,30 +96,30 @@ try
$oChange->Set("date", time()); $oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization"); $oChange->Set("userinfo", "Initialization");
$iChangeId = $oChange->DBInsert(); $iChangeId = $oChange->DBInsert();
SetupWebPage::log("Info - starting data load session"); SetupWebPage::log_info("starting data load session");
$oDataLoader->StartSession($oChange); $oDataLoader->StartSession($oChange);
} }
$oDataLoader->LoadFile($sFileName); $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; echo $sResult;
SetupWebPage::log($sResult); SetupWebPage::log_info($sResult);
if ($sSessionStatus == 'end') if ($sSessionStatus == 'end')
{ {
$oDataLoader->EndSession(); $oDataLoader->EndSession();
SetupWebPage::log("Info - ending data load session"); SetupWebPage::log_info("ending data load session");
} }
} }
catch(Exception $e) catch(Exception $e)
{ {
echo "<p>An error happened while loading the data</p>\n"; echo "<p>An error happened while loading the data</p>\n";
echo '<p>'.$e."</p>\n"; echo '<p>'.$e."</p>\n";
SetupWebPage::log("Error - An error happened while loading the data. ".$e); SetupWebPage::log_error("An error happened while loading the data. ".$e);
} }
if (function_exists('memory_get_peak_usage')) if (function_exists('memory_get_peak_usage'))
{ {
SetupWebPage::log("Info - loading file '$sFileName', peak memory usage. ".memory_get_peak_usage()); SetupWebPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage());
} }
?> ?>

View File

@@ -101,25 +101,25 @@ table.formTable {
public function info($sText) public function info($sText)
{ {
$this->add("<p class=\"info\">$sText</p>\n"); $this->add("<p class=\"info\">$sText</p>\n");
$this->log("Info - ".$sText); $this->log_info($sText);
} }
public function ok($sText) public function ok($sText)
{ {
$this->add("<p class=\"ok\">$sText</p>\n"); $this->add("<p class=\"ok\">$sText</p>\n");
$this->log("Ok - ".$sText); $this->log_ok($sText);
} }
public function warning($sText) public function warning($sText)
{ {
$this->add("<p class=\"warning\">$sText</p>\n"); $this->add("<p class=\"warning\">$sText</p>\n");
$this->log("Warning - ".$sText); $this->log_warning($sText);
} }
public function error($sText) public function error($sText)
{ {
$this->add("<p class=\"error\">$sText</p>\n"); $this->add("<p class=\"error\">$sText</p>\n");
$this->log("Error - ".$sText); $this->log_error($sText);
} }
public function form($aData) public function form($aData)
@@ -159,6 +159,26 @@ table.formTable {
return parent::output(); 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) public static function log($sText)
{ {
$hLogFile = @fopen(INSTALL_LOG_FILE, 'a'); $hLogFile = @fopen(INSTALL_LOG_FILE, 'a');

View File

@@ -177,7 +177,7 @@ class XMLDataLoader
// tested by Romain, little impact on perf (not significant on the intial setup) // tested by Romain, little impact on perf (not significant on the intial setup)
if (!$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode)) 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."'"; echo "Wrong value for attribute $sAttCode: '".$oXmlObj->$sAttCode."'";
} }
$oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode);
@@ -223,7 +223,7 @@ class XMLDataLoader
} }
catch(Exception $e) 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(); echo $e->GetHtmlDesc();
} }
$aParentClasses = MetaModel::EnumParentClasses($sClass); $aParentClasses = MetaModel::EnumParentClasses($sClass);
@@ -257,7 +257,7 @@ class XMLDataLoader
if ($iExtKey == 0) if ($iExtKey == 0)
{ {
$sMsg = "unresolved extkey in $sClass::".$oTargetObj->GetKey()."(".$oTargetObj->GetName().")::$sAttCode=$sTargetClass::$iTempKey"; $sMsg = "unresolved extkey in $sClass::".$oTargetObj->GetKey()."(".$oTargetObj->GetName().")::$sAttCode=$sTargetClass::$iTempKey";
SetupWebPage::log("Warning - $sMsg"); SetupWebPage::log_warning($sMsg);
echo "Warning: $sMsg<br/>\n"; echo "Warning: $sMsg<br/>\n";
echo "<pre>aKeys[".$sTargetClass."]:\n"; echo "<pre>aKeys[".$sTargetClass."]:\n";
print_r($this->m_aKeys[$sTargetClass]); print_r($this->m_aKeys[$sTargetClass]);

View File

@@ -18,6 +18,7 @@
// - only external fields attributes could be used as reconciliation keys for external keys // - only external fields attributes could be used as reconciliation keys for external keys
// - reconciliation is made on the first column // - reconciliation is made on the first column
// - no option to force 'always create' or 'never create' // - no option to force 'always create' or 'never create'
// - text qualifier hardcoded to "
// //
// Known issues // Known issues
// - ALMOST impossible to troubleshoot when an externl key has a wrong value // - ALMOST impossible to troubleshoot when an externl key has a wrong value
@@ -58,11 +59,9 @@ try
$sSep = utils::ReadParam('separator', ';'); $sSep = utils::ReadParam('separator', ';');
$sCSVData = utils::ReadPostedParam('csvdata'); $sCSVData = utils::ReadPostedParam('csvdata');
$oCSVParser = new CSVParser($sCSVData); $oCSVParser = new CSVParser($sCSVData, $sSep, $sDelimiter = '"');
$oCSVParser->SetSeparator($sSep);
$oCSVParser->SetSkipLines(1);
// 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(); $sRawFieldList = $oCSVParser->ListFields();
$aAttList = array(); $aAttList = array();
$aExtKeys = array(); $aExtKeys = array();
@@ -93,9 +92,6 @@ try
// Limitation: the reconciliation key is the first attribute // Limitation: the reconciliation key is the first attribute
$aReconcilKeys = array($sRawFieldList[0]); $aReconcilKeys = array($sRawFieldList[0]);
// print_r($oCSVParser->ListFields());
// print_r($oCSVParser->ToArray($oCSVParser->ListFields()));
$aData = $oCSVParser->ToArray(); $aData = $oCSVParser->ToArray();
$oBulk = new BulkChange( $oBulk = new BulkChange(
$sClass, $sClass,