CoolFace
Apppublic

kenken999/php

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
importpage.php1157 linesDownload Raw Back to classes
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?>