Index: branches/5.2.x/core/units/helpers/csv_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/csv_helper.php	(revision 16566)
+++ branches/5.2.x/core/units/helpers/csv_helper.php	(revision 16567)
@@ -1,379 +1,415 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	kUtil::safeDefine('EXPORT_STEP', 100); // export by 100 items
 	kUtil::safeDefine('IMPORT_STEP', 10);
 
 	class kCSVHelper extends kHelper {
 
 		var $PrefixSpecial;
 		var $grid;
 
 		var $delimiter_mapping = Array(0 => "\t", 1 => ',', 2 => ';', 3 => ' ', 4 => ':');
 		var $enclosure_mapping = Array(0 => '"', 1 => "'");
 		var $separator_mapping = Array(0 => "\n", 1 => "\r\n");
 
 		function ExportStep()
 		{
 			$export_data = $this->Application->RecallVar('export_data');
 			$export_rand = $this->Application->RecallVar('export_rand');
 			$get_rand = $this->Application->GetVar('export_rand');
 
 			/** @var FileHelper $file_helper */
 			$file_helper = $this->Application->recallObject('FileHelper');
 
 			if ( $export_data && $export_rand == $get_rand ) {
 				$export_data = unserialize($export_data);
 				$first_step = false;
 			}
 			else {
 				// first step
 				$export_data = Array ();
 				$export_data['prefix'] = $this->PrefixSpecial;
 				$export_data['grid'] = $this->grid;
 				$export_data['file_name'] = EXPORT_PATH . '/' . $file_helper->ensureUniqueFilename(EXPORT_PATH, 'export_' . $export_data['prefix'] . '.csv');
 				$export_data['step'] = EXPORT_STEP;
 				$export_data['delimiter'] = $this->delimiter_mapping[(int)$this->Application->ConfigValue('CSVExportDelimiter')];
 				$export_data['enclosure'] = $this->enclosure_mapping[(int)$this->Application->ConfigValue('CSVExportEnclosure')];
 				$export_data['record_separator'] = $this->separator_mapping[(int)$this->Application->ConfigValue('CSVExportSeparator')];
 				$export_data['page'] = 1;
 				$export_data['source_encoding'] = strtoupper(CHARSET);
 				$export_data['encoding'] = $this->Application->ConfigValue('CSVExportEncoding') ? false : 'UTF-16LE';
 
 				$this->Application->StoreVar('export_rand', $get_rand);
 
 				$first_step = true;
 			}
 
 			$file = fopen($export_data['file_name'], $first_step ? 'w' : 'a');
 
 			$prefix_elems = preg_split('/\.|_/', $export_data['prefix'], 2);
-			$grids = $this->Application->getUnitOption($prefix_elems[0], 'Grids');
-			$grid_config = $grids[$export_data['grid']]['Fields'];
+			$grid_config = $this->getGridColumns($export_data);
 
 			$list_params = Array ('per_page' => $export_data['step'], 'grid' => $export_data['grid']);
 
 			/** @var kDBList $list */
 			$list = $this->Application->recallObject(rtrim(implode('.', $prefix_elems), '.'), $prefix_elems[0] . '_List', $list_params);
 
 			$list->SetPage($export_data['page']);
 			$list->Query();
 			$list->GoFirst();
 
 			$picker_helper = new kColumnPickerHelper(rtrim(implode('.', $prefix_elems), '.'), $export_data['grid']);
 			$grid_config = $picker_helper->apply($grid_config);
 
 			if ( $first_step ) {
 				// if UTF-16, write Unicode marker
 				if ( $export_data['encoding'] == 'UTF-16LE' ) {
 					fwrite($file, chr(0xFF) . chr(0xFE));
 				}
 
 				// inserting header line
 				$headers = Array ();
 				foreach ($grid_config as $field_name => $field_data) {
 					$use_phrases = array_key_exists('use_phrases', $field_data) ? $field_data['use_phrases'] : true;
 					$field_title = isset($field_data['title']) ? $field_data['title'] : 'column:la_fld_' . $field_name;
 					$header = $use_phrases ? $this->Application->Phrase($field_title) : $field_title;
 					array_push($headers, $header);
 				}
 
 				$csv_line = kUtil::getcsvline($headers, $export_data['delimiter'], $export_data['enclosure'], $export_data['record_separator']);
 				if ( $export_data['encoding'] ) {
 					$csv_line = mb_convert_encoding($csv_line, $export_data['encoding'], $export_data['source_encoding']);
 				}
 				fwrite($file, $csv_line);
 			}
 
 			while (!$list->EOL()) {
 				$data = Array ();
 				foreach ($grid_config as $field_name => $field_data) {
 					if ( isset($field_data['export_field']) ) {
 						$field_name = $field_data['export_field'];
 					}
 					$value = $list->GetField($field_name, isset($field_data['format']) ? $field_data['format'] : null);
 					$value = str_replace("\r\n", "\n", $value);
 					$value = str_replace("\r", "\n", $value);
 					array_push($data, $value);
 				}
 
 				if ( $export_data['encoding'] == 'UTF-16LE' ) {
 					fwrite($file, chr(0xFF) . chr(0xFE));
 				}
 
 				$csv_line = kUtil::getcsvline($data, $export_data['delimiter'], $export_data['enclosure'], $export_data['record_separator']);
 				if ( $export_data['encoding'] ) {
 					$csv_line = mb_convert_encoding($csv_line, $export_data['encoding'], $export_data['source_encoding']);
 				}
 				fwrite($file, $csv_line);
 
 				$list->GoNext();
 			}
 
 			$records_processed = $export_data['page'] * $export_data['step'];
 			$percent_complete = min($records_processed / $list->GetRecordsCount() * 100, 100);
 
 			fclose($file);
 
 			if ( $records_processed >= $list->GetRecordsCount() ) {
 				$this->Application->StoreVar('export_data', serialize($export_data));
 				$this->Application->Redirect($this->Application->GetVar('finish_template'));
 			}
 
 			echo $percent_complete;
 
 			$export_data['page']++;
 			$this->Application->StoreVar('export_data', serialize($export_data));
 		}
 
 		function ExportData($name)
 		{
 			$export_data = unserialize($this->Application->RecallVar('export_data'));
 			return isset($export_data[$name]) ? $export_data[$name] : false;
 		}
 
 		/**
 		 * Returns prefix from request or from stored import/export data
 		 *
 		 * @param bool $is_import
 		 * @return string
 		 */
 		public function getPrefix($is_import = false)
 		{
 			$prefix = $this->Application->GetVar('PrefixSpecial');
 
 			if ( !$prefix ) {
 				return $is_import ? $this->ImportData('prefix') : $this->ExportData('prefix');
 			}
 
 			return $prefix;
 		}
 
 		function GetCSV()
 		{
 			kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
 
 			$export_data = unserialize($this->Application->RecallVar('export_data'));
 			$filename = preg_replace('/(.*)\.csv$/', '\1', basename($export_data['file_name'])) . '.csv';
 
 			$this->Application->setContentType('text/csv');
 			header('Content-Disposition: attachment; filename="' . $filename . '"');
 			readfile($export_data['file_name']);
 			die();
 		}
 
 		function ImportStart($filename)
 		{
 			if(!file_exists($filename) || !is_file($filename)) return 'cant_open_file';
 
 			$import_data = Array();
 			$import_data['source_encoding'] = strtoupper(CHARSET);
 			$import_data['encoding'] = $this->Application->ConfigValue('CSVExportEncoding') ? false : 'UTF-16LE';
 			$import_data['errors'] = '';
 
 			// convert file in case of UTF-16LE
 			if($import_data['source_encoding'] != $import_data['encoding']) {
 				copy($filename, $filename.'.orginal');
 				$file_content = file_get_contents($filename);
 				$file = fopen($filename, 'w');
 				fwrite($file, mb_convert_encoding(str_replace(chr(0xFF).chr(0xFE), '', $file_content), $import_data['source_encoding'], $import_data['encoding']));
 				fclose($file);
 
 			}
 
 			$import_data['prefix'] = $this->PrefixSpecial;
 			$import_data['grid'] = $this->grid;
 			$import_data['file'] = $filename;
 			$import_data['total_lines'] = count(file($filename));
 			if(!$import_data['total_lines']) $import_data['total_lines'] = 1;
 			unset($file_content);
 			$import_data['lines_processed'] = 0;
 			$import_data['delimiter'] = $this->delimiter_mapping[(int)$this->Application->ConfigValue('CSVExportDelimiter')];
 			$import_data['enclosure'] = $this->enclosure_mapping[(int)$this->Application->ConfigValue('CSVExportEnclosure')];
 			$import_data['step'] = IMPORT_STEP;
 			$import_data['not_imported_lines'] = '';
 			$import_data['added'] = 0;
 			$import_data['updated'] = 0;
 
 			$file = fopen($filename, 'r');
 			// getting first line for headers
 			$headers = fgetcsv($file, 8192, $import_data['delimiter'], $import_data['enclosure']);
 			fclose($file);
 
 			$prefix_elems = preg_split('/\.|_/', $import_data['prefix'], 2);
-			$grids = $this->Application->getUnitOption($prefix_elems[0], 'Grids');
-			$grid_config = $grids[ $import_data['grid'] ]['Fields'];
+			$grid_config = $this->getGridColumns($import_data);
 
 			$field_list = Array();
 			foreach($grid_config as $field_name => $field_data) {
 				if(isset($field_data['export_field'])) {
 					$field_name = $field_data['export_field'];
 				}
 				$field_title = isset($field_data['title']) ? $field_data['title'] : 'column:la_fld_' . $field_name;
 				$field_label = $this->Application->Phrase($field_title);
 				$field_pos = array_search($field_label, $headers);
 				if($field_pos !== false) {
 					$field_list[$field_pos] = $field_name;
 				}
 			}
 
 			if(!count($field_list)) return 'no_matching_columns';
 			$import_data['field_list'] = $field_list;
 
 			// getting key list
 			$field_positions = Array();
 			$config_key_list = $this->Application->getUnitOption($prefix_elems[0], 'ImportKeys');
 			if(!$config_key_list) $config_key_list = Array();
 			array_unshift($config_key_list, Array($this->Application->getUnitOption($prefix_elems[0], 'IDField')));
 
 			$key_list = Array();
 			foreach($config_key_list as $arr_key => $import_key) {
 				$key_list[$arr_key] = is_array($import_key) ? $import_key : Array($import_key);
 
 				foreach($key_list[$arr_key] as $key_field) {
 					$field_positions[$key_field] = array_search($key_field, $import_data['field_list']);
 					if($field_positions[$key_field] === false) {
 						// no such key field combination in imported file
 						unset($key_list[$arr_key]);
 						break;
 					}
 				}
 			}
 			$import_data['key_list'] = $key_list;
 			$import_data['field_positions'] = $field_positions;
 
 			$this->Application->StoreVar('import_data', serialize($import_data));
 			return true;
 		}
 
 		function ImportStep()
 		{
 			$import_data = unserialize($this->Application->RecallVar('import_data'));
 			$prefix_elems = preg_split('/\.|_/', $import_data['prefix'], 2);
 
 			/** @var kDBItem $object */
 			$object = $this->Application->recallObject($prefix_elems[0].'.-csvimport', $prefix_elems[0], Array('skip_autoload' => true, 'populate_ml_fields' => true));
 
 			$file = fopen($import_data['file'], 'r');
 			$eof = false;
 			// skipping lines that has been already imported
 			for($i = 0; $i < $import_data['lines_processed'] + 1; $i++) {
 				if(feof($file)) break;
 				fgets($file, 8192);
 			}
 
 			$import_event = new kEvent($prefix_elems[0].'.-csvimport:OnBeforeCSVLineImport');
 
 			for($i = 0; $i < $import_data['step']; $i++) {
 				if(feof($file)) break;
 				$data = fgetcsv($file, 8192, $import_data['delimiter'], $import_data['enclosure']);
 				if(!$data) continue;
 
 				$object->Clear();
 				$action = 'Create';
 
 				// 1. trying to load object by keys
 				foreach($import_data['key_list'] as $key) {
 					$fail = false;
 					$key_array = Array();
 					foreach($key as $key_field) {
 						if(!isset($data[ $import_data['field_positions'][$key_field] ])) {
 							$fail = true;
 							break;
 						}
 						$key_array[$key_field] = $data[ $import_data['field_positions'][$key_field] ];
 					}
 					if($fail) continue;
 					if($object->Load($key_array)) {
 						$action = 'Update';
 						break;
 					}
 				}
 
-				// 2. set object fields
-				foreach($import_data['field_list'] as $position => $field_name) {
-					if(isset($data[$position])) {
+				// 2. set object fields.
+				$grid_config = $this->getGridColumns($import_data);
+
+				foreach ( $import_data['field_list'] as $position => $field_name ) {
+					if ( isset($data[$position]) ) {
+						$formatter_class = $object->GetFieldOption($field_name, 'formatter');
+
+						if ( $formatter_class !== false ) {
+							$formatter = $this->Application->recallObject($formatter_class);
+
+							if ( $formatter instanceof kDateFormatter ) {
+								if ( isset($grid_config[$field_name]['format']) ) {
+									$format = $grid_config[$field_name]['format'];
+								}
+								else {
+									$format = $object->GetFieldOption($field_name, 'format');
+								}
+
+								// Use export format during import.
+								$object->SetFieldOption($field_name, 'input_format', $format);
+
+								// Read date/time from single column.
+								$object->SetDBField($field_name . '_combined', 1);
+							}
+						}
+
 						$object->SetField($field_name, $data[$position]);
 					}
 				}
 
 				// 3. validate item and run event
 				$status = $object->Validate();
 				$import_event->status = $status ? kEvent::erSUCCESS : kEvent::erFAIL;
 				$this->Application->HandleEvent($import_event);
 
 				if($import_event->status == kEvent::erSUCCESS && $object->$action()) {
 					$import_data[ ($action == 'Create') ? 'added' : 'updated' ]++;
 				}
 				else {
 					$msg = '';
 					$errors = $object->GetFieldErrors();
 
 					foreach ($errors as $field => $info) {
 						if (!$info['pseudo']) continue;
 						$msg .= "$field: {$info['pseudo']} ";
 					}
 
 					$import_data['errors'] .= ($i + $import_data['lines_processed'] + 1).": $msg\n";
 					$import_data['not_imported_lines'] .= ','.($i + $import_data['lines_processed'] + 1);
 				}
 			}
 
 			$import_data['lines_processed'] += $import_data['step'];
 
 			$import_data['not_imported_lines'] = ltrim($import_data['not_imported_lines'], ',');
 			$this->Application->StoreVar('import_data', serialize($import_data));
 
 			$feof = feof($file);
 			fclose($file);
 
 			if($feof) {
 				$this->Application->Redirect($this->Application->GetVar('finish_template'));
 			}
 			else {
 				$percent_complete = floor($import_data['lines_processed'] / $import_data['total_lines'] * 100);
 				if($percent_complete > 99) $percent_complete = 99;
 				echo $percent_complete;
 			}
 		}
 
+		/**
+		 * Returns grid columns.
+		 *
+		 * @param array $import_data Import data.
+		 *
+		 * @return array
+		 */
+		protected function getGridColumns(array $import_data)
+		{
+			$prefix_elements = preg_split('/\.|_/', $import_data['prefix'], 2);
+			$grids = $this->Application->getUnitOption($prefix_elements[0], 'Grids');
+
+			return $grids[$import_data['grid']]['Fields'];
+		}
+
 		function ImportData($name)
 		{
 			$import_data = unserialize($this->Application->RecallVar('import_data'));
 			return isset($import_data[$name]) ? $import_data[$name] : false;
 		}
 
 		function GetNotImportedLines()
 		{
 			$import_data = unserialize($this->Application->RecallVar('import_data'));
 
 			if(!$import_data['not_imported_lines']) return false;
 			$line_numbers = explode(',', $import_data['not_imported_lines']);
 			$line_numbers[] = 0; // include header row in output
 
 			$file = fopen($import_data['file'], 'r');
 			$eof = false;
 			$result = '';
 			for($i = 0; $i <= max($line_numbers); $i++) {
 				if(feof($file)) break;
 				$line = fgets($file, 8192);
 				if(in_array($i, $line_numbers)) {
 					$result .= $i.':'.$line;
 				}
 			}
 			return $result."\n\n".$import_data['errors'];
 		}
 	}