* @author Romain Quetiez * @author Denis Flaven * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */ try { ini_set('memory_limit', '256M'); require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); require_once(APPROOT.'/application/itopwebpage.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed $oAppContext = new ApplicationContext(); $iStep = utils::ReadParam('step', 1); $oPage = new iTopWebPage(Dict::S('UI:Title:BulkImport')); /** * Helper function to build a select from the list of valid classes for a given action * @param string $sName The name of the select in the HTML form * @param string $sDefaulfValue The defaut value (i.e the value selected by default) * @param integer $iWidthPx The width (in pixels) of the drop-down list * @param integer $iActionCode The ActionCode (from UserRights) to check for authorization for the classes * @return string The HTML fragment corresponding to the select tag */ function GetClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null) { $sHtml = ""; return $sHtml; } /** * Helper to 'check' an input in an HTML form if the current value equals the value given * @param mixed $sCurrentValue The current value to be chacked against the value of the input * @param mixed $sProposedValue The value of the input * @param bool $bInverseCondition Set to true to perform the reversed comparison * @return string Either ' checked' or an empty string */ function IsChecked($sCurrentValue, $sProposedValue, $bInverseCondition = false) { $bCondition = ($sCurrentValue == $sProposedValue); return ($bCondition xor $bInverseCondition) ? ' checked' : ''; } /** * 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 of ext_key_name->att_code * @return string A user friendly format of the string: AttributeName or AttributeName->ExtAttributeName */ 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; } /** * Returns the number of occurences of each char from the set in the specified string * @param string $sString The input data * @param array $aSet The set of characters to count * @return hash 'char' => nb of occurences */ function CountCharsFromSet($sString, $aSet) { $aResult = array(); $aCount = count_chars($sString); foreach($aSet as $sChar) { $aResult[$sChar] = isset($aCount[ord($sChar)]) ? $aCount[ord($sChar)] : 0; } return $aResult; } /** * Return the most frequent (and regularly occuring) character among the given set, in the specified lines * @param array $aCSVData The input data, one entry per line * @param array $aPossibleSeparators The list of characters to count * @return string The most frequent character from the set */ function GuessFromFrequency($aCSVData, $aPossibleSeparators) { $iLine = 0; $iMaxLine = 20; // Process max 20 lines to guess the parameters foreach($aPossibleSeparators as $sSep) { $aGuesses[$sSep]['total'] = $aGuesses[$sSep]['max'] = 0; $aGuesses[$sSep]['min'] = 999; } $aStats = array(); while(($iLine < count($aCSVData)) && ($iLine < $iMaxLine) ) { if (strlen($aCSVData[$iLine]) > 0) { $aStats[$iLine] = CountCharsFromSet($aCSVData[$iLine], $aPossibleSeparators); } $iLine++; } $iLine = 1; foreach($aStats as $aLineStats) { foreach($aPossibleSeparators as $sSep) { $aGuesses[$sSep]['total'] += $aLineStats[$sSep]; if ($aLineStats[$sSep] > $aGuesses[$sSep]['max']) $aGuesses[$sSep]['max'] = $aLineStats[$sSep]; if ($aLineStats[$sSep] < $aGuesses[$sSep]['min']) $aGuesses[$sSep]['min'] = $aLineStats[$sSep]; } $iLine++; } $aScores = array(); foreach($aGuesses as $sSep => $aData) { $aScores[$sSep] = $aData['total'] + $aData['max'] - $aData['min']; } arsort($aScores, SORT_NUMERIC); // Sort the array, higher scores first $aKeys = array_keys($aScores); $sSeparator = $aKeys[0]; // Take the first key, the one with the best score return $sSeparator; } /** * Try to predict the CSV parameters based on the input data * @param string $sCSVData The input data * @return hash 'separator' => the_guessed_separator, 'qualifier' => the_guessed_text_qualifier */ function GuessParameters($sCSVData) { $aData = explode("\n", $sCSVData); $sSeparator = GuessFromFrequency($aData, array("\t", ',', ';', '|')); // Guess the most frequent (and regular) character on each line $sQualifier = GuessFromFrequency($aData, array('"', "'")); // Guess the most frequent (and regular) character on each line return array('separator' => $sSeparator, 'qualifier' => $sQualifier); } /** * Process the CSV data, for real or as a simulation * @param WebPage $oPage The page used to display the wizard * @param bool $bSimulate Whether or not to simulate the data load * @return array The CSV lines in error that were rejected from the load (with the header line - if any) or null */ function ProcessCSVData(WebPage $oPage, $bSimulate = true) { $aResult = array(); $sCSVData = utils::ReadParam('csvdata', ''); $sCSVDataTruncated = utils::ReadParam('csvdata_truncated', ''); $sSeparator = utils::ReadParam('separator', ','); $sTextQualifier = utils::ReadParam('text_qualifier', '"'); $bHeaderLine = (utils::ReadParam('header_line', '0') == 1); $iRealSkippedLines = $iSkippedLines = utils::ReadParam('nb_skipped_lines', '0'); $sClassName = utils::ReadParam('class_name', ''); $aFieldsMapping = utils::ReadParam('field', array()); $aSearchFields = utils::ReadParam('search_field', array()); $iCurrentStep = $bSimulate ? 4 : 5; $bAdvanced = utils::ReadParam('advanced', 0); $sEncoding = utils::ReadParam('encoding', 'UTF-8'); // Parse the data set $oCSVParser = new CSVParser($sCSVData, $sSeparator, $sTextQualifier); $aData = $oCSVParser->ToArray($iSkippedLines); if ($bHeaderLine) { $aResult[] = $sTextQualifier.implode($sTextQualifier.$sSeparator.$sTextQualifier, array_shift($aData)).$sTextQualifier; // Remove the first line and store it in case of error $iRealSkippedLines++; } // Format for the line numbers $sMaxLen = (strlen(''.count($aData)) < 3) ? 3 : strlen(''.count($aData)); // Pad line numbers to the appropriate number of chars, but at least 3 // Compute the list of search/reconciliation criteria $aSearchKeys = array(); foreach($aSearchFields as $index => $sDummy) { $sSearchField = $aFieldsMapping[$index]; $aMatches = array(); if (preg_match('/(.+)->(.+)/', $sSearchField, $aMatches) > 0) { $sSearchField = $aMatches[1]; $aSearchKeys[$aMatches[1]] = ''; } else { $aSearchKeys[$sSearchField] = ''; } if (!MetaModel::IsValidFilterCode($sClassName, $sSearchField)) { // Remove invalid or unmapped search fields $aSearchFields[$index] = null; unset($aSearchKeys[$sSearchField]); } } // Compute the list of fields and external keys to process $aExtKeys = array(); $aAttributes = array(); $aExternalKeysByColumn = array(); foreach($aFieldsMapping as $iNumber => $sAttCode) { $iIndex = $iNumber-1; if (!empty($sAttCode) && ($sAttCode != ':none:') && ($sAttCode != 'finalclass')) { if (preg_match('/(.+)->(.+)/', $sAttCode, $aMatches) > 0) { $sAttribute = $aMatches[1]; $sField = $aMatches[2]; $aExtKeys[$sAttribute][$sField] = $iIndex; $aExternalKeysByColumn[$iIndex] = $sAttribute; } else { if ($sAttCode == 'id') { $aAttributes['id'] = $iIndex; } else { $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); if ($oAttDef->IsExternalKey()) { $aExtKeys[$sAttCode]['id'] = $iIndex; $aExternalKeysByColumn[$iIndex] = $sAttCode; } else { $aAttributes[$sAttCode] = $iIndex; } } } } } $oMyChange = null; if (!$bSimulate) { // We're doing it for real, let's create a change $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); if (UserRights::IsImpersonated()) { $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); } else { $sUserString = UserRights::GetUser(); } $sUserString .= ' (CSV)'; $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); } $sSynchroScope = null; // e.g. "SELECT Server"; $aSynchroUpdate = null; // e.g. array('status' => 'obsolete') $oBulk = new BulkChange( $sClassName, $aData, $aAttributes, $aExtKeys, array_keys($aSearchKeys), $sSynchroScope, $aSynchroUpdate ); $oPage->add(''); $aRes = $oBulk->Process($oMyChange); $sHtml = ''; $sHtml .= ''; $sHtml .= ''; $sHtml .= ''; foreach($aFieldsMapping as $iNumber => $sAttCode) { if (!empty($sAttCode) && ($sAttCode != ':none:') && ($sAttCode != 'finalclass')) { $sHtml .= ""; } } $sHtml .= ''; $sHtml .= ''; $iErrors = 0; $iCreated = 0; $iModified = 0; $iUnchanged = 0; foreach($aRes as $iLine => $aResRow) { $oStatus = $aResRow['__STATUS__']; $sUrl = ''; $sMessage = ''; $sCSSRowClass = ''; $sCSSMessageClass = 'cell_ok'; switch(get_class($oStatus)) { case 'RowStatus_NoChange': $iUnchanged++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = ''; $sCSSRowClass = 'row_unchanged'; break; case 'RowStatus_Modify': $iModified++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = ''; $sCSSRowClass = 'row_modified'; break; case 'RowStatus_Disappeared': $iModified++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = ''; $sCSSRowClass = 'row_modified'; if ($bSimulate) { $sMessage = 'Missing object: will be updated'; } else { $sMessage = 'Missing object: updated'; } break; case 'RowStatus_NewObj': $iCreated++; $sFinalClass = $aResRow['finalclass']; $sStatus = ''; $sCSSRowClass = 'row_added'; if ($bSimulate) { $sMessage = 'Object will be created'; } else { $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetValue()); $sUrl = $oObj->GetHyperlink(); $sMessage = 'Object created'; } break; case 'RowStatus_Issue': $iErrors++; $sMessage .= $oPage->GetP($oStatus->GetDescription()); $sStatus = ''; $sCSSMessageClass = 'cell_error'; $sCSSRowClass = 'row_error'; if (array_key_exists($iLine, $aData)) { $aRow = $aData[$iLine]; $aResult[] = $sTextQualifier.implode($sTextQualifier.$sSeparator.$sTextQualifier,$aRow).$sTextQualifier; // Remove the first line and store it in case of error } break; } $sHtml .= ''; $sHtml .= ""; $sHtml .= ""; $sHtml .= ""; foreach($aFieldsMapping as $iNumber => $sAttCode) { if (!empty($sAttCode) && ($sAttCode != ':none:') && ($sAttCode != 'finalclass')) { $oCellStatus = $aResRow[$iNumber -1]; $sCellMessage = ''; if (isset($aExternalKeysByColumn[$iNumber -1])) { $sExtKeyName = $aExternalKeysByColumn[$iNumber -1]; $oExtKeyCellStatus = $aResRow[$sExtKeyName]; switch(get_class($oExtKeyCellStatus)) { case 'CellStatus_Issue': case 'CellStatus_SearchIssue': case 'CellStatus_NullIssue': $sCellMessage .= $oPage->GetP($oExtKeyCellStatus->GetDescription()); break; case 'CellStatus_Ambiguous': $sCellMessage .= $oPage->GetP($oExtKeyCellStatus->GetDescription()); break; default: // Do nothing } } $sHtmlValue = htmlentities($oCellStatus->GetValue(), ENT_QUOTES, 'UTF-8'); switch(get_class($oCellStatus)) { case 'CellStatus_Issue': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= ''; break; case 'CellStatus_SearchIssue': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= ''; break; case 'CellStatus_Ambiguous': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= ''; break; case 'CellStatus_Modify': $sHtml .= ''; break; default: $sHtml .= ''; } } } $sHtml .= ""; $sHtml .= ''; } $sHtml .= '
LineStatusObject".GetFriendlyAttCodeName($sClassName, $sAttCode)."Message
".sprintf("%0{$sMaxLen}d", 1+$iLine+$iRealSkippedLines)."$sStatus$sUrlERROR: '.$sHtmlValue.$sCellMessage.'ERROR: '.$sHtmlValue.$sCellMessage.'AMBIGUOUS: '.$sHtmlValue.$sCellMessage.''.$sHtmlValue.''.$sHtmlValue.$sCellMessage.'$sMessage
'; $oPage->add('
'); $oPage->add('
'); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); foreach($aFieldsMapping as $iNumber => $sAttCode) { $oPage->add(''); } foreach($aSearchFields as $index => $sDummy) { $oPage->add(''); } $aFieldsMapping = utils::ReadParam('field', array()); $aSearchFields = utils::ReadParam('search_field', array()); $aDisplayFilters = array(); if ($bSimulate) { $aDisplayFilters['unchanged'] = Dict::S('UI:CSVImport:ObjectsWillStayUnchanged'); $aDisplayFilters['modified'] = Dict::S('UI:CSVImport:ObjectsWillBeModified'); $aDisplayFilters['added'] = Dict::S('UI:CSVImport:ObjectsWillBeAdded'); $aDisplayFilters['errors'] = Dict::S('UI:CSVImport:ObjectsWillHaveErrors'); } else { $aDisplayFilters['unchanged'] = Dict::S('UI:CSVImport:ObjectsRemainedUnchanged'); $aDisplayFilters['modified'] = Dict::S('UI:CSVImport:ObjectsWereModified'); $aDisplayFilters['added'] = Dict::S('UI:CSVImport:ObjectsWereAdded'); $aDisplayFilters['errors'] = Dict::S('UI:CSVImport:ObjectsHadErrors'); } $oPage->add('

  '.sprintf($aDisplayFilters['unchanged'], $iUnchanged).'  '); $oPage->add('  '.sprintf($aDisplayFilters['modified'], $iModified).'  '); $oPage->add('  '.sprintf($aDisplayFilters['added'], $iCreated).'  '); $oPage->add('  '.sprintf($aDisplayFilters['errors'], $iErrors).'

'); $oPage->add('
'); $oPage->add($sHtml); $oPage->add('
'); $oPage->add('

  '); if ($bSimulate) { $oPage->add('

'); } else { $oPage->add('

'); } $oPage->add('
'); $oPage->add('
'); $oPage->add_script( <<< EOF function CSVGoBack() { $('input[name=step]').val($iCurrentStep-1); $('#wizForm').submit(); } function ToggleRows(sCSSClass) { $('.'+sCSSClass).toggle(); } EOF ); if ($iErrors > 0) { return $aResult; } else { return null; } } /** * Perform the actual load of the CSV data and display the results * @param WebPage $oPage The web page to display the wizard * @return void */ function LoadData(WebPage $oPage) { $oPage->add('

'.Dict::S('UI:Title:CSVImportStep5').'

'); $aResult = ProcessCSVData($oPage, false /* simulate = false */); if (is_array($aResult)) { $oPage->StartCollapsibleSection(Dict::S('UI:CSVImport:LinesNotImported'), false); $oPage->p(Dict::S('UI:CSVImport:LinesNotImported+')); $oPage->add(''); $oPage->EndCollapsibleSection(); } } /** * Simulate the load of the CSV data and display the results * @param WebPage $oPage The web page to display the wizard * @return void */ function Preview(WebPage $oPage) { $oPage->add('

'.Dict::S('UI:Title:CSVImportStep4').'

'); ProcessCSVData($oPage, true /* simulate */); } /** * Select the mapping between the CSV column and the fields of the objects * @param WebPage $oPage The web page to display the wizard * @return void */ function SelectMapping(WebPage $oPage) { $sCSVData = utils::ReadParam('csvdata', ''); $sCSVDataTruncated = utils::ReadParam('csvdata_truncated', '');; $sSeparator = utils::ReadParam('separator', ','); if ($sSeparator == 'tab') $sSeparator = "\t"; if ($sSeparator == 'other') { $sSeparator = utils::ReadParam('other_separator', ','); } $sTextQualifier = utils::ReadParam('text_qualifier', '"'); if ($sTextQualifier == 'other') { $sTextQualifier = utils::ReadParam('other_qualifier', '"'); } $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', ''); $bAdvanced = utils::ReadParam('advanced', 0); $sEncoding = utils::ReadParam('encoding', 'UTF-8'); $oPage->add('

'.Dict::S('UI:Title:CSVImportStep3').'

'); $oPage->add('
'); $oPage->add('
'.Dict::S('UI:CSVImport:SelectClass').' '); $oPage->add(GetClassesSelect('class_name', $sClassName, 300, UR_ACTION_BULK_MODIFY)); $oPage->add(' '.Dict::S('UI:CSVImport:AdvancedMode').'
'); $oPage->add(''); $oPage->add('

'.Dict::S('UI:CSVImport:SelectAClassFirst').'

'); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add('

  '); $oPage->add('

'); $oPage->add('
'); $oPage->add('
'); $sAlertIncompleteMapping = Dict::S('UI:CSVImport:AlertIncompleteMapping'); $sAlertNoSearchCriteria = Dict::S('UI:CSVImport:AlertNoSearchCriteria'); $oPage->add_ready_script( <<add_ready_script("DoMapping();"); // There is already a class selected, run the mapping } $oPage->add_script( << 0) { // The 'id' field was mapped, it's the only possible reconciliation key $('#search_'+index).attr('checked', false); $('#search_'+index).attr('disabled', true); } else { $('#search_'+index).attr('disabled', false); if (nbSearchKeys == 0) { // No search key was selected, select the default ones for(j =0; j < aDefaultKeys.length; j++) { if (sMappingValue == aDefaultKeys[j]) { $('#search_'+index).attr('checked', true); } } } } } } } EOF ); } /** * Select the options of the CSV load and check for CSV parsing errors * @param WebPage $oPage The current web page * @return void */ function SelectOptions(WebPage $oPage) { $sOperation = utils::ReadParam('operation', 'csv_data'); $sCSVData = ''; switch($sOperation) { case 'file_upload': $oDocument = utils::ReadPostedDocument('csvdata'); if (!$oDocument->IsEmpty()) { $sCSVData = $oDocument->GetData(); } break; default: $sCSVData = utils::ReadPostedParam('csvdata', ''); } $sEncoding = utils::ReadParam('encoding', 'UTF-8'); // Compute a subset of the data set, now that we know the charset if ($sEncoding == 'UTF-8') { $sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData); } else { $sUTF8Data = iconv($sEncoding, 'UTF-8//IGNORE//TRANSLIT', $sCSVData); } $aGuesses = GuessParameters($sUTF8Data); // Try to predict the parameters, based on the input data $sSeparator = utils::ReadParam('separator', ''); if ($sSeparator == '') // May be set to an empty value by the previous page { $sSeparator = $aGuesses['separator']; } $iSkippedLines = utils::ReadParam('nb_skipped_lines', ''); $bBoxSkipLines = utils::ReadParam('box_skiplines', 0); if ($sSeparator == 'tab') $sSeparator = "\t"; $sOtherSeparator = in_array($sSeparator, array(',', ';', "\t")) ? '' : $sSeparator; $sTextQualifier = utils::ReadParam('text_qualifier', ''); if ($sTextQualifier == '') // May be set to an empty value by the previous page { $sTextQualifier = $aGuesses['qualifier']; } $sOtherTextQualifier = in_array($sTextQualifier, array('"', "'")) ? '' : $sTextQualifier; $bHeaderLine = utils::ReadParam('header_line', 0); $sClassName = utils::ReadParam('class_name', ''); $bAdvanced = utils::ReadParam('advanced', 0); // 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 $iMaxLines = 20; $iMaxLen = strlen($sUTF8Data); $iCurPos = true; while ( ($iCurPos > 0) && ($iMaxLines > 0)) { $pos = strpos($sUTF8Data, "\n", $iCurPos); if ($pos !== false) { $iCurPos = 1+$pos; } else { $iCurPos = strlen($sUTF8Data); $iMaxLines = 1; } $iMaxLines--; } $sCSVDataTruncated = substr($sUTF8Data, 0, $iCurPos); $oPage->add('

'.Dict::S('UI:Title:CSVImportStep2').'

'); $oPage->add('
'); $oPage->add('
'); $oPage->add('
'); $oPage->add('

'.Dict::S('UI:CSVImport:SeparatorCharacter').'

'); $oPage->add('

'.Dict::S('UI:CSVImport:SeparatorComma+').'
'); $oPage->add(' '.Dict::S('UI:CSVImport:SeparatorSemicolon+').'
'); $oPage->add(' '.Dict::S('UI:CSVImport:SeparatorTab+').'
'); $oPage->add(' '.Dict::S('UI:CSVImport:SeparatorOther').' '); $oPage->add('

'); $oPage->add('
'); $oPage->add('

'.Dict::S('UI:CSVImport:TextQualifierCharacter').'

'); $oPage->add('

'.Dict::S('UI:CSVImport:QualifierDoubleQuote+').'
'); $oPage->add(' '.Dict::S('UI:CSVImport:QualifierSimpleQuote+').'
'); $oPage->add(' '.Dict::S('UI:CSVImport:QualifierOther').' '); $oPage->add('

'); $oPage->add('
'); $oPage->add('

'.Dict::S('UI:CSVImport:CommentsAndHeader').'

'); $oPage->add('

'.Dict::S('UI:CSVImport:TreatFirstLineAsHeader').'

'); $oPage->add('

'.Dict::Format('UI:CSVImport:Skip_N_LinesAtTheBeginning', '').'

'); $oPage->add('

'); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add('
'); $oPage->add('

'.Dict::S('UI:CSVImport:CSVDataPreview').'

'); $oPage->add('
'); $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add('
'); $oPage->add_script( <<add_ready_script('DoPreview();'); } /** * Prompt for the data to be loaded (either via a file or a copy/paste) * @param WebPage $oPage The current web page * @return void */ function Welcome(iTopWebPage $oPage) { // Encodings supported: // ICONV_CODE => Display Name // Each iconv installation supports different encodings // Some reasonably common and useful encodnings are listed here $aPossibleEncodings = array( 'UTF-8' => 'Unicode (UTF-8)', 'ISO-8859-1' => 'Western (ISO-8859-1)', 'WINDOWS-1251' => 'Cyrilic (Windows 1251)', 'WINDOWS-1252' => 'Western (Windows 1252)', 'ISO-8859-15' => 'Western (ISO-8859-15)', ); // Some more encodings can be specified in the config file $aExtraCharsets = utils::GetConfig()->GetCSVImportCharsets(); $aPossibleEncodings = array_merge($aPossibleEncodings, $aExtraCharsets); asort($aPossibleEncodings); $oPage->add("

".Dict::S('UI:Title:BulkImport+')."

\n"); $oPage->AddTabContainer('tabs1'); $sSeparator = utils::ReadParam('separator', ''); $sTextQualifier = utils::ReadParam('text_qualifier', ''); $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'); $sFileLoadHtml = '

'.Dict::S('UI:CSVImport:SelectFile').'

'. '

'; $sFileLoadHtml .= '

'.Dict::S('UI:CSVImport:Encoding').': '; $sFileLoadHtml .= '

'; $sFileLoadHtml .= '

'. ''. ''. ''. ''. ''. ''. '
'; $oPage->AddToTab('tabs1', Dict::S('UI:CSVImport:Tab:LoadFromFile'), $sFileLoadHtml); $sCSVData = utils::ReadParam('csvdata', ''); $sPasteDataHtml = '

'.Dict::S('UI:CSVImport:PasteData').'

'. '

'; $sPasteDataHtml .= '

'.Dict::S('UI:CSVImport:Encoding').': '; $sPasteDataHtml .= '

'. '

'. ''. ''. ''. ''. ''. ''. ''. ''. '
'; $oPage->AddToTab('tabs1', Dict::S('UI:CSVImport:Tab:CopyPaste'), $sPasteDataHtml); if (!empty($sCSVData)) { // When there are some data, activate the 'copy & paste' tab by default $oPage->SelectTab('tabs1', Dict::S('UI:CSVImport:Tab:CopyPaste')); } $sTemplateHtml = '

'.Dict::S('UI:CSVImport:PickClassForTemplate').' '; $sTemplateHtml .= GetClassesSelect('template_class', '', 300, UR_ACTION_BULK_MODIFY); $sTemplateHtml .= '

'; $sTemplateHtml .= '
'; $sTemplateHtml .= '
'; $oPage->AddToTab('tabs1', Dict::S('UI:CSVImport:Tab:Templates'), $sTemplateHtml); $oPage->add_script( <<add_ready_script( <<output(); } catch(CoreException $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); $oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); $oP->output(); if (MetaModel::IsLogEnabledIssue()) { if (MetaModel::IsValidClass('EventIssue')) { $oLog = new EventIssue(); $oLog->Set('message', $e->getMessage()); $oLog->Set('userinfo', ''); $oLog->Set('issue', $e->GetIssue()); $oLog->Set('impact', 'Page could not be displayed'); $oLog->Set('callstack', $e->getTrace()); $oLog->Set('data', $e->getContextData()); $oLog->DBInsertNoReload(); } IssueLog::Error($e->getMessage()); } // For debugging only //throw $e; } catch(Exception $e) { require_once(APPROOT.'/setup/setuppage.class.inc.php'); $oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError')); $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); $oP->output(); if (MetaModel::IsLogEnabledIssue()) { if (MetaModel::IsValidClass('EventIssue')) { $oLog = new EventIssue(); $oLog->Set('message', $e->getMessage()); $oLog->Set('userinfo', ''); $oLog->Set('issue', 'PHP Exception'); $oLog->Set('impact', 'Page could not be displayed'); $oLog->Set('callstack', $e->getTrace()); $oLog->Set('data', array()); $oLog->DBInsertNoReload(); } IssueLog::Error($e->getMessage()); } } ?>