* @author Romain Quetiez * @author Denis Flaven * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */ require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); require_once(APPROOT.'/application/webpage.class.inc.php'); require_once(APPROOT.'/application/ajaxwebpage.class.inc.php'); require_once(APPROOT.'/application/wizardhelper.class.inc.php'); require_once(APPROOT.'/application/ui.linkswidget.class.inc.php'); require_once(APPROOT.'/application/csvpage.class.inc.php'); /** * Determines if the name of the field to be mapped correspond * to the name of an external key or an Id of the given class * @param string $sClassName The name of the class * @param string $sFieldCode The attribute code of the field , or empty if no match * @return bool true if the field corresponds to an id/External key, false otherwise */ function IsIdField($sClassName, $sFieldCode) { $bResult = false; if (!empty($sFieldCode)) { if ($sFieldCode == 'id') { $bResult = true; } else if (strpos($sFieldCode, '->') === false) { $oAttDef = MetaModel::GetAttributeDef($sClassName, $sFieldCode); $bResult = $oAttDef->IsExternalKey(); } } return $bResult; } /** * Get all the fields xxx->yyy based on the field xxx which is an external key * @param string $sExtKeyAttCode Attribute code of the external key * @param AttributeDefinition $oExtKeyAttDef Attribute definition of the external key * @param bool $bAdvanced True if advanced mode * @return Ash List of codes=>display name: xxx->yyy where yyy are the reconciliation keys for the object xxx */ function GetMappingsForExtKey($sAttCode, AttributeDefinition $oExtKeyAttDef, $bAdvanced) { $aResult = array(); $sTargetClass = $oExtKeyAttDef->GetTargetClass(); foreach(MetaModel::ListAttributeDefs($sTargetClass) as $sTargetAttCode => $oTargetAttDef) { if (MetaModel::IsReconcKey($sTargetClass, $sTargetAttCode)) { $bExtKey = $oTargetAttDef->IsExternalKey(); $sSuffix = ''; if ($bExtKey) { $sSuffix = '->id'; } if ($bAdvanced || !$bExtKey) { // When not in advanced mode do not allow to use reconciliation keys (on external keys) if they are themselves external keys ! $aResult[$sAttCode.'->'.$sTargetAttCode] = $oExtKeyAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel().$sSuffix; } } } return $aResult; } /** * Helper function to build the mapping drop-down list for a field * Spec: Possible choices are "writable" fields in this class plus external fields that are listed as reconciliation keys * for any class pointed to by an external key in the current class. * If not in advanced mode, all "id" fields (id and external keys) must be mapped to ":none:" (i.e -- ignore this field --) * External fields that do not correspond to a reconciliation key must be mapped to ":none:" * Otherwise, if a field equals either the 'code' or the 'label' (translated) of a field, then it's mapped automatically * @param string $sClassName Name of the class used for the mapping * @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 * @return string The HTML code corresponding to the drop-down list for this field */ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMode = false) { $aChoices = array('' => Dict::S('UI:CSVImport:MappingSelectOne')); $aChoices[':none:'] = Dict::S('UI:CSVImport:MappingNotApplicable'); $sFieldCode = ''; // Code of the attribute, if there is a match $aMatches = array(); if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches)) { // Remove any trailing "star" character. // A star character at the end can be used to indicate a mandatory field $sFieldName = $aMatches[1]; } if ($sFieldName == 'id') { $sFieldCode = 'id'; } if ($bAdvancedMode) { $aChoices['id'] = Dict::S('UI:CSVImport:idField'); } foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef) { $sStar = ''; if ($oAttDef->IsExternalKey()) { if ( ($sFieldName == $oAttDef->GetLabel()) || ($sFieldName == $sAttCode)) { $sFieldCode = $sAttCode; } if ($bAdvancedMode) { $aChoices[$sAttCode] = $oAttDef->GetLabel(); } $oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode()); if (!$oExtKeyAttDef->IsNullAllowed()) { $sStar = '*'; } // Get fields of the external class that are considered as reconciliation keys $sTargetClass = $oAttDef->GetTargetClass(); foreach(MetaModel::ListAttributeDefs($sTargetClass) as $sTargetAttCode => $oTargetAttDef) { if (MetaModel::IsReconcKey($sTargetClass, $sTargetAttCode)) { $bExtKey = $oTargetAttDef->IsExternalKey(); $sSuffix = ''; if ($bExtKey) { $sSuffix = '->id'; } if ($bAdvancedMode || !$bExtKey) { // When not in advanced mode do not allow to use reconciliation keys (on external keys) if they are themselves external keys ! $aChoices[$sAttCode.'->'.$sTargetAttCode] = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel().$sSuffix.$sStar; if ((strcasecmp($sFieldName, $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel().$sSuffix) == 0) || (strcasecmp($sFieldName, ($sAttCode.'->'.$sTargetAttCode.$sSuffix)) == 0) ) { $sFieldCode = $sAttCode.'->'.$sTargetAttCode; } } } } } else if ( ($oAttDef->IsWritable()) && (!$oAttDef->IsLinkSet()) ) { if (!$oAttDef->IsNullAllowed()) { $sStar = '*'; } $aChoices[$sAttCode] = $oAttDef->GetLabel().$sStar; if ( ($sFieldName == $oAttDef->GetLabel()) || ($sFieldName == $sAttCode)) { $sFieldCode = $sAttCode; } } } asort($aChoices); $sHtml = "\n"; return $sHtml; } try { require_once(APPROOT.'/application/startup.inc.php'); require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed $sOperation = utils::ReadParam('operation', ''); switch($sOperation) { case 'parser_preview': $oPage = new ajax_page(""); $oPage->no_cache(); $sSeparator = utils::ReadParam('separator', ','); if ($sSeparator == 'tab') $sSeparator = "\t"; $sTextQualifier = utils::ReadParam('qualifier', '"'); $iLinesToSkip = utils::ReadParam('nb_lines_skipped', 0); $bFirstLineAsHeader = utils::ReadParam('header_line', true); $sEncoding = utils::ReadParam('encoding', 'UTF-8'); $sData = stripslashes(utils::ReadParam('csvdata', true)); $oCSVParser = new CSVParser($sData, $sSeparator, $sTextQualifier); $aData = $oCSVParser->ToArray($iLinesToSkip); $iTarget = count($aData); if ($iTarget == 0) { $oPage->p(Dict::S('UI:CSVImport:NoData')); } else { $sMaxLen = (strlen(''.$iTarget) < 3) ? 3 : strlen(''.$iTarget); // Pad line numbers to the appropriate number of chars, but at least 3 $sFormat = '%0'.$sMaxLen.'d'; $oPage->p("

".Dict::S('UI:Title:DataPreview')."

\n"); $oPage->p("
\n"); $oPage->add(""); $iMaxIndex= 10; // Display maximum 10 lines for the preview $index = 1; foreach($aData as $aRow) { $sCSSClass = 'csv_row'.($index % 2); if ( ($bFirstLineAsHeader) && ($index == 1)) { $oPage->add("\n"); $iNbCols = count($aRow); } else { if ($index == 1) $iNbCols = count($aRow); $oPage->add("\n"); } $index++; if ($index > $iMaxIndex) break; } $oPage->add("
".sprintf($sFormat, $index).""); $oPage->add(implode('', $aRow)); $oPage->add("
".sprintf($sFormat, $index).""); $oPage->add(implode('', $aRow)); $oPage->add("
\n"); $oPage->add("
\n"); if($iNbCols == 1) { $oPage->p(' '.Dict::S('UI:CSVImport:ErrorOnlyOneColumn')); } else { $oPage->p(' '); } } break; case 'display_mapping_form': $oPage = new ajax_page(""); $oPage->no_cache(); $sSeparator = utils::ReadParam('separator', ','); $sTextQualifier = utils::ReadParam('qualifier', '"'); $iLinesToSkip = utils::ReadParam('nb_lines_skipped', 0); $bFirstLineAsHeader = utils::ReadParam('header_line', false); $sData = stripslashes(utils::ReadParam('csvdata', '')); $sClassName = utils::ReadParam('class_name', ''); $bAdvanced = utils::ReadParam('advanced', false); $sEncoding = utils::ReadParam('encoding', 'UTF-8'); $oCSVParser = new CSVParser($sData, $sSeparator, $sTextQualifier); $aData = $oCSVParser->ToArray($iLinesToSkip); $iTarget = count($aData); if ($iTarget == 0) { $oPage->p(Dict::S('UI:CSVImport:NoData')); } else { $oPage->add(""); $aFirstLine = $aData[0]; // Use the first row to determine the number of columns $iStartLine = 0; $iNbColumns = count($aFirstLine); if ($bFirstLineAsHeader) { $iStartLine = 1; foreach($aFirstLine as $sField) { $aHeader[] = $sField; } } else { // Build some conventional name for the fields: field1...fieldn $index= 1; foreach($aFirstLine as $sField) { $aHeader[] = Dict::Format('UI:CSVImport:FieldName', $index); $index++; } } $oPage->add("
\n"); $oPage->add(''); $oPage->add(''); $oPage->add(''); $index = 1; foreach($aHeader as $sField) { $oPage->add(''); $oPage->add(""); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $index++; } $oPage->add("
'.Dict::S('UI:CSVImport:HeaderFields').''.Dict::S('UI:CSVImport:HeaderMappings').' '.Dict::S('UI:CSVImport:HeaderSearch').''.Dict::S('UI:CSVImport:DataLine1').''.Dict::S('UI:CSVImport:DataLine2').'
$sField'.GetMappingForField($sClassName, $sField, $index, $bAdvanced).' '.(isset($aData[$iStartLine][$index-1]) ? htmlentities($aData[$iStartLine][$index-1], ENT_QUOTES, 'UTF-8') : ' ').''.(isset($aData[$iStartLine+1][$index-1]) ? htmlentities($aData[$iStartLine+1][$index-1], ENT_QUOTES, 'UTF-8') : ' ').'
\n"); $aReconciliationKeys = MetaModel::GetReconcKeys($sClassName); $aMoreReconciliationKeys = array(); // Store: key => void to automatically remove duplicates foreach($aReconciliationKeys as $sAttCode) { $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))).'"'; $oPage->add_ready_script( <<AddCondition('id', 0, '='); // Make sure we create an empty set $oSet = new CMDBObjectSet($oSearch); $sResult = cmdbAbstractObject::GetSetAsCSV($oSet, array('showMandatoryFields' => true)); //$aCSV = explode("\n", $sCSV); // If there are more than one line, let's assume that the first line is a comment and skip it. //if (count($aCSV) > 1) //{ // $sResult = $aCSV[0]; //} //else //{ // $sResult = $sCSV; //} $sClassDisplayName = MetaModel::GetName($sClassName); $sDisposition = utils::ReadParam('disposition', 'inline'); if ($sDisposition == 'attachment') { $oPage = new CSVPage(""); $oPage->add_header("Content-type: text/csv; charset=utf-8"); $oPage->add_header("Content-disposition: attachment; filename=\"{$sClassDisplayName}.csv\""); $oPage->no_cache(); $oPage->add($sResult); } else { $oPage = new ajax_page(""); $oPage->no_cache(); $oPage->add('


'.$sClassDisplayName.'.csv

'); $oPage->add('

'); } break; } $oPage->output(); } catch (Exception $e) { IssueLog::Error($e->getMessage()); } ?>