Index: branches/5.1.x/core/kernel/utility/unit_config_reader.php
===================================================================
--- branches/5.1.x/core/kernel/utility/unit_config_reader.php	(revision 13701)
+++ branches/5.1.x/core/kernel/utility/unit_config_reader.php	(revision 13702)
@@ -1,1041 +1,1041 @@
 <?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 {
 
 	/**
 	 * Configs readed
 	 *
 	 * @var Array
 	 * @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');
 
 	/**
 	 * Scan kernel and user classes
 	 * for available configs
 	 *
 	 * @access protected
 	 */
 	function Init($prefix,$special)
 	{
 		parent::Init($prefix,$special);
 
 		$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 . '#';
 	}
 
 	function CacheParsedData()
 	{
 		$event_manager =& $this->Application->recallObject('EventManager');
 		$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 
 		$config_vars = Array (
 			// session related
 			'SessionTimeout',
 			'SessionCookieName',
 			'SessionCookieDomains',
 			'SessionBrowserSignatureCheck',
 			'SessionIPAddressCheck',
 			'CookieSessions',
 			'KeepSessionOnBrowserClose',
 			'User_GuestGroup',
 			'User_LoggedInGroup',
 
 			// output related
 			'UseModRewrite',
 			'UseContentLanguageNegotiation',
 			'UseOutputCompression',
 			'OutputCompressionLevel',
 			'Config_Site_Time',
 			'SystemTagCache',
 
 			// tracking related
 			'UseChangeLog',
 			'UseVisitorTracking',
 			'ModRewriteUrlEnding',
 			'ForceModRewriteUrlEnding',
 			'UseCronForRegularEvent',
 		);
 
 		foreach ($config_vars as $var) {
 			$this->Application->ConfigValue($var);
 		}
 
 		$cache = Array(
 			'Factory.Files' => $this->Application->Factory->Files,
 			'Factory.realClasses' => $this->Application->Factory->realClasses,
 			'Factory.Dependencies' => $this->Application->Factory->Dependencies,
 			'ConfigReader.prefixFiles' => $this->prefixFiles,
 			'EventManager.buildEvents' => $event_manager->buildEvents,
 			'EventManager.beforeRegularEvents' => $event_manager->beforeRegularEvents,
 			'EventManager.afterRegularEvents' => $event_manager->afterRegularEvents,
 			'EventManager.beforeHooks' => $event_manager->beforeHooks,
 			'EventManager.afterHooks' => $event_manager->afterHooks,
 			'TagsAggregator.data' => $aggregator->_Array,
 
 			// the following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
 			'Application.Caches.ConfigVariables' => $this->Application->Caches['ConfigVariables'],
 			'Application.ConfigCacheIds' => $this->Application->ConfigCacheIds,
 			'Application.ConfigHash' => $this->Application->ConfigHash,
 			'Application.ReplacementTemplates' => $this->Application->ReplacementTemplates,
 			'Application.RewriteListeners' => $this->Application->RewriteListeners,
 			'Application.ModuleInfo' => $this->Application->ModuleInfo,
 		);
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$this->Application->setCache('master:configs_parsed', serialize($cache));
 			$this->Application->setCache('master:config_files', serialize($this->configFiles));
 		}
 		else {
 			$this->Application->setDBCache('configs_parsed', serialize($cache));
 			$this->Application->setDBCache('config_files', serialize($this->configFiles));
 		}
 
 		$cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s');
 		$this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by);
 
 		unset($this->configFiles);
 	}
 
 	function RestoreParsedData()
 	{
 		$conn =& $this->Application->GetADODBConnection();
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$data = $this->Application->getCache('master:configs_parsed', false);
 		}
 		else {
 			$data = $this->Application->getDBCache('configs_parsed');
 		}
 
 		if ($data) {
 			$cache = unserialize($data);
 			$this->Application->Factory->Files = $cache['Factory.Files'];
 			$this->Application->Factory->realClasses = $cache['Factory.realClasses'];
 			$this->Application->Factory->Dependencies = $cache['Factory.Dependencies'];
 			$this->prefixFiles = $cache['ConfigReader.prefixFiles'];
 
 			$event_manager =& $this->Application->recallObject('EventManager');
 			$event_manager->buildEvents = $cache['EventManager.buildEvents'];
 			$event_manager->beforeRegularEvents = $cache['EventManager.beforeRegularEvents'];
 			$event_manager->afterRegularEvents = $cache['EventManager.afterRegularEvents'];
 			$event_manager->beforeHooks = $cache['EventManager.beforeHooks'];
 			$event_manager->afterHooks = $cache['EventManager.afterHooks'];
 
 			$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 			$aggregator->_Array = $cache['TagsAggregator.data'];
 
 			$this->Application->ConfigHash = $cache['Application.ConfigHash'];
 
 			$this->Application->Caches['ConfigVariables'] = $cache['Application.ConfigCacheIds'];
 			$this->Application->ConfigCacheIds = $cache['Application.ConfigCacheIds'];
 
 			$this->Application->ReplacementTemplates = $cache['Application.ReplacementTemplates'];
 			$this->Application->RewriteListeners = $cache['Application.RewriteListeners'];
 
 			$this->Application->ModuleInfo = $cache['Application.ModuleInfo'];
 
 			return true;
 
 		}
 		else return false;
 	}
 
 	function ResetParsedData($include_sections = false)
 	{
 		$conn =& $this->Application->GetADODBConnection();
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$this->Application->deleteCache('master:configs_parsed');
 		}
 		else {
 			$this->Application->deleteDBCache('configs_parsed');
 		}
 
 		if ($include_sections) {
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$this->Application->deleteCache('master:sections_parsed');
 			}
 			else {
 				$this->Application->deleteDBCache('sections_parsed');
 			}
 		}
 	}
 
 	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->RestoreParsedData();
 			if ($restored) return;
 		}
 
 		$this->ProcessAllConfigs = true;
 
 		$this->includeConfigFiles($folderPath, $cache);
 		$this->ParseConfigs();
 
 		// tell AfterConfigRead to store cache if neede
 		// can't store it here beacuse AfterConfigRead needs ability to change config data
 		$this->StoreCache = $cache;
 	}
 
 	function findConfigFiles($folderPath, $level = 0)
 	{
 		/*if ($level == 0) {
 			if ($this->Application->isDebugMode()) {
 				$start_time = getmicrotime();
 				$this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . $folderPath . '")');
 				$this->Application->Debugger->appendTrace();
 			}
 		}*/
 
 		// 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);
 		}
 
 		/*if ($level == 0) {
 			if ($this->Application->isDebugMode()) {
 				$this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . FULL_PATH . $folderPath . '"): ' . (getmicrotime() - $start_time));
 			}
 		}*/
 	}
 
 	function includeConfigFiles($folderPath, $cache = true)
 	{
 		$this->Application->refreshModuleInfo();
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$data = $this->Application->getCache('master:config_files', false);
 		}
 		else {
 			$data = $this->Application->getDBCache('config_files');
 		}
 
 		if ($cache && $data) {
 			$this->configFiles = unserialize($data);
 			shuffle($this->configFiles);
 		}
 		else {
-			$this->findConfigFiles(FULL_PATH . '/core'); // search from core directory
+			$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from core directory
 			$this->findConfigFiles($folderPath); // search from modules directory
 		}
 
 		foreach ($this->configFiles as $filename)
 		{
 			$prefix = $this->PreloadConfigFile($filename);
 			if (!$prefix) {
 				trigger_error('Prefix not defined in config file ' . $filename, E_USER_ERROR);
 			}
 		}
 	}
 
 	/**
 	 * Process all read config files - called ONLY when there is no cache!
 	 *
 	 */
 	function ParseConfigs()
 	{
 		// 1. process normal configs and their dependencies
 		$prioritized_configs = array();
 		foreach ($this->configData as $prefix => $config) {
 			if (isset($config['ConfigPriority'])) {
 				$prioritized_configs[$prefix] = $config['ConfigPriority'];
 				continue;
 			}
 			$this->parseConfig($prefix);
 		}
 
 		foreach ($this->configData as $prefix => $config) {
 			$this->ProcessDependencies($prefix);
 			$this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 			$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
 		}
 
 		// 2. process prioritized configs and their dependencies
 		asort($prioritized_configs);
 		foreach ($prioritized_configs as $prefix => $priority) {
 			$this->parseConfig($prefix);
 		}
 
 		foreach ($prioritized_configs as $prefix => $priority) {
 			$this->ProcessDependencies($prefix);
 		}
 	}
 
 	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 overrided -> 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->CacheParsedData();
 
 			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 (!isset($config['TableName'])) continue;
 					$this->ValidateConfig($prefix);
 				}
 			}
 
 			$after_event = new kEvent('adm:OnAfterCacheRebuild');
 			$this->Application->HandleEvent($after_event);
 		}
 	}
 
 	/**
 	 * 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()
 	{
 		// clear restored cache (not in db)
 		$this->Application->Factory->Files = Array ();
 		$this->Application->Factory->realClasses = Array ();
 		$this->Application->Factory->Dependencies = Array ();
 
 		$this->Application->EventManager->beforeRegularEvents = Array ();
 		$this->Application->EventManager->afterRegularEvents = Array ();
 		$this->Application->EventManager->beforeHooks = Array ();
 		$this->Application->EventManager->afterHooks = Array ();
 
 		// otherwise ModulesHelper indirectly used from includeConfigFiles won't work
 		$this->Application->RegisterDefaultClasses();
 
 		// parse all configs
 		$this->ProcessAllConfigs = true;
 		$this->AfterConfigProcessed = Array ();
 		$this->includeConfigFiles(MODULES_PATH, false);
 		$this->ParseConfigs();
 		$this->AfterConfigRead(false);
 		$this->processDynamicClones();
 		$this->retrieveCollections();
 	}
 
 	/**
 	 * 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);
 			}
 		}
 
 		// call OnAfterConfigRead for cloned configs
 		$new_clones = array_unique($new_clones);
 		foreach ($new_clones as $prefix) {
 			$this->runAfterConfigRead($prefix);
 		}
 	}
 
 	/**
 	 * Process all collectable unit config options here to also catch ones, defined from OnAfterConfigRead events
 	 *
 	 */
 	function retrieveCollections()
 	{
 		foreach ($this->configData as $prefix => $config) {
 			// collect replacement templates
 			if (array_key_exists('ReplacementTemplates', $config) && $config['ReplacementTemplates']) {
 				$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config['ReplacementTemplates']);
 			}
 
 			// collect rewrite listeners
 			if (array_key_exists('RewriteListener', $config) && $config['RewriteListener']) {
 				$rewrite_listeners = $config['RewriteListener'];
 
 				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 = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false;
 
 				$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)
 	{
 		$config =& $this->configData[$prefix];
 		$event_manager =& $this->Application->recallObject('EventManager');
 		/* @var $event_manager kEventManager */
 
 		$register_classes = getArrayValue($config,'RegisterClasses');
 		if (!$register_classes) $register_classes = Array();
 		$class_params=Array('ItemClass','ListClass','EventHandlerClass','TagProcessorClass');
 		foreach($class_params as $param_name)
 		{
 			if ( !(isset($config[$param_name]) ) ) continue;
 			$config[$param_name]['pseudo'] = $this->getPrefixByParamName($param_name,$prefix);
 			$register_classes[] = $config[$param_name];
 		}
 
 		foreach($register_classes as $class_info)
 		{
 			$require_classes = getArrayValue($class_info, 'require_classes');
 			if ($require_classes) {
 				if (!is_array($require_classes)) {
 					$require_classes = array($require_classes);
 				}
 				if (!isset($config['_Dependencies'][$class_info['class']])) {
 					$config['_Dependencies'][$class_info['class']] = array();
 				}
 				$config['_Dependencies'][$class_info['class']] = array_merge($config['_Dependencies'][$class_info['class']], $require_classes);
 			}
 
 			$this->Application->registerClass(
 				$class_info['class'],
 				$config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'],
 				$class_info['pseudo']/*,
 				getArrayValue($class_info, 'require_classes')*/
 			);
 			if (getArrayValue($class_info, 'build_event')) {
 				$event_manager->registerBuildEvent($class_info['pseudo'],$class_info['build_event']);
 			}
 		}
 
 		$regular_events = getArrayValue($config, 'RegularEvents');
 		if ($regular_events) {
 			foreach ($regular_events as $short_name => $regular_event_info) {
 				$event_status = array_key_exists('Status', $regular_event_info) ? $regular_event_info['Status'] : STATUS_ACTIVE;
 				$event_manager->registerRegularEvent( $short_name, $config['Prefix'].':'.$regular_event_info['EventName'], $regular_event_info['RunInterval'], $regular_event_info['Type'], $event_status);
 			}
 		}
 
 		$hooks = getArrayValue($config, 'Hooks');
 		if (is_array($hooks) && count($hooks) > 0) {
 			foreach ($hooks as $hook) {
 				if (isset($config['ParentPrefix']) && $hook['HookToPrefix'] == $config['ParentPrefix']) {
 					trigger_error('Depricated Hook Usage [prefix: <b>'.$config['Prefix'].'</b>; do_prefix: <b>'.$hook['DoPrefix'].'</b>] use <b>#PARENT#</b> as <b>HookToPrefix</b> value, where HookToPrefix is same as ParentPrefix', E_USER_NOTICE);
 				}
 
 				if ($hook['HookToPrefix'] == '') {
 					$hook['HookToPrefix'] = $config['Prefix']; // new: set hooktoprefix to current prefix if not set
 				}
 
 				if (isset($config['ParentPrefix'])) {
 					// new: allow to set hook to parent prefix what ever it is
 					if ($hook['HookToPrefix'] == '#PARENT#') {
 						$hook['HookToPrefix'] = $config['ParentPrefix'];
 					}
 
 					if ($hook['DoPrefix'] == '#PARENT#') {
 						$hook['DoPrefix'] = $config['ParentPrefix'];
 					}
 				}
 				elseif ($hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#') {
 					continue; // we need parent prefix but it's not set !
 				}
 
 				$do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix'];
 
 				if ( !is_array($hook['HookToEvent']) ) {
 					$hook_events = Array( $hook['HookToEvent'] );
 				}
 				else {
 					$hook_events = $hook['HookToEvent'];
 				}
 				foreach ($hook_events as $hook_event) {
 					$this->Application->registerHook($hook['HookToPrefix'], $hook['HookToSpecial'], $hook_event, $hook['Mode'], $do_prefix, $hook['DoSpecial'], $hook['DoEvent'], $hook['Conditional']);
 				}
 			}
 		}
 
 		if ( is_array(getArrayValue($config, 'AggregateTags')) ) {
 			foreach ($config['AggregateTags'] as $aggregate_tag) {
 				if (isset($config['ParentPrefix'])) {
 					if ($aggregate_tag['AggregateTo'] == $config['ParentPrefix']) {
 						trigger_error('Depricated Aggregate Tag Usage [prefix: <b>'.$config['Prefix'].'</b>; AggregateTo: <b>'.$aggregate_tag['AggregateTo'].'</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', E_USER_NOTICE);
 					}
 
 					if ($aggregate_tag['AggregateTo'] == '#PARENT#') {
 						$aggregate_tag['AggregateTo'] = $config['ParentPrefix'];
 					}
 				}
 				$aggregate_tag['LocalPrefix'] = $config['Prefix'];
 				$this->Application->registerAggregateTag($aggregate_tag);
 			}
 		}
 	}
 
 	function ValidateConfig($prefix)
 	{
 		global $debugger;
 
 		$config =& $this->configData[$prefix];
 
 		$tablename = $config['TableName'];
 		$float_types = Array ('float', 'double', 'numeric');
 		$conn =& $this->Application->GetADODBConnection();
 
 		$table_found = $conn->Query('SHOW TABLES LIKE "'.$tablename.'"');
 		if (!$table_found) {
 			// config present, but table missing, strange
 			$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$tablename</strong> missing, but prefix <b>".$config['Prefix']."</b> requires it!");
 			safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 			return ;
 		}
 
 		$res = $conn->Query('DESCRIBE '.$tablename);
 		$config_link = $debugger->getFileLink(FULL_PATH.$this->prefixFiles[$config['Prefix']], 1, $config['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)',
 			'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
 		);
 
 		$config_errors = Array ();
 		$tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix
 
 		foreach ($res as $field) {
 			$f_name = $field['Field'];
 
 			if (getArrayValue($config, 'Fields')) {
 				if (preg_match('/l[\d]+_[\w]/', $f_name)) {
 					// skip multilingual fields
 					continue;
 				}
 
 				if (!array_key_exists ($f_name, $config['Fields'])) {
 					$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
 				}
 				else {
 					if (is_numeric($field['Default'])) {
 						$field['Default'] = preg_match('/[\.,]/', $field['Default']) ? (float)$field['Default'] : (int)$field['Default'];
 					}
 
 					$options = $config['Fields'][$f_name];
 					$default_missing = 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['IDField'] && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
 							$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
 						}
 						if (isset($options['not_null']) && $options['not_null'] && !isset($options['default']) ) {
 							$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
 						}
 					}
 					else {
 						if (isset($options['not_null']) && $options['not_null']) {
 							$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
 						}
 					}
 
 					if (!array_key_exists('type', $options)) {
 						$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
 					}
 
 					if (!$default_missing) {
 						if ($f_name == $config['IDField'] && $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 ($options['default'] != '#NOW#' && $field['Default'] !== $options['default'] && !in_array($options['type'], $float_types)) {
 							$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
 						}
 					}
 				}
 			}
 		}
 
 		if ($config_errors) {
 			$error_prefix = '<strong class="debug_error">Config Error'.(count($config_errors) > 1 ? 's' : '').': </strong> for prefix <strong>'.$config_link.'</strong> ('.$tablename.') in unit config:<br />';
 			$config_errors = $error_prefix.'&nbsp;&nbsp;&nbsp;'.implode('<br />&nbsp;&nbsp;&nbsp;', $config_errors);
 
 			$debugger->appendHTML($config_errors);
 			safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 		}
 	}
 
 	function varDump($value)
 	{
 		return '<strong>'.var_export($value, true).'</strong> of '.gettype($value);
 	}
 
 	function ProcessDependencies($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$deps = getArrayValue($config, '_Dependencies');
 		if (!$deps) return ;
 
 		foreach ($deps as $real_class => $requires) {
 			foreach ($requires as $class) {
 				$this->Application->registerDependency($real_class, $class);
 			}
 		}
 		unset($config['_Dependencies']);
 	}
 
 	function postProcessConfig($prefix, $config_key, $dst_prefix_var)
 	{
 		$main_config =& $this->configData[$prefix];
 		$sub_configs = isset($main_config[$config_key]) && $main_config[$config_key] ? $main_config[$config_key] : false; // getArrayValue($main_config, $config_key);
 		if (!$sub_configs) {
 			return array();
 		}
 		unset($main_config[$config_key]);
 
 		$processed = array();
 		foreach ($sub_configs as $sub_prefix => $sub_config) {
 			if ($config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix])) {
 				$this->loadConfig($sub_prefix);
 			}
 			$sub_config['Prefix'] = $sub_prefix;
 			$this->configData[$sub_prefix] = array_merge_recursive2($this->configData[$$dst_prefix_var], $sub_config);
 
 			// when merging empty array to non-empty results non-empty array, but empty is required
 			foreach ($sub_config as $sub_key => $sub_value) {
 				if (!$sub_value) {
 					unset($this->configData[$sub_prefix][$sub_key]);
 				}
 			}
 			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);
 			}
 			array_push($processed, $sub_prefix);
 		}
 
 		if (!$prefix) {
 			// configs, that used only for cloning & not used ifself
 			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_required_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) {
 			if (isset($config) && $config) {
 				// config file is included for 1st time -> save it's content for future processing
 				$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
 
 				preg_match($this->_moduleFolderRegExp, $filename, $rets);
-				$config['ModuleFolder'] = $rets[1];
+				$config['ModuleFolder'] = str_replace(DIRECTORY_SEPARATOR, '/', $rets[1]);
 				$config['BasePath'] = dirname(FULL_PATH . $filename);
 
 				if (array_key_exists('AdminTemplatePath', $config)) {
 					// append template base folder for admin templates path of this prefix
 					$module_templates = $rets[1] == 'core' ? '' : substr($rets[1], 8) . '/';
 					$config['AdminTemplatePath'] = $module_templates . $config['AdminTemplatePath'];
 				}
 
 				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;
 			}
 			elseif ($prefix = array_search($filename, $this->prefixFiles)) {
 				// 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])) {
 			if ($this->Application->isDebugMode()) $this->Application->Debugger->appendTrace();
 			trigger_error('Configuration file for prefix <b>'.$prefix.'</b> is unknown', E_USER_ERROR);
 			return ;
 		}
 		$file = $this->prefixFiles[$prefix];
 		$prefix = $this->PreloadConfigFile($file);
 
 		if ($this->FinalStage) {
 			// run prefix OnAfterConfigRead so all
 			// hooks to it can define their clonses
 			$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);
 		}
 	}
 
 	/**
 	 * Reads unit (specified by $prefix)
 	 * option specified by $option
 	 *
 	 * @param string $prefix
 	 * @param string $name
 	 * @param mixed $default
 	 * @return string
 	 * @access public
 	 */
 	function getUnitOption($prefix, $name, $default = false)
 	{
 		if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) {
 			if (!isset($this->configData[$rets[1]])) {
 				$this->loadConfig($rets[1]);
 			}
 			$ret = isset($this->configData[$rets[1]][$name][$rets[2]]) ? $this->configData[$rets[1]][$name][$rets[2]] : false;
 //			$ret = getArrayValue($this->configData, $rets[1], $name, $rets[2]);
 		}
 		else {
 			if (!isset($this->configData[$prefix])) {
 				$this->loadConfig($prefix);
 			}
 			$ret = isset($this->configData[$prefix][$name]) ? $this->configData[$prefix][$name] : false;
 //			$ret = getArrayValue($this->configData, $prefix, $name);
 		}
 		return $ret === false ? $default : $ret;
 	}
 
 	/**
 	 * Read all unit with $prefix options
 	 *
 	 * @param string $prefix
 	 * @return Array
 	 * @access public
 	 */
 	function getUnitOptions($prefix)
 	{
 		if (!isset($this->configData[$prefix])) {
 			$this->loadConfig($prefix);
 		}
 
 		return $this->configData[$prefix];
 	}
 
 	/**
 	 * Set's new unit option value
 	 *
 	 * @param string $prefix
 	 * @param string $name
 	 * @param string $value
 	 * @access public
 	 */
 	function setUnitOption($prefix, $name, $value)
 	{
 		if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) {
 			if (!isset($this->configData[$rets[1]])) {
 				$this->loadConfig($rets[1]);
 			}
 			$this->configData[$rets[1]][$name][$rets[2]] = $value;
 		}
 		else {
 			if (!isset($this->configData[$prefix])) {
 				$this->loadConfig($prefix);
 			}
 			$this->configData[$prefix][$name] = $value;
 		}
 
 	}
 
 	function getPrefixByParamName($paramName,$prefix)
 	{
 		$pseudo_class_map=Array(
 		'ItemClass'=>'%s',
 		'ListClass'=>'%s_List',
 		'EventHandlerClass'=>'%s_EventHandler',
 		'TagProcessorClass'=>'%s_TagProcessor'
 		);
 		return sprintf($pseudo_class_map[$paramName],$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[] = rtrim($module_info['Path'], '/');
+				$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_data) {
 			$callback_function[0]->$callback_function[1]($prefix, $config_data, $params);
 		}
 	}
 
 }
\ No newline at end of file
Index: branches/5.1.x/core/kernel/startup.php
===================================================================
--- branches/5.1.x/core/kernel/startup.php	(revision 13701)
+++ branches/5.1.x/core/kernel/startup.php	(revision 13702)
@@ -1,172 +1,172 @@
 <?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('KERNEL_PATH', FULL_PATH . '/core/kernel');
 
 	if (!function_exists('getmicrotime')) {
 		function getmicrotime()
 		{
 			list($usec, $sec) = explode(" ", microtime());
 			return ((float)$usec + (float)$sec);
 		}
 	}
 
 	$globals_start = getmicrotime();
 	include_once(KERNEL_PATH . '/globals.php');	// non OOP functions used through kernel, e.g. print_pre
 	include_once(KERNEL_PATH . '/utility/multibyte.php');	// emulating multi-byte php extension
 	$globals_end = getmicrotime();
 
 	$vars = parse_portal_ini();
 
 	$admin_directory = isset($vars['AdminDirectory']) ? $vars['AdminDirectory'] : '/admin';
 	define('ADMIN_DIRECTORY', $admin_directory);
 
 	# New path detection method: begin
 	if (defined('REL_PATH')) {
 		// location of index.php relatively to site base folder
 		$relative_path = preg_replace('/^[\/]{0,1}admin(.*)/', $admin_directory . '\\1', REL_PATH);
 	}
 	else {
 		// default index.php relative location is administrative console folder
 		define('REL_PATH', $admin_directory);
 		$relative_path = REL_PATH;
 	}
 
 	$ps = rtrim(preg_replace('/' . preg_quote(rtrim($relative_path, '/'), '/') . '$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/');
 	safeDefine('BASE_PATH', $ps); // in case in-portal has defined it before
 	# New path detection method: end
 
 	safeDefine('SERVER_NAME', $_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : $vars['Domain']);
 
 	$https_mark = getArrayValue($_SERVER, 'HTTPS');
 	safeDefine('PROTOCOL', ($https_mark == 'on') || ($https_mark == '1') ? 'https://' : 'http://');
 
 	$port = array_key_exists('HTTP_PORT', $_SERVER) ? $_SERVER['HTTP_PORT'] : false;
 
 	if ($port && ((PROTOCOL == 'http://' && $port != '80') || (PROTOCOL == 'https://' && $port != '443'))) {
 		// if non-standard port is used, then define it
         define('PORT', $_SERVER['HTTP_PORT']);
     }
 
 	safeDefine('APPLICATION_CLASS', isset($vars['ApplicationClass']) ? $vars['ApplicationClass'] : 'kApplication');
 	safeDefine('APPLICATION_PATH', isset($vars['ApplicationPath']) ? $vars['ApplicationPath'] : '/core/kernel/application.php');
 
 	if (isset($vars['WriteablePath'])) {
 		define('WRITEABLE', FULL_PATH . $vars['WriteablePath']);
 		define('WRITEBALE_BASE', $vars['WriteablePath']);
 	}
 
 	if ($vars === false || count($vars) == 0) {
 		global $rootURL;
 		echo 'In-Portal is probably not installed, or configuration file is missing.<br>';
 		echo 'Please use the installation script to fix the problem.<br><br>';
 		echo '<a href="' . PROTOCOL . SERVER_NAME . rtrim(BASE_PATH, '/') . '/core/install.php">Go to installation script</a><br><br>';
 		flush();
 		exit;
 	}
 
 	define('SQL_TYPE', $vars['DBType']);
 	define('SQL_SERVER', $vars['DBHost']);
 	define('SQL_USER', $vars['DBUser']);
 	define('SQL_PASS', $vars['DBUserPassword']);
 	define('SQL_DB', $vars['DBName']);
 
 	if (isset($vars['DBCollation']) && isset($vars['DBCharset'])) {
 		define('SQL_COLLATION', $vars['DBCollation']);
 		define('SQL_CHARSET', $vars['DBCharset']);
 	}
 	define('TABLE_PREFIX', $vars['TablePrefix']);
 
 	define('DOMAIN', getArrayValue($vars, 'Domain'));
 
 	ini_set('memory_limit', '50M');
 
-	define('MODULES_PATH', FULL_PATH . '/modules');
+	define('MODULES_PATH', FULL_PATH . DIRECTORY_SEPARATOR . 'modules');
 
 	define('EXPORT_BASE_PATH', WRITEBALE_BASE . '/export');
 	define('EXPORT_PATH', FULL_PATH . EXPORT_BASE_PATH);
 
 	define('GW_CLASS_PATH', MODULES_PATH . '/in-commerce/units/gateways/gw_classes'); // Payment Gateway Classes Path
 	define('SYNC_CLASS_PATH', FULL_PATH . '/sync');	// path for 3rd party user syncronization scripts
 
 	safeDefine('ENV_VAR_NAME','env');
 
 	define('IMAGES_PATH', WRITEBALE_BASE . '/images/');
 	define('IMAGES_PENDING_PATH', IMAGES_PATH . 'pending/');
 	safeDefine('MAX_UPLOAD_SIZE', min(ini_get('upload_max_filesize'), ini_get('post_max_size'))*1024*1024);
 
 	safeDefine('EDITOR_PATH', isset($vars['EditorPath']) ? $vars['EditorPath'] : '/core/editor/');
 
 	// caching types
 	define('CACHING_TYPE_NONE', 0);
 	define('CACHING_TYPE_MEMORY', 1);
 	define('CACHING_TYPE_TEMPORARY', 2);
 
 	if (ini_get('safe_mode')) {
 		// safe mode will be removed at all in PHP6
 		define('SAFE_MODE', 1);
 	}
 
 	if (file_exists(WRITEABLE . '/debug.php')) {
 		include_once(WRITEABLE . '/debug.php');
 		if (array_key_exists('DEBUG_MODE', $dbg_options) && $dbg_options['DEBUG_MODE']) {
 			$debugger_start = getmicrotime();
 			include_once(KERNEL_PATH . '/utility/debugger.php');
 			$debugger_end = getmicrotime();
 
 			if (isset($debugger) && constOn('DBG_PROFILE_INCLUDES')) {
 				$debugger->profileStart('inc_globals', KERNEL_PATH . '/globals.php', $globals_start);
 				$debugger->profileFinish('inc_globals', KERNEL_PATH . '/globals.php', $globals_end);
 				$debugger->profilerAddTotal('includes', 'inc_globals');
 
 				$debugger->profileStart('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_start);
 				$debugger->profileFinish('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_end);
 				$debugger->profilerAddTotal('includes', 'inc_debugger');
 			}
 		}
 	}
 
 	safeDefine('SILENT_LOG', 0);
 
 	$includes = Array(
 			KERNEL_PATH . '/application.php',
 			FULL_PATH . APPLICATION_PATH,
 			KERNEL_PATH . '/db/db_connection.php',
 			KERNEL_PATH . "/kbase.php",
 			KERNEL_PATH . '/utility/event.php',
 			KERNEL_PATH . "/utility/factory.php",
 			KERNEL_PATH . "/languages/phrases_cache.php",
 			KERNEL_PATH . "/db/dblist.php",
 			KERNEL_PATH . "/db/dbitem.php",
 			KERNEL_PATH . "/event_handler.php",
 			KERNEL_PATH . '/db/db_event_handler.php',
 	);
 
 	foreach ($includes as $a_file) {
 		k4_include_once($a_file);
 	}
 
 	if (defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger)) {
 		$debugger->AttachToApplication();
 	}
 
 	if( !function_exists('adodb_mktime') ) {
 		include_once(KERNEL_PATH . '/utility/adodb-time.inc.php');
 	}
 
 	// system users
 	define('USER_ROOT', -1);
 	define('USER_GUEST', -2);
\ No newline at end of file
Index: branches/5.1.x/core/units/helpers/minifiers/minify_helper.php
===================================================================
--- branches/5.1.x/core/units/helpers/minifiers/minify_helper.php	(revision 13701)
+++ branches/5.1.x/core/units/helpers/minifiers/minify_helper.php	(revision 13702)
@@ -1,246 +1,246 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2010 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 MinifyHelper extends kHelper {
 
 		/**
 		 * Debug mode mark
 		 *
 		 * @var bool
 		 */
 		var $debugMode = false;
 
 		/**
 		 * Path to compress information file
 		 *
 		 * @var string
 		 */
 		var $infoFile = '';
 
 		/**
 		 * Compress information
 		 *
 		 * @var Array
 		 */
 		var $compressInfo = Array ();
 
 		function MinifyHelper()
 		{
 			parent::kHelper();
 
 			$this->debugMode = $this->Application->isDebugMode(false);
 			$this->infoFile = WRITEABLE . '/cache/compress_info.txt';
 
 			if ( file_exists($this->infoFile) ) {
 				$this->compressInfo = unserialize( file_get_contents($this->infoFile) );
 			}
 		}
 
 		/**
 		 * When used as non-block tag, then compress given files and return url to result
 		 *
 		 * @param Array $files
 		 * @return string
 		 */
 		function CompressScriptTag($params)
 		{
 			// put to queue
 			if (array_key_exists('to', $params)) {
 				$files = $this->Application->GetVar($params['to'], '');
 				$this->Application->SetVar($params['to'], $files . '|' . $params['files']);
 
 				return '';
 			}
 
 			if (array_key_exists('from', $params)) {
 				// get from queue
 				$files = $this->Application->GetVar($params['from']);
 			}
 			else {
 				// get from tag
 				$files = $params['files'];
 			}
 
 			$files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) );
 			$extension = pathinfo($files[0], PATHINFO_EXTENSION);
 
 			$hash = ($this->debugMode ? 'd' : 'c') . '_' . $this->_getHash($files);
 			$file_mask = 'cache/' . $hash . '_%s.' . $extension;
 
 			$was_compressed = array_key_exists($hash, $this->compressInfo);
 
 			if ($was_compressed) {
 				$current_file = WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]);
 
 				if (!file_exists($current_file)) {
 					// when info exists, but file doesn't -> re-compress
 					$was_compressed = false;
 				}
 
 				if ($this->debugMode) {
 					// check if any of listed files was changed since compressed file was created (only, when in debug mode)
 					foreach ($files as $file) {
 						if (filemtime($file) > $this->compressInfo[$hash]) {
 							$was_compressed = false;
 							break;
 						}
 					}
 				}
 			}
 
 			if (!$was_compressed) {
 				$string = '';
 				$path_length = strlen(FULL_PATH) + 1;
 
 				foreach ($files as $file) {
 					// add filename before for easier debugging
 					if ($this->debugMode) {
 						$string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n";
 						$string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n";
 					}
 
 					// add file content
 					$string .= file_get_contents($file) . "\n\n";
 				}
 
 				// remove previous version of compressed file
 				if (isset($current_file)) {
 					if (file_exists($current_file)) {
 						unlink($current_file);
 					}
 				}
 
 				// replace templates base
 				$templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ());
 				$templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base);
 				$string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string);
 
 				// compress collected data
 				$this->compressInfo[$hash] = adodb_mktime();
 
 				if (!$this->debugMode) {
 					// don't compress merged js/css file in debug mode to allow js/css debugging
 					$this->compressString($string, $extension);
 				}
 
 				// save compressed file
 				$fp = fopen(WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]), 'w');
 				fwrite($fp, $string);
 				fclose($fp);
 
 				$this->_saveInfo();
 			}
 
 			// got compressed file -> use it
-			return $this->Application->BaseURL(WRITEBALE_BASE) . sprintf($file_mask, $this->compressInfo[$hash]);
+			return $this->Application->BaseURL( str_replace(DIRECTORY_SEPARATOR, '/', WRITEBALE_BASE)) . sprintf($file_mask, $this->compressInfo[$hash]);
 		}
 
 		/**
 		 * Returns hash string based on given files
 		 *
 		 * @param Array $files
 		 * @return int
 		 */
 		function _getHash($files)
 		{
 			$hash = $files;
 
 			if ($this->Application->isAdmin) {
 				array_unshift($hash, 'A:1');
 			}
 			else {
 				array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme'));
 			}
 
 			return crc32( implode('|', $hash) );
 		}
 
 		/**
 		 * Saves file with compress information
 		 *
 		 */
 		function _saveInfo()
 		{
 			$fp = fopen($this->infoFile, 'w');
 			fwrite($fp, serialize($this->compressInfo));
 			fclose($fp);
 		}
 
 		/**
 		 * Deletes compression info file
 		 *
 		 * @todo also delete all listed there files
 		 */
 		function delete()
 		{
 			if (file_exists($this->infoFile)) {
 				unlink($this->infoFile);
 			}
 		}
 
 		/**
 		 * Compress $string based on $extension
 		 *
 		 * @param string $string
 		 * @param string $extension
 		 */
 		function compressString(&$string, $extension)
 		{
 			if ($extension == 'js') {
 				$minifier =& $this->Application->makeClass('JsMinifyHelper');
 				/* @var $minifier JsMinifyHelper */
 
 				$string = $minifier->minify($string);
 			}
 			elseif ($extension == 'css') {
 				$minifier =& $this->Application->makeClass('CssMinifyHelper');
 				/* @var $minifier CssMinifyHelper */
 
 				$string = $minifier->minify($string);
 			}
 		}
 
 		/**
 		 * Get full paths on disk for each of given templates
 		 *
 		 * @param Array $templates
 		 * @return Array
 		 */
 		function _getTemplatePaths($templates)
 		{
 			$ret = Array ();
 			$reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/';
 
 			foreach ($templates as $template) {
 				if (!$template) {
 					continue;
 				}
 
 				if (preg_match($reg_exp, $template, $regs)) {
 					// full url (from current domain) to a file
 					$path = FULL_PATH . '/' . $regs[1];
 				}
 				else {
 					list ($path, $module_filename) = $this->Application->TemplatesCache->GetTemplatePaths($template);
 					$path .= DIRECTORY_SEPARATOR . $module_filename;
 				}
 
 				$ret[] = $path;
 			}
 
 			return $ret;
 		}
 	}
\ No newline at end of file