mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
CSV import/export reworked:
Trac #174 and #283: import.php localized by default, option no_localize to disable Trac #554: export.php localized by default, option no_localize to disable Trac #555: friendlyname abusively used as a reconciliation key + Default charset is ISO-8859-1 to be compatible with Excel (See config parameter csv_file_default_charset) + CSV export in UTF-8 with BOM to help Excel in getting it right (not all versions) + Fixed reporting issues (wrong class, exceptions, changed external key) + Fixed settings lost when navigating in the import wizard + Fixed issues when some html entities were found in the data (reporting + export) + Added a link to download the CSV export.php SVN:trunk[2253]
This commit is contained in:
@@ -895,6 +895,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$aFields = explode(',', $aParams['fields']);
|
||||
}
|
||||
|
||||
$bFieldsAdvanced = false;
|
||||
if (isset($aParams['fields_advanced']))
|
||||
{
|
||||
$bFieldsAdvanced = (bool) $aParams['fields_advanced'];
|
||||
}
|
||||
|
||||
$bLocalize = true;
|
||||
if (isset($aParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aParams['localize_values'];
|
||||
}
|
||||
|
||||
$aList = array();
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
@@ -920,7 +932,29 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
// Standard list of attributes (no link sets)
|
||||
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
|
||||
{
|
||||
$aList[$sAlias][$sAttCode] = $oAttDef;
|
||||
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
|
||||
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
|
||||
{
|
||||
if ($bFieldsAdvanced)
|
||||
{
|
||||
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
|
||||
{
|
||||
$sRemoteClass = $oAttDef->GetTargetClass();
|
||||
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
|
||||
{
|
||||
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any other attribute
|
||||
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -932,36 +966,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
}
|
||||
$aHeader[] = 'id';
|
||||
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
|
||||
if ($bFieldsAdvanced)
|
||||
{
|
||||
$aHeader[] = 'id';
|
||||
}
|
||||
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
$sStar = '';
|
||||
if ($oAttDef->IsExternalField())
|
||||
if (!$oAttDef->IsNullAllowed() && isset($aParams['showMandatoryFields']))
|
||||
{
|
||||
$sExtKeyLabel = MetaModel::GetLabel($sClassName, $oAttDef->GetKeyAttCode());
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode());
|
||||
if (!$oExtKeyAttDef->IsNullAllowed() && isset($aParams['showMandatoryFields']))
|
||||
{
|
||||
$sStar = '*';
|
||||
}
|
||||
$sRemoteAttLabel = MetaModel::GetLabel($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||
$oTargetAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||
$sSuffix = '';
|
||||
if ($oTargetAttDef->IsExternalKey())
|
||||
{
|
||||
$sSuffix = '->id';
|
||||
}
|
||||
|
||||
$aHeader[] = $sExtKeyLabel.'->'.$sRemoteAttLabel.$sSuffix.$sStar;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$oAttDef->IsNullAllowed() && isset($aParams['showMandatoryFields']))
|
||||
{
|
||||
$sStar = '*';
|
||||
}
|
||||
$aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode).$sStar;
|
||||
$sStar = '*';
|
||||
}
|
||||
$aHeader[] = ($bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx).$sStar;
|
||||
}
|
||||
}
|
||||
$sHtml = implode($sSeparator, $aHeader)."\n";
|
||||
@@ -972,15 +988,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
foreach($aAuthorizedClasses as $sAlias => $sClassName)
|
||||
{
|
||||
$oObj = $aObjects[$sAlias];
|
||||
if (is_null($oObj))
|
||||
{
|
||||
$aRow[] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[] = $oObj->GetKey();
|
||||
}
|
||||
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
|
||||
if ($bFieldsAdvanced)
|
||||
{
|
||||
if (is_null($oObj))
|
||||
{
|
||||
@@ -988,7 +996,19 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, $sTextQualifier);
|
||||
$aRow[] = $oObj->GetKey();
|
||||
}
|
||||
}
|
||||
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
if (is_null($oObj))
|
||||
{
|
||||
$aRow[] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oObj->Get($sAttCodeEx);
|
||||
$aRow[] = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1015,6 +1035,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$aFields = explode(',', $aParams['fields']);
|
||||
}
|
||||
|
||||
$bFieldsAdvanced = false;
|
||||
if (isset($aParams['fields_advanced']))
|
||||
{
|
||||
$bFieldsAdvanced = (bool) $aParams['fields_advanced'];
|
||||
}
|
||||
|
||||
$bLocalize = true;
|
||||
if (isset($aParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aParams['localize_values'];
|
||||
}
|
||||
|
||||
$aList = array();
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
@@ -1040,7 +1072,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
// Standard list of attributes (no link sets)
|
||||
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
|
||||
{
|
||||
$aList[$sAlias][$sAttCode] = $oAttDef;
|
||||
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
|
||||
|
||||
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
|
||||
if ($bFieldsAdvanced && $oAttDef->IsExternalKey(EXTKEY_RELATIVE))
|
||||
{
|
||||
$sRemoteClass = $oAttDef->GetTargetClass();
|
||||
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
|
||||
{
|
||||
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1067,25 +1110,10 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
|
||||
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
$sExtKeyLabel = MetaModel::GetLabel($sClassName, $oAttDef->GetKeyAttCode());
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode());
|
||||
$sRemoteAttLabel = MetaModel::GetLabel($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||
$oTargetAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||
$sSuffix = '';
|
||||
if ($oTargetAttDef->IsExternalKey())
|
||||
{
|
||||
$sSuffix = '->id';
|
||||
}
|
||||
$sColLabel = $sExtKeyLabel.'->'.$sRemoteAttLabel.$sSuffix;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sColLabel = MetaModel::GetLabel($sClassName, $sAttCode);
|
||||
}
|
||||
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
|
||||
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime')
|
||||
{
|
||||
@@ -1111,8 +1139,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
foreach($aAuthorizedClasses as $sAlias => $sClassName)
|
||||
{
|
||||
$oObj = $aObjects[$sAlias];
|
||||
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
|
||||
|
||||
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
|
||||
{
|
||||
if (is_null($oObj))
|
||||
{
|
||||
@@ -1123,13 +1150,22 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime')
|
||||
{
|
||||
$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($sAttCode));
|
||||
$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($sAttCodeEx));
|
||||
$aRow[] = '<td>'.date('Y-m-d', $iDate).'</td>';
|
||||
$aRow[] = '<td>'.date('H:i:s', $iDate).'</td>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[] = '<td>'.(string) $oObj->Get($sAttCode).'</td>';
|
||||
$rawValue = $oObj->Get($sAttCodeEx);
|
||||
if ($bLocalize)
|
||||
{
|
||||
$outputValue = htmlentities($oFinalAttDef->GetValueLabel($rawValue), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$aRow[] = '<td>'.$outputValue.'</td>';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1144,6 +1180,12 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
||||
{
|
||||
$bLocalize = true;
|
||||
if (isset($aParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aParams['localize_values'];
|
||||
}
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
|
||||
$aAuthorizedClasses = array();
|
||||
@@ -1189,7 +1231,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet())
|
||||
{
|
||||
$sValue = $oObj->GetAsXML($sAttCode);
|
||||
$sValue = $oObj->GetAsXML($sAttCode, $bLocalize);
|
||||
$oPage->add("<$sAttCode>$sValue</$sAttCode>\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ class CSVPage extends WebPage
|
||||
function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Content-type: text/plain; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
//$this->add_header("Content-Transfer-Encoding: binary");
|
||||
}
|
||||
|
||||
public function output()
|
||||
|
||||
@@ -73,6 +73,10 @@ class DataTable
|
||||
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
|
||||
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu']))
|
||||
{
|
||||
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
|
||||
}
|
||||
if (UserRights::IsPortalUser())
|
||||
{
|
||||
// Portal users have a limited access to data, for now they can only see what's configured for them
|
||||
@@ -341,6 +345,12 @@ EOF;
|
||||
|
||||
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$bLocalize = true;
|
||||
if (isset($aExtraParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aExtraParams['localize_values'];
|
||||
}
|
||||
|
||||
$aValues = array();
|
||||
$this->oSet->Seek(0);
|
||||
$iMaxObjects = $iPageSize;
|
||||
@@ -384,7 +394,7 @@ EOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode);
|
||||
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +711,7 @@ class DisplayBlock
|
||||
$oFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$aCounts[$sStateValue] = $oSet->Count();
|
||||
$aStateLabels[$sStateValue] = $oAttDef->GetValueLabel($sStateValue);
|
||||
$aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8');
|
||||
if ($aCounts[$sStateValue] == 0)
|
||||
{
|
||||
$aCounts[$sStateValue] = '-';
|
||||
@@ -733,8 +733,67 @@ class DisplayBlock
|
||||
break;
|
||||
|
||||
case 'csv':
|
||||
$bAdvancedMode = utils::ReadParam('advanced', false);
|
||||
|
||||
$sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL()).'&format=csv&filename='.urlencode($sCsvFile);
|
||||
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
if ($bAdvancedMode)
|
||||
{
|
||||
$sDownloadLink .= '&fields_advanced=1';
|
||||
$sChecked = 'CHECKED';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
|
||||
$sChecked = '';
|
||||
}
|
||||
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
|
||||
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
if ($sCharset == 'UTF-8')
|
||||
{
|
||||
$bLostChars = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConverted = @iconv('UTF-8', $sCharset, $sCSVData);
|
||||
$sRestored = @iconv($sCharset, 'UTF-8', $sConverted);
|
||||
$bLostChars = ($sRestored != $sCSVData);
|
||||
}
|
||||
|
||||
if ($bLostChars)
|
||||
{
|
||||
$sCharsetNotice = " <span id=\"csv_charset_issue\">";
|
||||
$sCharsetNotice .= '<img src="../images/error.png" style="vertical-align:middle"/>';
|
||||
$sCharsetNotice .= "</span>";
|
||||
|
||||
$sTip = "<p>".htmlentities(Dict::S('UI:CSVExport:LostChars'), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$sTip .= "<p>".htmlentities(Dict::Format('UI:CSVExport:LostChars+', $sCharset), ENT_QUOTES, 'UTF-8')."</p>";
|
||||
$oPage->add_ready_script("$('#csv_charset_issue').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCharsetNotice = '';
|
||||
}
|
||||
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= '<table style="width:100%" class="transparent">';
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= '<td><a href="'.$sDownloadLink.'">'.Dict::Format('UI:Download-CSV', $sCsvFile).'</a>'.$sCharsetNotice.'</td>';
|
||||
$sHtml .= '<td style="text-align:right"><input type="checkbox" '.$sChecked.' onClick="window.location.href=\''.$sLinkToToggle.'\'"> '.Dict::S('UI:CSVExport:AdvancedMode').'</td>';
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '</table>';
|
||||
if ($bAdvancedMode)
|
||||
{
|
||||
$sHtml .= "<p>";
|
||||
$sHtml .= htmlentities(Dict::S('UI:CSVExport:AdvancedMode+'), ENT_QUOTES, 'UTF-8');
|
||||
$sHtml .= "</p>";
|
||||
}
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$sHtml .= "<textarea style=\"width:95%;height:98%\">\n";
|
||||
$sHtml .= cmdbAbstractObject::GetSetAsCSV($this->m_oSet);
|
||||
$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
|
||||
$sHtml .= "</textarea>\n";
|
||||
break;
|
||||
|
||||
|
||||
@@ -60,8 +60,6 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
$this->add_linked_script("../js/jquery.treeview.js");
|
||||
$this->add_linked_script("../js/jquery.autocomplete.js");
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_linked_script("../js/date.js");
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../js/utils.js");
|
||||
@@ -77,8 +75,6 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_script('../js/g.pie.js');
|
||||
$this->add_linked_script('../js/g.dot.js');
|
||||
$this->add_linked_script('../js/charts.js');
|
||||
$this->add_linked_script('../js/field_sorter.js');
|
||||
$this->add_linked_script('../js/datatable.js');
|
||||
|
||||
$this->m_sInitScript =
|
||||
<<< EOF
|
||||
|
||||
@@ -44,6 +44,10 @@ class NiceWebPage extends WebPage
|
||||
$this->add_linked_script("../js/jquery.tablesorter.min.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
|
||||
$this->add_linked_script("../js/jquery.tablehover.js");
|
||||
$this->add_linked_script('../js/field_sorter.js');
|
||||
$this->add_linked_script('../js/datatable.js');
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_ready_script(
|
||||
<<< EOF
|
||||
//add new widget called TruncatedList to properly display truncated lists when they are sorted
|
||||
|
||||
@@ -235,12 +235,21 @@ abstract class AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label corresponding to the given value
|
||||
* Get the label corresponding to the given value (in plain text)
|
||||
* To be overloaded for localized enums
|
||||
*/
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
return $this->GetAsHTML($sValue);
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value from a given string (plain text, CSV import)
|
||||
* Return null if no match could be found
|
||||
*/
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
return $this->MakeRealValue($sProposedValue, null);
|
||||
}
|
||||
|
||||
public function GetLabel_Obsolete()
|
||||
@@ -422,7 +431,7 @@ abstract class AttributeDefinition
|
||||
/**
|
||||
* Override to display the value in the GUI
|
||||
*/
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html((string)$sValue);
|
||||
}
|
||||
@@ -430,7 +439,7 @@ abstract class AttributeDefinition
|
||||
/**
|
||||
* Override to export the value in XML
|
||||
*/
|
||||
public function GetAsXML($sValue, $oHostObject = null)
|
||||
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2xml((string)$sValue);
|
||||
}
|
||||
@@ -438,7 +447,7 @@ abstract class AttributeDefinition
|
||||
/**
|
||||
* Override to escape the value when read by DBObject::GetAsCSV()
|
||||
*/
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return (string)$sValue;
|
||||
}
|
||||
@@ -591,7 +600,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
public function GetBasicFilterLooseOperator() {return '';}
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value) {return '';}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof DBObjectSet))
|
||||
{
|
||||
@@ -619,12 +628,12 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetAsXML($sValue, $oHostObject = null)
|
||||
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return "Sorry, no yet implemented";
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
|
||||
$sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
|
||||
@@ -684,8 +693,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
// Specific to this kind of attribute : transform a string into a value
|
||||
public function MakeValueFromString($sProposedValue, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
if (is_null($sSepItem) || empty($sSepItem))
|
||||
{
|
||||
@@ -1080,7 +1088,7 @@ class AttributeInteger extends AttributeDBField
|
||||
*/
|
||||
class AttributePercentage extends AttributeInteger
|
||||
{
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$iWidth = 5; // Total width of the percentage bar graph, in em...
|
||||
$iValue = (int)$sValue;
|
||||
@@ -1237,7 +1245,7 @@ class AttributeBoolean extends AttributeInteger
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function GetAsXML($sValue, $oHostObject = null)
|
||||
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $sValue ? '1' : '0';
|
||||
}
|
||||
@@ -1355,7 +1363,7 @@ class AttributeString extends AttributeDBField
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -1403,7 +1411,7 @@ class AttributeClass extends AttributeString
|
||||
return $sDefault;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) return '';
|
||||
return MetaModel::GetName($sValue);
|
||||
@@ -1492,7 +1500,7 @@ class AttributeFinalClass extends AttributeString
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) return '';
|
||||
return MetaModel::GetName($sValue);
|
||||
@@ -1550,7 +1558,7 @@ class AttributePassword extends AttributeString
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (strlen($sValue) == 0)
|
||||
{
|
||||
@@ -1708,9 +1716,9 @@ class AttributeText extends AttributeString
|
||||
return $sText;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sValue = parent::GetAsHTML($sValue);
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
$aStyles = array();
|
||||
if ($this->GetWidth() != '')
|
||||
@@ -1772,7 +1780,7 @@ class AttributeText extends AttributeString
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
@@ -1948,7 +1956,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
@@ -1975,11 +1983,11 @@ class AttributeCaseLog extends AttributeLongText
|
||||
return "<div class=\"caselog\" $sStyle>".$sContent.'</div>'; }
|
||||
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
return parent::GetAsCSV($value->GetText(), $sSeparator, $sTextQualifier, $oHostObject);
|
||||
return parent::GetAsCSV($value->GetText(), $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1987,11 +1995,11 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
return parent::GetAsXML($value->GetText(), $oHostObject);
|
||||
return parent::GetAsXML($value->GetText(), $oHostObject, $bLocalize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2009,7 +2017,7 @@ class AttributeHTML extends AttributeLongText
|
||||
{
|
||||
public function GetEditClass() {return "HTML";}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $sValue;
|
||||
}
|
||||
@@ -2028,7 +2036,7 @@ class AttributeEmailAddress extends AttributeString
|
||||
return "^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$";
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) return '';
|
||||
return '<a class="mailto" href="mailto:'.$sValue.'">'.parent::GetAsHTML($sValue).'</a>';
|
||||
@@ -2092,7 +2100,7 @@ class AttributeTemplateHTML extends AttributeText
|
||||
{
|
||||
public function GetEditClass() {return "HTML";}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $sValue;
|
||||
}
|
||||
@@ -2226,14 +2234,51 @@ class AttributeEnum extends AttributeString
|
||||
return $sDescription;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sLabel = $this->GetValueLabel($sValue);
|
||||
$sDescription = $this->GetValueDescription($sValue);
|
||||
// later, we could imagine a detailed description in the title
|
||||
return "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
|
||||
if ($bLocalize)
|
||||
{
|
||||
$sLabel = $this->GetValueLabel($sValue);
|
||||
$sDescription = $this->GetValueDescription($sValue);
|
||||
// later, we could imagine a detailed description in the title
|
||||
$sRes = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRes = parent::GetAsHtml($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($bLocalize)
|
||||
{
|
||||
$sFinalValue = $this->GetValueLabel($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFinalValue = $value;
|
||||
}
|
||||
$sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($bLocalize)
|
||||
{
|
||||
$sFinalValue = $this->GetValueLabel($sValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFinalValue = $sValue;
|
||||
}
|
||||
$sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return $this->GetValueLabel($sValue);
|
||||
@@ -2254,11 +2299,46 @@ class AttributeEnum extends AttributeString
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sKey => $sValue)
|
||||
{
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
$aLocalizedValues[$sKey] = Str::pure2html($this->GetValueLabel($sKey));
|
||||
}
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum can be localized
|
||||
*/
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
if ($bLocalizedValue)
|
||||
{
|
||||
// Lookup for the value matching the input
|
||||
//
|
||||
$sFoundValue = null;
|
||||
$aRawValues = parent::GetAllowedValues();
|
||||
if (!is_null($aRawValues))
|
||||
{
|
||||
foreach ($aRawValues as $sKey => $sValue)
|
||||
{
|
||||
$sRefValue = $this->GetValueLabel($sKey);
|
||||
if ($sProposedValue == $sRefValue)
|
||||
{
|
||||
$sFoundValue = $sKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($sFoundValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return $this->MakeRealValue($sFoundValue, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue, $sAttributeQualifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the input value to align it with the values supported
|
||||
* by this type of attribute. In this case: turns empty strings into nulls
|
||||
@@ -2424,17 +2504,17 @@ class AttributeDateTime extends AttributeDBField
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html($value);
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -2546,7 +2626,7 @@ class AttributeDuration extends AttributeInteger
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html(self::FormatDuration($value));
|
||||
}
|
||||
@@ -2625,7 +2705,7 @@ class AttributeDate extends AttributeDateTime
|
||||
*/
|
||||
class AttributeDeadline extends AttributeDateTime
|
||||
{
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sResult = self::FormatDeadline($value);
|
||||
return $sResult;
|
||||
@@ -3140,20 +3220,20 @@ class AttributeExternalField extends AttributeDefinition
|
||||
return $oExtAttDef->FromSQLToValue($aCols, $sPrefix);
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsHTML($value);
|
||||
return $oExtAttDef->GetAsHTML($value, null, $bLocalize);
|
||||
}
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsXML($value);
|
||||
return $oExtAttDef->GetAsXML($value, null, $bLocalize);
|
||||
}
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier);
|
||||
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier, null, $bLocalize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3172,7 +3252,7 @@ class AttributeURL extends AttributeString
|
||||
|
||||
public function GetEditClass() {return "String";}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sTarget = $this->Get("target");
|
||||
if (empty($sTarget)) $sTarget = "_blank";
|
||||
@@ -3322,7 +3402,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
return 'true';
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value))
|
||||
{
|
||||
@@ -3330,12 +3410,12 @@ class AttributeBlob extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return ''; // Not exportable in CSV !
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return ''; // Not exportable in XML, or as CDATA + some subtags ??
|
||||
}
|
||||
@@ -3561,7 +3641,7 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
return 'true';
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value))
|
||||
{
|
||||
@@ -3569,12 +3649,12 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
@@ -3891,21 +3971,21 @@ class AttributeSubItem extends AttributeDefinition
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsHTML($this->Get('item_code'), $value);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsCSV($this->Get('item_code'), $value, $sSeparator = ',', $sTextQualifier = '"');
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsXML($this->Get('item_code'), $value);
|
||||
@@ -4057,7 +4137,7 @@ class AttributeOneWayPassword extends AttributeDefinition
|
||||
return 'true';
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value))
|
||||
{
|
||||
@@ -4065,12 +4145,12 @@ class AttributeOneWayPassword extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return ''; // Not exportable in CSV
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return ''; // Not exportable in XML
|
||||
}
|
||||
@@ -4132,7 +4212,7 @@ class AttributeTable extends AttributeDBField
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value))
|
||||
{
|
||||
@@ -4160,13 +4240,13 @@ class AttributeTable extends AttributeDBField
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
// Not implemented
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (count($value) == 0)
|
||||
{
|
||||
@@ -4203,7 +4283,7 @@ class AttributePropertySet extends AttributeTable
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null)
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value))
|
||||
{
|
||||
@@ -4232,7 +4312,7 @@ class AttributePropertySet extends AttributeTable
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (count($value) == 0)
|
||||
{
|
||||
@@ -4258,7 +4338,7 @@ class AttributePropertySet extends AttributeTable
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null)
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (count($value) == 0)
|
||||
{
|
||||
@@ -4455,7 +4535,7 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null)
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html((string)$sValue);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
*/
|
||||
|
||||
|
||||
// The BOM is added at the head of exported UTF-8 CSV data, and removed (if present) from input UTF-8 data.
|
||||
// This helps MS-Excel (Version > 2007, Windows only) in changing its interpretation of a CSV file (by default Excel reads data as ISO-8859-1 -not 100% sure!)
|
||||
define('UTF8_BOM', chr(239).chr(187).chr(191)); // 0xEF, 0xBB, 0xBF
|
||||
|
||||
/**
|
||||
* BulkChange
|
||||
* Interpret a given data set and update the DB accordingly (fake mode avail.)
|
||||
@@ -84,15 +88,16 @@ class CellStatus_Modify extends CellChangeSpec
|
||||
{
|
||||
protected $m_previousValue;
|
||||
|
||||
public function __construct($proposedValue, $previousValue)
|
||||
public function __construct($proposedValue, $previousValue = null)
|
||||
{
|
||||
$this->m_previousValue = $previousValue;
|
||||
// Unused (could be costly to know -see the case of reconciliation on ext keys)
|
||||
//$this->m_previousValue = $previousValue;
|
||||
parent::__construct($proposedValue);
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Modified';
|
||||
return Dict::S('UI:CSVReport-Value-Modified');
|
||||
}
|
||||
|
||||
//public function GetPreviousValue()
|
||||
@@ -115,9 +120,9 @@ class CellStatus_Issue extends CellStatus_Modify
|
||||
{
|
||||
if (is_null($this->m_proposedValue))
|
||||
{
|
||||
return 'Could not be changed - reason: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
|
||||
}
|
||||
return 'Could not be changed to '.$this->m_proposedValue.' - reason: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +135,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'No match';
|
||||
return Dict::S('UI:CSVReport-Value-NoMatch');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +148,7 @@ class CellStatus_NullIssue extends CellStatus_Issue
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Missing mandatory value';
|
||||
return Dict::S('UI:CSVReport-Value-Missing');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +167,7 @@ class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
public function GetDescription()
|
||||
{
|
||||
$sCount = $this->m_iCount;
|
||||
return "Ambiguous: found $sCount objects";
|
||||
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +191,7 @@ class RowStatus_NoChange extends RowStatus
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "unchanged";
|
||||
return Dict::S('UI:CSVReport-Row-Unchanged');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +199,7 @@ class RowStatus_NewObj extends RowStatus
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "created";
|
||||
return Dict::S('UI:CSVReport-Row-Created');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +214,7 @@ class RowStatus_Modify extends RowStatus
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return "updated ".$this->m_iChanged." cols";
|
||||
return Dict::Format('UI:CSVReport-Row-Updated', $this->m_iChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +222,7 @@ class RowStatus_Disappeared extends RowStatus_Modify
|
||||
{
|
||||
public function GetDescription()
|
||||
{
|
||||
return "disappeared, changed ".$this->m_iChanged." cols";
|
||||
return Dict::Format('UI:CSVReport-Row-Disappeared', $this->m_iChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +237,7 @@ class RowStatus_Issue extends RowStatus
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return 'Issue: '.$this->m_sReason;
|
||||
return Dict::Format('UI:CSVReport-Row-Issue', $this->m_sReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,8 +258,9 @@ class BulkChange
|
||||
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
|
||||
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
|
||||
protected $m_sDateFormat; // Date format specification, see utils::StringToTime()
|
||||
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
|
||||
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null)
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_aData = $aData;
|
||||
@@ -264,6 +270,7 @@ class BulkChange
|
||||
$this->m_sSynchroScope = $sSynchroScope;
|
||||
$this->m_aOnDisappear = $aOnDisappear;
|
||||
$this->m_sDateFormat = $sDateFormat;
|
||||
$this->m_bLocalizedValues = $bLocalize;
|
||||
}
|
||||
|
||||
protected $m_bReportHtml = false;
|
||||
@@ -331,6 +338,7 @@ class BulkChange
|
||||
{
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// Default reporting
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
@@ -340,8 +348,8 @@ class BulkChange
|
||||
}
|
||||
else
|
||||
{
|
||||
$aErrors[$sAttCode] = "Null not allowed";
|
||||
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), 'Null not allowed');
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-Null');
|
||||
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), Dict::S('UI:CSVReport-Value-Issue-Null'));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -357,7 +365,7 @@ class BulkChange
|
||||
switch($oExtObjects->Count())
|
||||
{
|
||||
case 0:
|
||||
$aErrors[$sAttCode] = "Object not found";
|
||||
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
|
||||
$aResults[$sAttCode]= new CellStatus_SearchIssue();
|
||||
break;
|
||||
case 1:
|
||||
@@ -366,7 +374,7 @@ class BulkChange
|
||||
$oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
|
||||
break;
|
||||
default:
|
||||
$aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches";
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $oExtObjects->Count());
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $oExtObjects->Count(), $oReconFilter->ToOql());
|
||||
}
|
||||
}
|
||||
@@ -384,6 +392,11 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// Report the change on reconciliation values as well
|
||||
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -405,31 +418,39 @@ class BulkChange
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = "the attribute '$sAttCode' is read-only and cannot be modified (current value: ".$oTargetObj->Get($sAttCode).", proposed value: {$aRowData[$iCol]}).";
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
}
|
||||
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol]);
|
||||
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
$oTargetObj->Set($sAttCode, $oSet);
|
||||
}
|
||||
catch(CoreException $e)
|
||||
{
|
||||
$aErrors[$sAttCode] = "Failed to process input: ".$e->getMessage();
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Format', $e->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$res = $oTargetObj->CheckValue($sAttCode, $aRowData[$iCol]);
|
||||
if ($res === true)
|
||||
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
|
||||
{
|
||||
$oTargetObj->Set($sAttCode, $aRowData[$iCol]);
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $res is a string with the error description
|
||||
$aErrors[$sAttCode] = "Unexpected value for attribute '$sAttCode': $res";
|
||||
$res = $oTargetObj->CheckValue($sAttCode, $value);
|
||||
if ($res === true)
|
||||
{
|
||||
$oTargetObj->Set($sAttCode, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $res is a string with the error description
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Unknown', $sAttCode, $res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,17 +468,19 @@ class BulkChange
|
||||
{
|
||||
if ($this->m_bReportHtml)
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsHTML($sAttCode);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode);
|
||||
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
$sInput = htmlentities($aRowData[$iCol], ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
|
||||
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sInput = $aRowData[$iCol];
|
||||
}
|
||||
if (isset($aErrors[$sAttCode]))
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Issue($sCurValue, $sOrigValue, $aErrors[$sAttCode]);
|
||||
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
|
||||
}
|
||||
elseif (array_key_exists($sAttCode, $aChangedFields))
|
||||
{
|
||||
@@ -484,7 +507,7 @@ class BulkChange
|
||||
if ($res !== true)
|
||||
{
|
||||
// $res contains the error description
|
||||
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
|
||||
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
@@ -548,7 +571,7 @@ class BulkChange
|
||||
if ($res !== true)
|
||||
{
|
||||
// $res contains the error description
|
||||
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
|
||||
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
@@ -562,7 +585,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return $oTargetObj;
|
||||
}
|
||||
|
||||
@@ -581,7 +604,7 @@ class BulkChange
|
||||
if (count($aMissingKeys) > 0)
|
||||
{
|
||||
$sMissingKeys = implode(', ', $aMissingKeys);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Could not be created, due to missing external key(s): $sMissingKeys");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-MissingExtKey', $sMissingKeys));
|
||||
return $oTargetObj;
|
||||
}
|
||||
|
||||
@@ -615,7 +638,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -656,7 +679,7 @@ class BulkChange
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$sErrors = implode(', ', $aErrors);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -732,8 +755,8 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
// Leave the cell unchanged
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("wrong date format");
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], 'Wrong date format');
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,91 +776,98 @@ class BulkChange
|
||||
// An issue at the earlier steps - skip the rest
|
||||
continue;
|
||||
}
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
try
|
||||
{
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
|
||||
$bSkipQuery = false;
|
||||
foreach($this->m_aReconcilKeys as $sAttCode)
|
||||
{
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
$valuecondition = null;
|
||||
if (array_key_exists($sAttCode, $this->m_aExtKeys))
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
|
||||
{
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oExtKey->IsNullAllowed())
|
||||
{
|
||||
$valuecondition = $oExtKey->GetNullValue();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
|
||||
}
|
||||
// The value has to be found or verified
|
||||
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1)
|
||||
{
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
}
|
||||
elseif (count($aMatches) == 0)
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value has to be found or verified
|
||||
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
|
||||
|
||||
if (count($aMatches) == 1)
|
||||
{
|
||||
$oRemoteObj = reset($aMatches); // first item
|
||||
$valuecondition = $oRemoteObj->GetKey();
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
|
||||
}
|
||||
elseif (count($aMatches) == 0)
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
$bSkipQuery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
}
|
||||
if ($bSkipQuery)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("failed to reconcile");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch($oReconciliationSet->Count())
|
||||
{
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
$bSkipQuery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation");
|
||||
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
|
||||
$aResult[$iRow]["finalclass"]= 'n/a';
|
||||
}
|
||||
if ($bSkipQuery)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
|
||||
switch($oReconciliationSet->Count())
|
||||
{
|
||||
case 0:
|
||||
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
break;
|
||||
case 1:
|
||||
$oTargetObj = $oReconciliationSet->Fetch();
|
||||
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
|
||||
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
|
||||
if (!is_null($this->m_sSynchroScope))
|
||||
{
|
||||
$aVisited[] = $oTargetObj->GetKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Found several matches, ambiguous
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
|
||||
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
|
||||
$aResult[$iRow]["finalclass"]= 'n/a';
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1166,49 +1196,6 @@ EOF
|
||||
}
|
||||
$oPage->table($aConfig, $aDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user friendly name for an 'extended' attribute code i.e 'name', becomes 'Name' and 'org_id->name' becomes 'Organization->Name'
|
||||
* @param string $sClassName The name of the class
|
||||
* @param string $sAttCodeEx Either an attribute code or ext_key_name->att_code
|
||||
* @return string A user friendly format of the string: AttributeName or AttributeName->ExtAttributeName
|
||||
*/
|
||||
public static function GetFriendlyAttCodeName($sClassName, $sAttCodeEx)
|
||||
{
|
||||
$sFriendlyName = '';
|
||||
if (preg_match('/(.+)->(.+)/', $sAttCodeEx, $aMatches) > 0)
|
||||
{
|
||||
$sAttribute = $aMatches[1];
|
||||
$sField = $aMatches[2];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttribute);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetAttDef = MetaModel::GetAttributeDef($sTargetClass, $sField);
|
||||
$sFriendlyName = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// hum, hum... should never happen, we'd better raise an exception
|
||||
throw(new Exception(Dict::Format('UI:CSVImport:ErrorExtendedAttCode', $sAttCodeEx, $sAttribute, $sClassName)));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sAttCodeEx == 'id')
|
||||
{
|
||||
$sFriendlyName = Dict::S('UI:CSVImport:idField');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCodeEx);
|
||||
$sFriendlyName = $oAttDef->GetLabel();
|
||||
}
|
||||
}
|
||||
return $sFriendlyName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -537,6 +537,16 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
'csv_file_default_charset' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).',
|
||||
// examples... not used
|
||||
'default' => 'ISO-8859-1',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
|
||||
@@ -548,7 +548,7 @@ abstract class DBObject
|
||||
$this->ComputeValues();
|
||||
}
|
||||
|
||||
public function GetAsHTML($sAttCode)
|
||||
public function GetAsHTML($sAttCode, $bLocalize = true)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$oAtt = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
@@ -571,7 +571,7 @@ abstract class DBObject
|
||||
}
|
||||
|
||||
// That's a standard attribute (might be an ext field or a direct field, etc.)
|
||||
return $oAtt->GetAsHTML($this->Get($sAttCode), $this);
|
||||
return $oAtt->GetAsHTML($this->Get($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetEditValue($sAttCode)
|
||||
@@ -609,34 +609,34 @@ abstract class DBObject
|
||||
return $sEditValue;
|
||||
}
|
||||
|
||||
public function GetAsXML($sAttCode)
|
||||
public function GetAsXML($sAttCode, $bLocalize = true)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsXML($this->Get($sAttCode), $this);
|
||||
return $oAtt->GetAsXML($this->Get($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"')
|
||||
public function GetAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier, $this);
|
||||
return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetOriginalAsHTML($sAttCode)
|
||||
public function GetOriginalAsHTML($sAttCode, $bLocalize = true)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsHTML($this->GetOriginal($sAttCode), $this);
|
||||
return $oAtt->GetAsHTML($this->GetOriginal($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetOriginalAsXML($sAttCode)
|
||||
public function GetOriginalAsXML($sAttCode, $bLocalize = true)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsXML($this->GetOriginal($sAttCode), $this);
|
||||
return $oAtt->GetAsXML($this->GetOriginal($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetOriginalAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"')
|
||||
public function GetOriginalAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsCSV($this->GetOriginal($sAttCode), $sSeparator, $sTextQualifier, $this);
|
||||
return $oAtt->GetAsCSV($this->GetOriginal($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize);
|
||||
}
|
||||
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
|
||||
@@ -907,11 +907,45 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
|
||||
public static function GetLabel($sClass, $sAttCode)
|
||||
/**
|
||||
* Get the attribute label
|
||||
* @param string sClass Persistent class
|
||||
* @param string sAttCodeEx Extended attribute code: attcode[->attcode]
|
||||
* @return string A user friendly format of the string: AttributeName or AttributeName->ExtAttributeName
|
||||
*/
|
||||
public static function GetLabel($sClass, $sAttCodeEx)
|
||||
{
|
||||
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef) return $oAttDef->GetLabel();
|
||||
return "";
|
||||
$sLabel = '';
|
||||
if (preg_match('/(.+)->(.+)/', $sAttCodeEx, $aMatches) > 0)
|
||||
{
|
||||
$sAttribute = $aMatches[1];
|
||||
$sField = $aMatches[2];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttribute);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetAttDef = MetaModel::GetAttributeDef($sTargetClass, $sField);
|
||||
$sLabel = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's return something displayable... but this should never happen!
|
||||
$sLabel = $oAttDef->GetLabel().'->'.$aMatches[2];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sAttCodeEx == 'id')
|
||||
{
|
||||
$sLabel = Dict::S('UI:CSVImport:idField');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCodeEx);
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
}
|
||||
}
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public static function GetDescription($sClass, $sAttCode)
|
||||
|
||||
@@ -550,7 +550,53 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:UniversalSearchTitle' => 'iTop - Universal Search',
|
||||
'UI:UniversalSearch:Error' => 'Error: %1$s',
|
||||
'UI:UniversalSearch:LabelSelectTheClass' => 'Select the class to search: ',
|
||||
|
||||
|
||||
'UI:CSVReport-Value-Modified' => 'Modified',
|
||||
'UI:CSVReport-Value-SetIssue' => 'Could not be changed - reason: %1$s',
|
||||
'UI:CSVReport-Value-ChangeIssue' => 'Could not be changed to %1$s - reason: %2$s',
|
||||
'UI:CSVReport-Value-NoMatch' => 'No match',
|
||||
'UI:CSVReport-Value-Missing' => 'Missing mandatory value',
|
||||
'UI:CSVReport-Value-Ambiguous' => 'Ambiguous: found %1$s objects',
|
||||
'UI:CSVReport-Row-Unchanged' => 'unchanged',
|
||||
'UI:CSVReport-Row-Created' => 'created',
|
||||
'UI:CSVReport-Row-Updated' => 'updated %1$d cols',
|
||||
'UI:CSVReport-Row-Disappeared' => 'disappeared, changed %1$d cols',
|
||||
'UI:CSVReport-Row-Issue' => 'Issue: %1$s',
|
||||
'UI:CSVReport-Value-Issue-Null' => 'Null not allowed',
|
||||
'UI:CSVReport-Value-Issue-NotFound' => 'Object not found',
|
||||
'UI:CSVReport-Value-Issue-FoundMany' => 'Found %1$d matches',
|
||||
'UI:CSVReport-Value-Issue-Readonly' => 'The attribute \'%1$s\' is read-only and cannot be modified (current value: %2$s, proposed value: %3$s)',
|
||||
'UI:CSVReport-Value-Issue-Format' => 'Failed to process input: %1$s',
|
||||
'UI:CSVReport-Value-Issue-NoMatch' => 'Unexpected value for attribute \'%1$s\': no match found, check spelling',
|
||||
'UI:CSVReport-Value-Issue-Unknown' => 'Unexpected value for attribute \'%1$s\': %2$s',
|
||||
'UI:CSVReport-Row-Issue-Inconsistent' => 'Attributes not consistent with each others: %1$s',
|
||||
'UI:CSVReport-Row-Issue-Attribute' => 'Unexpected attribute value(s)',
|
||||
'UI:CSVReport-Row-Issue-MissingExtKey' => 'Could not be created, due to missing external key(s): %1$s',
|
||||
'UI:CSVReport-Row-Issue-DateFormat' => 'wrong date format',
|
||||
'UI:CSVReport-Row-Issue-Reconciliation' => 'failed to reconcile',
|
||||
'UI:CSVReport-Row-Issue-Ambiguous' => 'ambiguous reconciliation',
|
||||
'UI:CSVReport-Row-Issue-Internal' => 'Internal error: %1$s, %2$s',
|
||||
|
||||
'UI:CSVReport-Icon-Unchanged' => 'Unchanged',
|
||||
'UI:CSVReport-Icon-Modified' => 'Modified',
|
||||
'UI:CSVReport-Icon-Missing' => 'Missing',
|
||||
'UI:CSVReport-Object-MissingToUpdate' => 'Missing object: will be updated',
|
||||
'UI:CSVReport-Object-MissingUpdated' => 'Missing object: updated',
|
||||
'UI:CSVReport-Icon-Created' => 'Created',
|
||||
'UI:CSVReport-Object-ToCreate' => 'Object will be created',
|
||||
'UI:CSVReport-Object-Created' => 'Object created',
|
||||
'UI:CSVReport-Icon-Error' => 'Error',
|
||||
'UI:CSVReport-Object-Error' => 'ERROR: %1$s',
|
||||
'UI:CSVReport-Object-Ambiguous' => 'AMBIGUOUS: %1$s',
|
||||
'UI:CSVReport-Stats-Errors' => '%1$.0f %% of the loaded objects have errors and will be ignored.',
|
||||
'UI:CSVReport-Stats-Created' => '%1$.0f %% of the loaded objects will be created.',
|
||||
'UI:CSVReport-Stats-Modified' => '%1$.0f %% of the loaded objects will be modified.',
|
||||
|
||||
'UI:CSVExport:AdvancedMode' => 'Advanced mode',
|
||||
'UI:CSVExport:AdvancedMode+' => 'In advanced mode, several columns are added to the export: the id of the object, the id of external keys and their reconciliation attributes.',
|
||||
'UI:CSVExport:LostChars' => 'Encoding issue',
|
||||
'UI:CSVExport:LostChars+' => 'The downloaded file will be encoded into %1$s. iTop has detected some characters that are not compatible with this format. Those characters will either be replaced by a substitute (e.g. accentuated chars losing the accent), or they will be discarded. You can copy/paste the data from your web browser. Alternatively, you can contact your administrator to change the encoding (See parameter \'csv_file_default_charset\').',
|
||||
|
||||
'UI:Audit:Title' => 'iTop - CMDB Audit',
|
||||
'UI:Audit:InteractiveAudit' => 'Interactive Audit',
|
||||
'UI:Audit:HeaderAuditRule' => 'Audit Rule',
|
||||
@@ -870,6 +916,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
'UI:OpenDocumentInNewWindow_' => 'Open this document in a new window: %1$s',
|
||||
'UI:DownloadDocument_' => 'Download this document: %1$s',
|
||||
'UI:Document:NoPreview' => 'No preview is available for this type of document',
|
||||
'UI:Download-CSV' => 'Download %1$s',
|
||||
|
||||
'UI:DeadlineMissedBy_duration' => 'Missed by %1$s',
|
||||
'UI:Deadline_LessThan1Min' => '< 1 min',
|
||||
|
||||
@@ -423,6 +423,53 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:CSVImport:AlertIncompleteMapping' => 'Veuillez choisir le correspondance de chacun des champs.',
|
||||
'UI:CSVImport:AlertNoSearchCriteria' => 'Veuillez choisir au moins une clef de recherche.',
|
||||
'UI:CSVImport:Encoding' => 'Encodage des caractères',
|
||||
|
||||
'UI:CSVReport-Value-Modified' => 'Modifié',
|
||||
'UI:CSVReport-Value-SetIssue' => 'Modification impossible - cause : %1$s',
|
||||
'UI:CSVReport-Value-ChangeIssue' => 'Ne peut pas prendre la valeur \'%1$s\' - cause : %2$s',
|
||||
'UI:CSVReport-Value-NoMatch' => 'Pas de correspondance',
|
||||
'UI:CSVReport-Value-Missing' => 'Absence de valeur obligatoire',
|
||||
'UI:CSVReport-Value-Ambiguous' => 'Ambigüité: %1$d objets trouvés',
|
||||
'UI:CSVReport-Row-Unchanged' => 'inchangé',
|
||||
'UI:CSVReport-Row-Created' => 'créé',
|
||||
'UI:CSVReport-Row-Updated' => '%1$d colonnes modifiées',
|
||||
'UI:CSVReport-Row-Disappeared' => 'disparu, %1$d colonnes modifiées',
|
||||
'UI:CSVReport-Row-Issue' => 'Erreur: %1$s',
|
||||
'UI:CSVReport-Value-Issue-Null' => 'Valeur obligatoire',
|
||||
'UI:CSVReport-Value-Issue-NotFound' => 'Objet non trouvé',
|
||||
'UI:CSVReport-Value-Issue-FoundMany' => 'Plusieurs objets trouvés (%1$d)',
|
||||
'UI:CSVReport-Value-Issue-Readonly' => 'L\'attribut \'%1$s\' est en lecture seule (valeur courante: %2$s, valeur proposée: %3$s)',
|
||||
'UI:CSVReport-Value-Issue-Format' => 'Echec de traitement de la valeur: %1$s',
|
||||
'UI:CSVReport-Value-Issue-NoMatch' => 'Valeur incorrecte pour \'%1$s\': pas de correspondance, veuillez vérifier la syntaxe',
|
||||
'UI:CSVReport-Value-Issue-Unknown' => 'Valeur incorrecte pour \'%1$s\': %2$s',
|
||||
'UI:CSVReport-Row-Issue-Inconsistent' => 'Incohérence entre attributs: %1$s',
|
||||
'UI:CSVReport-Row-Issue-Attribute' => 'Des attributs ont des valeurs incorrectes',
|
||||
'UI:CSVReport-Row-Issue-MissingExtKey' => 'Ne peut pas être créé car il manque des clés externes : %1$s',
|
||||
'UI:CSVReport-Row-Issue-DateFormat' => 'Format de date incorrect',
|
||||
'UI:CSVReport-Row-Issue-Reconciliation' => 'Echec de réconciliation',
|
||||
'UI:CSVReport-Row-Issue-Ambiguous' => 'Réconciliation ambigüe',
|
||||
'UI:CSVReport-Row-Issue-Internal' => 'Erreur interne: %1$s, %2$s',
|
||||
|
||||
'UI:CSVReport-Icon-Unchanged' => 'Non modifié',
|
||||
'UI:CSVReport-Icon-Modified' => 'Modifié',
|
||||
'UI:CSVReport-Icon-Missing' => 'A disparu',
|
||||
'UI:CSVReport-Object-MissingToUpdate' => 'Objet disparu: sera modifié',
|
||||
'UI:CSVReport-Object-MissingUpdated' => 'Objet disparu: modifié',
|
||||
'UI:CSVReport-Icon-Created' => 'Créé',
|
||||
'UI:CSVReport-Object-ToCreate' => 'L\'objet sera créé',
|
||||
'UI:CSVReport-Object-Created' => 'Objet créé',
|
||||
'UI:CSVReport-Icon-Error' => 'Erreur',
|
||||
'UI:CSVReport-Object-Error' => 'Erreur: %1$s',
|
||||
'UI:CSVReport-Object-Ambiguous' => 'Ambigüité: %1$s',
|
||||
'UI:CSVReport-Stats-Errors' => '%1$.0f %% des lignes chargées sont en erreur et seront ignorées.',
|
||||
'UI:CSVReport-Stats-Created' => '%1$.0f %% des lignes chargées vont engendrer un nouvel objet.',
|
||||
'UI:CSVReport-Stats-Modified' => '%1$.0f %% des lignes chargées vont modifier un objet.',
|
||||
|
||||
'UI:CSVExport:AdvancedMode' => 'Mode expert',
|
||||
'UI:CSVExport:AdvancedMode+' => 'Dans le mode expert, des colonnes supplémentaires apparaissent: l\'identifiant de l\'objet, la valeur des clés externes et leurs attributs de reconciliation.',
|
||||
'UI:CSVExport:LostChars' => 'Problème d\'encodage',
|
||||
'UI:CSVExport:LostChars+' => 'Le fichier téléchargé sera encodé en %1$s. iTop a détecté des caractères incompatible avec ce format. Ces caractères seront soit remplacés par des caractères de substitution (par exemple: \'é\' transformé en \'e\'), soit perdus. Vous pouvez utiliser le copier/coller depuis votre navigateur web, ou bien contacter votre administrateur pour que l\'encodage corresponde mieux à votre besoin (Cf. paramètre \'csv_file_default_charset\').',
|
||||
|
||||
'UI:UniversalSearchTitle' => 'iTop - Recherche Universelle',
|
||||
'UI:UniversalSearch:Error' => 'Erreur : %1$s',
|
||||
'UI:UniversalSearch:LabelSelectTheClass' => 'Sélectionnez le type d\'objets à rechercher : ',
|
||||
@@ -716,6 +763,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
'UI:OpenDocumentInNewWindow_' => 'Ouvrir ce document dans uns autre fenêtre: %1$s',
|
||||
'UI:DownloadDocument_' => 'Télécharger ce document: %1$s',
|
||||
'UI:Document:NoPreview' => 'L\'aperçu n\'est pas disponible pour ce type de documents',
|
||||
'UI:Download-CSV' => 'Télécharger %1$s',
|
||||
'UI:DeadlineMissedBy_duration' => 'Passé de %1$s',
|
||||
'UI:Deadline_LessThan1Min' => '< 1 min',
|
||||
'UI:Deadline_Minutes' => '%1$d min',
|
||||
|
||||
@@ -98,9 +98,10 @@ function GetMappingsForExtKey($sAttCode, AttributeDefinition $oExtKeyAttDef, $bA
|
||||
* @param string $sFieldName Name of the field, as it comes from the data file (header line)
|
||||
* @param integer $iFieldIndex Number of the field in the sequence
|
||||
* @param bool $bAdvancedMode Whether or not advanced mode was chosen
|
||||
* @param string $sDefaultChoice If set, this will be the item selected by default
|
||||
* @return string The HTML code corresponding to the drop-down list for this field
|
||||
*/
|
||||
function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMode = false)
|
||||
function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMode, $sDefaultChoice)
|
||||
{
|
||||
$aChoices = array('' => Dict::S('UI:CSVImport:MappingSelectOne'));
|
||||
$aChoices[':none:'] = Dict::S('UI:CSVImport:MappingNotApplicable');
|
||||
@@ -178,7 +179,7 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
|
||||
}
|
||||
}
|
||||
asort($aChoices);
|
||||
|
||||
|
||||
$sHtml = "<select id=\"mapping_{$iFieldIndex}\" name=\"field[$iFieldIndex]\">\n";
|
||||
$bIsIdField = IsIdField($sClassName, $sFieldCode);
|
||||
foreach($aChoices as $sAttCode => $sLabel)
|
||||
@@ -198,7 +199,11 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
}
|
||||
else if ($sFieldCode == $sAttCode) // Otherwise map by default if there is a match
|
||||
else if (is_null($sDefaultChoice) && ($sFieldCode == $sAttCode))
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
else if (!is_null($sDefaultChoice) && ($sDefaultChoice == $sAttCode))
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
@@ -228,7 +233,7 @@ try
|
||||
$sSeparator = utils::ReadParam('separator', ',', false, 'raw_data');
|
||||
if ($sSeparator == 'tab') $sSeparator = "\t";
|
||||
$sTextQualifier = utils::ReadParam('qualifier', '"', false, 'raw_data');
|
||||
$iLinesToSkip = utils::ReadParam('nb_lines_skipped', 0);
|
||||
$iLinesToSkip = utils::ReadParam('do_skip_lines', 0);
|
||||
$bFirstLineAsHeader = utils::ReadParam('header_line', true);
|
||||
$sEncoding = utils::ReadParam('encoding', 'UTF-8');
|
||||
$sData = stripslashes(utils::ReadParam('csvdata', true, false, 'raw_data'));
|
||||
@@ -253,18 +258,24 @@ try
|
||||
$sCSSClass = 'csv_row'.($index % 2);
|
||||
if ( ($bFirstLineAsHeader) && ($index == 1))
|
||||
{
|
||||
$oPage->add("<tr class=\"$sCSSClass\"><td style=\"border-left:#999 3px solid;padding-right:10px;padding-left:10px;\">".sprintf($sFormat, $index)."</td><th>");
|
||||
$oPage->add(implode('</th><th>', $aRow));
|
||||
$oPage->add("</th></tr>\n");
|
||||
$oPage->add("<tr class=\"$sCSSClass\"><td style=\"border-left:#999 3px solid;padding-right:10px;padding-left:10px;\">".sprintf($sFormat, $index)."</td>");
|
||||
foreach ($aRow as $sCell)
|
||||
{
|
||||
$oPage->add('<th>'.htmlentities($sCell, ENT_QUOTES, 'UTF-8').'</th>');
|
||||
}
|
||||
$oPage->add("</tr>\n");
|
||||
$iNbCols = count($aRow);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($index == 1) $iNbCols = count($aRow);
|
||||
$oPage->add("<tr class=\"$sCSSClass\"><td style=\"border-left:#999 3px solid;padding-right:10px;padding-left:10px;\">".sprintf($sFormat, $index)."</td><td>");
|
||||
$oPage->add(implode('</td><td>', $aRow));
|
||||
$oPage->add("</td></tr>\n");
|
||||
$oPage->add("<tr class=\"$sCSSClass\"><td style=\"border-left:#999 3px solid;padding-right:10px;padding-left:10px;\">".sprintf($sFormat, $index)."</td>");
|
||||
foreach ($aRow as $sCell)
|
||||
{
|
||||
$oPage->add('<td>'.htmlentities($sCell, ENT_QUOTES, 'UTF-8').'</td>');
|
||||
}
|
||||
$oPage->add("</tr>\n");
|
||||
}
|
||||
$index++;
|
||||
if ($index > $iMaxIndex) break;
|
||||
@@ -288,12 +299,18 @@ try
|
||||
$oPage->SetContentType('text/html');
|
||||
$sSeparator = utils::ReadParam('separator', ',', false, 'raw_data');
|
||||
$sTextQualifier = utils::ReadParam('qualifier', '"', false, 'raw_data');
|
||||
$iLinesToSkip = utils::ReadParam('nb_lines_skipped', 0);
|
||||
$iLinesToSkip = utils::ReadParam('do_skip_lines', 0);
|
||||
$bFirstLineAsHeader = utils::ReadParam('header_line', false);
|
||||
$sData = stripslashes(utils::ReadParam('csvdata', '', false, 'raw_data'));
|
||||
$sClassName = utils::ReadParam('class_name', '');
|
||||
$bAdvanced = utils::ReadParam('advanced', false);
|
||||
$sEncoding = utils::ReadParam('encoding', 'UTF-8');
|
||||
|
||||
$sInitFieldMapping = utils::ReadParam('init_field_mapping', '', false, 'raw_data');
|
||||
$sInitSearchField = utils::ReadParam('init_search_field', '', false, 'raw_data');
|
||||
$aInitFieldMapping = empty($sInitFieldMapping) ? array() : json_decode($sInitFieldMapping, true);
|
||||
$aInitSearchField = empty($sInitSearchField) ? array() : json_decode($sInitSearchField, true);
|
||||
|
||||
$oCSVParser = new CSVParser($sData, $sSeparator, $sTextQualifier);
|
||||
$aData = $oCSVParser->ToArray($iLinesToSkip);
|
||||
$iTarget = count($aData);
|
||||
@@ -332,9 +349,14 @@ try
|
||||
$index = 1;
|
||||
foreach($aHeader as $sField)
|
||||
{
|
||||
$sDefaultChoice = null;
|
||||
if (isset($aInitFieldMapping[$index]))
|
||||
{
|
||||
$sDefaultChoice = $aInitFieldMapping[$index];
|
||||
}
|
||||
$oPage->add('<tr>');
|
||||
$oPage->add("<th>$sField</th>");
|
||||
$oPage->add('<td>'.GetMappingForField($sClassName, $sField, $index, $bAdvanced).'</td>');
|
||||
$oPage->add('<td>'.GetMappingForField($sClassName, $sField, $index, $bAdvanced, $sDefaultChoice).'</td>');
|
||||
$oPage->add('<td> </td>');
|
||||
$oPage->add('<td><input id="search_'.$index.'" type="checkbox" name="search_field['.$index.']" value="1" /></td>');
|
||||
$oPage->add('<td>'.(isset($aData[$iStartLine][$index-1]) ? htmlentities($aData[$iStartLine][$index-1], ENT_QUOTES, 'UTF-8') : ' ').'</td>');
|
||||
@@ -343,28 +365,46 @@ try
|
||||
$index++;
|
||||
}
|
||||
$oPage->add("</table>\n");
|
||||
$aReconciliationKeys = MetaModel::GetReconcKeys($sClassName);
|
||||
$aMoreReconciliationKeys = array(); // Store: key => void to automatically remove duplicates
|
||||
foreach($aReconciliationKeys as $sAttCode)
|
||||
|
||||
if (empty($sInitSearchField))
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($sClassName, $sAttCode)) continue;
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
// Propose a reconciliation scheme
|
||||
//
|
||||
$aReconciliationKeys = MetaModel::GetReconcKeys($sClassName);
|
||||
$aMoreReconciliationKeys = array(); // Store: key => void to automatically remove duplicates
|
||||
foreach($aReconciliationKeys as $sAttCode)
|
||||
{
|
||||
// An external key is specified as a reconciliation key: this means that all the reconciliation
|
||||
// keys of this class are proposed to identify the target object
|
||||
$aMoreReconciliationKeys = array_merge($aMoreReconciliationKeys, GetMappingsForExtKey($sAttCode, $oAttDef, $bAdvanced));
|
||||
}
|
||||
elseif($oAttDef->IsExternalField())
|
||||
{
|
||||
// An external field is specified as a reconciliation key, translate the field into a field on the target class
|
||||
// since external fields are not writable, and thus never appears in the mapping form
|
||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
$sTargetAttCode = $oAttDef->GetExtAttCode();
|
||||
$aMoreReconciliationKeys[$sKeyAttCode.'->'.$sTargetAttCode] = '';
|
||||
if (!MetaModel::IsValidAttCode($sClassName, $sAttCode)) continue;
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
// An external key is specified as a reconciliation key: this means that all the reconciliation
|
||||
// keys of this class are proposed to identify the target object
|
||||
$aMoreReconciliationKeys = array_merge($aMoreReconciliationKeys, GetMappingsForExtKey($sAttCode, $oAttDef, $bAdvanced));
|
||||
}
|
||||
elseif($oAttDef->IsExternalField())
|
||||
{
|
||||
// An external field is specified as a reconciliation key, translate the field into a field on the target class
|
||||
// since external fields are not writable, and thus never appears in the mapping form
|
||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
$sTargetAttCode = $oAttDef->GetExtAttCode();
|
||||
$aMoreReconciliationKeys[$sKeyAttCode.'->'.$sTargetAttCode] = '';
|
||||
}
|
||||
}
|
||||
$sDefaultKeys = '"'.implode('", "',array_merge($aReconciliationKeys, array_keys($aMoreReconciliationKeys))).'"';
|
||||
}
|
||||
else
|
||||
{
|
||||
// The reconciliation scheme is given (navigating back in the wizard)
|
||||
//
|
||||
$aDefaultKeys = array();
|
||||
foreach ($aInitSearchField as $iSearchField => $void)
|
||||
{
|
||||
$sAttCodeEx = $aInitFieldMapping[$iSearchField];
|
||||
$aDefaultKeys[] = $sAttCodeEx;
|
||||
}
|
||||
$sDefaultKeys = '"'.implode('", "', $aDefaultKeys).'"';
|
||||
}
|
||||
$sDefaultKeys = '"'.implode('", "',array_merge($aReconciliationKeys, array_keys($aMoreReconciliationKeys))).'"';
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('select[name^=field]').change( DoCheckMapping );
|
||||
|
||||
@@ -196,7 +196,11 @@ try
|
||||
$sSeparator = utils::ReadParam('separator', ',', false, 'raw_data');
|
||||
$sTextQualifier = utils::ReadParam('text_qualifier', '"', false, 'raw_data');
|
||||
$bHeaderLine = (utils::ReadParam('header_line', '0') == 1);
|
||||
$iRealSkippedLines = $iSkippedLines = utils::ReadParam('nb_skipped_lines', '0');
|
||||
$iSkippedLines = 0;
|
||||
if (utils::ReadParam('box_skiplines', '0') == 1)
|
||||
{
|
||||
$iSkippedLines = utils::ReadParam('nb_skipped_lines', '0');
|
||||
}
|
||||
$sClassName = utils::ReadParam('class_name', '', false, 'class');
|
||||
$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data');
|
||||
$aSearchFields = utils::ReadParam('search_field', array(), false, 'field_name');
|
||||
@@ -223,6 +227,7 @@ try
|
||||
// Parse the data set
|
||||
$oCSVParser = new CSVParser($sCSVData, $sSeparator, $sTextQualifier);
|
||||
$aData = $oCSVParser->ToArray($iSkippedLines);
|
||||
$iRealSkippedLines = $iSkippedLines;
|
||||
if ($bHeaderLine)
|
||||
{
|
||||
$aResult[] = $sTextQualifier.implode($sTextQualifier.$sSeparator.$sTextQualifier, array_shift($aData)).$sTextQualifier; // Remove the first line and store it in case of error
|
||||
@@ -311,7 +316,9 @@ try
|
||||
$aExtKeys,
|
||||
array_keys($aSearchKeys),
|
||||
empty($sSynchroScope) ? null : $sSynchroScope,
|
||||
$aSynchroUpdate
|
||||
$aSynchroUpdate,
|
||||
null, // date format
|
||||
true // localize
|
||||
);
|
||||
$oBulk->SetReportHtml();
|
||||
|
||||
@@ -326,7 +333,7 @@ try
|
||||
{
|
||||
if (!empty($sAttCode) && ($sAttCode != ':none:') && ($sAttCode != 'finalclass'))
|
||||
{
|
||||
$sHtml .= "<th style=\"padding:2px;border-right: 2px #fff solid;\">".BulkChange::GetFriendlyAttCodeName($sClassName, $sAttCode)."</th>";
|
||||
$sHtml .= "<th style=\"padding:2px;border-right: 2px #fff solid;\">".MetaModel::GetLabel($sClassName, $sAttCode)."</th>";
|
||||
}
|
||||
}
|
||||
$sHtml .= '<th>Message</th>';
|
||||
@@ -351,7 +358,7 @@ try
|
||||
$sFinalClass = $aResRow['finalclass'];
|
||||
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
|
||||
$sUrl = $oObj->GetHyperlink();
|
||||
$sStatus = '<img src="../images/unchanged.png" title="Unchanged">';
|
||||
$sStatus = '<img src="../images/unchanged.png" title="'.Dict::S('UI:CSVReport-Icon-Unchanged').'">';
|
||||
$sCSSRowClass = 'row_unchanged';
|
||||
break;
|
||||
|
||||
@@ -360,7 +367,7 @@ try
|
||||
$sFinalClass = $aResRow['finalclass'];
|
||||
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
|
||||
$sUrl = $oObj->GetHyperlink();
|
||||
$sStatus = '<img src="../images/modified.png" title="Modified">';
|
||||
$sStatus = '<img src="../images/modified.png" title="'.Dict::S('UI:CSVReport-Icon-Modified').'">';
|
||||
$sCSSRowClass = 'row_modified';
|
||||
break;
|
||||
|
||||
@@ -369,40 +376,40 @@ try
|
||||
$sFinalClass = $aResRow['finalclass'];
|
||||
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
|
||||
$sUrl = $oObj->GetHyperlink();
|
||||
$sStatus = '<img src="../images/delete.png" title="Missing">';
|
||||
$sStatus = '<img src="../images/delete.png" title="'.Dict::S('UI:CSVReport-Icon-Missing').'">';
|
||||
$sCSSRowClass = 'row_modified';
|
||||
if ($bSimulate)
|
||||
{
|
||||
$sMessage = 'Missing object: will be updated';
|
||||
$sMessage = Dict::S('UI:CSVReport-Object-MissingToUpdate');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMessage = 'Missing object: updated';
|
||||
$sMessage = Dict::S('UI:CSVReport-Object-MissingUpdated');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RowStatus_NewObj':
|
||||
$iCreated++;
|
||||
$sFinalClass = $aResRow['finalclass'];
|
||||
$sStatus = '<img src="../images/added.png" title="Created">';
|
||||
$sStatus = '<img src="../images/added.png" title="'.Dict::S('UI:CSVReport-Icon-Created').'">';
|
||||
$sCSSRowClass = 'row_added';
|
||||
if ($bSimulate)
|
||||
{
|
||||
$sMessage = 'Object will be created';
|
||||
$sMessage = Dict::S('UI:CSVReport-Object-ToCreate');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFinalClass = $aResRow['finalclass'];
|
||||
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
|
||||
$sUrl = $oObj->GetHyperlink();
|
||||
$sMessage = 'Object created';
|
||||
$sMessage = Dict::S('UI:CSVReport-Object-Created');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RowStatus_Issue':
|
||||
$iErrors++;
|
||||
$sMessage .= $oPage->GetP($oStatus->GetDescription());
|
||||
$sStatus = '<img src="../images/error.png" title="Error">';
|
||||
$sStatus = '<img src="../images/error.png" title="'.Dict::S('UI:CSVReport-Icon-Error').'">';//translate
|
||||
$sCSSMessageClass = 'cell_error';
|
||||
$sCSSRowClass = 'row_error';
|
||||
if (array_key_exists($iLine, $aData))
|
||||
@@ -447,7 +454,7 @@ try
|
||||
{
|
||||
case 'CellStatus_Issue':
|
||||
$sCellMessage .= $oPage->GetP($oCellStatus->GetDescription());
|
||||
$sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">ERROR: '.$sHtmlValue.$sCellMessage.'</td>';
|
||||
$sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">'.Dict::Format('UI:CSVReport-Object-Error', $sHtmlValue).$sCellMessage.'</td>';
|
||||
break;
|
||||
|
||||
case 'CellStatus_SearchIssue':
|
||||
@@ -457,7 +464,7 @@ try
|
||||
|
||||
case 'CellStatus_Ambiguous':
|
||||
$sCellMessage .= $oPage->GetP($oCellStatus->GetDescription());
|
||||
$sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">AMBIGUOUS: '.$sHtmlValue.$sCellMessage.'</td>';
|
||||
$sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">'.Dict::Format('UI:CSVReport-Object-Ambiguous', $sHtmlValue).$sCellMessage.'</td>';
|
||||
break;
|
||||
|
||||
case 'CellStatus_Modify':
|
||||
@@ -481,8 +488,8 @@ try
|
||||
$oPage->add('<input type="hidden" name="separator" value="'.htmlentities($sSeparator, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="text_qualifier" value="'.htmlentities($sTextQualifier, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="header_line" value="'.$bHeaderLine.'"/>');
|
||||
$oPage->add('<input type="hidden" name="box_skiplines" value="'.(($iSkippedLines > 0) ? 1 : 0).'"/>');
|
||||
$oPage->add('<input type="hidden" name="nb_skipped_lines" value="'.$iSkippedLines.'"/>');
|
||||
$oPage->add('<input type="hidden" name="nb_skipped_lines" value="'.utils::ReadParam('nb_skipped_lines', '0').'"/>');
|
||||
$oPage->add('<input type="hidden" name="box_skiplines" value="'.utils::ReadParam('box_skiplines', '0').'"/>');
|
||||
$oPage->add('<input type="hidden" name="csvdata" value="'.htmlentities($sCSVData, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="csvdata_truncated" value="'.htmlentities($sCSVDataTruncated, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="class_name" value="'.$sClassName.'"/>');
|
||||
@@ -542,19 +549,19 @@ try
|
||||
$fErrorsPercentage = (100.0*$iErrors)/count($aRes);
|
||||
if ($fErrorsPercentage >= MetaModel::GetConfig()->Get('csv_import_errors_percentage'))
|
||||
{
|
||||
$sMessage = sprintf("%.0f %% of the loaded objects have errors and will be ignored.", $fErrorsPercentage);
|
||||
$sMessage = Dict::Format('UI:CSVReport-Stats-Errors', $fErrorsPercentage);
|
||||
$bShouldConfirm = true;
|
||||
}
|
||||
$fCreatedPercentage = (100.0*$iCreated)/count($aRes);
|
||||
if ($fCreatedPercentage >= MetaModel::GetConfig()->Get('csv_import_creations_percentage'))
|
||||
{
|
||||
$sMessage = sprintf("%.0f %% of the loaded objects will be created.", $fCreatedPercentage);
|
||||
$sMessage = Dict::Format('UI:CSVReport-Stats-Created', $fCreatedPercentage);
|
||||
$bShouldConfirm = true;
|
||||
}
|
||||
$fModifiedPercentage = (100.0*$iModified)/count($aRes);
|
||||
if ($fModifiedPercentage >= MetaModel::GetConfig()->Get('csv_import_modifications_percentage'))
|
||||
{
|
||||
$sMessage = sprintf("%.0f %% of the loaded objects will be modified.", $fModifiedPercentage);
|
||||
$sMessage = Dict::Format('UI:CSVReport-Stats-Modified', $fModifiedPercentage);
|
||||
$bShouldConfirm = true;
|
||||
}
|
||||
|
||||
@@ -799,11 +806,6 @@ EOF
|
||||
$sTextQualifier = utils::ReadParam('other_qualifier', '"', false, 'raw_data');
|
||||
}
|
||||
$bHeaderLine = (utils::ReadParam('header_line', '0') == 1);
|
||||
$iSkippedLines = 0;
|
||||
if (utils::ReadParam('box_skiplines', '0') == 1)
|
||||
{
|
||||
$iSkippedLines = utils::ReadParam('nb_skipped_lines', '0');
|
||||
}
|
||||
$sClassName = utils::ReadParam('class_name', '', false, 'class');
|
||||
$bAdvanced = utils::ReadParam('advanced', 0);
|
||||
$sEncoding = utils::ReadParam('encoding', 'UTF-8');
|
||||
@@ -835,8 +837,8 @@ EOF
|
||||
$oPage->add('<input type="hidden" name="separator" value="'.htmlentities($sSeparator, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="text_qualifier" value="'.htmlentities($sTextQualifier, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="header_line" value="'.$bHeaderLine.'"/>');
|
||||
$oPage->add('<input type="hidden" name="box_skiplines" value="'.(($iSkippedLines > 0) ? 1 : 0).'"/>');
|
||||
$oPage->add('<input type="hidden" name="nb_skipped_lines" value="'.$iSkippedLines.'"/>');
|
||||
$oPage->add('<input type="hidden" name="nb_skipped_lines" value="'.utils::ReadParam('nb_skipped_lines', '0').'"/>');
|
||||
$oPage->add('<input type="hidden" name="box_skiplines" value="'.utils::ReadParam('box_skiplines', '0').'"/>');
|
||||
$oPage->add('<input type="hidden" name="csvdata_truncated" id="csvdata_truncated" value="'.htmlentities($sCSVDataTruncated, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="csvdata" value="'.htmlentities($sCSVData, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="encoding" value="'.$sEncoding.'">');
|
||||
@@ -859,12 +861,17 @@ EOF
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#select_class_name').change( DoMapping );
|
||||
$('#select_class_name').change( function(ev) { DoMapping(); } );
|
||||
EOF
|
||||
);
|
||||
if ($sClassName != '')
|
||||
{
|
||||
$oPage->add_ready_script("DoMapping();"); // There is already a class selected, run the mapping
|
||||
$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data');
|
||||
$aSearchFields = utils::ReadParam('search_field', array(), false, 'field_name');
|
||||
$sFieldsMapping = addslashes(json_encode($aFieldsMapping));
|
||||
$sSearchFields = addslashes(json_encode($aSearchFields));
|
||||
|
||||
$oPage->add_ready_script("DoMapping('$sFieldsMapping', '$sSearchFields');"); // There is already a class selected, run the mapping
|
||||
}
|
||||
|
||||
$oPage->add_script(
|
||||
@@ -889,7 +896,7 @@ EOF
|
||||
|
||||
var ajax_request = null;
|
||||
|
||||
function DoMapping()
|
||||
function DoMapping(sInitFieldsMapping, sInitSearchFields)
|
||||
{
|
||||
var class_name = $('select[name=class_name]').val();
|
||||
var advanced = $('input[name=advanced]:checked').val();
|
||||
@@ -906,7 +913,11 @@ EOF
|
||||
var separator = $('input[name=separator]').val();
|
||||
var text_qualifier = $('input[name=text_qualifier]').val();
|
||||
var header_line = $('input[name=header_line]').val();
|
||||
var nb_lines_skipped = $('input[name=nb_skipped_lines]').val();
|
||||
var do_skip_lines = 0;
|
||||
if ($('input[name=box_skiplines]').val() == '1')
|
||||
{
|
||||
do_skip_lines = $('input[name=nb_skipped_lines]').val();
|
||||
}
|
||||
var csv_data = $('input[name=csvdata]').val();
|
||||
var encoding = $('input[name=encoding]').val();
|
||||
if (advanced != 1)
|
||||
@@ -922,11 +933,19 @@ EOF
|
||||
ajax_request.abort();
|
||||
ajax_request = null;
|
||||
}
|
||||
|
||||
|
||||
var aParams = { operation: 'display_mapping_form', enctype: 'multipart/form-data', csvdata: csv_data, separator: separator,
|
||||
qualifier: text_qualifier, do_skip_lines: do_skip_lines, header_line: header_line, class_name: class_name,
|
||||
advanced: advanced, encoding: encoding };
|
||||
|
||||
if (sInitFieldsMapping != undefined)
|
||||
{
|
||||
aParams.init_field_mapping = sInitFieldsMapping;
|
||||
aParams.init_search_field = sInitSearchFields;
|
||||
}
|
||||
|
||||
ajax_request = $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.csvimport.php',
|
||||
{ operation: 'display_mapping_form', enctype: 'multipart/form-data', csvdata: csv_data, separator: separator,
|
||||
qualifier: text_qualifier, nb_lines_skipped: nb_lines_skipped, header_line: header_line, class_name: class_name,
|
||||
advanced: advanced, encoding: encoding },
|
||||
aParams,
|
||||
function(data) {
|
||||
$('#mapping').empty();
|
||||
$('#mapping').append(data);
|
||||
@@ -1067,6 +1086,13 @@ EOF
|
||||
// Compute a subset of the data set, now that we know the charset
|
||||
if ($sEncoding == 'UTF-8')
|
||||
{
|
||||
// Remove the BOM if any
|
||||
if (substr($sCSVData, 0, 3) == UTF8_BOM)
|
||||
{
|
||||
$sCSVData = substr($sCSVData, 3);
|
||||
}
|
||||
// Clean the input
|
||||
// Todo: warn the user if some characters are lost/substituted
|
||||
$sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
else
|
||||
@@ -1094,6 +1120,8 @@ EOF
|
||||
$bHeaderLine = utils::ReadParam('header_line', 0);
|
||||
$sClassName = utils::ReadParam('class_name', '', false, 'class');
|
||||
$bAdvanced = utils::ReadParam('advanced', 0);
|
||||
$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data');
|
||||
$aSearchFields = utils::ReadParam('search_field', array(), false, 'field_name');
|
||||
|
||||
// Create a truncated version of the data used for the fast preview
|
||||
// Take about 20 lines of data... knowing that some lines may contain carriage returns
|
||||
@@ -1157,9 +1185,19 @@ EOF
|
||||
$oPage->add('</td></tr></table>');
|
||||
$oPage->add('<input type="hidden" name="csvdata_truncated" id="csvdata_truncated" value="'.htmlentities($sCSVDataTruncated, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
$oPage->add('<input type="hidden" name="csvdata" id="csvdata" value="'.htmlentities($sUTF8Data, ENT_QUOTES, 'UTF-8').'"/>');
|
||||
// The encoding has changed, keep that information within the wizard
|
||||
$oPage->add('<input type="hidden" name="encoding" value="UTF-8">');
|
||||
$oPage->add('<input type="hidden" name="class_name" value="'.$sClassName.'"/>');
|
||||
$oPage->add('<input type="hidden" name="advanced" value="'.$bAdvanced.'"/>');
|
||||
$oPage->add('<input type="hidden" name="synchro_scope" value="'.$sSynchroScope.'"/>');
|
||||
foreach($aFieldsMapping as $iNumber => $sAttCode)
|
||||
{
|
||||
$oPage->add('<input type="hidden" name="field['.$iNumber.']" value="'.$sAttCode.'"/>');
|
||||
}
|
||||
foreach($aSearchFields as $index => $sDummy)
|
||||
{
|
||||
$oPage->add('<input type="hidden" name="search_field['.$index.']" value="1"/>');
|
||||
}
|
||||
$oPage->add('<input type="hidden" name="step" value="3"/>');
|
||||
if (!empty($sSynchroScope))
|
||||
{
|
||||
@@ -1199,10 +1237,10 @@ EOF
|
||||
{
|
||||
text_qualifier = $('#other_qualifier').val();
|
||||
}
|
||||
var nb_lines_skipped = 0;
|
||||
var do_skip_lines = 0;
|
||||
if ($('#box_skiplines:checked').val() != null)
|
||||
{
|
||||
nb_lines_skipped = $('#nb_skipped_lines').val();
|
||||
do_skip_lines = $('#nb_skipped_lines').val();
|
||||
}
|
||||
var header_line = 0;
|
||||
if ($('#box_header:checked').val() != null)
|
||||
@@ -1222,7 +1260,7 @@ EOF
|
||||
}
|
||||
|
||||
ajax_request = $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.csvimport.php',
|
||||
{ operation: 'parser_preview', enctype: 'multipart/form-data', csvdata: $("#csvdata_truncated").val(), separator: separator, qualifier: text_qualifier, nb_lines_skipped: nb_lines_skipped, header_line: header_line, encoding: encoding },
|
||||
{ operation: 'parser_preview', enctype: 'multipart/form-data', csvdata: $("#csvdata_truncated").val(), separator: separator, qualifier: text_qualifier, do_skip_lines: do_skip_lines, header_line: header_line, encoding: encoding },
|
||||
function(data) {
|
||||
$('#preview').empty();
|
||||
$('#preview').append(data);
|
||||
@@ -1280,11 +1318,16 @@ EOF
|
||||
$sSeparator = utils::ReadParam('separator', '', false, 'raw_data');
|
||||
$sTextQualifier = utils::ReadParam('text_qualifier', '', false, 'raw_data');
|
||||
$bHeaderLine = utils::ReadParam('header_line', true);
|
||||
$iSkippedLines = utils::ReadParam('nb_skipped_lines', '');
|
||||
$sClassName = utils::ReadParam('class_name', '');
|
||||
$bAdvanced = utils::ReadParam('advanced', 0);
|
||||
$sEncoding = utils::ReadParam('encoding', 'UTF-8');
|
||||
|
||||
$sEncoding = utils::ReadParam('encoding', '');
|
||||
if ($sEncoding == '')
|
||||
{
|
||||
$sEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
}
|
||||
$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data');
|
||||
$aSearchFields = utils::ReadParam('search_field', array(), false, 'field_name');
|
||||
|
||||
$sFileLoadHtml = '<div><form enctype="multipart/form-data" method="post"><p>'.Dict::S('UI:CSVImport:SelectFile').'</p>'.
|
||||
'<p><input type="file" name="csvdata"/></p>';
|
||||
|
||||
@@ -1304,7 +1347,8 @@ EOF
|
||||
'<input type="hidden" name="step" value="2"/>'.
|
||||
'<input type="hidden" name="operation" value="file_upload"/>'.
|
||||
'<input type="hidden" name="header_line" value="'.$bHeaderLine.'"/>'.
|
||||
'<input type="hidden" name="nb_skipped_lines" value="'.$iSkippedLines.'"/>'.
|
||||
'<input type="hidden" name="nb_skipped_lines" value="'.utils::ReadParam('nb_skipped_lines', '0').'"/>'.
|
||||
'<input type="hidden" name="box_skiplines" value="'.utils::ReadParam('box_skiplines', '0').'"/>'.
|
||||
'<input type="hidden" name="class_name" value="'.$sClassName.'"/>'.
|
||||
'<input type="hidden" name="advanced" value="'.$bAdvanced.'"/>'.
|
||||
'<input type="hidden" name="synchro_scope" value="'.$sSynchroScope.'"/>';
|
||||
@@ -1315,6 +1359,15 @@ EOF
|
||||
$sFileLoadHtml .= '<input type="hidden" name="synchro_update['.$sKey.']" value="'.$value.'"/>';
|
||||
}
|
||||
}
|
||||
foreach($aFieldsMapping as $iNumber => $sAttCode)
|
||||
{
|
||||
$oPage->add('<input type="hidden" name="field['.$iNumber.']" value="'.$sAttCode.'"/>');
|
||||
}
|
||||
foreach($aSearchFields as $index => $sDummy)
|
||||
{
|
||||
$oPage->add('<input type="hidden" name="search_field['.$index.']" value="1"/>');
|
||||
}
|
||||
|
||||
$sFileLoadHtml .= '</form></div>';
|
||||
|
||||
$oPage->AddToTab('tabs1', Dict::S('UI:CSVImport:Tab:LoadFromFile'), $sFileLoadHtml);
|
||||
@@ -1339,7 +1392,8 @@ EOF
|
||||
'<input type="hidden" name="separator" value="'.htmlentities($sSeparator, ENT_QUOTES, 'UTF-8').'"/>'.
|
||||
'<input type="hidden" name="text_qualifier" value="'.htmlentities($sTextQualifier, ENT_QUOTES, 'UTF-8').'"/>'.
|
||||
'<input type="hidden" name="header_line" value="'.$bHeaderLine.'"/>'.
|
||||
'<input type="hidden" name="nb_skipped_lines" value="'.$iSkippedLines.'"/>'.
|
||||
'<input type="hidden" name="nb_skipped_lines" value="'.utils::ReadParam('nb_skipped_lines', '0').'"/>'.
|
||||
'<input type="hidden" name="box_skiplines" value="'.utils::ReadParam('box_skiplines', '0').'"/>'.
|
||||
'<input type="hidden" name="class_name" value="'.$sClassName.'"/>'.
|
||||
'<input type="hidden" name="advanced" value="'.$bAdvanced.'"/>'.
|
||||
'<input type="hidden" name="synchro_scope" value="'.$sSynchroScope.'"/>';
|
||||
@@ -1350,6 +1404,14 @@ EOF
|
||||
$sPasteDataHtml .= '<input type="hidden" name="synchro_update['.$sKey.']" value="'.$value.'"/>';
|
||||
}
|
||||
}
|
||||
foreach($aFieldsMapping as $iNumber => $sAttCode)
|
||||
{
|
||||
$sPasteDataHtml .= '<input type="hidden" name="field['.$iNumber.']" value="'.$sAttCode.'"/>';
|
||||
}
|
||||
foreach($aSearchFields as $index => $sDummy)
|
||||
{
|
||||
$sPasteDataHtml .= '<input type="hidden" name="search_field['.$index.']" value="1"/>';
|
||||
}
|
||||
$sPasteDataHtml .= '</form></div>';
|
||||
|
||||
$oPage->AddToTab('tabs1', Dict::S('UI:CSVImport:Tab:CopyPaste'), $sPasteDataHtml);
|
||||
|
||||
@@ -2019,7 +2019,8 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
return null;
|
||||
}
|
||||
// MakeValueFromString() throws an exception in case of failure
|
||||
$retValue = $oAttDef->MakeValueFromString($rawValue, $oSyncAtt->Get('row_separator'), $oSyncAtt->Get('attribute_separator'), $oSyncAtt->Get('value_separator'), $oSyncAtt->Get('attribute_qualifier'));
|
||||
$bLocalizedValue = false;
|
||||
$retValue = $oAttDef->MakeValueFromString($rawValue, $bLocalizedValue, $oSyncAtt->Get('row_separator'), $oSyncAtt->Get('attribute_separator'), $oSyncAtt->Get('value_separator'), $oSyncAtt->Get('attribute_qualifier'));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -148,7 +148,7 @@ class BenchmarkDataCreation
|
||||
// transform into a link set
|
||||
$sCSVSpec = implode('|', $value);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sProp);
|
||||
$value = $oAttDef->MakeValueFromString($sCSVSpec, $sSepItem = '|', $sSepAttribute = ';', $sSepValue = ':', $sAttributeQualifier = '"');
|
||||
$value = $oAttDef->MakeValueFromString($sCSVSpec, $bLocalizedValue = false, $sSepItem = '|', $sSepAttribute = ';', $sSepValue = ':', $sAttributeQualifier = '"');
|
||||
}
|
||||
$oMyObject->Set($sProp, $value);
|
||||
}
|
||||
|
||||
@@ -3251,7 +3251,7 @@ class TestSetLinkset extends TestBizModel
|
||||
$sLinkSetSpec = "profileid:10;reason:service manager|profileid->name:Problem Manager;'reason:problem manager;glandeur";
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef('UserLocal', 'profile_list');
|
||||
$oSet = $oAttDef->MakeValueFromString($sLinkSetSpec);
|
||||
$oSet = $oAttDef->MakeValueFromString($sLinkSetSpec, $bLocalizedValue = false);
|
||||
$oUser->Set('profile_list', $oSet);
|
||||
|
||||
// Create a change to record the history of the User object
|
||||
|
||||
@@ -69,14 +69,17 @@ else
|
||||
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
|
||||
|
||||
|
||||
$sOperation = utils::ReadParam('operation', 'menu');
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iActiveNodeId = utils::ReadParam('menu', -1);
|
||||
$currentOrganization = utils::ReadParam('org_id', '');
|
||||
|
||||
$bLocalize = (utils::ReadParam('no_localize', 0) != 1);
|
||||
$sFileName = utils::ReadParam('filename', '', true, 'string');
|
||||
|
||||
// Main program
|
||||
$sExpression = utils::ReadParam('expression', '', true /* Allow CLI */, 'raw_data');
|
||||
$sFields = trim(utils::ReadParam('fields', '', true, 'raw_data')); // CSV field list (allows to specify link set attributes, still not taken into account for XML export)
|
||||
$bFieldsAdvanced = utils::ReadParam('fields_advanced', 0);
|
||||
|
||||
if (strlen($sExpression) == 0)
|
||||
{
|
||||
@@ -187,11 +190,11 @@ if (!empty($sExpression))
|
||||
$bViewLink = false;
|
||||
}
|
||||
$sFields = implode(',', $aFields);
|
||||
$aExtraParams = array('menu' => false, 'display_limit' => false, 'zlist' => false, 'extra_fields' => $sFields, 'view_link' => $bViewLink);
|
||||
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'display_limit' => false, 'localize_values' => $bLocalize, 'zlist' => false, 'extra_fields' => $sFields, 'view_link' => $bViewLink);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aExtraParams = array('menu' => false, 'display_limit' => false, 'zlist' => 'details');
|
||||
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'display_limit' => false, 'localize_values' => $bLocalize, 'zlist' => 'details');
|
||||
}
|
||||
|
||||
$oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
|
||||
@@ -201,7 +204,26 @@ if (!empty($sExpression))
|
||||
case 'csv':
|
||||
$oP = new CSVPage("iTop - Export");
|
||||
$sFields = implode(',', $aFields);
|
||||
cmdbAbstractObject::DisplaySetAsCSV($oP, $oSet, array('fields' => $sFields));
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize));
|
||||
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
if ($sCharset == 'UTF-8')
|
||||
{
|
||||
$sOutputData = UTF8_BOM.iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOutputData = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
if ($sFileName == '')
|
||||
{
|
||||
// Plain text => Firefox will NOT propose to download the file
|
||||
$oP->add_header("Content-type: text/plain; charset=$sCharset");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add_header("Content-type: text/csv; charset=$sCharset");
|
||||
}
|
||||
$oP->add($sOutputData);
|
||||
break;
|
||||
|
||||
case 'spreadsheet':
|
||||
@@ -214,12 +236,12 @@ if (!empty($sExpression))
|
||||
header("Cache-control:", true);
|
||||
|
||||
$sFields = implode(',', $aFields);
|
||||
cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, array('fields' => $sFields));
|
||||
cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize));
|
||||
break;
|
||||
|
||||
case 'xml':
|
||||
$oP = new XMLPage("iTop - Export", true /* passthrough */);
|
||||
cmdbAbstractObject::DisplaySetAsXML($oP, $oSet);
|
||||
cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, array('localize_values' => $bLocalize));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -261,7 +283,13 @@ if (!$oP)
|
||||
$oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
|
||||
$oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv' or 'xml'");
|
||||
$oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a coma");
|
||||
$oP->p(" * fields_advanced: (optional, no effect on XML format ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
|
||||
$oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
|
||||
}
|
||||
|
||||
if ($sFileName != '')
|
||||
{
|
||||
$oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
|
||||
}
|
||||
$oP->output();
|
||||
?>
|
||||
|
||||
@@ -87,8 +87,8 @@ $aPageParams = array
|
||||
(
|
||||
'mandatory' => false,
|
||||
'modes' => 'http,cli',
|
||||
'default' => 'UTF-8',
|
||||
'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15',
|
||||
'default' => '',
|
||||
'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
|
||||
),
|
||||
'date_format' => array
|
||||
(
|
||||
@@ -148,6 +148,13 @@ $aPageParams = array
|
||||
'default' => '',
|
||||
'description' => 'Comment to be added into the change log',
|
||||
),
|
||||
'no_localize' => array
|
||||
(
|
||||
'mandatory' => false,
|
||||
'modes' => 'http,cli',
|
||||
'default' => '0',
|
||||
'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
|
||||
),
|
||||
);
|
||||
|
||||
function UsageAndExit($oP)
|
||||
@@ -268,20 +275,22 @@ else
|
||||
|
||||
try
|
||||
{
|
||||
$aWarnings = array();
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
//
|
||||
// Read parameters
|
||||
//
|
||||
$sClass = ReadMandatoryParam($oP, 'class', 'class');
|
||||
$sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
|
||||
$sSep = ReadParam($oP, 'separator', 'raw_data');
|
||||
$sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
|
||||
$sCharSet = ReadParam($oP, 'charset', 'raw_data');
|
||||
$sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
|
||||
$sOutput = ReadParam($oP, 'output', 'string');
|
||||
// $sReportLevel = ReadParam($oP, 'reportlevel');
|
||||
$sReconcKeys = ReadParam($oP, 'reconciliationkeys', 'raw_data');
|
||||
$sSimulate = ReadParam($oP, 'simulate');
|
||||
$sComment = ReadParam($oP, 'comment', 'raw_data');
|
||||
$bLocalize = (ReadParam($oP, 'no_localize') != 1);
|
||||
|
||||
if (strtolower(trim($sSep)) == 'tab')
|
||||
{
|
||||
@@ -322,16 +331,10 @@ try
|
||||
$sDateFormat = null;
|
||||
}
|
||||
|
||||
/*
|
||||
$aReportLevels = explode('|', $sReportLevel);
|
||||
foreach($aReportLevels as $sLevel)
|
||||
if ($sCharSet == '')
|
||||
{
|
||||
if (!in_array($sLevel, explode('|', 'errors|warnings|created|changed|unchanged')))
|
||||
{
|
||||
throw new BulkLoadException("Unknown level in reporting level: '$sLevel'");
|
||||
}
|
||||
$sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
}
|
||||
*/
|
||||
|
||||
if ($sSimulate == '1')
|
||||
{
|
||||
@@ -357,6 +360,7 @@ try
|
||||
{
|
||||
$oP->add_comment("Date format: <none>");
|
||||
}
|
||||
$oP->add_comment("Localize: ".($bLocalize?'yes':'no'));
|
||||
$oP->add_comment("Data Size: ".strlen($sCSVData));
|
||||
}
|
||||
//////////////////////////////////////////////////
|
||||
@@ -370,24 +374,31 @@ try
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
//
|
||||
// Make translated column reference
|
||||
// Create an index of the known column names (in lower case)
|
||||
// If data is localized, an array of <TranslatedName> => array of <ExtendedAttCode> (several leads to ambiguity)
|
||||
// Otherwise an array of <ExtendedAttCode> => array of <ExtendedAttCode> (1 element by construction)
|
||||
//
|
||||
// array of <LowercaseTranslatedName> => <ExtendedAttCode>
|
||||
//
|
||||
// Examples:
|
||||
// 'organization' => 'org_id'
|
||||
// 'organization->name' => 'org_id->name'
|
||||
// Examples (localized in french):
|
||||
// 'lieu' => 'location_id'
|
||||
// 'lieu->name' => 'location_id->name'
|
||||
//
|
||||
// Note: it may happen that an external field has the same label as the external key
|
||||
// in that case, we consider that the external key has precedence
|
||||
//
|
||||
$aFriendlyToInternalAttCode = array();
|
||||
$aKnownColumnNames = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$sFriendlyName = strtolower(BulkChange::GetFriendlyAttCodeName($sClass, $sAttCode));
|
||||
if (!$oAttDef->IsExternalField() || !array_key_exists($sFriendlyName, $aFriendlyToInternalAttCode))
|
||||
if ($bLocalize)
|
||||
{
|
||||
$sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aFriendlyToInternalAttCode[$sFriendlyName] = $sAttCode;
|
||||
$sColName = strtolower($sAttCode);
|
||||
}
|
||||
if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames))
|
||||
{
|
||||
$aKnownColumnNames[$sColName][] = $sAttCode;
|
||||
}
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
|
||||
{
|
||||
@@ -395,22 +406,40 @@ try
|
||||
foreach(MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
|
||||
{
|
||||
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
|
||||
$sFriendlyName = strtolower(BulkChange::GetFriendlyAttCodeName($sClass, $sAttCodeEx));
|
||||
if (!array_key_exists($sFriendlyName, $aFriendlyToInternalAttCode))
|
||||
if ($bLocalize)
|
||||
{
|
||||
$sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sColName = strtolower($sAttCodeEx);
|
||||
}
|
||||
if (!array_key_exists($sColName, $aKnownColumnNames))
|
||||
{
|
||||
$aFriendlyToInternalAttCode[$sFriendlyName] = $sAttCodeEx;
|
||||
$aKnownColumnNames[$sColName][] = $sAttCodeEx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//print_r($aKnownColumnNames);
|
||||
//print_r(array_keys($aKnownColumnNames));
|
||||
//exit;
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
//
|
||||
// Parse first line, check attributes, analyse the request
|
||||
//
|
||||
if ($sCharSet == 'UTF-8')
|
||||
{
|
||||
$sUTF8Data = $sCSVData;
|
||||
// Remove the BOM if any
|
||||
if (substr($sCSVData, 0, 3) == UTF8_BOM)
|
||||
{
|
||||
$sCSVData = substr($sCSVData, 3);
|
||||
}
|
||||
// Clean the input
|
||||
// Todo: warn the user if some characters are lost/substituted
|
||||
$sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -433,15 +462,25 @@ try
|
||||
// Ignore any trailing "star" (*) that simply indicates a mandatory field
|
||||
$sFieldName = $aMatches[1];
|
||||
}
|
||||
if (array_key_exists(strtolower($sFieldName), $aFriendlyToInternalAttCode))
|
||||
if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames))
|
||||
{
|
||||
$aFieldList[$iFieldId] = $aFriendlyToInternalAttCode[strtolower($sFieldName)];
|
||||
$aColumns = $aKnownColumnNames[strtolower($sFieldName)];
|
||||
if (count($aColumns) > 1)
|
||||
{
|
||||
$aCompetitors = array();
|
||||
foreach ($aColumns as $sAttCodeEx)
|
||||
{
|
||||
$aCompetitors[] = $sAttCodeEx;
|
||||
}
|
||||
$aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
|
||||
}
|
||||
$aFieldList[$iFieldId] = $aColumns[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Secure the field names against XSS injection (no <> neither " chars)
|
||||
// Protect against XSS injection
|
||||
$sSafeName = str_replace(array('"', '<', '>'), '', $sFieldName);
|
||||
$aFieldList[$iFieldId] = $sSafeName;
|
||||
throw new BulkLoadException("Unknown column: '$sSafeName'");
|
||||
}
|
||||
}
|
||||
// Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
|
||||
@@ -459,16 +498,19 @@ try
|
||||
$sRemoteAttCode = $aMatches[2];
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode))
|
||||
{
|
||||
// Safety net - should not happen now that column names are checked against known names
|
||||
throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
|
||||
}
|
||||
$oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
|
||||
if (!$oAtt->IsExternalKey())
|
||||
{
|
||||
// Safety net - should not happen now that column names are checked against known names
|
||||
throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
|
||||
}
|
||||
$sTargetClass = $oAtt->GetTargetClass();
|
||||
if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode))
|
||||
{
|
||||
// Safety net - should not happen now that column names are checked against known names
|
||||
throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
|
||||
}
|
||||
$aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
|
||||
@@ -483,6 +525,7 @@ try
|
||||
//
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sFieldName))
|
||||
{
|
||||
// Safety net - should not happen now that column names are checked against known names
|
||||
throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
|
||||
}
|
||||
$oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
|
||||
@@ -515,7 +558,14 @@ try
|
||||
{
|
||||
if (in_array($sReconcKeyAttCode, $aFieldList))
|
||||
{
|
||||
$aReconcSpec[] = $sReconcKeyAttCode;
|
||||
if ($bLocalize)
|
||||
{
|
||||
$aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aReconcSpec[] = $sReconcKeyAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aReconcSpec) == 0)
|
||||
@@ -534,10 +584,26 @@ try
|
||||
$sReconcKey = trim($sReconcKey);
|
||||
if (empty($sReconcKey)) continue; // skip empty spec
|
||||
|
||||
if (array_key_exists(strtolower($sReconcKey), $aFriendlyToInternalAttCode))
|
||||
if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames))
|
||||
{
|
||||
// Translate from a translated name to codes
|
||||
$sReconcKey = $aFriendlyToInternalAttCode[strtolower($sReconcKey)];
|
||||
$aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
|
||||
if (count($aColumns) > 1)
|
||||
{
|
||||
$aCompetitors = array();
|
||||
foreach ($aColumns as $sAttCodeEx)
|
||||
{
|
||||
$aCompetitors[] = $sAttCodeEx;
|
||||
}
|
||||
$aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
|
||||
}
|
||||
$sReconcKey = $aColumns[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Protect against XSS injection
|
||||
$sSafeName = str_replace(array('"', '<', '>'), '', $sReconcKey);
|
||||
throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
|
||||
}
|
||||
|
||||
// Check that the reconciliation key is either a given column, or an external key
|
||||
@@ -565,7 +631,7 @@ try
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sReconcKey))
|
||||
{
|
||||
// Safety net: should never happen, but...
|
||||
// Safety net - should not happen now that column names are checked against known names
|
||||
throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
|
||||
}
|
||||
$oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
|
||||
@@ -617,6 +683,11 @@ try
|
||||
}
|
||||
}
|
||||
$oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
|
||||
|
||||
foreach ($aWarnings as $sWarning)
|
||||
{
|
||||
$oP->add_comment("Warning: ".$sWarning);
|
||||
}
|
||||
}
|
||||
|
||||
$oBulk = new BulkChange(
|
||||
@@ -627,7 +698,8 @@ try
|
||||
$aFinalReconcilKeys,
|
||||
null, // synchro scope
|
||||
null, // on delete
|
||||
$sDateFormat
|
||||
$sDateFormat,
|
||||
$bLocalize
|
||||
);
|
||||
|
||||
if ($bSimulate)
|
||||
@@ -717,7 +789,6 @@ try
|
||||
|
||||
if (($sOutput == "summary") || ($sOutput == 'details'))
|
||||
{
|
||||
// $oP->add_comment("Report level: ".$sReportLevel);
|
||||
$oP->add_comment("Change tracking comment: ".$sComment);
|
||||
$oP->add_comment("Issues: ".$iCountErrors);
|
||||
$oP->add_comment("Warnings: ".$iCountWarnings);
|
||||
|
||||
Reference in New Issue
Block a user