Index: branches/5.3.x/core/kernel/utility/unit_config.php
===================================================================
--- branches/5.3.x/core/kernel/utility/unit_config.php	(revision 15731)
+++ branches/5.3.x/core/kernel/utility/unit_config.php	(revision 15732)
@@ -1,1029 +1,1380 @@
 <?php
 /**
  * @version    $Id$
  * @package    In-Portal
  * @copyright    Copyright (C) 1997 - 2012 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!');
 
 /**
  * TODO: Rename following settings
  *
  * ConfigMapping -> ConfigMappings
  * StatusField -> StatusFields
  * PermSection -> PermSections
  */
 
 /**
  * @method Array getFilterMenu(mixed $default = false)
  * @method kUnitConfig setFilterMenu(Array $value)
  *
  * @method Array getConfigMapping(mixed $default = false)
  * @method kUnitConfig setConfigMapping(Array $value)
  *
  *
  * @method string getModuleFolder(mixed $default = false)
  * @method kUnitConfig setModuleFolder(string $value)
  *
  * @method string getBasePath(mixed $default = false)
  * @method kUnitConfig setBasePath(string $value)
  *
  * @method Array getEventHandlerClass(mixed $default = false)
  * @method kUnitConfig setEventHandlerClass(Array $value)
  *
  * @method Array getTagProcessorClass(mixed $default = false)
  * @method kUnitConfig setTagProcessorClass(Array $value)
  *
  * @method Array getItemClass(mixed $default = false)
  * @method kUnitConfig setItemClass(Array $value)
  *
  * @method Array getListClass(mixed $default = false)
  * @method kUnitConfig setListClass(Array $value)
  *
  * @method Array getValidatorClass(mixed $default = false)
  * @method kUnitConfig setValidatorClass(Array $value)
  *
  * @method Array getQueryString(mixed $default = false)
  * @method kUnitConfig setQueryString(Array $value)
  *
  * @method string getPermItemPrefix(mixed $default = false)
  * @method kUnitConfig setPermItemPrefix(string $value)
  *
  * @method string getPermTabText(mixed $default = false)
  * @method kUnitConfig setPermTabText(string $value)
  *
  * @method Array getPermSection(mixed $default = false)
  * @method kUnitConfig setPermSection(Array $value)
  *
  * @method bool getAutoLoad(mixed $default = false)
  * @method kUnitConfig setAutoLoad(bool $value)
  *
  * @method string getIDField(mixed $default = false)
  * @method kUnitConfig setIDField(string $value)
  *
  * @method string getTableName(mixed $default = false)
  * @method kUnitConfig setTableName(string $value)
  *
  * @method string getCustomDataTableName(mixed $default = false)
  * @method kUnitConfig setCustomDataTableName(string $value)
  *
  * @method kUnitConfig setStatusField(Array $value)
  *
  * @method string getTitleField(mixed $default = false)
  * @method kUnitConfig setTitleField(string $value)
  *
  * @method string getOrderField(mixed $default = false)
  * @method kUnitConfig setOrderField(string $value)
  *
  * @method string getOwnerField(mixed $default = false)
  * @method kUnitConfig setOwnerField(string $value)
  *
  * @method int getConfigPriority(mixed $default = false)
  * @method kUnitConfig setConfigPriority(int $value)
  *
  * @method bool getCatalogItem(mixed $default = false)
  * @method kUnitConfig setCatalogItem(bool $value)
  *
  * @method string getCatalogSelectorName(mixed $default = false)
  * @method kUnitConfig setCatalogSelectorName(string $value)
  *
  * @method string getAdminTemplatePath(mixed $default = false)
  * @method kUnitConfig setAdminTemplatePath(string $value)
  *
  * @method string getAdminTemplatePrefix(mixed $default = false)
  * @method kUnitConfig setAdminTemplatePrefix(string $value)
  *
  * @method string getSearchConfigPostfix(mixed $default = false)
  * @method kUnitConfig setSearchConfigPostfix(string $value)
  *
  * @method string getTitlePhrase(mixed $default = false)
  * @method kUnitConfig setTitlePhrase(string $value)
  *
  * @method int getItemType(mixed $default = false)
  * @method kUnitConfig setItemType(int $value)
  *
  * @method Array getStatisticsInfo(mixed $default = false)
  * @method kUnitConfig setStatisticsInfo(Array $value)
  *
  * @method string getViewMenuPhrase(mixed $default = false)
  * @method kUnitConfig setViewMenuPhrase(string $value)
  *
  * @method string getCatalogTabIcon(mixed $default = false)
  * @method kUnitConfig setCatalogTabIcon(string $value)
  *
  * @method bool getCacheModRewrite(mixed $default = false)
  * @method kUnitConfig setCacheModRewrite(bool $value)
  *
  * @method kUnitConfig setParentTableKey(mixed $value)
  *
  * @method kUnitConfig setForeignKey(mixed $value)
  *
  * @method string getConstrain(mixed $default = false)
  * @method kUnitConfig setConstrain(string $value)
  *
  * @method bool getAutoDelete(mixed $default = false)
  * @method kUnitConfig setAutoDelete(bool $value)
  *
  * @method bool getAutoClone(mixed $default = false)
  * @method kUnitConfig setAutoClone(bool $value)
  *
  * @method string getParentPrefix(mixed $default = false)
  * @method kUnitConfig setParentPrefix(string $value)
  *
  * @method bool getCheckSimulatniousEdit(mixed $default = false)
  * @method kUnitConfig setCheckSimulatniousEdit(bool $value)
  *
  * @method bool getPortalStyleEnv(mixed $default = false)
  * @method kUnitConfig setPortalStyleEnv(bool $value)
  *
  * @method int getRewritePriority(mixed $default = false)
  * @method kUnitConfig setRewritePriority(int $value)
  *
  * @method Array getRewriteListener(mixed $default = false)
  * @method kUnitConfig setRewriteListener(Array $value)
  *
  * @method bool getForceDontLogChanges(mixed $default = false)
  * @method kUnitConfig setForceDontLogChanges(bool $value)
  *
  * @method Array getUserProfileMapping(mixed $default = false)
  * @method kUnitConfig setUserProfileMapping(Array $value)
  *
  * @method bool getUsePendingEditing(mixed $default = false)
  * @method kUnitConfig setUsePendingEditing(bool $value)
  *
  * @method string getNavigationSelectClause(mixed $default = false)
  * @method kUnitConfig setNavigationSelectClause(string $value)
  *
  * @method bool getPopulateMlFields(bool $default = false)
  * @method kUnitConfig setPopulateMlFields(bool $value)
  *
  * @method bool getLogChanges(bool $default = false)
  * @method kUnitConfig setLogChanges(bool $value)
  *
  * @method int getFileCount(bool $default = false)
  * @method kUnitConfig setFileCount(int $value)
  *
  * @method int getImageCount(bool $default = false)
  * @method kUnitConfig setImageCount(int $value)
  *
  * @method string getDownloadHelperClass(bool $default = false)
  * @method kUnitConfig setDownloadHelperClass(string $value)
  *
  * @method string getSectionPrefix(bool $default = false)
  * @method kUnitConfig setSectionPrefix(string $value)
  *
  * @method bool getSiteConfigProcessed(bool $default = false)
  * @method kUnitConfig setSiteConfigProcessed(bool $value)
  *
  *
  * @method Array getRegisterClasses(mixed $default = false)
  * @method kUnitConfig setRegisterClasses(Array $value)
  * @method kUnitConfig addRegisterClasses(Array $value, string $name = null)
  * @method kUnitConfig removeRegisterClasses(string $name = null)
  *
  * @method Array getScheduledTasks(mixed $default = false)
  * @method Array getScheduledTaskByName(string $name, mixed $default = false)
  * @method kUnitConfig setScheduledTasks(Array $value)
  * @method kUnitConfig addScheduledTasks(Array $value, string $name = null)
  * @method kUnitConfig removeScheduledTasks(string $name = null)
  *
  * @method Array getTitlePresets(mixed $default = false)
  * @method Array getTitlePresetByName(string $name, mixed $default = false)
  * @method kUnitConfig setTitlePresets(Array $value)
  * @method kUnitConfig addTitlePresets(Array $value, string $name = null)
  * @method kUnitConfig removeTitlePresets(string $name = null)
  *
  * @method Array getSections(mixed $default = false)
  * @method Array getSectionByName(string $name, mixed $default = false)
  * @method kUnitConfig setSections(Array $value)
  * @method kUnitConfig addSections(Array $value, string $name = null)
  * @method kUnitConfig removeSections(string $name = null)
  *
  * @method Array getFields(mixed $default = false)
  * @method Array getFieldByName(string $name, mixed $default = false)
  * @method kUnitConfig setFields(Array $value)
  * @method kUnitConfig addFields(Array $value, string $name = null)
  * @method kUnitConfig removeFields(string $name = null)
  *
  * @method Array getVirtualFields(mixed $default = false)
  * @method Array getVirtualFieldByName(string $name, mixed $default = false)
  * @method kUnitConfig setVirtualFields(Array $value)
  * @method kUnitConfig addVirtualFields(Array $value, string $name = null)
  * @method kUnitConfig removeVirtualFields(string $name = null)
  *
  * @method Array getGrids(mixed $default = false)
  * @method Array getGridByName(string $name, mixed $default = false)
  * @method kUnitConfig setGrids(Array $value)
  * @method kUnitConfig addGrids(Array $value, string $name = null)
  * @method kUnitConfig removeGrids(string $name = null)
  *
  * @method Array getHooks(mixed $default = false)
  * @method kUnitConfig setHooks(Array $value)
  * @method kUnitConfig addHooks(Array $value, string $name = null)
  * @method kUnitConfig removeHooks(string $name = null)
  *
  * @method Array getAggregateTags(mixed $default = false)
  * @method kUnitConfig setAggregateTags(Array $value)
  * @method kUnitConfig addAggregateTags(Array $value, string $name = null)
  * @method kUnitConfig removeAggregateTags(string $name = null)
  *
  * @method Array getEditTabPresets(mixed $default = false)
  * @method Array getEditTabPresetByName(string $name, mixed $default = false)
  * @method kUnitConfig setEditTabPresets(Array $value)
  * @method kUnitConfig addEditTabPresets(Array $value, string $name = null)
  * @method kUnitConfig removeEditTabPresets(string $name = null)
  *
  * @method Array getSubItems(mixed $default = false)
  * @method kUnitConfig setSubItems(Array $value)
  * @method kUnitConfig addSubItems(string $value, string $name = null)
  * @method kUnitConfig removeSubItems(string $name = null)
  *
  * @method Array getCustomFields(mixed $default = false)
  * @method string getCustomFieldByName(string $name, mixed $default = false)
  * @method kUnitConfig setCustomFields(Array $value)
  * @method kUnitConfig addCustomFields(Array $value, string $name = null)
  * @method kUnitConfig removeCustomFields(string $name = null)
  *
  * @method Array getClones(mixed $default = false)
  * @method Array getCloneByName(string $name, mixed $default = false)
  * @method kUnitConfig setClones(Array $value)
  * @method kUnitConfig addClones(Array $value, string $name = null)
  * @method kUnitConfig removeClones(string $name = null)
  *
  * @method Array getProcessPrefixes(mixed $default = false)
  * @method kUnitConfig setProcessPrefixes(Array $value)
  * @method kUnitConfig addProcessPrefixes(string $value, string $name = null)
  * @method kUnitConfig removeProcessPrefixes(string $name = null)
  *
  * @method Array getForms(mixed $default = false)
  * @method Array getFormByName(string $name, mixed $default = false)
  * @method kUnitConfig setForms(Array $value)
  * @method kUnitConfig addForms(Array $value, string $name = null)
  * @method kUnitConfig removeForms(string $name = null)
  *
  * @method Array getReplacementTemplates(mixed $default = false)
  * @method string getReplacementTemplateByName(string $name, mixed $default = false)
  * @method kUnitConfig setReplacementTemplates(Array $value)
  * @method kUnitConfig addReplacementTemplates(string $value, string $name = null)
  * @method kUnitConfig removeReplacementTemplates(string $name = null)
  *
  * @method Array getItemPropertyMappings(mixed $default = false)
  * @method string getItemPropertyMappingByName(string $name, mixed $default = false)
  * @method kUnitConfig setItemPropertyMappings(Array $value)
  * @method kUnitConfig addItemPropertyMappings(string $value, string $name = null)
  * @method kUnitConfig removeItemPropertyMappings(string $name = null)
  *
  * @method Array getSectionAdjustments(mixed $default = false)
  * @method mixed getSectionAdjustmentByName(string $name, mixed $default = false)
  * @method kUnitConfig setSectionAdjustments(Array $value)
  * @method kUnitConfig addSectionAdjustments(mixed $value, string $name = null)
  * @method kUnitConfig removeSectionAdjustments(string $name = null)
  *
  * @method Array getImportKeys(mixed $default = false)
  * @method kUnitConfig setImportKeys(Array $value)
  * @method kUnitConfig addImportKeys(mixed $value, string $name = null)
  * @method kUnitConfig removeImportKeys(string $name = null)
  *
  *
  * @method Array getCalculatedFieldSpecials(mixed $default = Array ())
  * @method Array getCalculatedFieldsBySpecial(mixed $special, mixed $default = Array ())
  * @method kUnitConfig setCalculatedFieldsBySpecial(mixed $special, Array $value)
  * @method kUnitConfig addCalculatedFieldsBySpecial(mixed $special, mixed $value, string $name = null)
  * @method kUnitConfig removeCalculatedFieldsBySpecial(mixed $special, string $name = null)
  *
  * @method Array getAggregatedCalculatedFieldSpecials(mixed $default = Array ())
  * @method Array getAggregatedCalculatedFieldsBySpecial(mixed $special, mixed $default = Array ())
  * @method kUnitConfig setAggregatedCalculatedFieldsBySpecial(mixed $special, Array $value)
  * @method kUnitConfig addAggregatedCalculatedFieldsBySpecial(mixed $special, mixed $value, string $name = null)
  * @method kUnitConfig removeAggregatedCalculatedFieldsBySpecial(mixed $special, string $name = null)
  *
  * @method Array getListSQLSpecials(mixed $default = Array ())
  * @method string getListSQLsBySpecial(mixed $special, mixed $default = Array ())
  * @method kUnitConfig setListSQLsBySpecial(mixed $special, string $value)
  * @method kUnitConfig removeListSQLsBySpecial(mixed $special, string $name = null)
  *
  * @method Array getListSortingSpecials(mixed $default = Array ())
  * @method Array getListSortingsBySpecial(mixed $special, mixed $default = Array ())
  * @method kUnitConfig setListSortingsBySpecial(mixed $special, Array $value)
  * @method kUnitConfig addListSortingsBySpecial(mixed $special, mixed $value, string $name = null)
  * @method kUnitConfig removeListSortingsBySpecial(mixed $special, string $name = null)
  *
  * @method Array getItemSQLSpecials(mixed $default = Array ())
  * @method string getItemSQLsBySpecial(mixed $special, mixed $default = Array ())
  * @method kUnitConfig setItemSQLsBySpecial(mixed $special, string $value)
  * @method kUnitConfig removeItemSQLsBySpecial(mixed $special, string $name = null)
  */
 class kUnitConfig {
 
 	/**
+	 * Reference to global kApplication instance
+	 *
+	 * @var kApplication
+	 * @access protected
+	 */
+	protected $Application = null;
+
+	/**
+	* Connection to database
+	*
+	* @var kDBConnection
+	* @access protected
+	*/
+	protected $Conn = null;
+
+	/**
 	 * Unit config prefix
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected $_prefix = '';
 
 	/**
+	 * Filename, where unit config is stored
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $_filename = '';
+
+	/**
 	 * Default unit config data
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected static $_defaults = Array (
 		'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
 		'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
 		'EventHandlerClass' => Array ('class' => 'kDBEventHandler', 'file' => '', 'build_event' => 'OnBuild'),
 		'TagProcessorClass' => Array ('class' => 'kDBTagProcessor', 'file' => '', 'build_event' => 'OnBuild'),
 
 		'AutoLoad' => true,
 
 		'QueryString' => Array (
 			1 => 'id',
 			2 => 'Page',
 			3 => 'PerPage',
 			4 => 'event',
 			5 => 'mode',
 		),
 
 		'ListSQLs' => Array (
 			'' => '	SELECT %1$s.* %2$s
 					FROM %1$s',
 		),
 
 		'Fields' => Array (),
 	);
 
 	/**
 	 * Word endings, that are suffixed with "es" instead of just "s" during pluralisation
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected static $_singularEndingsRegExp = '/(x|s|z|sh|ch)$/';
 
 	/**
 	 * Words, that ends with es/s, but are always singulars
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected static $_alwaysSingularRegExp = '/(class|LogChanges|ForceDontLogChanges|PopulateMlFields)$/i';
 
 	/**
 	 * Unit config data
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_data = Array ();
 
 	/**
 	 * Unit config settings that can have different values based on special
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_specialBased = Array (
 		'CalculatedFields', 'AggregatedCalculatedFields', 'ListSQLs', 'ListSortings', 'ItemSQLs',
 	);
 
 	/**
 	 * Unit config settings, that allow data to be added without a key
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_withoutKeys = Array (
 		'RegisterClasses', 'Hooks', 'AggregateTags'
 	);
 
 	/**
 	 * Dynamic method name, that was called
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected $_currentMethodName = '';
 
 	/**
 	 * Arguments given to dynamic method name, that was called
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_currentArguments = Array ();
 
 	/**
 	 * Creates new instance of kUnitConfig class
 	 *
 	 * @param string $prefix
 	 * @param Array $defaults
 	 * @param bool $use_global_defaults
 	 * @access public
 	 */
 	public function __construct($prefix, $defaults = null, $use_global_defaults = true)
 	{
 		$this->_prefix = $prefix;
 
 		$merge_with = $use_global_defaults ? self::$_defaults : Array ();
 		$this->_data = array_merge($merge_with, isset($defaults) ? $defaults : Array ());
+
+		$this->Application =& kApplication::Instance();
+		$this->Conn =& $this->Application->GetADODBConnection();
+	}
+
+	/**
+	 * Sets filename, where unit config is stored
+	 *
+	 * @param string $filename
+	 * @return void
+	 * @access public
+	 */
+	public function setFilename($filename)
+	{
+		$this->_filename = $filename;
 	}
 
 	/**
 	 * Returns unit config prefix
 	 *
 	 * @return string
 	 * @access public
 	 */
 	public function getPrefix()
 	{
 		return $this->_prefix;
 	}
 
 	/**
 	 * Ensures, that only unit config data is saved when object is serialized
 	 *
 	 * @return Array
 	 * @access public
 	 */
 	public function __sleep()
 	{
-		return Array ('_prefix', '_data');
+		return Array ('_prefix', '_data', 'Application', 'Conn');
 	}
 
 	/**
 	 * Dumps unit config into a file
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function dump()
 	{
 		kUtil::print_r($this->_data, 'Unit Config', true);
 	}
 
 	/**
 	 * Returns unit config data in raw form
 	 *
 	 * @return Array
 	 * @access public
 	 */
 	public function getRaw()
 	{
 		return $this->_data;
 	}
 
 	/**
 	 * Processed dynamically created methods for changing unit config settings
 	 *
 	 * @param string $method_name
 	 * @param Array $arguments
 	 * @return mixed
 	 * @throws InvalidArgumentException
 	 * @throws RuntimeException
 	 * @access public
 	 */
 	public function __call($method_name, $arguments)
 	{
 //		=== regular ===
 //		get{SettingName}()
 //		set{SettingName}($value)
 //		add{SettingName}s(string $value, string $name = null)
 //		remove{SettingName}(string $name = null)
 
 //		=== by special ===
 //		get{SettingName}Specials()
 //		get{SettingName}sBySpecial($special)
 //		set{SettingName}BySpecial($special, $value)
 //		add{SettingName}sBySpecial(string $special, Array $value, string $name = null)
 //		remove{SettingName}sBySpecial(string $special, $name = null)
 
 		if ( !preg_match('/^(get|set|add|remove)(.*?)(BySpecial|Specials|ByName)*$/', $method_name, $regs) ) {
 			throw new RuntimeException('Unknown method <strong>' . __CLASS__ . '::' . $this->_currentMethodName . '</strong>');
 		}
 
 		$this->_currentMethodName = $method_name;
 		$this->_currentArguments = $arguments;
 		$to_call = '_process' . ucfirst($regs[1]);
 
 		return $this->$to_call($regs[2], isset($regs[3]) ? $regs[3] : '');
 	}
 
 	/**
 	 * Processes calls to "get*" methods
 	 *
 	 * @param string $setting_name
 	 * @param string $suffix
 	 * @return mixed
 	 * @access protected
 	 */
 	protected function _processGet($setting_name, $suffix = '')
 	{
 		$is_plural = $this->_isPluralSetting($setting_name);
 
 		if ( $suffix == 'BySpecial' && $is_plural ) {
 			// get{SettingName}BySpecial($special, $default = false)
 			$this->_verifyArguments(Array ('special'));
 
 			return $this->getSetting($setting_name, $this->_getDefaultValue(1, Array ()), $this->_currentArguments[0]);
 		}
 		elseif ( $suffix == 'Specials' && !$is_plural ) {
 			// get{SettingName}Specials($default = Array ())
 			$result = $this->getSetting($this->_getPlural($setting_name), $this->_getDefaultValue(0, Array ()));
 
 			return array_keys($result);
 		}
 		elseif ( $suffix == 'ByName' && !$is_plural ) {
 			$sub_key = $this->_currentArguments[0];
 			$result = $this->getSetting($this->_getPlural($setting_name), Array ());
 
 			return isset($result[$sub_key]) ? $result[$sub_key] : $this->_getDefaultValue(1, false);
 		}
 
 		// get{SettingName}($default = false)
 		return $this->getSetting($setting_name, $this->_getDefaultValue(0, false));
 	}
 
 	/**
 	 * Returns default value from given argument or false, when not passed
 	 *
 	 * @param int $arg_index
 	 * @param mixed $default
 	 * @return bool
 	 * @access protected
 	 */
 	protected function _getDefaultValue($arg_index, $default = false)
 	{
 		return isset($this->_currentArguments[$arg_index]) ? $this->_currentArguments[$arg_index] : $default;
 	}
 
 	/**
 	 * Processes calls to "set*" methods
 	 *
 	 * @param string $setting_name
 	 * @param string $suffix
 	 * @return kUnitConfig
 	 * @access protected
 	 */
 	protected function _processSet($setting_name, $suffix = '')
 	{
 		$is_plural = $this->_isPluralSetting($setting_name);
 
 		if ( $suffix == 'BySpecial' && $is_plural ) {
 			// set{SettingName}BySpecial($special, $value)
 			$this->_verifyArguments(Array ('special', 'value'));
 
 			return $this->setSetting($setting_name, $this->_currentArguments[1], $this->_currentArguments[0]);
 		}
 
 		// set{SettingName}($value)
 		$this->_verifyArguments(Array ('value'));
 
 		return $this->setSetting($setting_name, $this->_currentArguments[0]);
 	}
 
 	/**
 	 * Processes calls to "add*" method
 	 *
 	 * @param string $setting_name
 	 * @param string $suffix
 	 * @return kUnitConfig
 	 * @access protected
 	 * @throws InvalidArgumentException
 	 */
 	protected function _processAdd($setting_name, $suffix = '')
 	{
 		$arguments = $this->_currentArguments;
 
 		if ( !$this->_isPluralSetting($setting_name) ) {
 			throw new InvalidArgumentException('Setting "' . $setting_name . '" isn\'t plural');
 		}
 
 		if ( $suffix == 'BySpecial' ) {
 			// add{SettingName}BySpecial(string $special, string $value, string $name = null)
 			$this->_verifyArguments(Array ('special', 'value'));
 
 			if ( isset($arguments[2]) ) {
 				// add a single value
 				$this->_addToSetting($this->_getSingular($setting_name), $arguments[1], $arguments[2], $arguments[0]);
 			}
 			else {
 				// add multiple values
 				$this->_addToSetting($setting_name, $arguments[1], null, $arguments[0]);
 			}
 		}
 		else {
 			// add{SettingName}(string $value, string $name = null)
 			$this->_verifyArguments(Array ('value'));
 
 			if ( isset($arguments[1]) ) {
 				// add a single value
 				$this->_addToSetting($this->_getSingular($setting_name), $arguments[0], $arguments[1]);
 			}
 			else {
 				// add multiple value
 				$this->_addToSetting($setting_name, $arguments[0], null);
 			}
 		}
 
 		return $this;
 	}
 
 	/**
 	 * Adds a value to a setting
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param string $array_key
 	 * @param string $special
 	 * @return kUnitConfig
 	 * @throws InvalidArgumentException
 	 */
 	protected function _addToSetting($name, $value, $array_key, $special = null)
 	{
 		if ( $this->_isPluralSetting($name) ) {
 			// multiple values given - merge with current values
 			if ( !is_array($value) ) {
 				throw new InvalidArgumentException('Argument $value must be an array');
 			}
 
 			$result = $this->getSetting($name, Array (), $special);
 			$this->setSetting($name, array_merge($result, $value), $special);
 		}
 		else {
 			// single value given
 			$result = $this->getSetting($this->_getPlural($name), Array (), $special);
 
 			if ( $this->_isWithoutKeySetting($name) ) {
 				$result[] = $value;
 			}
 			else {
 				$result[$array_key] = $value;
 			}
 
 			$this->setSetting($this->_getPlural($name), $result, $special);
 		}
 
 		return $this;
 	}
 
 	/**
 	 * Processes calls to "remove*" method
 	 *
 	 * @param string $setting_name
 	 * @param string $suffix
 	 * @return kUnitConfig
 	 * @access protected
 	 * @throws InvalidArgumentException
 	 */
 	protected function _processRemove($setting_name, $suffix = '')
 	{
 		$arguments = $this->_currentArguments;
 
 		if ( !$this->_isPluralSetting($setting_name) ) {
 			throw new InvalidArgumentException('Setting "' . $setting_name . '" isn\'t plural');
 		}
 
 		if ( $suffix == 'BySpecial' ) {
 			// remove{SettingName}BySpecial(string $special, string $name = null)
 			$this->_verifyArguments(Array ('special'));
 
 			if ( isset($arguments[1]) ) {
 				// remove single value
 				$this->_removeFromSetting($this->_getSingular($setting_name), $arguments[1], $arguments[0]);
 			}
 			else {
 				// remove multiple value
 				$this->_removeFromSetting($setting_name, null, $arguments[0]);
 			}
 		}
 		else {
 			// remove{SettingName}(string $name = null)
 			if ( isset($arguments[0]) ) {
 				// remove single value
 				$this->_removeFromSetting($this->_getSingular($setting_name), $arguments[0]);
 			}
 			else {
 				// remove multiple values
 				$this->_removeFromSetting($setting_name, null);
 			}
 		}
 
 		return $this;
 	}
 
 	/**
 	 * Removes a value from a setting
 	 *
 	 * @param string $name
 	 * @param string $array_key
 	 * @param string $special
 	 * @return kUnitConfig
 	 * @access protected
 	 * @throws RuntimeException
 	 */
 	protected function _removeFromSetting($name, $array_key = null, $special = null)
 	{
 		if ( $this->_isPluralSetting($name) ) {
 			// remove multiple values
 			if ( $this->getSetting($name, false, $special) !== false ) {
 				$this->setSetting($name, null, $special);
 			}
 		}
 		else {
 			// remove single value
 			if ( $this->_isWithoutKeySetting($name) ) {
 				throw new RuntimeException('Unable to change setting without key');
 			}
 
 			$result = $this->getSetting($this->_getPlural($name), false, $special);
 
 			if ( $result !== false ) {
 				unset($result[$array_key]);
 				$this->setSetting($this->_getPlural($name), $result, $special);
 			}
 		}
 
 		return $this;
 	}
 
 	/**
 	 * Verifies argument count given
 	 *
 	 * @param Array $argument_names
 	 * @return void
 	 * @access protected
 	 * @throws InvalidArgumentException
 	 */
 	protected function _verifyArguments($argument_names)
 	{
 		if ( count($this->_currentArguments) < count($argument_names) ) {
 			throw new InvalidArgumentException('Method <strong>' . __CLASS__ . '::' . $this->_currentMethodName . '</strong> expects following arguments: <strong>$' . implode('</strong>, <strong>$', $argument_names) . '</strong>');
 		}
 	}
 
 	/**
 	 * Returns setting value by name and filter by special (if passed)
 	 *
 	 * @param string $name
 	 * @param mixed $default
 	 * @param string|kBase $special
 	 * @return mixed
 	 * @access public
 	 */
 	public function getSetting($name, $default = false, $special = null)
 	{
 		if ( in_array($name, $this->_specialBased) && isset($special) ) {
 			$use_special = $this->_guessSpecial($name, $special);
 
 			return isset($this->_data[$name][$use_special]) ? $this->_data[$name][$use_special] : $default;
 		}
 
 		return isset($this->_data[$name]) ? $this->_data[$name] : $default;
 	}
 
 	/**
 	 * Changes value of a setting
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param string|kBase $special
 	 * @return kUnitConfig
 	 * @access public
 	 */
 	public function setSetting($name, $value, $special = null)
 	{
 		if ( in_array($name, $this->_specialBased) && isset($special) ) {
 			if ( !isset($this->_data[$name]) ) {
 				$this->_data[$name] = Array ();
 			}
 
 			$use_special = $this->_guessSpecial($name, $special);
 
 			if ( !isset($value) ) {
 				unset($this->_data[$name][$use_special]);
 			}
 			else {
 				$this->_data[$name][$use_special] = $value;
 			}
 		}
 		else {
 			if ( !isset($value) ) {
 				unset($this->_data[$name]);
 			}
 			else {
 				$this->_data[$name] = $value;
 			}
 		}
 
 		return $this;
 	}
 
 	/**
 	 * Detects settings, that accept arrays
 	 *
 	 * @param string $name
 	 * @return bool
 	 * @access protected
 	 */
 	protected function _isPluralSetting($name)
 	{
 		if ( preg_match(self::$_alwaysSingularRegExp, $name) ) {
 			// simplified exceptions
 			return false;
 		}
 
 		return preg_match('/(es|s)$/', $name);
 	}
 
 	/**
 	 * Detects anonymous settings
 	 *
 	 * @param string $name
 	 * @return bool
 	 * @access protected
 	 */
 	protected function _isWithoutKeySetting($name)
 	{
 		return in_array($this->_getPlural($name), $this->_withoutKeys);
 	}
 
 	/**
 	 * Returns plural form given word
 	 *
 	 * @param string $name
 	 * @return string
 	 * @access protected
 	 */
 	protected function _getPlural($name)
 	{
 		return preg_match(self::$_singularEndingsRegExp, $name) ? $name . 'es' : $name . 's';
 	}
 
 	/**
 	 * Returns singular form of given word
 	 *
 	 * @param $name
 	 * @return mixed
 	 * @throws InvalidArgumentException
 	 */
 	protected function _getSingular($name)
 	{
 		if ( !$this->_isPluralSetting($name) ) {
 			throw new InvalidArgumentException('Setting "' . $name . '" isn\'t plural');
 		}
 
 		return preg_replace('/(es|s)$/', '', $name);
 	}
 
 	/**
 	 * Guesses special to be used by event
 	 *
 	 * @param string $setting_name
 	 * @param string|kBase $special
 	 * @return string
 	 * @access protected
 	 */
 	protected function _guessSpecial($setting_name, $special)
 	{
 		$use_special = $special instanceof kBase ? $special->Special : $special;
 		$value = $this->getSetting($setting_name, Array ());
 
 		return isset($value[$use_special]) ? $use_special : '';
 	}
 
 	/**
 	 * Adds new tab to existing edit tab preset
 	 *
 	 * @param string $preset_name
 	 * @param Array $value
 	 * @param string $name
 	 * @return kUnitConfig
 	 * @throws InvalidArgumentException
 	 * @access public
 	 */
 	public function addEditTabPresetTabs($preset_name, $value, $name = null)
 	{
 		$preset = $this->getEditTabPresetByName($preset_name, false);
 
 		if ( $preset === false ) {
 			throw new InvalidArgumentException('Edit tab preset "' . $preset_name . '" not defined in "' . $this->_prefix . '" unit config');
 		}
 
 		if ( isset($name) ) {
 			$preset[$name] = $value;
 		}
 		else {
 			$preset = array_merge($preset, $value);
 		}
 
 		$this->addEditTabPresets($preset, $preset_name);
 
 		return $this;
 	}
 
 	public function addGridFields($grid_name, $value, $name = null)
 	{
 		$grid = $this->getGridByName($grid_name, false);
 
 		if ( $grid === false ) {
 			throw new InvalidArgumentException('Grid "' . $grid_name . '" not defined in "' . $this->_prefix . '" unit config');
 		}
 
 		if ( isset($name) ) {
 			$grid['Fields'][$name] = $value;
 		}
 		else {
 			$grid['Fields'] = array_merge($grid['Fields'], $value);
 		}
 
 		$this->addGrids($grid, $grid_name);
 
 		return $this;
 	}
 
 	/**
 	 * Returns individual permission section
 	 *
 	 * @param string $name
 	 * @param Array $default
 	 * @return Array
 	 * @access public
 	 * @todo Rename setting to plural form and them remove this method
 	 */
 	public function getPermSectionByName($name, $default = Array ())
 	{
 		$perm_sections = $this->getPermSection(Array ());
 
 		return isset($perm_sections[$name]) ? $perm_sections[$name] : $default;
 	}
 
 	/**
 	 * Returns foreign key by given prefix
 	 *
 	 * @param string $parent_prefix
 	 * @param mixed $default
 	 * @return string|bool
 	 * @access public
 	 */
 	public function getForeignKey($parent_prefix = null, $default = false)
 	{
 		return $this->_getSettingByPrefix('ForeignKey', $parent_prefix, $default);
 	}
 
 	/**
 	 * Returns parent table key by given prefix
 	 *
 	 * @param string $parent_prefix
 	 * @param mixed $default
 	 * @return string|bool
 	 * @access public
 	 */
 	public function getParentTableKey($parent_prefix = null, $default = false)
 	{
 		return $this->_getSettingByPrefix('ParentTableKey', $parent_prefix, $default);
 	}
 
 	/**
 	 * Returns value of a setting by prefix (special workaround for non-special settings)
 	 *
 	 * @param string $name
 	 * @param string $prefix
 	 * @param mixed $default
 	 * @return mixed
 	 * @access protected
 	 */
 	protected function _getSettingByPrefix($name, $prefix = null, $default = false)
 	{
 		$value = $this->getSetting($name, $default);
 
 		if ( !is_array($value) || !isset($prefix) ) {
 			return $value;
 		}
 
 		return isset($value[$prefix]) ? $value[$prefix] : $default;
 	}
 
 	/**
 	 * Returns status field with option to get first field only
 	 *
 	 * @param bool $first_only
 	 * @param mixed $default
 	 * @return mixed
 	 * @access public
 	 */
 	public function getStatusField($first_only = false, $default = false)
 	{
 		$value = $this->getSetting('StatusField', $default);
 
 		return $first_only ? $value[0] : $value;
 	}
+
+	/**
+	 * Register necessary classes
+	 * This method should only process the data which is cached!
+	 *
+	 * @return void
+	 * @access public
+	 */
+	public function parse()
+	{
+		$this->_parseClasses();
+		$this->_parseScheduledTasks();
+		$this->_parseHooks();
+		$this->_parseAggregatedTags();
+	}
+
+	protected function _parseClasses()
+	{
+		$register_classes = $this->_getClasses();
+
+		foreach ($register_classes as $class_info) {
+			$this->Application->registerClass(
+				$class_info['class'],
+				$this->getBasePath() . DIRECTORY_SEPARATOR . $class_info['file'],
+				$class_info['pseudo']
+			);
+
+			if ( isset($class_info['build_event']) && $class_info['build_event'] && $class_info['build_event'] != 'OnBuild' ) {
+				$this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event']));
+			}
+		}
+	}
+
+	protected function _getClasses()
+	{
+		$register_classes = $this->getRegisterClasses();
+		$class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass');
+
+		foreach ($class_params as $param_name) {
+			$value = $this->getSetting($param_name);
+
+			if ( !$value ) {
+				continue;
+			}
+
+			$value['pseudo'] = $this->_getPseudoByOptionName($param_name);
+			$this->setSetting($param_name, $value);
+
+			$register_classes[] = $value;
+		}
+
+		return $register_classes;
+	}
+
+	protected function _getPseudoByOptionName($option_name)
+	{
+		$pseudo_class_map = Array (
+			'ItemClass' => '%s',
+			'ListClass' => '%s_List',
+			'EventHandlerClass' => '%s_EventHandler',
+			'TagProcessorClass' => '%s_TagProcessor'
+		);
+
+		return sprintf($pseudo_class_map[$option_name], $this->_prefix);
+	}
+
+	protected function _parseScheduledTasks()
+	{
+		if ( !$this->getScheduledTasks() ) {
+			return ;
+		}
+
+		$scheduled_tasks = $this->getScheduledTasks();
+
+		foreach ($scheduled_tasks as $short_name => $scheduled_task_info) {
+			$event_status = array_key_exists('Status', $scheduled_task_info) ? $scheduled_task_info['Status'] : STATUS_ACTIVE;
+			$this->Application->delayUnitProcessing('registerScheduledTask', Array ($short_name, $this->_prefix . ':' . $scheduled_task_info['EventName'], $scheduled_task_info['RunSchedule'], $event_status));
+		}
+	}
+
+	protected function _parseHooks()
+	{
+		$hooks = $this->getHooks();
+
+		if ( !$hooks ) {
+			return;
+		}
+
+		foreach ($hooks as $hook) {
+			if ( $this->getParentPrefix() && ($hook['HookToPrefix'] == $this->getParentPrefix()) ) {
+				trigger_error('Deprecated Hook Usage [prefix: <strong>' . $this->_prefix . '</strong>; do_prefix: <strong>' . $hook['DoPrefix'] . '</strong>] use <strong>#PARENT#</strong> as <strong>HookToPrefix</strong> value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
+			}
+
+			if ( $hook['HookToPrefix'] == '' ) {
+				// new: set hooktoprefix to current prefix if not set
+				$hook['HookToPrefix'] = $this->_prefix;
+			}
+
+			if ( $this->getParentPrefix() ) {
+				// new: allow to set hook to parent prefix what ever it is
+				if ( $hook['HookToPrefix'] == '#PARENT#' ) {
+					$hook['HookToPrefix'] = $this->getParentPrefix();
+				}
+
+				if ( $hook['DoPrefix'] == '#PARENT#' ) {
+					$hook['DoPrefix'] = $this->getParentPrefix();
+				}
+			}
+			elseif ( $hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#' ) {
+				// we need parent prefix but it's not set !
+				continue;
+			}
+
+			$hook_events = (array)$hook['HookToEvent'];
+			$do_prefix = $hook['DoPrefix'] == '' ? $this->_prefix : $hook['DoPrefix'];
+
+			foreach ($hook_events as $hook_event) {
+				$hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event;
+				$do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'];
+
+				$this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional']));
+			}
+		}
+	}
+
+	protected function _parseAggregatedTags()
+	{
+		$aggregated_tags = $this->getAggregateTags();
+
+		if ( !$aggregated_tags ) {
+			return;
+		}
+
+		foreach ($aggregated_tags as $aggregate_tag) {
+			if ( $this->getParentPrefix() ) {
+				if ( $aggregate_tag['AggregateTo'] == $this->getParentPrefix() ) {
+					trigger_error('Deprecated Aggregate Tag Usage [prefix: <b>' . $this->_prefix . '</b>; AggregateTo: <b>' . $aggregate_tag['AggregateTo'] . '</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
+				}
+
+				if ( $aggregate_tag['AggregateTo'] == '#PARENT#' ) {
+					$aggregate_tag['AggregateTo'] = $this->getParentPrefix();
+				}
+			}
+
+			$aggregate_tag['LocalPrefix'] = $this->_prefix;
+			$this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag));
+		}
+	}
+
+	public function validate()
+	{
+		global $debugger;
+
+		$table_name = $this->getTableName();
+		$float_types = Array ('float', 'double', 'numeric');
+
+		$table_found = $this->Conn->Query('SHOW TABLES LIKE "' . $table_name . '"');
+
+		if ( !$table_found ) {
+			// config present, but table missing, strange
+			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
+			$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$table_name</strong> missing, but prefix <b>" . $this->_prefix . "</b> requires it!");
+			$debugger->WarningCount++;
+
+			return;
+		}
+
+		$res = $this->Conn->Query('DESCRIBE ' . $table_name);
+		$config_link = $debugger->getFileLink(FULL_PATH . $this->_filename, 1, $this->_prefix);
+
+		$error_messages = Array (
+			'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config',
+			'default_missing' => 'Default value for field <strong>%s</strong> not set in config',
+			'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required',
+			'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>',
+			'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>',
+			'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)',
+			'date_column_not_null_error' => 'Field <strong>%s</strong> must be NULL in config and database, since it contains date',
+			'user_column_default_error' => 'Field <strong>%s</strong> must be have NULL as default value, since it holds user id',
+			'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
+			'virtual_type_missing' => '<strong>Type definition</strong> for virtual field <strong>%s</strong> missing in config',
+			'virtual_default_missing' => 'Default value for virtual field <strong>%s</strong> not set in config',
+			'virtual_not_null_error' => 'Virtual field <strong>%s</strong> cannot be not null, since it doesn\'t exist in database',
+			'invalid_calculated_field' => 'Calculated field <strong>%s</strong> is missing corresponding virtual field',
+		);
+
+		$config_errors = Array ();
+		$fields = $this->getFields();
+		$table_name = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', '\\1', $table_name); // remove table prefix
+
+		if ( $fields ) {
+			// validate unit config field declaration in relation to database table structure
+			foreach ($res as $field) {
+				$f_name = $field['Field'];
+
+				if ( preg_match('/l[\d]+_[\w]/', $f_name) ) {
+					// skip multilingual fields
+					continue;
+				}
+
+				if ( !array_key_exists($f_name, $fields) ) {
+					$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
+				}
+				else {
+					$db_default = $field['Default'];
+
+					if ( is_numeric($db_default) ) {
+						$db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default;
+					}
+
+					$default_missing = false;
+					$options = $fields[$f_name];
+					$not_null = isset($options['not_null']) && $options['not_null'];
+					$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
+
+					if ( !array_key_exists('default', $options) ) {
+						$config_errors[] = sprintf($error_messages['default_missing'], $f_name);
+						$default_missing = true;
+					}
+
+					if ( $field['Null'] != 'YES' ) {
+						// field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "")
+						if ( $f_name != $this->getIDField() && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
+							$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
+						}
+						if ( $not_null && !isset($options['default']) ) {
+							$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
+						}
+					}
+					elseif ( $not_null ) {
+						$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
+					}
+
+					if ( ($formatter == 'kDateFormatter') && $not_null ) {
+						$config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name);
+					}
+
+					// columns, holding userid should have NULL as default value
+					if ( array_key_exists('type', $options) && !$default_missing ) {
+						// both type and default value set
+
+						if ( preg_match('/ById$/', $f_name) && $options['default'] !== null ) {
+							$config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name);
+						}
+					}
+
+					if ( !array_key_exists('type', $options) ) {
+						$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
+					}
+
+					if ( !$default_missing && ($field['Type'] != 'text') ) {
+						if ( is_null($db_default) && $not_null ) {
+							$db_default = $options['type'] == 'string' ? '' : 0;
+						}
+
+						if ( $f_name == $this->getIDField() && $options['type'] != 'string' && $options['default'] !== 0 ) {
+							$config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->_varDump($options['default']), $this->_varDump($field['Default']));
+						}
+						else if ( ((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types) ) {
+							$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->_varDump($options['default']), $this->_varDump($db_default));
+						}
+					}
+				}
+			}
+		}
+
+		// validate virtual fields
+		$virtual_fields = $this->getVirtualFields();
+
+		if ( $virtual_fields ) {
+			foreach ($virtual_fields as $f_name => $options) {
+				if ( !array_key_exists('type', $options) ) {
+					$config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name);
+				}
+
+				if ( array_key_exists('not_null', $options) ) {
+					$config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name);
+				}
+
+				if ( !array_key_exists('default', $options) ) {
+					$config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name);
+				}
+			}
+		}
+
+		// validate calculated fields
+		if ( $this->getCalculatedFieldSpecials() ) {
+			foreach ($this->getCalculatedFieldSpecials() as $special) {
+				foreach ($this->getCalculatedFieldsBySpecial($special) as $calculated_field => $calculated_field_expr) {
+					if ( !isset($virtual_fields[$calculated_field]) ) {
+						$config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field);
+					}
+				}
+			}
+
+			$config_errors = array_unique($config_errors);
+		}
+
+		if ( $config_errors ) {
+			$error_prefix = '<strong class="debug_error">Config Error' . (count($config_errors) > 1 ? 's' : '') . ': </strong> for prefix <strong>' . $config_link . '</strong> (' . $table_name . ') in unit config:<br />';
+			$config_errors = $error_prefix . '&nbsp;&nbsp;&nbsp;' . implode('<br />&nbsp;&nbsp;&nbsp;', $config_errors);
+
+			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
+			$debugger->appendHTML($config_errors);
+			$debugger->WarningCount++;
+		}
+	}
+
+	protected function _varDump($value)
+	{
+		return '<strong>' . var_export($value, true) . '</strong> of ' . gettype($value);
+	}
 }
Index: branches/5.3.x/core/kernel/utility/unit_config_reader.php
===================================================================
--- branches/5.3.x/core/kernel/utility/unit_config_reader.php	(revision 15731)
+++ branches/5.3.x/core/kernel/utility/unit_config_reader.php	(revision 15732)
@@ -1,1009 +1,691 @@
 <?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!');
 
 class kUnitConfigReader extends kBase implements kiCacheable {
 
 	/**
 	 * Configs reader
 	 *
 	 * @var Array|kUnitConfig[]
 	 * @access private
 	 */
 	var $configData = Array();
 
 	var $configFiles = Array();
 
 	var $CacheExpired = false;
 
 	var $prefixFiles = array();
 
 	var $ProcessAllConfigs = false;
 	var $FinalStage = false;
 	var $StoreCache = false;
 	var $AfterConfigProcessed = array();
 
 	/**
 	 * Escaped directory separator for using in regular expressions
 	 *
 	 * @var string
 	 */
 	var $_directorySeparator = '';
 
 	/**
 	 * Regular expression for detecting module folder
 	 *
 	 * @var string
 	 */
 	var $_moduleFolderRegExp = '';
 
 	/**
 	 * Folders to skip during unit config search
 	 *
 	 * @var Array
 	 */
 	var $_skipFolders = Array ('CVS', '.svn', 'admin_templates', 'libchart');
 
 	/**
 	 * Creates instance of unit config reader
 	 *
 	 */
 	public function __construct()
 	{
 		parent::__construct();
 
 		$this->_directorySeparator = preg_quote(DIRECTORY_SEPARATOR);
 
 		$editor_path = explode('/', trim(EDITOR_PATH, '/'));
 		$this->_skipFolders[] = array_pop($editor_path); // last of cmseditor folders
 
 		$this->_moduleFolderRegExp = '#' . $this->_directorySeparator . '(core|modules' . $this->_directorySeparator . '.*?)' . $this->_directorySeparator . '#';
 	}
 
 	/**
 	 * Sets data from cache to object
 	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
 		$this->prefixFiles = $data['ConfigReader.prefixFiles'];
 	}
 
 	/**
 	 * Gets object data for caching
 	 *
 	 * @access public
 	 * @return Array
 	 */
 	public function getToCache()
 	{
 		return Array (
 			'ConfigReader.prefixFiles' => $this->prefixFiles,
 		);
 	}
 
 	function scanModules($folderPath, $cache = true)
 	{
 		if (defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE')) {
 			// disable config caching during installation
 			$cache = false;
 		}
 
 		if ($cache) {
 			$restored = $this->Application->cacheManager->LoadUnitCache();
 
 			if ($restored) {
 				if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
 					$this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache');
 				}
 
 				return;
 			}
 		}
 
 		if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
 			$this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache');
 		}
 
 		$this->ProcessAllConfigs = true;
 
 		$this->includeConfigFiles($folderPath, $cache);
 		$this->ParseConfigs();
 
 		// tell AfterConfigRead to store cache if needed
 		// can't store it here because AfterConfigRead needs ability to change config data
 		$this->StoreCache = $cache;
 
 		if ( !$this->Application->InitDone ) {
 			// scanModules is called multiple times during installation process
 			$this->Application->InitManagers();
 
 			// get build-in rewrite listeners ONLY to be able to parse mod-rewrite url when unit config cache is missing
 			$this->retrieveCollections();
 			$this->_sortRewriteListeners();
 		}
 
 		$this->Application->cacheManager->applyDelayedUnitProcessing();
 	}
 
 	function findConfigFiles($folderPath, $level = 0)
 	{
 		// if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted
 		$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
 		$folderPath = preg_replace($reg_exp, '', $folderPath, 1); // this make sense, since $folderPath may NOT contain FULL_PATH
 
 		$base_folder = FULL_PATH . $folderPath . DIRECTORY_SEPARATOR;
 		$sub_folders = glob($base_folder . '*', GLOB_ONLYDIR);
 		if (!$sub_folders) {
 			return ;
 		}
 
 		if ($level == 0) {
 			// don't scan Front-End themes because of extensive directory structure
 			$sub_folders = array_diff($sub_folders, Array ($base_folder . 'themes', $base_folder . 'tools'));
 		}
 
 		foreach ($sub_folders as $full_path) {
 			$sub_folder = substr($full_path, strlen($base_folder));
 
 			if (in_array($sub_folder, $this->_skipFolders)) {
 				continue;
 			}
 
 			if (preg_match('/^\./', $sub_folder)) {
 				// don't scan ".folders"
 				continue;
 			}
 
 			$config_name = $this->getConfigName($folderPath . DIRECTORY_SEPARATOR . $sub_folder);
 
 			if (file_exists(FULL_PATH . $config_name)) {
 				$this->configFiles[] = $config_name;
 			}
 
 			$this->findConfigFiles($full_path, $level + 1);
 		}
 	}
 
 	function includeConfigFiles($folderPath, $cache = true)
 	{
 		$this->Application->refreshModuleInfo();
 
 		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 			$data = $this->Application->getCache('master:config_files', false, $cache ? CacheSettings::$unitCacheRebuildTime : 0);
 		}
 		else {
 			$data = $this->Application->getDBCache('config_files', $cache ? CacheSettings::$unitCacheRebuildTime : 0);
 		}
 
 		if ( $data ) {
 			$this->configFiles = unserialize($data);
 
 			if ( !defined('DBG_VALIDATE_CONFIGS') && !DBG_VALIDATE_CONFIGS ) {
 				shuffle($this->configFiles);
 			}
 		}
 		else {
 			$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from core directory
 			$this->findConfigFiles($folderPath); // search from modules directory
 
 			if ( $cache ) {
 				if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 					$this->Application->setCache('master:config_files', serialize($this->configFiles));
 				}
 				else {
 					$this->Application->setDBCache('config_files', serialize($this->configFiles));
 				}
 			}
 		}
 
 		foreach ($this->configFiles as $filename) {
 			$prefix = $this->PreloadConfigFile($filename);
 
 			if (!$prefix) {
 				throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>');
 			}
 		}
 
 		if ($cache) {
 			unset($this->configFiles);
 		}
 	}
 
 	/**
 	 * Process all read config files - called ONLY when there is no cache!
 	 *
 	 */
 	function ParseConfigs()
 	{
 		// 1. process normal configs
 		$prioritized_configs = Array ();
 
 		foreach ($this->configData as $prefix => $config) {
 			if ( $config->getConfigPriority() !== false ) {
 				$prioritized_configs[$prefix] = $config->getConfigPriority();
 				continue;
 			}
 
-			$this->parseConfig($prefix);
+			$config->parse();
 		}
 
 		foreach ($this->configData as $prefix => $config) {
 			$this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 			$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
 		}
 
 		// 2. process prioritized configs
 		asort($prioritized_configs);
+
 		foreach ($prioritized_configs as $prefix => $priority) {
-			$this->parseConfig($prefix);
+			$this->configData[$prefix]->parse();
 		}
 	}
 
 	function AfterConfigRead($store_cache = null)
 	{
 //		if (!$this->ProcessAllConfigs) return ;
 		$this->FinalStage = true;
 		foreach ($this->configData as $prefix => $config) {
 			$this->runAfterConfigRead($prefix);
 		}
 
 		if ( !isset($store_cache) ) {
 			// $store_cache not overridden -> use global setting
 			$store_cache = $this->StoreCache;
 		}
 
 		if ( $store_cache || (defined('IS_INSTALL') && IS_INSTALL) ) {
 			// cache is not stored during install, but dynamic clones should be processed in any case
 			$this->processDynamicClones();
 			$this->retrieveCollections();
 		}
 
 		if ( $store_cache ) {
 			$this->_sortRewriteListeners();
 
 			$this->Application->HandleEvent(new kEvent('adm:OnAfterCacheRebuild'));
 
 			$this->Application->cacheManager->UpdateUnitCache();
 
 			if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS ) {
 				// validate configs here to have changes from OnAfterConfigRead hooks to prefixes
 				foreach ($this->configData as $prefix => $config) {
 					if ( !$config->getTableName() ) {
 						continue;
 					}
 
-					$this->ValidateConfig($prefix);
+					$config->validate();
 				}
 			}
 		}
 	}
 
 	/**
 	 * Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first)
 	 *
 	 */
 	function _sortRewriteListeners()
 	{
 		$listeners = Array ();
 		$prioritized_listeners = Array ();
 
 		// process non-prioritized listeners
 		foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
 			if ($listener_data['priority'] === false) {
 				$listeners[$prefix] = $listener_data;
 			}
 			else {
 				$prioritized_listeners[$prefix] = $listener_data['priority'];
 			}
 		}
 
 		// process prioritized listeners
 		asort($prioritized_listeners, SORT_NUMERIC);
 		foreach ($prioritized_listeners as $prefix => $priority) {
 			$listeners[$prefix] = $this->Application->RewriteListeners[$prefix];
 		}
 
 		$this->Application->RewriteListeners = $listeners;
 	}
 
 	/**
 	 * Re-reads all configs
 	 *
 	 */
 	function ReReadConfigs()
 	{
 		// don't reset prefix file, since file scanning could slow down the process
 		$prefix_files_backup = $this->prefixFiles;
 		$this->Application->cacheManager->EmptyUnitCache();
 		$this->prefixFiles = $prefix_files_backup;
 
 		// parse all configs
 		$this->ProcessAllConfigs = true;
 		$this->AfterConfigProcessed = Array ();
 		$this->includeConfigFiles(MODULES_PATH, false);
 		$this->ParseConfigs();
 		$this->AfterConfigRead(false);
 		$this->processDynamicClones();
 
 		// don't call kUnitConfigReader::retrieveCollections since it
 		// will overwrite what we already have in kApplication class instance
 	}
 
 	/**
 	 * Process clones, that were defined via OnAfterConfigRead event
 	 *
 	 */
 	function processDynamicClones()
 	{
 		$new_clones = Array();
 		foreach ($this->configData as $prefix => $config) {
 			$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
 
 			if ($clones) {
 				$new_clones = array_merge($new_clones, $clones);
 			}
 		}
 
 		// execute delayed methods for cloned unit configs
 		$this->Application->cacheManager->applyDelayedUnitProcessing();
 
 		// call OnAfterConfigRead for cloned configs
 		$new_clones = array_unique($new_clones);
 		foreach ($new_clones as $prefix) {
 			$this->runAfterConfigRead($prefix);
 		}
 	}
 
 	/**
 	 * Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events
 	 *
 	 */
 	function retrieveCollections()
 	{
 		foreach ($this->configData as $prefix => $config) {
 			// collect replacement templates
 			if ( $config->getReplacementTemplates() ) {
 				$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config->getReplacementTemplates());
 			}
 
 			// collect rewrite listeners
 			if ( $config->getRewriteListener() ) {
 				$rewrite_listeners = $config->getRewriteListener();
 
 				if ( !is_array($rewrite_listeners) ) {
 					// when one method is used to build and parse url
 					$rewrite_listeners = Array ($rewrite_listeners, $rewrite_listeners);
 				}
 
 				foreach ($rewrite_listeners as $index => $rewrite_listener) {
 					if ( strpos($rewrite_listener, ':') === false ) {
 						$rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener;
 					}
 				}
 
 				$rewrite_priority = $config->getRewritePriority();
 
 				$this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listeners, 'priority' => $rewrite_priority);
 			}
 		}
 	}
 
-	/**
-	 * Register nessasary classes
-	 * This method should only process the data which is cached!
-	 *
-	 * @param string $prefix
-	 * @access private
-	 */
-	function parseConfig($prefix)
-	{
-		$this->parseClasses($prefix);
-		$this->parseScheduledTasks($prefix);
-		$this->parseHooks($prefix);
-		$this->parseAggregatedTags($prefix);
-	}
-
-	protected function parseClasses($prefix)
-	{
-		$config = $this->configData[$prefix];
-		$register_classes = $this->getClasses($prefix);
-
-		foreach ($register_classes as $class_info) {
-			$this->Application->registerClass(
-				$class_info['class'],
-				$config->getBasePath() . DIRECTORY_SEPARATOR . $class_info['file'],
-				$class_info['pseudo']
-			);
-
-			if ( isset($class_info['build_event']) && $class_info['build_event'] ) {
-				$this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event']));
-			}
-		}
-	}
-
-	protected function parseScheduledTasks($prefix)
-	{
-		$config = $this->configData[$prefix];
-
-		if ( !$config->getScheduledTasks() ) {
-			return ;
-		}
-
-		$scheduled_tasks = $config->getScheduledTasks();
-
-		foreach ($scheduled_tasks as $short_name => $scheduled_task_info) {
-			$event_status = array_key_exists('Status', $scheduled_task_info) ? $scheduled_task_info['Status'] : STATUS_ACTIVE;
-			$this->Application->delayUnitProcessing('registerScheduledTask', Array ( $short_name, $config->getPrefix() . ':' . $scheduled_task_info['EventName'], $scheduled_task_info['RunSchedule'], $event_status ));
-		}
-	}
-
-	protected function parseHooks($prefix)
-	{
-		$config = $this->configData[$prefix];
-
-		if ( !$config->getHooks() ) {
-			return;
-		}
-
-		$hooks = $config->getHooks();
-
-		foreach ($hooks as $hook) {
-			if ( $config->getParentPrefix() && ($hook['HookToPrefix'] == $config->getParentPrefix()) ) {
-				trigger_error('Deprecated Hook Usage [prefix: <strong>' . $config->getPrefix() . '</strong>; do_prefix: <strong>' . $hook['DoPrefix'] . '</strong>] use <strong>#PARENT#</strong> as <strong>HookToPrefix</strong> value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
-			}
-
-			if ( $hook['HookToPrefix'] == '' ) {
-				// new: set hooktoprefix to current prefix if not set
-				$hook['HookToPrefix'] = $config->getPrefix();
-			}
-
-			if ( $config->getParentPrefix() ) {
-				// new: allow to set hook to parent prefix what ever it is
-				if ( $hook['HookToPrefix'] == '#PARENT#' ) {
-					$hook['HookToPrefix'] = $config->getParentPrefix();
-				}
-
-				if ( $hook['DoPrefix'] == '#PARENT#' ) {
-					$hook['DoPrefix'] = $config->getParentPrefix();
-				}
-			}
-			elseif ( $hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#' ) {
-				// we need parent prefix but it's not set !
-				continue;
-			}
-
-			$hook_events = (array)$hook['HookToEvent'];
-			$do_prefix = $hook['DoPrefix'] == '' ? $config->getPrefix() : $hook['DoPrefix'];
-
-			foreach ($hook_events as $hook_event) {
-				$hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event;
-				$do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'];
-
-				$this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional']));
-			}
-		}
-	}
-
-	protected function parseAggregatedTags($prefix)
-	{
-		$config = $this->configData[$prefix];
-		$aggregated_tags = $config->getAggregateTags();
-
-		if ( !$aggregated_tags ) {
-			return;
-		}
-
-		foreach ($aggregated_tags as $aggregate_tag) {
-			if ( $config->getParentPrefix() ) {
-				if ( $aggregate_tag['AggregateTo'] == $config->getParentPrefix() ) {
-					trigger_error('Deprecated Aggregate Tag Usage [prefix: <b>' . $config->getPrefix() . '</b>; AggregateTo: <b>' . $aggregate_tag['AggregateTo'] . '</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
-				}
-
-				if ( $aggregate_tag['AggregateTo'] == '#PARENT#' ) {
-					$aggregate_tag['AggregateTo'] = $config->getParentPrefix();
-				}
-			}
-
-			$aggregate_tag['LocalPrefix'] = $config->getPrefix();
-			$this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag));
-		}
-	}
-
-	function ValidateConfig($prefix)
-	{
-		global $debugger;
-
-		$config = $this->configData[$prefix];
-
-		$table_name = $config->getTableName();
-		$float_types = Array ('float', 'double', 'numeric');
-
-		$table_found = $this->Conn->Query('SHOW TABLES LIKE "' . $table_name . '"');
-		if ( !$table_found ) {
-			// config present, but table missing, strange
-			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
-			$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$table_name</strong> missing, but prefix <b>" . $prefix . "</b> requires it!");
-			$debugger->WarningCount++;
-
-			return;
-		}
-
-		$res = $this->Conn->Query('DESCRIBE ' . $table_name);
-		$config_link = $debugger->getFileLink(FULL_PATH . $this->prefixFiles[$prefix], 1, $prefix);
-
-		$error_messages = Array (
-			'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config',
-			'default_missing' => 'Default value for field <strong>%s</strong> not set in config',
-			'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required',
-			'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>',
-			'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>',
-			'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)',
-			'date_column_not_null_error' => 'Field <strong>%s</strong> must be NULL in config and database, since it contains date',
-			'user_column_default_error' => 'Field <strong>%s</strong> must be have NULL as default value, since it holds user id',
-			'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
-			'virtual_type_missing' => '<strong>Type definition</strong> for virtual field <strong>%s</strong> missing in config',
-			'virtual_default_missing' => 'Default value for virtual field <strong>%s</strong> not set in config',
-			'virtual_not_null_error' => 'Virtual field <strong>%s</strong> cannot be not null, since it doesn\'t exist in database',
-			'invalid_calculated_field' => 'Calculated field <strong>%s</strong> is missing corresponding virtual field',
-		);
-
-		$config_errors = Array ();
-		$fields = $config->getFields();
-		$table_name = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', '\\1', $table_name); // remove table prefix
-
-		if ( $fields ) {
-			// validate unit config field declaration in relation to database table structure
-			foreach ($res as $field) {
-				$f_name = $field['Field'];
-
-				if ( preg_match('/l[\d]+_[\w]/', $f_name) ) {
-					// skip multilingual fields
-					continue;
-				}
-
-				if ( !array_key_exists($f_name, $fields) ) {
-					$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
-				}
-				else {
-					$db_default = $field['Default'];
-
-					if ( is_numeric($db_default) ) {
-						$db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default;
-					}
-
-					$default_missing = false;
-					$options = $fields[$f_name];
-					$not_null = isset($options['not_null']) && $options['not_null'];
-					$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
-
-					if ( !array_key_exists('default', $options) ) {
-						$config_errors[] = sprintf($error_messages['default_missing'], $f_name);
-						$default_missing = true;
-					}
-
-					if ( $field['Null'] != 'YES' ) {
-						// field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "")
-						if ( $f_name != $config->getIDField() && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
-							$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
-						}
-						if ( $not_null && !isset($options['default']) ) {
-							$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
-						}
-					}
-					elseif ( $not_null ) {
-						$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
-					}
-
-					if ( ($formatter == 'kDateFormatter') && $not_null ) {
-						$config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name);
-					}
-
-					// columns, holding userid should have NULL as default value
-					if ( array_key_exists('type', $options) && !$default_missing ) {
-						// both type and default value set
-
-						if ( preg_match('/ById$/', $f_name) && $options['default'] !== null ) {
-							$config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name);
-						}
-					}
-
-					if ( !array_key_exists('type', $options) ) {
-						$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
-					}
-
-					if ( !$default_missing && ($field['Type'] != 'text') ) {
-						if ( is_null($db_default) && $not_null ) {
-							$db_default = $options['type'] == 'string' ? '' : 0;
-						}
-
-						if ( $f_name == $config->getIDField() && $options['type'] != 'string' && $options['default'] !== 0 ) {
-							$config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
-						}
-						else if ( ((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types) ) {
-							$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($db_default));
-						}
-					}
-				}
-			}
-		}
-
-		// validate virtual fields
-		if ( $config->getVirtualFields()  ) {
-			foreach ($config->getVirtualFields() as $f_name => $options) {
-				if ( !array_key_exists('type', $options) ) {
-					$config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name);
-				}
-
-				if ( array_key_exists('not_null', $options) ) {
-					$config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name);
-				}
-
-				if ( !array_key_exists('default', $options) ) {
-					$config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name);
-				}
-			}
-		}
-
-		// validate calculated fields
-		if ( $config->getCalculatedFieldSpecials() ) {
-			$virtual_fields = $config->getVirtualFields();
-
-			foreach ($config->getCalculatedFieldSpecials() as $special) {
-				foreach ($config->getCalculatedFieldsBySpecial($special) as $calculated_field => $calculated_field_expr) {
-					if ( !isset($virtual_fields[$calculated_field]) ) {
-						$config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field);
-					}
-				}
-			}
-
-			$config_errors = array_unique($config_errors);
-		}
-
-		if ( $config_errors ) {
-			$error_prefix = '<strong class="debug_error">Config Error' . (count($config_errors) > 1 ? 's' : '') . ': </strong> for prefix <strong>' . $config_link . '</strong> (' . $table_name . ') in unit config:<br />';
-			$config_errors = $error_prefix . '&nbsp;&nbsp;&nbsp;' . implode('<br />&nbsp;&nbsp;&nbsp;', $config_errors);
-
-			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
-			$debugger->appendHTML($config_errors);
-			$debugger->WarningCount++;
-		}
-	}
-
-	function varDump($value)
-	{
-		return '<strong>'.var_export($value, true).'</strong> of '.gettype($value);
-	}
-
 	function postProcessConfig($prefix, $config_key, $dst_prefix_var)
 	{
 		$main_config = $this->configData[$prefix];
 		$sub_configs = $main_config->getSetting($config_key);
 
 		if ( !$sub_configs ) {
 			return Array ();
 		}
 
 		$processed = Array ();
 		$main_config->setSetting($config_key, null);
 
 		foreach ($sub_configs as $sub_prefix => $sub_config_data) {
 			if ( $config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix]) ) {
 				$this->loadConfig($sub_prefix);
 			}
 
 			$sub_config_data['Prefix'] = $sub_prefix;
 			$sub_config_base = $this->configData[$$dst_prefix_var]->getRaw();
 			$sub_config = new kUnitConfig($sub_prefix, kUtil::array_merge_recursive($sub_config_base, $sub_config_data));
 			$this->configData[$sub_prefix] = $sub_config;
 
 			// when merging empty array to non-empty results non-empty array, but empty is required
 			foreach ($sub_config_data as $sub_key => $sub_value) {
 				if ( !$sub_value && is_array($sub_value) ) {
 					$sub_config->setSetting($sub_key, null);
 				}
 			}
 
 			if ( $config_key == 'Clones' ) {
 				$this->prefixFiles[$sub_prefix] = $this->prefixFiles[$prefix];
 			}
 
 			$this->postProcessConfig($sub_prefix, $config_key, $dst_prefix_var);
 
 			if ( $config_key == 'AggregateConfigs' ) {
 				$processed = array_merge($this->postProcessConfig($sub_prefix, 'Clones', 'prefix'), $processed);
 			}
 			elseif ( $this->ProcessAllConfigs ) {
-				$this->parseConfig($sub_prefix);
+				$this->configData[$sub_prefix]->parse();
 			}
 
 			array_push($processed, $sub_prefix);
 		}
 
 		if ( !$prefix ) {
 			// configs, that used only for cloning & not used itself
 			unset($this->configData[$prefix]);
 		}
 
 		return array_unique($processed);
 	}
 
 	function PreloadConfigFile($filename)
 	{
 		$config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename);
 
 		if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) {
 			if ( in_array($filename, get_included_files()) ) {
 				return '';
 			}
 
 			global $debugger;
 
 			if ( $config_found ) {
 				$file = FULL_PATH . $filename;
 				$file_crc = crc32($file);
 
 				$debugger->ProfileStart('inc_' . $file_crc, $file);
 				include_once($file);
 				$debugger->ProfileFinish('inc_' . $file_crc);
 				$debugger->profilerAddTotal('includes', 'inc_' . $file_crc);
 			}
 		}
 		elseif ( $config_found ) {
 			include_once(FULL_PATH . $filename);
 		}
 
 		if ( $config_found ) {
 			/* @var $config kUnitConfig|Array */
 
 			if ( isset($config) && $config ) {
 				// config file is included for 1st time -> save it's content for future processing
 				if ( !is_object($config) ) {
 					$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
 					$config = new kUnitConfig($prefix, $config);
 				}
 				else {
 					$prefix = $config->getPrefix();
 				}
 
 				preg_match($this->_moduleFolderRegExp, $filename, $regs);
 				$config->setModuleFolder(str_replace(DIRECTORY_SEPARATOR, '/', $regs[1]));
 				$config->setBasePath(dirname(FULL_PATH . $filename));
+				$config->setFilename($filename);
 
 				if ( $config->getAdminTemplatePath() !== false ) {
 					// append template base folder for admin templates path of this prefix
 					$module_templates = $regs[1] == 'core' ? '' : substr($regs[1], 8) . '/';
 					$config->setAdminTemplatePath($module_templates . $config->getAdminTemplatePath());
 				}
 
 				if ( array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename) ) {
 					trigger_error(
 						'Single unit config prefix "<strong>' . $prefix . '</strong>" ' .
 						'is used in multiple unit config files: ' .
 						'"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"',
 						E_USER_WARNING
 					);
 				}
 
 				$this->configData[$prefix] = $config;
 				$this->prefixFiles[$prefix] = $filename;
 
 				return $prefix;
 			}
 			else {
 				$prefix = array_search($filename, $this->prefixFiles);
 
 				if ( $prefix ) {
 					// attempt is made to include config file twice or more, but include_once prevents that,
 					// but file exists on hdd, then it is already saved to all required arrays, just return it's prefix
 					return $prefix;
 				}
 			}
 		}
 
 		return 'dummy';
 	}
 
 	function loadConfig($prefix)
 	{
 		if ( !isset($this->prefixFiles[$prefix]) ) {
 			throw new Exception('Configuration file for prefix "<strong>' . $prefix . '</strong>" is unknown');
 
 			return ;
 		}
 
 		$file = $this->prefixFiles[$prefix];
 		$prefix = $this->PreloadConfigFile($file);
 
 		if ($this->FinalStage) {
 			// run prefix OnAfterConfigRead so all
-			// hooks to it can define their clonses
+			// hooks to it can define their clones
 			$this->runAfterConfigRead($prefix);
 		}
 
 		$clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 		$clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones);
 
 		if ($this->FinalStage) {
 			$clones = array_unique($clones);
 
 			foreach ($clones as $a_prefix) {
 				$this->runAfterConfigRead($a_prefix);
 			}
 		}
 	}
 
 	function runAfterConfigRead($prefix)
 	{
 		if (in_array($prefix, $this->AfterConfigProcessed)) {
 			return ;
 		}
 
 		$this->Application->HandleEvent( new kEvent($prefix . ':OnAfterConfigRead') );
 
 		if (!(defined('IS_INSTALL') && IS_INSTALL)) {
 			// allow to call OnAfterConfigRead multiple times during install
 			array_push($this->AfterConfigProcessed, $prefix);
 		}
 	}
 
 	/**
 	 * Returns unit config for given prefix
 	 *
 	 * @param string $prefix
 	 * @return kUnitConfig
 	 * @access public
 	 */
 	public function getUnitConfig($prefix = null)
 	{
 		if ( !isset($this->configData[$prefix]) ) {
 			$this->loadConfig($prefix);
 		}
 
 		return $this->configData[$prefix];
 	}
 
 	/**
 	 * Returns prefixes of unit configs, that were registered
 	 *
 	 * @return Array
 	 * @access public
 	 */
 	public function getPrefixes()
 	{
 		return array_keys($this->configData);
 	}
 
-	protected function getClasses($prefix)
-	{
-		$config = $this->configData[$prefix];
-		$class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass');
-		$register_classes = $config->getRegisterClasses();
-
-		foreach ($class_params as $param_name) {
-			$value = $config->getSetting($param_name);
-
-			if ( !$value ) {
-				continue;
-			}
-
-			$value['pseudo'] = $this->getPseudoByOptionName($param_name, $prefix);
-			$config->setSetting($param_name, $value);
-
-			$register_classes[] = $value;
-		}
-
-		return $register_classes;
-	}
-
-	protected function getPseudoByOptionName($option_name, $prefix)
-	{
-		$pseudo_class_map = Array (
-			'ItemClass' => '%s',
-			'ListClass' => '%s_List',
-			'EventHandlerClass' => '%s_EventHandler',
-			'TagProcessorClass' => '%s_TagProcessor'
-		);
-
-		return sprintf($pseudo_class_map[$option_name], $prefix);
-	}
-
 	/**
 	 * Get's config file name based
 	 * on folder name supplied
 	 *
 	 * @param string $folderPath
 	 * @return string
 	 * @access private
 	 */
 	function getConfigName($folderPath)
 	{
 		return $folderPath . DIRECTORY_SEPARATOR . basename($folderPath) . '_config.php';
 	}
 
 	/**
 	 * Checks if config file is allowed for includion (if module of config is installed)
 	 *
 	 * @param string $config_path relative path from in-portal directory
 	 */
 	function configAllowed($config_path)
 	{
 		static $module_paths = null;
 
 		if (defined('IS_INSTALL') && IS_INSTALL) {
 			// at installation start no modules in db and kernel configs could not be read
 			return true;
 		}
 
 		if (preg_match('#^' . $this->_directorySeparator . 'core#', $config_path)) {
 			// always allow to include configs from "core" module's folder
 			return true;
 		}
 
 		if (!$this->Application->ModuleInfo) {
 			return false;
 		}
 
 		if (!isset($module_paths)) {
 			$module_paths = Array ();
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				$module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/'));
 			}
 
 			$module_paths = array_unique($module_paths);
 		}
 
 		preg_match($this->_moduleFolderRegExp, $config_path, $rets);
 
 		// config file path starts with module folder path
 		return in_array($rets[1], $module_paths);
 	}
 
 	/**
 	 * Returns true if config exists and is allowed for reading
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
 	function prefixRegistred($prefix)
 	{
 		return isset($this->prefixFiles[$prefix]) ? true : false;
 	}
 
 	/**
 	 * Returns config file for given prefix
 	 *
 	 * @param string $prefix
 	 * @return string
 	 */
 	function getPrefixFile($prefix)
 	{
 		return array_key_exists($prefix, $this->prefixFiles) ? $this->prefixFiles[$prefix] : false;
 	}
 
 	function iterateConfigs($callback_function, $params)
 	{
 		$this->includeConfigFiles(MODULES_PATH); //make sure to re-read all configs
 		$this->AfterConfigRead();
 
 		foreach ($this->configData as $prefix => $config) {
 			$callback_function[0]->$callback_function[1]($prefix, $config, $params);
 		}
 	}
 
 }
\ No newline at end of file