Index: branches/5.2.x/core/units/helpers/cat_dbitem_export_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/cat_dbitem_export_helper.php	(revision 14958)
+++ branches/5.2.x/core/units/helpers/cat_dbitem_export_helper.php	(revision 14959)
@@ -1,1565 +1,1569 @@
 <?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!');
 
 	define('EXPORT_STEP', 100); // export by 200 items (e.g. links)
 	define('IMPORT_STEP', 20); // export by 200 items (e.g. links)
 	define('IMPORT_CHUNK', 10240); // 10240); //30720); //50120); // 5 KB
 
 	define('IMPORT_TEMP', 1);
 	define('IMPORT_LIVE', 2);
 
 	class kCatDBItemExportHelper extends kHelper {
 
 		var $false = false;
 
 		var $cache = Array();
 
 		/**
 		 * Allows to find out what items are new in cache
 		 *
 		 * @var Array
 		 */
 		var $cacheStatus = Array();
 
 		var $cacheTable = '';
 
 		var $exportFields = Array();
 
 		/**
 		 * Export options
 		 *
 		 * @var Array
 		 */
 		var $exportOptions = Array();
 
 		/**
 		 * Item beeing currenly exported
 		 *
 		 * @var kCatDBItem
 		 */
 		var $curItem = null;
 
 		/**
 		 * Dummy category object
 		 *
 		 * @var CategoriesItem
 		 */
 		var $dummyCategory = null;
 
 		/**
 		 * Pointer to opened file
 		 *
 		 * @var resource
 		 */
 		var $filePointer = null;
 
 		/**
 		 * Custom fields definition of current item
 		 *
 		 * @var Array
 		 */
 		var $customFields = Array();
 
 		public function __construct()
 		{
 			parent::__construct();
 
 			$this->cacheTable = TABLE_PREFIX.'ImportCache';
 		}
 
 		/**
 		 * Returns value from cache if found or false otherwise
 		 *
 		 * @param string $type
 		 * @param int $key
 		 * @return mixed
 		 */
 		function getFromCache($type, $key)
 		{
 			return getArrayValue($this->cache, $type, $key);
 		}
 
 		/**
 		 * Adds value to be cached
 		 *
 		 * @param string $type
 		 * @param int $key
 		 * @param mixed $value
 		 * @param bool $is_new
 		 */
 		function addToCache($type, $key, $value, $is_new = true)
 		{
 			/*if ( !isset($this->cache[$type]) ) {
 				$this->cache[$type] = Array ();
 			}*/
 
 			$this->cache[$type][$key] = $value;
 
 			if ( $is_new ) {
 				$this->cacheStatus[$type][$key] = true;
 			}
 		}
 
 		function storeCache($cache_types)
 		{
 			$cache_types = explode(',', $cache_types);
 
 			$values_sql = '';
 			foreach ($cache_types as $cache_type) {
 				$sql_mask = '('.$this->Conn->qstr($cache_type).',%s,%s),';
 				$cache = getArrayValue($this->cacheStatus, $cache_type);
 				if (!$cache) $cache = Array();
 				foreach ($cache as $var_name => $cache_status) {
 					$var_value = $this->cache[$cache_type][$var_name];
 					$values_sql .= sprintf($sql_mask, $this->Conn->qstr($var_name), $this->Conn->qstr($var_value) );
 				}
 			}
 
 			$values_sql = substr($values_sql, 0, -1);
 
 			if ($values_sql) {
 				$sql = 'INSERT INTO '.$this->cacheTable.'(`CacheName`,`VarName`,`VarValue`) VALUES '.$values_sql;
 				$this->Conn->Query($sql);
 			}
 
 		}
 
 		function loadCache()
 		{
 			$sql = 'SELECT * FROM '.$this->cacheTable;
 			$records = $this->Conn->Query($sql);
 
 			$this->cache = Array();
 			foreach ($records as $record) {
 				$this->addToCache($record['CacheName'], $record['VarName'], $record['VarValue'], false);
 			}
 		}
 
 		/**
 		 * Fill required fields with dummy values
 		 *
 		 * @param kEvent|bool $event
 		 * @param kCatDBItem|bool $object
 		 * @param bool $set_status
 		 */
 		function fillRequiredFields(&$event, &$object, $set_status = false)
 		{
 			if ( $object == $this->false ) {
 				$object =& $event->getObject();
 				/* @var $object kCatDBItem */
 			}
 
 			$has_empty = false;
 			$fields = $object->getFields();
 
 			if ( $object->isField('CreatedById') ) {
 				// CSV file was created without required CreatedById column
 				if ( $object->isRequired('CreatedById') ) {
 					$object->setRequired('CreatedById', false);
 				}
 
 				if ( !is_numeric( $object->GetDBField('CreatedById') ) ) {
 					$object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
 				}
 			}
 
 			foreach ($fields as $field_name => $field_options) {
 				if ( $object->isVirtualField($field_name) || !$object->isRequired($field_name) ) {
 					continue;
 				}
 
 				if ( $object->GetDBField($field_name) ) {
 					continue;
 				}
 
 				$formatter_class = getArrayValue($field_options, 'formatter');
 
 				if ( $formatter_class ) {
 					// not tested
 					$formatter =& $this->Application->recallObject($formatter_class);
 					/* @var $formatter kFormatter */
 
 					$sample_value = $formatter->GetSample($field_name, $field_options, $object);
 				}
 
 				$has_empty = true;
 				$object->SetField($field_name, isset($sample_value) && $sample_value ? $sample_value : 'no value');
 			}
 			$object->UpdateFormattersSubFields();
 
 			if ( $set_status && $has_empty ) {
 				$object->SetDBField('Status', 0);
 			}
 		}
 
 		/**
 		 * Verifies that all user entered export params are correct
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function verifyOptions(&$event)
 		{
 			if ($this->Application->RecallVar($event->getPrefixSpecial().'_ForceNotValid'))
 			{
 				$this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 0);
 				return false;
 			}
 
 			$this->fillRequiredFields($event, $this->false);
 
 			$object =& $event->getObject();
 			/* @var $object kCatDBItem */
 
 			$cross_unique_fields = Array('FieldsSeparatedBy', 'FieldsEnclosedBy');
 			if (($object->GetDBField('CategoryFormat') == 1) || ($event->Special == 'import')) // in one field
 			{
 				$object->setRequired('CategorySeparator');
 				$cross_unique_fields[] = 'CategorySeparator';
 			}
 
 			$ret = $object->Validate();
 
 			// check if cross unique fields has no same values
 			foreach ($cross_unique_fields as $field_index => $field_name)
 			{
 				if ($object->GetErrorPseudo($field_name) == 'required') {
 					continue;
 				}
 
 				$check_fields = $cross_unique_fields;
 				unset($check_fields[$field_index]);
 
 				foreach ($check_fields as $check_field)
 				{
 					if ($object->GetDBField($field_name) == $object->GetDBField($check_field))
 					{
 						$object->SetError($check_field, 'unique');
 					}
 				}
 			}
 
 			if ($event->Special == 'import')
 			{
 				$this->exportOptions = $this->loadOptions($event);
 
 				$automatic_fields = ($object->GetDBField('FieldTitles') == 1);
 				$object->setRequired('ExportColumns', !$automatic_fields);
 				$category_prefix = '__CATEGORY__';
 				if ( $automatic_fields && ($this->exportOptions['SkipFirstRow']) ) {
 					$this->openFile($event);
 					$this->exportOptions['ExportColumns'] = $this->readRecord();
 
 					if (!$this->exportOptions['ExportColumns']) {
 						$this->exportOptions['ExportColumns'] = Array ();
 					}
 
 					$this->closeFile();
 
 					// remove additional (non-parseble columns)
 					foreach ($this->exportOptions['ExportColumns'] as $field_index => $field_name) {
 						if (!$this->validateField($field_name, $object)) {
 							unset($this->exportOptions['ExportColumns'][$field_index]);
 						}
 					}
 					$category_prefix = '';
 				}
 
 				// 1. check, that we have column definitions
 				if (!$this->exportOptions['ExportColumns']) {
 					$object->setError('ExportColumns', 'required');
 					$ret = false;
 				}
 				else {
 					// 1.1. check that all required fields are present in imported file
 					$missing_columns = Array();
 					$fields = $object->getFields();
 
 					foreach ($fields as $field_name => $field_options) {
 						if ($object->skipField($field_name)) continue;
 						if ( $object->isRequired($field_name) && !in_array($field_name, $this->exportOptions['ExportColumns']) ) {
 							$missing_columns[] = $field_name;
 							$object->setError('ExportColumns', 'required_fields_missing', 'la_error_RequiredColumnsMissing');
 							$ret = false;
 						}
 					}
 
 					if (!$ret && $this->Application->isDebugMode()) {
 						$this->Application->Debugger->appendHTML('Missing required for import/export:');
 						$this->Application->Debugger->dumpVars($missing_columns);
 					}
 				}
 
 
 				// 2. check, that we have only mixed category field or only separated category fields
 				$category_found['mixed'] = false;
 				$category_found['separated'] = false;
 
 				foreach ($this->exportOptions['ExportColumns'] as $import_field) {
 					if (preg_match('/^'.$category_prefix.'Category(Path|[0-9]+)/', $import_field, $rets)) {
 						$category_found[$rets[1] == 'Path' ? 'mixed' : 'separated'] = true;
 					}
 				}
 				if ($category_found['mixed'] && $category_found['separated']) {
 					$object->SetError('ExportColumns', 'unique_category', 'la_error_unique_category_field');
 					$ret = false;
 				}
 
 				// 3. check, that duplicates check fields are selected & present in imported fields
 				if ($this->exportOptions['ReplaceDuplicates']) {
 					if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
 						$check_fields = Array($object->IDField);
 					}
 					else {
 						$check_fields = $this->exportOptions['DuplicateCheckFields'] ? explode('|', substr($this->exportOptions['DuplicateCheckFields'], 1, -1)) : Array();
 						$object =& $event->getObject();
 
 						$fields = $object->getFields();
 						$language_id = $this->Application->GetDefaultLanguageId();
 
 						foreach ($check_fields as $index => $check_field) {
 							foreach ($fields as $field_name => $field_options) {
 								if ($field_name == 'l'.$language_id.'_'.$check_field) {
 									$check_fields[$index] = 'l'.$language_id.'_'.$check_field;
 									break;
 								}
 							}
 						}
 					}
 					$this->exportOptions['DuplicateCheckFields'] = $check_fields;
 
 					if (!$check_fields) {
 						$object->setError('CheckDuplicatesMethod', 'required');
 						$ret = false;
 					}
 					else {
 						foreach ($check_fields as $check_field) {
 							$check_field = preg_replace('/^cust_(.*)/', 'Custom_\\1', $check_field);
 							if (!in_array($check_field, $this->exportOptions['ExportColumns'])) {
 								$object->setError('ExportColumns', 'required');
 								$ret = false;
 								break;
 							}
 						}
 					}
 				}
 				$this->saveOptions($event);
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Returns filename to read import data from
 		 *
 		 * @return string
 		 */
 		function getImportFilename()
 		{
 			if ($this->exportOptions['ImportSource'] == 1)
 			{
 				$ret = $this->exportOptions['ImportFilename']; // ['name']; commented by Kostja
 			}
 			else {
 				$ret = $this->exportOptions['ImportLocalFilename'];
 			}
 			return EXPORT_PATH.'/'.$ret;
 		}
 
 		/**
 		 * Returns filename to write export data to
 		 *
 		 * @return string
 		 */
 		function getExportFilename()
 		{
 			$extension = $this->getFileExtension();
 			$filename = preg_replace('/(.*)\.' . $extension . '$/', '\1', $this->exportOptions['ExportFilename']) . '.' . $extension;
 
 			return EXPORT_PATH . DIRECTORY_SEPARATOR . $filename;
 		}
 
 		/**
 		 * Opens file required for export/import operations
 		 *
 		 * @param kEvent $event
 		 */
 		function openFile(&$event)
 		{
 			$file_helper =& $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$file_helper->CheckFolder(EXPORT_PATH);
 
 			if ($event->Special == 'export') {
 				$write_mode = ($this->exportOptions['start_from'] == 0) ? 'w' : 'a';
 				$this->filePointer = fopen($this->getExportFilename(), $write_mode);
 			}
 			else {
 				$this->filePointer = fopen($this->getImportFilename(), 'r');
 			}
 
 			// skip UTF-8 BOM Modifier
 			$first_chars = fread($this->filePointer, 3);
 			if (bin2hex($first_chars) != 'efbbbf') {
 				fseek($this->filePointer, 0);
 			}
 		}
 
 		/**
 		 * Closes opened file
 		 *
 		 */
 		function closeFile()
 		{
 			fclose($this->filePointer);
 		}
 
 		function getCustomSQL()
 		{
 			$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 			/* @var $ml_formatter kMultiLanguage */
 
 			$custom_sql = '';
 
 			foreach ($this->customFields as $custom_id => $custom_name) {
 				$custom_sql .= 'custom_data.' . $ml_formatter->LangFieldName('cust_' . $custom_id) . ' AS cust_' . $custom_name . ', ';
 			}
 
 			return substr($custom_sql, 0, -2);
 		}
 
 		function getPlainExportSQL($count_only = false)
 		{
 			if ( $count_only && isset($this->exportOptions['ForceCountSQL']) ) {
 				$sql = $this->exportOptions['ForceCountSQL'];
 			}
 			elseif ( !$count_only && isset($this->exportOptions['ForceSelectSQL']) ) {
 				$sql = $this->exportOptions['ForceSelectSQL'];
 			}
 			else {
 				$items_list =& $this->Application->recallObject($this->curItem->Prefix . '.export-items-list', $this->curItem->Prefix . '_List');
 				/* @var $items_list kDBList */
 
 				$items_list->SetPerPage(-1);
 
 				if ( $this->exportOptions['export_ids'] != '' ) {
 					$items_list->addFilter('export_ids', $items_list->TableName . '.' . $items_list->IDField . ' IN (' . implode(',', $this->exportOptions['export_ids']) . ')');
 				}
 
 				if ( $count_only ) {
 					$sql = $items_list->getCountSQL($items_list->GetSelectSQL(true, false));
 				}
 				else {
 					$sql = $items_list->GetSelectSQL();
 				}
 			}
 
 			if ( !$count_only ) {
 				$sql .= ' LIMIT ' . $this->exportOptions['start_from'] . ',' . EXPORT_STEP;
 			}
 			/*else {
 				$sql = preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql);
 			}*/
 
 			return $sql;
 		}
 
 		function getExportSQL($count_only = false)
 		{
 			if ( !$this->Application->getUnitOption($this->curItem->Prefix, 'CatalogItem') ) {
 				return $this->GetPlainExportSQL($count_only); // in case this is not a CategoryItem
 			}
 
 			if ( $this->exportOptions['export_ids'] === false ) {
 				// get links from current category & all it's subcategories
 				$join_clauses = Array ();
 
 				$custom_sql = $this->getCustomSQL();
 
 				if ( $custom_sql ) {
 					$custom_table = $this->Application->getUnitOption($this->curItem->Prefix . '-cdata', 'TableName');
 					$join_clauses[$custom_table . ' custom_data'] = 'custom_data.ResourceId = item_table.ResourceId';
 				}
 
 				$join_clauses[TABLE_PREFIX . 'CategoryItems ci'] = 'ci.ItemResourceId = item_table.ResourceId';
 				$join_clauses[TABLE_PREFIX . 'Category c'] = 'c.CategoryId = ci.CategoryId';
 
 				$sql = 'SELECT item_table.*, ci.CategoryId' . ($custom_sql ? ', ' . $custom_sql : '') . '
 						FROM ' . $this->curItem->TableName . ' item_table';
 
 				foreach ($join_clauses as $table_name => $join_expression) {
 					$sql .= ' LEFT JOIN ' . $table_name . ' ON ' . $join_expression;
 				}
 
 				$sql .= ' WHERE ';
 
 				if ( $this->exportOptions['export_cats_ids'][0] == 0 ) {
 					$sql .= '1';
 				}
 				else {
 					foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
 						$sql .= '(c.ParentPath LIKE "%|' . $category_id . '|%") OR ';
 					}
 
 					$sql = substr($sql, 0, -4);
 				}
 
 				$sql .= ' ORDER BY ci.PrimaryCat DESC'; // NEW
 			}
 			else {
 				// get only selected links
 				$sql = 'SELECT item_table.*, ' . $this->exportOptions['export_cats_ids'][0] . ' AS CategoryId
 						FROM ' . $this->curItem->TableName . ' item_table
 						WHERE ' . $this->curItem->IDField . ' IN (' . implode(',', $this->exportOptions['export_ids']) . ')';
 			}
 
 			if ( !$count_only ) {
 				$sql .= ' LIMIT ' . $this->exportOptions['start_from'] . ',' . EXPORT_STEP;
 			}
 			else {
 				$sql = preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql);
 			}
 
 			return $sql;
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function performExport(&$event)
 		{
 			$this->exportOptions = $this->loadOptions($event);
 			$this->exportFields = $this->exportOptions['ExportColumns'];
 			$this->curItem =& $event->getObject( Array('skip_autoload' => true) );
 			$this->customFields = $this->Application->getUnitOption($event->Prefix, 'CustomFields');
 			$this->openFile($event);
 
 			if ($this->exportOptions['start_from'] == 0) // first export step
 			{
 				if (!getArrayValue($this->exportOptions, 'IsBaseCategory')) {
 					$this->exportOptions['IsBaseCategory'] = 0;
 				}
 
 				if ($this->exportOptions['IsBaseCategory'] ) {
 					$sql = 'SELECT ParentPath
 							FROM '.TABLE_PREFIX.'Category
 							WHERE CategoryId = ' . (int)$this->Application->GetVar('m_cat_id');
 					$parent_path = $this->Conn->GetOne($sql);
 					$parent_path = explode('|', substr($parent_path, 1, -1));
 					if ($parent_path && $parent_path[0] == $this->Application->getBaseCategory()) {
 						array_shift($parent_path);
 					}
 
 					$this->exportOptions['BaseLevel'] = count($parent_path); // level to cut from other categories
 				}
 
 				// 1. export field titles if required
 				if ($this->exportOptions['IncludeFieldTitles'])
 				{
 					$data_array = Array();
 					foreach ($this->exportFields as $export_field)
 					{
 						$data_array = array_merge($data_array, $this->getFieldCaption($export_field));
 					}
 					$this->writeRecord($data_array);
 				}
 				$this->exportOptions['total_records'] = $this->Conn->GetOne( $this->getExportSQL(true) );
 			}
 
 			// 2. export data
 			$records = $this->Conn->Query( $this->getExportSQL() );
 			$records_exported = 0;
 			foreach ($records as $record_info) {
 				$this->curItem->LoadFromHash($record_info);
 
 				$data_array = Array();
 				foreach ($this->exportFields as $export_field)
 				{
 					$data_array = array_merge($data_array, $this->getFieldValue($export_field) );
 				}
 				$this->writeRecord($data_array);
 				$records_exported++;
 			}
 			$this->closeFile();
 
 			$this->exportOptions['start_from'] += $records_exported;
 			$this->saveOptions($event);
 
 			return $this->exportOptions;
 		}
 
 		function getItemFields()
 		{
 			// just in case dummy user selected automtic mode & moved columns too :(
 			$src_options = $this->curItem->GetFieldOption('ExportColumns', 'options');
 			$dst_options = $this->curItem->GetFieldOption('AvailableColumns', 'options');
 
 			return array_merge($dst_options, $src_options);
 		}
 
 		/**
 		 * Checks if field really belongs to importable field list
 		 *
 		 * @param string $field_name
 		 * @param kCatDBItem $object
 		 * @return bool
 		 */
 		function validateField($field_name, &$object)
 		{
 			// 1. convert custom field
 			$field_name = preg_replace('/^Custom_(.*)/', '__CUSTOM__\\1', $field_name);
 
 			// 2. convert category field (mixed version & separated version)
 			$field_name = preg_replace('/^Category(Path|[0-9]+)/', '__CATEGORY__Category\\1', $field_name);
 
 			$valid_fields = $object->getPossibleExportColumns();
 			return isset($valid_fields[$field_name]) || isset($valid_fields['__VIRTUAL__'.$field_name]);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function performImport(&$event)
 		{
 			if (!$this->exportOptions) {
 				// load import options in case if not previously loaded in verification function
 				$this->exportOptions = $this->loadOptions($event);
 			}
 
 			$backup_category_id = $this->Application->GetVar('m_cat_id');
 			$this->Application->SetVar('m_cat_id', (int)$this->Application->RecallVar('ImportCategory') );
 
 			$this->openFile($event);
 
 			$bytes_imported = 0;
 			if ($this->exportOptions['start_from'] == 0) // first export step
 			{
 				// 1st time run
 				if ($this->exportOptions['SkipFirstRow']) {
 					$this->readRecord();
 					$this->exportOptions['start_from'] = ftell($this->filePointer);
 					$bytes_imported = ftell($this->filePointer);
 				}
 
 				$current_category_id = $this->Application->GetVar('m_cat_id');
 				if ($current_category_id > 0) {
 					$sql = 'SELECT ParentPath FROM '.TABLE_PREFIX.'Category WHERE CategoryId = '.$current_category_id;
 					$this->exportOptions['ImportCategoryPath'] = $this->Conn->GetOne($sql);
 				}
 				else {
 					$this->exportOptions['ImportCategoryPath'] = '';
 				}
 				$this->exportOptions['total_records'] = filesize($this->getImportFilename());
 			}
 			else {
 				$this->loadCache();
 			}
 
 			$this->exportFields = $this->exportOptions['ExportColumns'];
 			$this->addToCache('category_parent_path', $this->Application->GetVar('m_cat_id'), $this->exportOptions['ImportCategoryPath']);
 
 			// 2. import data
 			$this->dummyCategory =& $this->Application->recallObject('c.-tmpitem', 'c', Array('skip_autoload' => true));
 			fseek($this->filePointer, $this->exportOptions['start_from']);
 
 			$items_processed = 0;
 			while (($bytes_imported < IMPORT_CHUNK && $items_processed < IMPORT_STEP) && !feof($this->filePointer)) {
 				$data = $this->readRecord();
 				if ($data) {
 					if ($this->exportOptions['ReplaceDuplicates']) {
 						// set fields used as keys for replace duplicates code
 						$this->resetImportObject($event, IMPORT_TEMP, $data);
 					}
 
 					$this->processCurrentItem($event, $data);
 				}
 				$bytes_imported = ftell($this->filePointer) - $this->exportOptions['start_from'];
 				$items_processed++;
 			}
 
 			$this->closeFile();
 			$this->Application->SetVar('m_cat_id', $backup_category_id);
 
 			$this->exportOptions['start_from'] += $bytes_imported;
 			$this->storeCache('new_ids');
 
 			$this->saveOptions($event);
 
 			if ($this->exportOptions['start_from'] == $this->exportOptions['total_records']) {
 				$this->Conn->Query('TRUNCATE TABLE '.$this->cacheTable);
 			}
 
 			return $this->exportOptions;
 		}
 
 		function setCurrentID()
 		{
 			$this->curItem->setID( $this->curItem->GetDBField($this->curItem->IDField) );
 		}
 
 		/**
 		 * Sets value of import/export object
 		 * @param int $field_index
 		 * @param mixed $value
 		 * @return void
 		 * @access protected
 		 */
 		protected function setFieldValue($field_index, $value)
 		{
 			if ( empty($value) ) {
 				$value = null;
 			}
 
 			$field_name = getArrayValue($this->exportFields, $field_index);
 			if ( $field_name == 'ResourceId' ) {
 				return ;
 			}
 
 			if ( substr($field_name, 0, 7) == 'Custom_' ) {
 				$field_name = 'cust_' . substr($field_name, 7);
 				$this->curItem->SetField($field_name, $value);
 			}
 			elseif ( $field_name == 'CategoryPath' || $field_name == '__CATEGORY__CategoryPath' ) {
 				$this->curItem->CategoryPath = $value ? explode($this->exportOptions['CategorySeparator'], $value) : Array ();
 			}
 			elseif ( substr($field_name, 0, 8) == 'Category' ) {
 				$this->curItem->CategoryPath[(int)substr($field_name, 8) - 1] = $value;
 			}
 			elseif ( substr($field_name, 0, 20) == '__CATEGORY__Category' ) {
 				$this->curItem->CategoryPath[(int)substr($field_name, 20) - 1] = $value;
 			}
 			elseif ( substr($field_name, 0, 11) == '__VIRTUAL__' ) {
 				$field_name = substr($field_name, 11);
 				$this->curItem->SetField($field_name, $value);
 			}
 			else {
 				$this->curItem->SetField($field_name, $value);
 			}
 
 			if ( $this->curItem->GetErrorPseudo($field_name) ) {
 				$this->curItem->SetDBField($field_name, null);
 				$this->curItem->RemoveError($field_name);
 			}
 		}
 
 		/**
 		 * Resets import object
 		 *
 		 * @param kEvent $event
 		 * @param int $object_type
 		 * @param Array $record_data
 		 * @return void
 		 */
 		function resetImportObject(&$event, $object_type, $record_data = null)
 		{
 			switch ($object_type) {
 				case IMPORT_TEMP:
 					$this->curItem =& $event->getObject( Array('skip_autoload' => true) );
 					break;
 
 				case IMPORT_LIVE:
 					$this->curItem =& $this->Application->recallObject($event->Prefix.'.-tmpitem'.$event->Special, $event->Prefix, Array('skip_autoload' => true));
 					break;
 			}
 			$this->curItem->Clear();
 			$this->curItem->SetDBField('CategoryId', NULL); // since default value is import root category
 			$this->customFields = $this->Application->getUnitOption($event->Prefix, 'CustomFields');
 
 			if (isset($record_data)) {
 				$this->setImportData($record_data);
 			}
 		}
 
 		function setImportData($record_data)
 		{
 			foreach ($record_data as $field_index => $field_value) {
 				$this->setFieldValue($field_index, $field_value);
 			}
 			$this->setCurrentID();
 		}
 
 
 		function getItemCategory()
 		{
 			static $lang_prefix = null;
 			$backup_category_id = $this->Application->GetVar('m_cat_id');
 
 			$category_id = $this->getFromCache('category_names', implode(':', $this->curItem->CategoryPath));
 			if ($category_id) {
 				$this->Application->SetVar('m_cat_id', $category_id);
 				return $category_id;
 			}
 
 			if (is_null($lang_prefix)) {
 				$lang_prefix = 'l'.$this->Application->GetVar('m_lang').'_';
 			}
 
 			foreach ($this->curItem->CategoryPath as $category_index => $category_name) {
 				if (!$category_name) continue;
 				$category_key = crc32( implode(':', array_slice($this->curItem->CategoryPath, 0, $category_index + 1) ) );
 
 				$category_id = $this->getFromCache('category_names', $category_key);
 				if ($category_id === false) {
 					// get parent category path to search only in it
 					$current_category_id = $this->Application->GetVar('m_cat_id');
 //					$parent_path = $this->getParentPath($current_category_id);
 
 					// get category id from database by name
 					$sql = 'SELECT CategoryId
 							FROM '.TABLE_PREFIX.'Category
 							WHERE ('.$lang_prefix.'Name = '.$this->Conn->qstr($category_name).') AND (ParentId = '.(int)$current_category_id.')';
 					$category_id = $this->Conn->GetOne($sql);
 
-					if ($category_id === false) {
+					if ( $category_id === false ) {
 						// category not in db -> create
-						$category_fields = Array(	$lang_prefix.'Name' => $category_name, $lang_prefix.'Description' => $category_name,
-													'Status' => STATUS_ACTIVE, 'ParentId' => $current_category_id, 'AutomaticFilename' => 1
-											);
+						$category_fields = Array (
+							$lang_prefix.'Name' => $category_name, $lang_prefix.'Description' => $category_name,
+							'Status' => STATUS_ACTIVE, 'ParentId' => $current_category_id, 'AutomaticFilename' => 1
+						);
+
+						$this->dummyCategory->Clear();
 						$this->dummyCategory->SetDBFieldsFromHash($category_fields);
-						if ($this->dummyCategory->Create()) {
+
+						if ( $this->dummyCategory->Create() ) {
 							$category_id = $this->dummyCategory->GetID();
 							$this->addToCache('category_parent_path', $category_id, $this->dummyCategory->GetDBField('ParentPath'));
 							$this->addToCache('category_names', $category_key, $category_id);
 						}
 					}
 					else {
 						$this->addToCache('category_names', $category_key, $category_id);
 					}
 				}
 
 				if ($category_id) {
 					$this->Application->SetVar('m_cat_id', $category_id);
 				}
 			}
 			if (!$this->curItem->CategoryPath) {
 				$category_id = $backup_category_id;
 			}
 
 			return $category_id;
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @param Array $record_data
 		 * @return bool
 		 */
 		function processCurrentItem(&$event, $record_data)
 		{
 			$save_method = 'Create';
 			$load_keys = Array();
 
 			// create/update categories
 			$backup_category_id = $this->Application->GetVar('m_cat_id');
 
 			// perform replace duplicates code
 			if ($this->exportOptions['ReplaceDuplicates']) {
 				// get replace keys first, then reset current item to empty one
 				$category_id = $this->getItemCategory();
 				if ($this->exportOptions['CheckDuplicatesMethod'] == 1) {
 					if ($this->curItem->GetID()) {
 						$load_keys = Array($this->curItem->IDField => $this->curItem->GetID());
 					}
 				}
 				else {
 					$key_fields = $this->exportOptions['DuplicateCheckFields'];
 					foreach ($key_fields as $key_field) {
 						$load_keys[$key_field] = $this->curItem->GetDBField($key_field);
 					}
 				}
 
 				$this->resetImportObject($event, IMPORT_LIVE);
 
 				if (count($load_keys)) {
 					$where_clause = '';
 					$language_id = (int)$this->Application->GetVar('m_lang');
 
 					if (!$language_id) {
 						$language_id = 1;
 					}
 
 					foreach ($load_keys as $field_name => $field_value) {
 						if (preg_match('/^cust_(.*)/', $field_name, $regs)) {
 							$custom_id = array_search($regs[1], $this->customFields);
 							$field_name = 'l'.$language_id.'_cust_'.$custom_id;
 							$where_clause .= '(custom_data.`'.$field_name.'` = '.$this->Conn->qstr($field_value).') AND ';
 						}
 						else {
 							$where_clause .= '(item_table.`'.$field_name.'` = '.$this->Conn->qstr($field_value).') AND ';
 						}
 
 					}
 					$where_clause = substr($where_clause, 0, -5);
 
 					$item_id = $this->getFromCache('new_ids', crc32($where_clause));
 					if (!$item_id) {
 						if ($this->exportOptions['CheckDuplicatesMethod'] == 2) {
 							// by other fields
 							$parent_path = $this->getParentPath($category_id);
 							$where_clause = '(c.ParentPath LIKE "'.$parent_path.'%") AND '.$where_clause;
 						}
 
 						$cdata_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName');
 						$sql = 'SELECT '.$this->curItem->IDField.'
 								FROM '.$this->curItem->TableName.' item_table
 								LEFT JOIN '.$cdata_table.' custom_data ON custom_data.ResourceId = item_table.ResourceId
 								LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId
 								LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
 								WHERE '.$where_clause;
 						$item_id = $this->Conn->GetOne($sql);
 					}
 					$save_method = $item_id && $this->curItem->Load($item_id) ? 'Update' : 'Create';
 					if ($save_method == 'Update') {
 						// replace id from csv file with found id (only when ID is found in cvs file)
 						if (in_array($this->curItem->IDField, $this->exportFields)) {
 							$record_data[ array_search($this->curItem->IDField, $this->exportFields) ] = $item_id;
 						}
 					}
 				}
 
 				$this->setImportData($record_data);
 			}
 			else {
 				$this->resetImportObject($event, IMPORT_LIVE, $record_data);
 				$category_id = $this->getItemCategory();
 			}
 
 			// create main record
 			if ($save_method == 'Create') {
 				$this->fillRequiredFields($this->false, $this->curItem, true);
 			}
 
 //			$sql_start = microtime(true);
 			if (!$this->curItem->$save_method()) {
 				$this->Application->SetVar('m_cat_id', $backup_category_id);
 				return false;
 			}
 //			$sql_end = microtime(true);
 //			$this->saveLog('SQL ['.$save_method.'] Time: '.($sql_end - $sql_start).'s');
 
 			if ($load_keys && ($save_method == 'Create') && $this->exportOptions['ReplaceDuplicates']) {
 				// map new id to old id
 				$this->addToCache('new_ids', crc32($where_clause), $this->curItem->GetID() );
 			}
 
 			// assign item to categories
 			$this->curItem->assignToCategory($category_id, false);
 
 			$this->Application->SetVar('m_cat_id', $backup_category_id);
 			return true;
 		}
 
 		/*function saveLog($msg)
 		{
 			static $first_time = true;
 
 			$fp = fopen((defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/sqls.log', $first_time ? 'w' : 'a');
 			fwrite($fp, $msg."\n");
 			fclose($fp);
 
 			$first_time = false;
 		}*/
 
 		/**
 		 * Returns category parent path, if possible, then from cache
 		 *
 		 * @param int $category_id
 		 * @return string
 		 */
 		function getParentPath($category_id)
 		{
 			$parent_path = $this->getFromCache('category_parent_path', $category_id);
 			if ($parent_path === false) {
 				$sql = 'SELECT ParentPath
 						FROM '.TABLE_PREFIX.'Category
 						WHERE CategoryId = '.$category_id;
 				$parent_path = $this->Conn->GetOne($sql);
 				$this->addToCache('category_parent_path', $category_id, $parent_path);
 			}
 			return $parent_path;
 		}
 
 		function getFileExtension()
 		{
 			return $this->exportOptions['ExportFormat'] == 1 ? 'csv' : 'xml';
 		}
 
 		function getLineSeparator($option = 'LineEndings')
 		{
 			return $this->exportOptions[$option] == 1 ? "\r\n" : "\n";
 		}
 
 		/**
 		 * Returns field caption for any exported field
 		 *
 		 * @param string $field
 		 * @return string
 		 */
 		function getFieldCaption($field)
 		{
 			if (substr($field, 0, 10) == '__CUSTOM__')
 			{
 				$ret = 'Custom_'.substr($field, 10, strlen($field) );
 			}
 			elseif (substr($field, 0, 12) == '__CATEGORY__')
 			{
 				return $this->getCategoryTitle();
 			}
 			elseif (substr($field, 0, 11) == '__VIRTUAL__') {
 				$ret = substr($field, 11);
 			}
 			else
 			{
 				$ret = $field;
 			}
 
 			return Array($ret);
 		}
 
 		/**
 		 * Returns requested field value (including custom fields and category fields)
 		 *
 		 * @param string $field
 		 * @return string
 		 */
 		function getFieldValue($field)
 		{
 			if (substr($field, 0, 10) == '__CUSTOM__') {
 				$field = 'cust_'.substr($field, 10, strlen($field));
 				$ret = $this->curItem->GetField($field);
 			}
 			elseif (substr($field, 0, 12) == '__CATEGORY__') {
 				return $this->getCategoryPath();
 			}
 			elseif (substr($field, 0, 11) == '__VIRTUAL__') {
 				$field = substr($field, 11);
 				$ret = $this->curItem->GetField($field);
 			}
 			else
 			{
 				$ret = $this->curItem->GetField($field);
 			}
 
 			$ret = str_replace("\r\n", $this->getLineSeparator('LineEndingsInside'), $ret);
 			return Array($ret);
 		}
 
 		/**
 		 * Returns category field(-s) caption based on export mode
 		 *
 		 * @return string
 		 */
 		function getCategoryTitle()
 		{
 			// category path in separated fields
 			$category_count = $this->getMaxCategoryLevel();
 			if ($this->exportOptions['CategoryFormat'] == 1)
 			{
 				// category path in one field
 				return $category_count ? Array('CategoryPath') : Array();
 			}
 			else
 			{
 				$i = 0;
 				$ret = Array();
 				while ($i < $category_count) {
 					$ret[] = 'Category'.($i + 1);
 					$i++;
 				}
 				return $ret;
 			}
 		}
 
 		/**
 		 * Returns category path in required format for current link
 		 *
 		 * @return string
 		 */
 		function getCategoryPath()
 		{
 			$category_id = $this->curItem->GetDBField('CategoryId');
 			$category_path = $this->getFromCache('category_path', $category_id);
 
 			if ( !$category_path ) {
 				$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 				/* @var $ml_formatter kMultiLanguage */
 
 				$sql = 'SELECT ' . $ml_formatter->LangFieldName('CachedNavbar') . '
 						FROM ' . TABLE_PREFIX . 'Category
 						WHERE CategoryId = ' . $category_id;
 				$category_path = $this->Conn->GetOne($sql);
 
 				$category_path = $category_path ? explode('&|&', $category_path) : Array ();
 
 				if ( $category_path && strtolower($category_path[0]) == 'content' ) {
 					array_shift($category_path);
 				}
 
 				if ( $this->exportOptions['IsBaseCategory'] ) {
 					$i = $this->exportOptions['BaseLevel'];
 					while ( $i > 0 ) {
 						array_shift($category_path);
 						$i--;
 					}
 				}
 
 				$category_count = $this->getMaxCategoryLevel();
 
 				if ( $this->exportOptions['CategoryFormat'] == 1 ) {
 					// category path in single field
 					$category_path = $category_count ? Array (implode($this->exportOptions['CategorySeparator'], $category_path)) : Array ();
 				}
 				else {
 					// category path in separated fields
 					$levels_used = count($category_path);
 
 					if ( $levels_used < $category_count ) {
 						$i = 0;
 						while ( $i < $category_count - $levels_used ) {
 							$category_path[] = '';
 							$i++;
 						}
 					}
 				}
 				$this->addToCache('category_path', $category_id, $category_path);
 			}
 
 			return $category_path;
 		}
 
 		/**
 		 * Get maximal category deep level from links beeing exported
 		 *
 		 * @return int
 		 */
 		function getMaxCategoryLevel()
 		{
 			static $max_level = -1;
 
 			if ($max_level != -1)
 			{
 				return $max_level;
 			}
 
 			$sql = 'SELECT IF(c.CategoryId IS NULL, 0, MAX( LENGTH(c.ParentPath) - LENGTH( REPLACE(c.ParentPath, "|", "") ) - 1 ))
 					FROM '.$this->curItem->TableName.' item_table
 					LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON item_table.ResourceId = ci.ItemResourceId
 					LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
 					WHERE (ci.PrimaryCat = 1) AND ';
 
 			$where_clause = '';
 			if ($this->exportOptions['export_ids'] === false) {
 				// get links from current category & all it's subcategories
 				if ($this->exportOptions['export_cats_ids'][0] == 0) {
 					$where_clause = 1;
 				}
 				else {
 					foreach ($this->exportOptions['export_cats_ids'] as $category_id) {
 						$where_clause .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR ';
 					}
 					$where_clause = substr($where_clause, 0, -4);
 				}
 			}
 			else {
 				// get only selected links
 				$where_clause = $this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')';
 			}
 
 			$max_level = $this->Conn->GetOne($sql.'('.$where_clause.')');
 
 			if ($this->exportOptions['IsBaseCategory'] ) {
 				$max_level -= $this->exportOptions['BaseLevel'];
 			}
 
 			return $max_level;
 		}
 
 		/**
 		 * Saves one record to export file
 		 *
 		 * @param Array $fields_hash
 		 */
 		function writeRecord($fields_hash)
 		{
 			kUtil::fputcsv($this->filePointer, $fields_hash, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy'], $this->getLineSeparator() );
 		}
 
 		function readRecord()
 		{
 			return fgetcsv($this->filePointer, 10000, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy']);
 		}
 
 		/**
 		 * Saves import/export options
 		 *
 		 * @param kEvent $event
 		 * @param Array $options
 		 * @return void
 		 */
 		function saveOptions(&$event, $options = null)
 		{
 			if ( !isset($options) ) {
 				$options = $this->exportOptions;
 			}
 
 			$this->Application->StoreVar($event->getPrefixSpecial() . '_options', serialize($options));
 		}
 
 		/**
 		 * Loads import/export options
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 */
 		function loadOptions(&$event)
 		{
 			return unserialize( $this->Application->RecallVar($event->getPrefixSpecial() . '_options') );
 		}
 
 		/**
 		 * Sets correct available & export fields
 		 *
 		 * @param kEvent $event
 		 */
 		function prepareExportColumns(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kCatDBItem */
 
 			if ( !$object->isField('ExportColumns') ) {
 				// import/export prefix was used (see kDBEventHandler::prepareObject) but object don't plan to be imported/exported
 				return ;
 			}
 
 			$available_columns = Array();
 
 			if ($this->Application->getUnitOption($event->Prefix, 'CatalogItem')) {
 				// category field (mixed)
 				$available_columns['__CATEGORY__CategoryPath'] = 'CategoryPath';
 
 				if ($event->Special == 'import') {
 					// category field (separated fields)
 					$max_level = $this->Application->ConfigValue('MaxImportCategoryLevels');
 					$i = 0;
 					while ($i < $max_level) {
 						$available_columns['__CATEGORY__Category'.($i + 1)] = 'Category'.($i + 1);
 						$i++;
 					}
 				}
 			}
 
 			// db fields
 			$fields = $object->getFields();
 
 			foreach ($fields as $field_name => $field_options) {
 				if ( !$object->skipField($field_name) ) {
 					$available_columns[$field_name] = $field_name.( $object->isRequired($field_name) ? '*' : '');
 				}
 			}
 
 			$handler =& $this->Application->recallObject($event->Prefix.'_EventHandler');
 			/* @var $handler kDBEventHandler */
 
 			$available_columns = array_merge($available_columns, $handler->getCustomExportColumns($event));
 
 			// custom fields
 			$custom_fields = $object->getCustomFields();
 
 			foreach ($custom_fields as $custom_id => $custom_name)
 			{
 				$available_columns['__CUSTOM__'.$custom_name] = $custom_name;
 			}
 
 			// columns already in use
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if ($items_info)
 			{
 				list($item_id, $field_values) = each($items_info);
 				$export_keys = $field_values['ExportColumns'];
 				$export_keys = $export_keys ? explode('|', substr($export_keys, 1, -1) ) : Array();
 			}
 			else {
 				$export_keys = Array();
 			}
 
 			$export_columns = Array();
 			foreach ($export_keys as $field_key)
 			{
 				$field_name = $this->getExportField($field_key);
 				$export_columns[$field_key] = $field_name;
 				unset($available_columns[$field_key]);
 			}
 
 			$options = $object->GetFieldOptions('ExportColumns');
 			$options['options'] = $export_columns;
 			$object->SetFieldOptions('ExportColumns', $options);
 
 			$options = $object->GetFieldOptions('AvailableColumns');
 			$options['options'] = $available_columns;
 			$object->SetFieldOptions('AvailableColumns', $options);
 
 			$this->updateImportFiles($event);
 			$this->PrepareExportPresets($event);
 		}
 
 		/**
 		 * Prepares export presets
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 */
 		function PrepareExportPresets(&$event)
 		{
 			$object =& $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$options = $object->GetFieldOptions('ExportPresets');
 			$export_settings = $this->Application->RecallPersistentVar('export_settings');
 
 			if ( !$export_settings ) {
 				return;
 			}
 
 			$export_settings = unserialize($export_settings);
 
 			if ( !isset($export_settings[$event->Prefix]) ) {
 				return;
 			}
 
 			$export_presets = array ('' => '');
 
 			foreach ($export_settings[$event->Prefix] as $key => $val) {
 				$export_presets[implode('|', $val['ExportColumns'])] = $key;
 			}
 
 			$options['options'] = $export_presets;
 			$object->SetFieldOptions('ExportPresets', $options);
 		}
 
 		function getExportField($field_key)
 		{
 			$prepends = Array('__CUSTOM__', '__CATEGORY__');
 			foreach ($prepends as $prepend)
 			{
 				if (substr($field_key, 0, strlen($prepend) ) == $prepend)
 				{
 					$field_key = substr($field_key, strlen($prepend), strlen($field_key) );
 					break;
 				}
 			}
 			return $field_key;
 		}
 
 		/**
 		 * Updates uploaded files list
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function updateImportFiles(&$event)
 		{
 			if ( $event->Special != 'import' ) {
 				return ;
 			}
 
 			$file_helper =& $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$import_filenames = Array ();
 			$file_helper->CheckFolder(EXPORT_PATH);
 
 			$iterator = new DirectoryIterator(EXPORT_PATH);
 			/* @var $file_info DirectoryIterator */
 
 			foreach ($iterator as $file_info) {
 				$file = $file_info->getFilename();
 
 				if ( $file_info->isDir() || $file == 'dummy' || $file_info->getSize() == 0 ) {
 					continue;
 				}
 
 				$import_filenames[$file] = $file . ' (' . kUtil::formatSize( $file_info->getSize() ) . ')';
 			}
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$object->SetFieldOption('ImportLocalFilename', 'options', $import_filenames);
 		}
 
 		/**
 		 * Returns module folder
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function getModuleName(&$event)
 		{
 			$module_path = $this->Application->getUnitOption($event->Prefix, 'ModuleFolder') . '/';
 			$module_name = $this->Application->findModule('Path', $module_path, 'Name');
 
 			return mb_strtolower($module_name);
 		}
 
 		/**
 		 * Export form validation & processing
 		 *
 		 * @param kEvent $event
 		 */
 		function OnExportBegin(&$event)
 		{
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			if ( !$items_info ) {
 				$items_info = unserialize($this->Application->RecallVar($event->getPrefixSpecial() . '_ItemsInfo'));
 				$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
 			}
 
 			list($item_id, $field_values) = each($items_info);
 
 			$object =& $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$object->SetFieldsFromHash($field_values);
 			$field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!!
 
 			$object->setID($item_id);
 			$this->setRequiredFields($event);
 
 			// save export/import options
 			if ( $event->Special == 'export' ) {
 				$export_ids = $this->Application->RecallVar($event->Prefix . '_export_ids');
 				$export_cats_ids = $this->Application->RecallVar($event->Prefix . '_export_cats_ids');
 
 				// used for multistep export
 				$field_values['export_ids'] = $export_ids ? explode(',', $export_ids) : false;
 				$field_values['export_cats_ids'] = $export_cats_ids ? explode(',', $export_cats_ids) : Array ($this->Application->GetVar('m_cat_id'));
 			}
 
 			$field_values['ExportColumns'] = $field_values['ExportColumns'] ? explode('|', substr($field_values['ExportColumns'], 1, -1) ) : Array();
 			$field_values['start_from'] = 0;
 
 			$nevent = new kEvent($event->Prefix . ':OnBeforeExportBegin');
 			$nevent->setEventParam('options', $field_values);
 			$this->Application->HandleEvent($nevent);
 			$field_values = $nevent->getEventParam('options');
 
 			$this->saveOptions($event, $field_values);
 
 			if ( $this->verifyOptions($event) ) {
 				if ( $this->_getExportSavePreset($object) ) {
 					$name = $object->GetDBField('ExportPresetName');
 
 					$export_settings = $this->Application->RecallPersistentVar('export_settings');
 					$export_settings = $export_settings ? unserialize($export_settings) : array ();
 					$export_settings[$event->Prefix][$name] = $field_values;
 					$this->Application->StorePersistentVar('export_settings', serialize($export_settings));
 				}
 
 				$progress_t = $this->Application->RecallVar('export_progress_t');
 				if ( $progress_t ) {
 					$this->Application->RemoveVar('export_progress_t');
 				}
 				else {
 					$progress_t = $this->getModuleName($event) . '/' . $event->Special . '_progress';
 				}
 				$event->redirect = $progress_t;
 
 				if ( $event->Special == 'import' ) {
 					$import_category = (int)$this->Application->RecallVar('ImportCategory');
 
 					// in future could use module root category if import category will be unavailable :)
 					$event->SetRedirectParam('m_cat_id', $import_category); // for template permission checking
 					$this->Application->StoreVar('m_cat_id', $import_category); // for event permission checking
 				}
 			}
 			else {
 				// make uploaded file local & change source selection
 				$filename = getArrayValue($field_values, 'ImportFilename');
 
 				if ( $filename ) {
 					$this->updateImportFiles($event);
 					$object->SetDBField('ImportSource', 2);
 					$field_values['ImportSource'] = 2;
 					$object->SetDBField('ImportLocalFilename', $filename);
 					$field_values['ImportLocalFilename'] = $filename;
 					$this->saveOptions($event, $field_values);
 				}
 
 				$event->status = kEvent::erFAIL;
 				$event->redirect = false;
 			}
 		}
 
 		/**
 		 * Returns export save preset name, when used at all
 		 *
 		 * @param kDBItem $object
 		 * @return string
 		 */
 		function _getExportSavePreset(&$object)
 		{
 			if ( !$object->isField('ExportSavePreset') ) {
 				return '';
 			}
 
 			return $object->GetDBField('ExportSavePreset');
 		}
 
 		/**
 		 * set required fields based on import or export params
 		 *
 		 * @param kEvent $event
 		 */
 		function setRequiredFields(&$event)
 		{
 			$required_fields['common'] = Array('FieldsSeparatedBy', 'LineEndings', 'CategoryFormat');
 
 			$required_fields['export'] = Array('ExportFormat', 'ExportFilename','ExportColumns');
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			if ($this->_getExportSavePreset($object)) {
 				$required_fields['export'][] = 'ExportPresetName';
 			}
 
 			$required_fields['import'] = Array('FieldTitles', 'ImportSource', 'CheckDuplicatesMethod'); // ImportFilename, ImportLocalFilename
 
 			if ($event->Special == 'import')
 			{
 				$import_source = Array(1 => 'ImportFilename', 2 => 'ImportLocalFilename');
 				$used_field = $import_source[ $object->GetDBField('ImportSource') ];
 
 				$required_fields[$event->Special][] = $used_field;
 				$object->SetFieldOption($used_field, 'error_field', 'ImportSource');
 
 				if ($object->GetDBField('FieldTitles') == 2) $required_fields[$event->Special][] = 'ExportColumns'; // manual field titles
 			}
 
 			$required_fields = array_merge($required_fields['common'], $required_fields[$event->Special]);
 			$object->setRequired($required_fields);
 		}
 
 	}
\ No newline at end of file