kenken999/php
0
1<?php2 3class ImportPage extends RunnerPage4{5 /**6 * An audit object instance7 * @type Mixed8 */9 public $audit = null;10 11 /**12 * @type String13 */14 public $action;15 16 /**17 * @type String18 */19 public $importType;20 21 /**22 * @type String23 */24 public $importText;25 26 /**27 * @type Boolean28 */29 public $useXHR = false;30 31 /**32 * @type Array33 */34 public $importData;35 36 /**37 * The final date format used in the actual import.38 * This variable is set in the beginning of the actual import process ( insert into the database ) and used through it.39 * @type string40 */41 public $currentDateFormat;42 43 44 /**45 * @constructor46 * @param &Array params47 */48 function __construct(&$params)49 {50 parent::__construct($params);51 52 $this->audit = GetAuditObject( $this->tName );53 $this->jsSettings["tableSettings"][ $this->tName ]["importFieldsLabels"] = $this->getImportfieldsLabels();54 }55 56 /**57 * Get import fields labels data58 * @return Array59 */60 protected function getImportfieldsLabels() {61 $importFieldsLabels = array();62 foreach( $this->pSet->getImportFields() as $importField ) {63 $importFieldsLabels[ $importField ] = GetFieldLabel( GoodFieldName($this->tName), GoodFieldName($importField) );64 }65 66 return $importFieldsLabels;67 }68 69 /**70 * Process the page71 */72 public function process()73 {74 if( !strlen( $this->action ) )75 $this->removeOldTemporaryFiles();76 77 // Before Process event78 if( $this->eventsObject->exists("BeforeProcessImport") )79 $this->eventsObject->BeforeProcessImport( $this );80 81 if( $this->action == "importPreview" ) {82 $this->prepareAndSentPreviewData();83 return;84 }85 86 if( $this->action == "importData" ) {87 // CSRF protection88 if( !isPostRequest() )89 return false;90 91 $this->runImportAndSendResultReport();92 return;93 }94 95 if( $this->action == "downloadReport" )96 {97 $this->downloadReport();98 return;99 }100 101 if( $this->action == "downloadUnprocessed" )102 {103 $this->downloadUnprocessed();104 return;105 }106 107 $this->doCommonAssignments();108 109 $this->addButtonHandlers();110 $this->addCommonJs();111 112 $this->displayImportPage();113 }114 115 /**116 * Send the preview data to client117 */118 protected function prepareAndSentPreviewData()119 {120 $response = array();121 122 // prepare the temp import file name123 $rnrTempFileName = $this->getImportTempFileName();124 // prepare the temp file path125 $rnrTempImportFilePath = getabspath("templates_c/".$rnrTempFileName.".csv");126 127 if( $this->importType == "text" ) {128 // save file in a temporary directory129 runner_save_textfile( $rnrTempImportFilePath, $this->importText );130 131 $response["previewData"] = $this->getPreviewDataFromText( $this->importText );132 } else {133 $ext = getImportFileExtension( "importFile".$this->id );134 $isExcel = strtoupper( $ext ) == "XLS" || strtoupper( $ext ) == "XLSX";135 136 if( $isExcel )137 $rnrTempImportFilePath = getabspath("templates_c/".$rnrTempFileName.".".$ext);138 139 $importFileData = getImportFileData( "importFile".$this->id );140 // save file in a temporary directory141 upload_File( $importFileData, $rnrTempImportFilePath );142 143 if( $isExcel ) {144 $response["previewData"] = $this->getPreviewDataFromExcel( $rnrTempImportFilePath );145 } else {146 $importText = CSVFileToText( $rnrTempImportFilePath, true );147 $response["previewData"] = $this->getPreviewDataFromText( $importText );148 }149 }150 151 // keep the temporary path in the SESSION variable152 $_SESSION[ $this->sessionPrefix ."_tempImportFilePath" ] = $rnrTempImportFilePath;153 154 $returnJSON = printJSON( $response, $this->useXHR );155 156 if( $returnJSON != false )157 echo $returnJSON;158 else159 echo "The file you're trying to import cannot be parsed";160 161 exit();162 }163 164 /**165 * Import the data and send a report166 */167 protected function runImportAndSendResultReport()168 {169 if( $this->eventsObject->exists("BeforeImport") )170 {171 $message = "";172 if( $this->eventsObject->BeforeImport($this, $message) === false )173 {174 echo printJSON( array( "failed" => true, "message" => $message ) );175 exit();176 }177 }178 179 $rnrTempImportFilePath = $_SESSION[ $this->sessionPrefix ."_tempImportFilePath" ];180 $resultData = $this->ImportFromFile( $rnrTempImportFilePath, $this->importData );181 // remove a temporary import file182 runner_delete_file( $rnrTempImportFilePath );183 184 if( $this->eventsObject->exists("AfterImport") )185 $this->eventsObject->AfterImport( $resultData["totalRecordsNumber"], $resultData["unprocessedRecordsNumber"], $this);186 187 // keep all necessary data in SESSION variables188 $_SESSION[ $this->sessionPrefix ."_tempImportLogFilePath" ] = $resultData["logFilePath"];189 if( $resultData["unprocessedRecordsNumber"] )190 $_SESSION[ $this->sessionPrefix ."_tempDataFilePath" ] = $resultData["unprocessedFilePath"];191 192 echo printJSON( $resultData );193 exit();194 }195 196 /**197 *198 */199 protected function downloadReport()200 {201 $logFilePath = $_SESSION[ $this->sessionPrefix ."_tempImportLogFilePath" ];202 if( !myfile_exists( $logFilePath ) )203 {204 $data = array( "success" => false );205 echo printJSON( $data );206 exit();207 }208 209 header("Content-Type: text/plain");210 header("Content-Disposition: attachment;Filename=importLog.txt");211 header("Cache-Control: private");212 213 printfile( $logFilePath );214 exit();215 }216 217 /**218 *219 */220 protected function downloadUnprocessed()221 {222 $dataFilePath = $_SESSION[ $this->sessionPrefix ."_tempDataFilePath" ];223 if( !myfile_exists( $dataFilePath ) )224 {225 $data = array( "success" => false );226 echo printJSON( $data );227 exit();228 }229 230 header("Content-Type: application/csv");231 header("Content-Disposition: attachment;Filename=unpocessedData.csv");232 233 printfile( $dataFilePath );234 exit();235 }236 237 /**238 * Assign 'body' element239 */240 public function doCommonAssignments()241 {242 243 // assign body begin244 $this->body["begin"] = GetBaseScriptsForPage(false);245 // assign body end246 $this->body["end"] = XTempl::create_method_assignment( "assignBodyEnd", $this);247 248 $this->xt->assignbyref("body", $this->body);249 }250 251 /**252 * Clear session kyes253 * @intellisense254 */255 public function clearSessionKeys()256 {257 parent::clearSessionKeys();258 259 if( !count($_POST) && !count($_GET) )260 {261 unset( $_SESSION[ $this->sessionPrefix ."_tempImportFilePath" ] );262 unset( $_SESSION[ $this->sessionPrefix ."_tempImportLogFilePath" ] );263 unset( $_SESSION[ $this->sessionPrefix ."_tempDataFilePath" ] );264 }265 }266 267 268 /**269 * A wrapper for the import function getPreviewDataFromExcel270 * @param String filePath271 * @param String ext272 * @return Array273 */274 protected function getPreviewDataFromExcel( $filePath )275 {276 $fileHandle = openImportExcelFile( $filePath );277 278 $headerFieldsFromExcel = getImportExcelFields( $fileHandle );279 $fieldsData = $this->getCorrespondingImportFieldsData( $headerFieldsFromExcel );280 281 $previewData = getPreviewDataFromExcel( $fileHandle, $fieldsData );282 $previewData["fieldsData"] = $fieldsData;283 284 return $previewData;285 }286 287 /**288 * Get preview data for an importing text289 * @param String importText290 * @return Array291 */292 public function getPreviewDataFromText( $importText ) {293 $lines = $this->removeEmptyLines( ImportPage::CSVTextToLines( $importText ) );294 if( !$lines )295 return array();296 297 // find delimiter basing on first two lines298 $delimiter = $this->getCSVDelimiter( array_slice( $lines, 0, 2 ) );299 300 $headerFieldsFromCSV = parseCSVLineNew( $lines[0], $delimiter );301 $fieldsData = $this->getCorrespondingImportFieldsData( $headerFieldsFromCSV );302 303 $previewData = array();304 $previewData["CSVPreview"] = true;305 $previewData["delimiter"] = $delimiter;306 $previewData["fieldsData"] = $fieldsData;307 308 // first 100 lines for preview309 $previewData["CSVlinesData"] = array_slice( $lines, 0, 100 );310 311 // always show Date Format box.312 $dateFormat = $this->geDateFormat( $lines, $delimiter, $fieldsData );313 $previewData["dateFormat"] = $this->getImportDateFormat( $dateFormat );314 315 return $previewData;316 }317 318 /**319 *320 */321 protected function geDateFormat( $lines, $delimiter, $fieldsData ) {322 $dateFormat = "";323 324 foreach( $lines as $line ) {325 $elems = parseCSVLineNew( $line, $delimiter );326 foreach( $elems as $idx => $elem ) {327 if( isset($fieldsData[ $idx ]) && $fieldsData[ $idx ]["dateTimeType"] ) {328 $dateFormat = ImportPage::extractDateFormat( $elem );329 if( strlen( $dateFormat ) ) {330 return $dateFormat;331 }332 }333 }334 }335 336 return $dateFormat;337 }338 339 /**340 * Remove elementes containing empty lines forms the lines array341 * @param Array lines342 * @return Array343 */344 protected function removeEmptyLines( $lines )345 {346 $resultLines = array();347 348 foreach( $lines as $line )349 {350 if( strlen( trim($line) ) )351 $resultLines[] = $line;352 }353 354 return $resultLines;355 }356 357 /**358 * Extract a date format form the dateTime string //#9684359 * @param String dateString360 * @return String361 */362 public static function extractDateFormat($dateString)363 {364 global $locale_info;365 366 $dateComponents = parsenumbers( $dateString );367 if( count($dateComponents) < 3 )368 return "";369 370 $dateSeparator = $locale_info["LOCALE_SDATE"];371 $format = "";372 373 if( $dateComponents[0] > 31 && ImportPage::testMonth($dateComponents[1]) && $dateComponents[2] >= 12)374 {375 $year = $dateComponents[0];376 $format = "Y".$dateSeparator."M".$dateSeparator."D";377 }378 379 if( $dateComponents[0] >= 12 && ImportPage::testMonth($dateComponents[1]) && $dateComponents[2] > 31 )380 {381 $year = $dateComponents[3];382 $format = "D".$dateSeparator."M".$dateSeparator."Y";383 }384 385 if( ImportPage::testMonth($dateComponents[0]) && $dateComponents[1] >= 12 && $dateComponents[2] > 31 )386 {387 $year = $dateComponents[3];388 $format = "M".$dateSeparator."D".$dateSeparator."Y";389 }390 391 if( $format )392 $format = str_replace("Y", $year < 100 ? "YY" : "YYYY", $format);393 394 return $format;395 }396 397 /**398 * Check if the number passed could be a month component of a date399 * @param Number number400 * @return Boolean401 */402 public static function testMonth( $number )403 {404 $match = array();405 $matched = preg_match('/0[1-9]|1[0-2]/', $number, $match);406 407 // add [1-9]| to pattern ??408 if( $matched && count($match) || 1 <= $number && $number <= 12 )409 return true;410 411 return false;412 }413 414 /**415 * Get the date format string416 * @param String dateFormat417 * @return String418 */419 public static function getRefinedDateFormat( $dateFormat ) {420 $refinedFormat = "";421 422 $dateFormat = strtolower( $dateFormat );423 for( $i = 0; $i < strlen($dateFormat); $i++ ) {424 $letter = $dateFormat[$i];425 if( ( $letter == "d" || $letter == "m" || $letter == "y" ) && strpos($refinedFormat, $letter) === false )426 $refinedFormat.= $letter;427 }428 429 return $refinedFormat;430 }431 432 /**433 * Detect a delimiter value by the first two not empty file (or text) lines434 * @param Array firstTwoLinesData An array containing no more then first two file (or text) lines435 * @return String436 */437 protected function getCSVDelimiter( $firstTwoLines )438 {439 // the most possible delimiters values440 $delimiters = array(",", ";", "\t", " ");441 $delimitersData = array();442 443 foreach($delimiters as $delim)444 {445 $delimitersData[ $delim ] = array();446 447 foreach($firstTwoLines as $idx => $line)448 {449 $elemsNumber = count( parseCSVLineNew( $line, $delim ) );450 if( $elemsNumber <= 1 )451 break;452 453 $delimitersData[ $delim ][ $idx ] = $elemsNumber;454 }455 }456 457 // the default delimiter value458 $delimiter = ",";459 $maxNumOfElems = 1;460 461 foreach($delimitersData as $delim => $data)462 {463 if( !$data )464 continue;465 466 if( (count($firstTwoLines) == 1 || count($firstTwoLines) == 2 && $data[0] == $data[1]) && $data[0] > $maxNumOfElems )467 {468 $maxNumOfElems = $data[0];469 $delimiter = $delim;470 }471 }472 473 return $delimiter;474 }475 476 /**477 * Check if header fields correspond to any import dateTime field478 * @param Array479 * @return Boolean480 */481 static function hasDateTimeFields($fieldsData)482 {483 // always show Date Format box.484 return true;485 }486 487 /**488 * Get the field names array for Import489 * @param Array headerFields490 * @return Array491 */492 public function getCorrespondingImportFieldsData( $headerFields )493 {494 $importFields = $this->pSet->getImportFields();495 $tempFieldArray = array();496 $tempLabelArray = array();497 $tempGNamesArray = array();498 499 foreach($headerFields as $idx => $headerField)500 {501 $lowerHeaderField = strtolower($headerField);502 foreach($importFields as $importField)503 {504 $dateTimeType = IsDateFieldType( $this->pSet->getFieldType($importField) );505 506 if( $lowerHeaderField == strtolower($importField) )507 {508 $tempFieldArray[ $idx ]["fName"] = $importField;509 $tempFieldArray[ $idx ]["dateTimeType"] = $dateTimeType;510 }511 512 $trimHeaderField = trim($lowerHeaderField);513 $gName = GoodFieldName($importField);514 if( $trimHeaderField == strtolower(trim( $gName )) )515 {516 $tempGNamesArray[ $idx ]["fName"] = $importField;517 $tempGNamesArray[ $idx ]["dateTimeType"] = $dateTimeType;518 }519 520 $label = GetFieldLabel(GoodFieldName($this->tName), GoodFieldName($importField));521 if( $trimHeaderField == strtolower(trim( $label )) )522 {523 $tempLabelArray[ $idx ]["fName"] = $importField;524 $tempLabelArray[ $idx ]["dateTimeType"] = $dateTimeType;525 }526 }527 }528 529 if( !$tempFieldArray && !$tempGNamesArray && !$tempLabelArray )530 return array();531 532 if( count($tempFieldArray) >= count($tempLabelArray) && count($tempFieldArray) >= count($tempGNamesArray) )533 return $tempFieldArray;534 535 if( count($tempLabelArray) >= count($tempFieldArray) && count($tempLabelArray) >= count($tempGNamesArray) )536 return $tempLabelArray;537 538 return $tempGNamesArray;539 }540 541 /**542 * Import data form a file to db543 * @param String filePath544 * @param Array &importData545 * @return Array546 */547 public function ImportFromFile( $filePath, &$importData )548 {549 $fieldsData = $this->refineImportFielsData( $importData["importFieldsData"] );550 $dateFormat = ImportPage::getRefinedDateFormat( $this->getImportDateFormat( $importData["dateFormat"] ) );551 $this->currentDateFormat = $dateFormat;552 553 $headersLineOption = null;554 $skipLinesOption = null;555 if ( $importData["useHeadersLineOption"] ) {556 $headersLineOption = array();557 $headersLineOption["number"] = $importData["headersLineNumber"];558 }559 560 if ( $importData["useSkipLinesOption"] ) {561 $skipLinesOption = array();562 $skipLinesOption["amount"] = $importData["skipLinesAmount"];563 }564 565 if( $importData["CSV"] )566 $metaData = $this->importFromCSV( $filePath, $fieldsData, $importData["delimiter"], $headersLineOption, $skipLinesOption);567 else568 $metaData = $this->importFromExcel( $filePath, $fieldsData, $headersLineOption, $skipLinesOption );569 570 571 $resultData = array();572 $resultData["reportText"] = $this->getBasicReportText( $metaData["totalRecords"], $metaData["addedRecords"], $metaData["updatedRecords"] );573 $resultData["unprocessedRecordsNumber"] = count( $metaData["errorMessages"] );574 $resultData["totalRecordsNumber"] = $metaData["totalRecords"] - $resultData["unprocessedRecordsNumber"];575 576 // prepare a report file577 $reportFileText = $this->getBasicReportText( $metaData["totalRecords"], $metaData["addedRecords"], $metaData["updatedRecords"], false, "\r\n", $metaData["errorMessages"], $metaData["unprocessedData"] );578 $logFilePath = getabspath("templates_c/".$this->getImportLogFileName().".txt");579 runner_save_file( $logFilePath, $reportFileText );580 $resultData["logFilePath"] = $logFilePath;581 582 if( count( $metaData["unprocessedData"] ) )583 {584 // prepare an unprocessed data log585 $unprocFilePath = getabspath("templates_c/".$this->getUnprocessedDataFileName().".csv");586 $unprocContent = $this->getUnprocessedDataContent( $metaData["unprocessedData"] );587 runner_save_file( $unprocFilePath, $unprocContent );588 $resultData["unprocessedFilePath"] = $unprocFilePath;589 }590 591 return $resultData;592 }593 594 /**595 * @param String596 * @return String dateFormat597 */598 protected function getImportDateFormat( $dateFormat ) {599 global $locale_info;600 return !strlen($dateFormat) ? $locale_info["LOCALE_SSHORTDATE"] : $dateFormat;601 }602 603 /**604 * Refine user import fields data605 * @param Array importFiledsData606 * @return Array607 */608 protected function refineImportFielsData( $importFiledsData )609 {610 $fieldsData = array();611 foreach($importFiledsData as $idx => $fData)612 {613 $fName = $fData["fName"];614 if( $fName )615 $fieldsData[ $idx ] = array( "fName" => $fName, "type" => $this->pSet->getFieldType($fName) );616 }617 618 return $fieldsData;619 }620 621 /**622 *623 */624 protected function importFromCSV( $filePath, $fieldsData, $delimiter, $headersLineOption, $skipLinesOption) {625 $text = CSVFileToText( $filePath, false );626 $lines = ImportPage::CSVTextToLines( $text );627 628 629 $autoinc = $this->hasAutoincImportFields( $fieldsData );630 631 $errorMessages = array();632 $unprocessedData = array();633 $addedRecords = 0;634 $updatedRecords = 0;635 $totalRecords = 0;636 637 if ( $headersLineOption != null ) {638 $idx = $headersLineOption["number"] - 1;639 unset($lines[$idx]);640 }641 642 if ( $skipLinesOption != null) {643 $linesCount = $skipLinesOption["amount"];644 for ($i = 0; $i < $linesCount; $i++) {645 unset($lines[$i]);646 }647 }648 649 foreach( $lines as $line ) {650 $elems = parseCSVLineNew( $line, $delimiter );651 652 $fieldsValuesData = array();653 654 foreach( $elems as $idx => $elem )655 {656 if( !isset( $fieldsData[ $idx ] ) )657 continue;658 659 $importFieldName = $fieldsData[ $idx ]["fName"];660 $fType = $fieldsData[ $idx ]["type"];661 662 $fieldsValuesData[ $importFieldName ] = $elem;663 }664 665 $this->importRecord( $fieldsValuesData, $autoinc, $addedRecords, $updatedRecords, $errorMessages, $unprocessedData );666 $totalRecords = $totalRecords + 1;667 }668 669 $metaData = array();670 $metaData["totalRecords"] = $totalRecords;671 $metaData["addedRecords"] = $addedRecords;672 $metaData["updatedRecords"] = $updatedRecords;673 $metaData["errorMessages"] = $errorMessages;674 $metaData["unprocessedData"] = $unprocessedData;675 676 return $metaData;677 }678 679 680 /**681 * Import data form an Excel file682 * @param String filePath683 * @param Array fieldsData684 * @param Boolean useFirstLine685 * @param String dateFormat686 * @return Array687 */688 protected function importFromExcel( $filePath, $fieldsData, $headersLineOption, $skipLinesOption) {689 $fileHandle = openImportExcelFile( $filePath );690 $autoinc = $this->hasAutoincImportFields( $fieldsData );691 692 return ImportDataFromExcel( $fileHandle, $fieldsData, $this, $autoinc, $headersLineOption, $skipLinesOption );693 }694 695 /**696 * Check if there is an auto-incremented field among the import fields697 * @param Array fieldsData698 * @return Boolean699 */700 protected function hasAutoincImportFields( $fieldsData )701 {702 foreach( $fieldsData as $f )703 {704 if( $this->pSet->isAutoincField( $f[ "fName" ] ) )705 return true;706 }707 708 return false;709 }710 711 712 713 /**714 * Prepare fields' values of numeric and time types for db715 * The fields of other types have been already db-prepared716 * @param Array fieldsValuesData717 * @return Array718 */719 protected function prepareFiledsValuesData( $fieldsValuesData )720 {721 global $locale_info;722 723 $refinedFieldsValuesData = array();724 725 $this->setUpdatedLatLng( $fieldsValuesData );726 727 foreach($fieldsValuesData as $field => $val)728 {729 $type = $this->pSet->getFieldType($field);730 731 if( IsTimeType($type) || $this->pSet->getEditFormat( $field ) == EDIT_FORMAT_TIME )732 {733 $value = prepare_for_db( $field, $val, "time", "", $this->tName );734 735 if ( strlen($value) > 0 )736 $refinedFieldsValuesData[ $field ] = $value;737 else738 $refinedFieldsValuesData[ $field ] = NULL;739 740 continue;741 }742 if( IsDateFieldType($type) )743 {744 if( !dateInDbFormat( $val ) )745 $value = localdatetime2db($val, $this->currentDateFormat);746 else747 $value = $val;748 749 if ( strlen($value) > 0 )750 $refinedFieldsValuesData[ $field ] = $value;751 else {752 $refinedFieldsValuesData[ $field ] = NULL;753 }754 755 continue;756 }757 758 if( !IsNumberType($type) || is_numeric( $val ) ) {759 $refinedFieldsValuesData[ $field ] = $val;760 continue;761 }762 763 $value = str_replace(",", ".", (string)$val);764 765 if( strlen($value) > 0 )766 {767 if( strpos($value, $locale_info["LOCALE_SCURRENCY"]) !== FALSE )768 {769 // try to process the currency format770 $value = str_replace( array($locale_info["LOCALE_SCURRENCY"], " "), array("", ""), $value );771 772 $matches = array();773 if( preg_match('/^\((.*)\)$/', $value, $matches) )774 $value = -1 * $matches[1];775 }776 777 if (is_numeric($value))778 $refinedFieldsValuesData[$field] = (float)$value;779 else780 $refinedFieldsValuesData[$field] = 0;781 }782 else783 $refinedFieldsValuesData[ $field ] = NULL;784 }785 786 return $refinedFieldsValuesData;787 }788 789 /**790 *791 */792 protected function callBeforeInsert( &$rawvalues, &$fieldsValuesData, &$errorMessage ) {793 if( !$this->eventsObject->exists("BeforeInsert") )794 return true;795 796 // fire event797 if( $this->eventsObject->BeforeInsert($rawvalues, $fieldsValuesData, $this, $errorMessage) === false )798 return false;799 800 return true;801 }802 803 804 /**805 * Insert an imported record to the database806 * @param Array values Import data as array( <fieldName> => <fieldValue>, ... );807 * @param Boolean identiyInsertOff The flag indicating if there is any autoincremented import field808 * @param &Number addedRecords809 * @param &Number updatedRecords810 * @param &Array errorMmessages811 * @param &Array unprocessedData812 */813 public function importRecord( $values, $identiyInsertOff, &$addedRecords, &$updatedRecords, &$errorMessages, &$unprocessedData )814 {815 $rawValues = $values;816 $values = $this->prepareFiledsValuesData( $values );817 $errorMessage = "";818 819 if( $this->callBeforeInsert( $rawValues, $values, $errorMessage ) )820 $failed = !$this->_importRecord( $values, $identiyInsertOff, $addedRecords, $updatedRecords, $errorMessage );821 else822 $failed = true;823 824 if( $failed ) {825 // report error826 if( !$unprocessedData ) {827 $fieldNames = array_keys( $values );828 $unprocessedData[] = $this->getImportFieldsLogCSVLine( $fieldNames );829 }830 // nothing to update831 $unprocessedData[] = $this->parseValuesDataInLogCSVLine( $rawValues );832 $errorMessages[] = $errorMessage;833 }834 }835 836 /**837 * @return Boolean838 */839 protected function _importRecord( $values, $identiyInsertOff, &$addedRecords, &$updatedRecords, &$errorMessage ) {840 $dc = new DsCommand();841 $dc->identiyInsertOff = $identiyInsertOff;842 $dc->values = &$values;843 844 $insertResult = $this->dataSource->insertSingle( $dc );845 if( $insertResult !== false ) {846 // successfully inserted847 $addedRecords = $addedRecords + 1;848 849 if( $this->audit )850 $this->audit->LogAdd( $this->tName, $values, $this->getRecordKeys( $insertResult ) );851 852 return true;853 }854 855 $errorMessage = $this->dataSource->lastError();856 857 $_keys = $this->getRecordKeys( $values );858 // don't update if we don't have keys859 if( !$_keys )860 return false;861 862 // prepare for updating attempt863 $dc = new DsCommand();864 $dc->keys = $_keys;865 866 $rs = $this->dataSource->getSingle( $dc );867 $recordData = null;868 if( $rs ) {869 $fetchedArray = $rs->fetchAssoc();870 $recordData = $this->cipherer->DecryptFetchedArray( $fetchedArray );871 }872 if( !$recordData ) {873 // nothing to update874 return false;875 }876 877 $dc = new DsCommand();878 $dc->identiyInsertOff = $identiyInsertOff;879 $dc->keys = $_keys;880 $dc->filter = Security::SelectCondition( "E", $this->pSet );881 882 $updateValues = array();883 foreach( $values as $f => $v ) {884 if( !isset( $_keys[ $f ] ) ) {885 $updateValues[ $f ] = $v;886 }887 }888 $dc->values = $updateValues;889 890 $updateResult = $this->dataSource->updateSingle( $dc );891 if( $updateResult ) {892 // successfully updated893 $updatedRecords = $updatedRecords + 1;894 895 if( $this->audit )896 $this->audit->LogEdit( $this->tName, $values, $recordData , $_keys );897 898 return true;899 }900 901 return false;902 }903 904 905 protected function getRecordKeys( $values ) {906 $keys = array();907 $keyFields = $this->pSet->getTableKeys();908 909 foreach( $keyFields as $kf ) {910 if( array_key_exists( $kf, $values ) )911 $keys[ $kf ] = $values[ $kf ];912 }913 914 if( count( $keys ) != count( $keyFields ) )915 return array();916 917 return $keys;918 }919 920 921 /**922 * Get a data line for an unprocessed data log923 * @param Array fieldsValuesData924 * @return String925 */926 protected function parseValuesDataInLogCSVLine( $fieldsValuesData )927 {928 $values = array();929 foreach($fieldsValuesData as $fName => $value)930 {931 $fType = $this->pSet->getFieldType($fName);932 if( !IsBinaryType($fType) )933 $values[] = '"'.str_replace('"', '""', $value).'"';934 }935 936 return implode(",", $values);937 }938 939 /**940 * Get a headers line for an unprocessed data log941 * @param Array importFields942 * @return String943 */944 protected function getImportFieldsLogCSVLine( $importFields )945 {946 $headerFields = array();947 foreach( $importFields as $fName )948 {949 $fType = $this->pSet->getFieldType($fName);950 if( !IsBinaryType($fType) )951 $headerFields[] = '"'.str_replace('"', '""', $fName).'"';952 }953 954 return implode(",", $headerFields);955 }956 957 /**958 * Get content for an unprocessed data log959 * @param Array unprocessedData960 * @return String961 */962 protected function getUnprocessedDataContent( $unprocessedData )963 {964 global $useUTF8;965 966 $content = $headerLine.implode( "\r\n", $unprocessedData );967 return $useUTF8 ? "\xEF\xBB\xBF".$content : $content;968 }969 970 /**971 * Get report text972 * @param Number totalRecords973 * @param Number addedRecords974 * @param Number updatedRecords975 * @param Boolean isNotLogFile976 * @rturn String977 */978 protected function getBasicReportText( $totalRecords, $addedRecords, $updatedRecords,979 $isNotLogFile = true, $lineBreak = "<br>", $errorMessages = array(), $unprocessedData = array() )980 {981 $importedReords = $addedRecords + $updatedRecords;982 $notImportedRecords = $totalRecords - $importedReords;983 $boldBegin = "";984 $boldEnd = "";985 $reportText = "";986 if( $isNotLogFile )987 {988 $boldBegin = "<b>";989 $boldEnd = "</b>";990 }991 else992 {993 $reportText .= "Import into"." ".$this->strOriginalTableName.$lineBreak.994 str_format_datetime( db2time( now() ) ) .$lineBreak.$lineBreak;995 }996 997 $reportText .= mysprintf("%s out of %s records processed successfully.", array($boldBegin.$importedReords.$boldEnd, $boldBegin.$totalRecords.$boldEnd))998 . $lineBreak999 . mysprintf("%s records added.", array($boldBegin.$addedRecords.$boldEnd)) .$lineBreak1000 . mysprintf("%s records updated.", array($boldBegin.$updatedRecords.$boldEnd)) .$lineBreak;1001 1002 if( $notImportedRecords )1003 $reportText.= mysprintf("%s records processed with errors", array($boldBegin.$notImportedRecords.$boldEnd));1004 1005 if( $notImportedRecords && count($errorMessages) )1006 {1007 $reportText .= ":";1008 for( $i = 0; $i < count($errorMessages); $i++ )1009 {1010 if( $isNotLogFile )1011 {1012 $reportText.= $lineBreak.$errorMessages[ $i ];1013 }1014 else1015 {1016 $reportText.= $lineBreak.$lineBreak.$errorMessages[ $i ].$lineBreak.$unprocessedData[ $i + 1 ];1017 }1018 }1019 }1020 return $reportText;1021 }1022 1023 /**1024 * Get a temporary importing file name1025 * @return String1026 */1027 public function getImportTempFileName()1028 {1029 return "import".$this->getUniqueFileNameSuffix();1030 }1031 1032 /**1033 * Get a temporary log file name1034 * @return String1035 */1036 public function getImportLogFileName()1037 {1038 return "importLog".$this->getUniqueFileNameSuffix();1039 }1040 1041 /**1042 * Get an unprocessed data CSV file name1043 * @return String1044 */1045 public function getUnprocessedDataFileName()1046 {1047 return "importUnprocessed".$this->getUniqueFileNameSuffix();1048 }1049 1050 /**1051 * Get the unique name suffix containig the date stamp1052 * @return String1053 */1054 protected function getUniqueFileNameSuffix()1055 {1056 $dateMarker = getYMDdate( time() );1057 return $dateMarker."_".$this->tName."_".generatePassword(5);1058 }1059 1060 /**1061 * Remove temp import files older than 3 days1062 * from the 'templates_c' directory1063 */1064 public function removeOldTemporaryFiles()1065 {1066 $this->deleteTemporaryFilesFromDir( "templates_c/" );1067 }1068 1069 public function deleteTemporaryFilesFromDir( $dir )1070 {1071 $tempFilesDirectory = getabspath($dir);1072 $fileNamesList = getFileNamesFromDir( $tempFilesDirectory );1073 $currentTime = strtotime(now());1074 // the word "import" + some letters + the 'Y-m-d' formatted date value + "_" + the table name + "_"1075 // + the unique sequence of length 5 + any extension1076 $tempNamePattern = "/^import.*([\d]{4}-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1])).*_".$this->tName."_.{5}\.\w+/";1077 1078 foreach($fileNamesList as $fileName)1079 {1080 $matches = array();1081 if( preg_match($tempNamePattern, $fileName, $matches) )1082 {1083 $timeFromFileName = strtotime( $matches[1] );1084 if( $timeFromFileName !== FALSE && $currentTime - $timeFromFileName > 259200 )1085 deleteImportTempFile( $tempFilesDirectory.$fileName );1086 }1087 }1088 }1089 1090 /**1091 * @param String text1092 * @return Array1093 */1094 public static function CSVTextToLines( $text ) {1095 $inQuotes = false;1096 1097 $j = 0;1098 $lines = array();1099 1100 //possible values are \r\n or \n1101 $eol = "";1102 1103 for( $i = 0; $i < strlen( $text ); $i++ ) {1104 $char = substr( $text, $i, 1 );1105 $charNext = substr( $text, $i + 1, 1 );1106 1107 if( $char == "\"" ) {1108 if( !$inQuotes )1109 $inQuotes = true;1110 else {1111 if( $charNext == "\"" )1112 {1113 $i++;1114 $lines[ $j ].= $char;1115 }1116 else1117 $inQuotes = false;1118 }1119 }1120 1121 if( !$inQuotes && !$eol ) {1122 //no in quotes1123 if( ord( $char ) == 10 ) {1124 //"\n"1125 $eol = $char;1126 } else if( ord( $char ) == 13 && ord( $charNext ) == 10 ) {1127 //"\r\n"1128 $eol = $char.$charNext;1129 }1130 }1131 1132 1133 if( !$inQuotes && $eol && substr( $text, $i, strlen( $eol ) ) == $eol ) {1134 $j++;1135 $i+= strlen( $eol ) - 1;1136 }1137 else1138 $lines[ $j ].= $char;1139 }1140 1141 return $lines;1142 }1143 1144 /**1145 * Display the import page1146 */1147 protected function displayImportPage()1148 {1149 $templatefile = $this->templatefile;1150 1151 if( $this->eventsObject->exists("BeforeShowImport") )1152 $this->eventsObject->BeforeShowImport($this->xt, $templatefile, $this);1153 1154 $this->display( $templatefile );1155 }1156}1157?>