Index: branches/5.1.x/core/units/modules/modules_tag_processor.php
===================================================================
--- branches/5.1.x/core/units/modules/modules_tag_processor.php	(revision 13188)
+++ branches/5.1.x/core/units/modules/modules_tag_processor.php	(revision 13189)
@@ -1,117 +1,117 @@
 <?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 ModulesTagProcessor extends kDBTagProcessor  {
 
 		function ModuleInstalled($params)
 		{
 			return $this->Application->isModuleEnabled($params['name']);
 		}
 
 		function _hasPrivileges()
 		{
 			return $this->Application->RecallVar('user_id') == -1;
 		}
 
 		function AlreadyInstalled($params)
 		{
 			if (!$this->_hasPrivileges()) {
 				// don't show licenses status for non-privileged users
 				return true;
 			}
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			return $modules_helper->moduleInstalled( $object->GetDBField('Name') );
 		}
 
 		function ModuleLicensed($params)
 		{
 			if (!$this->_hasPrivileges()) {
 				// don't show licenses status for non-privileged users
 				return true;
 			}
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			$licensed_modules = array_map('strtolower', $modules_helper->_GetModules());
 
 			return in_array(strtolower($object->GetDBField('Name')), $licensed_modules);
 		}
 
 		function PrerequisitesMet($params)
 		{
 			return !$this->_getPrerequisitesErrors($params);
 		}
 
 		function _getPrerequisitesErrors($params)
 		{
 			static $errors = Array ();
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
-			$module_path = strtolower( $object->GetDBField('Name') );
+			$module_path = $object->GetDBField('Path');
 
 			if (!array_key_exists($module_path, $errors)) {
 				require_once FULL_PATH . '/core/install/install_toolkit.php';
 
 				$toolkit = new kInstallToolkit();
 
 				$module_version = $toolkit->GetMaxModuleVersion($module_path);
 
-				$errors[$module_path] = $toolkit->CheckPrerequisites($module_path . '/', Array ($module_version), 'standalone');
+				$errors[$module_path] = $toolkit->CheckPrerequisites($module_path, Array ($module_version), 'standalone');
 			}
 
 			return $errors[$module_path];
 		}
 
 		function ListPrerequisites($params)
 		{
 			$errors = $this->_getPrerequisitesErrors($params);
 
 			$ret = '';
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 
 			foreach ($errors as $error) {
 				$block_params['error'] = $error;
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 
 			return $ret;
 		}
 
 		function InstallLink($params)
 		{
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
 			$module_path = strtolower( $object->GetDBField('Path') );
 			$url_params = Array ('redirect' => 1, 'admin' => 1);
 
 			return $this->Application->HREF('dummy', '_FRONT_END_', $url_params, $module_path . 'install.php');
 		}
 
 	}
\ No newline at end of file
Index: branches/5.1.x/core/units/modules/modules_event_handler.php
===================================================================
--- branches/5.1.x/core/units/modules/modules_event_handler.php	(revision 13188)
+++ branches/5.1.x/core/units/modules/modules_event_handler.php	(revision 13189)
@@ -1,187 +1,187 @@
 <?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 ModulesEventHandler extends kDBEventHandler {
 
 		/**
 		 * Builds item
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnItemBuild(&$event)
 		{
 			$this->Application->SetVar($event->getPrefixSpecial(true).'_id', $event->Special);
 			parent::OnItemBuild($event);
 		}
 
 		/**
 		 * List with one record if special passed
 		 *
 		 * @param kEvent $event
 		 */
 		function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			if ($event->Special) {
 				$object->addFilter('current_module', 'Name = '.$event->Special);
 			}
 
 			$object->addFilter('not_core', '%1$s.Name <> "Core"');
 		}
 
 		function mapEvents()
 		{
 			parent::mapEvents();
 			$this->eventMethods['OnMassApprove'] = 'moduleAction';
 			$this->eventMethods['OnMassDecline'] = 'moduleAction';
 		}
 
 		/**
 		 * Disabled modules, but not In-Portal
 		 *
 		 * @param kEvent $event
 		 */
 		function moduleAction(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return ;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$ids = $this->StoreSelectedIDs($event);
 
 
 			if (!$ids) {
 				return true;
 			}
 
 			$status_field = array_shift( $this->Application->getUnitOption($event->Prefix, 'StatusField') );
 
 			foreach ($ids as $id) {
 				if (($event->Name == 'OnMassDecline') && ($id == 'In-Portal')) {
 					// don't allow to disable in-portal
 					continue;
 				}
 
 				if ($id == 'Core') {
 					// don't allow any kind of manupulations with kernel
 					continue;
 				}
 
 				$object->Load($id);
 
 				$object->SetDBField($status_field, $event->Name == 'OnMassApprove' ? 1 : 0);
 
 				if ($object->Update()) {
 					$sql = 'UPDATE ' . TABLE_PREFIX . 'ImportScripts
 								SET Status = ' . ($event->Name == 'OnMassApprove' ? STATUS_ACTIVE : STATUS_DISABLED) . '
  								WHERE Module = "' . $object->GetDBField('Name') . '"';
  					$this->Conn->Query($sql);
 
  					$event->status = erSUCCESS;
 					$event->redirect_params = Array('opener' => 's'); //stay!
 				}
 				else {
 					$event->status = erFAIL;
 					$event->redirect = false;
 					break;
 				}
 			}
 
 			$this->Application->UnitConfigReader->ResetParsedData(true); //true to reset sections cache also
 			$event->SetRedirectParam('RefreshTree', 1);
 		}
 
 		/**
 		 * Occures after list is queried
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterListQuery(&$event)
 		{
 			parent::OnAfterListQuery($event);
 
 			$new_modules = $this->_getNewModules();
 			if (!$new_modules || $this->Application->RecallVar('user_id') != -1) {
 				return ;
 			}
 
 			require_once FULL_PATH . '/core/install/install_toolkit.php';
 
 			$toolkit = new kInstallToolkit();
 
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			foreach ($new_modules as $module) {
 				$module_record = Array (
 					'Name' => $toolkit->getModuleName($module),
 					'Path' => 'modules/' . $module . '/',
-					'Version' => $toolkit->GetMaxModuleVersion($module),
+					'Version' => $toolkit->GetMaxModuleVersion('modules/' . $module . '/'),
 					'Loaded' => 0,
 					'BuildDate' => null,
 				);
 
 				$object->addRecord($module_record);
 			}
 		}
 
 		/**
 		 * Returns list of modules, that are not installed, but available in file system
 		 *
 		 * @return Array
 		 */
 		function _getNewModules()
 		{
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			$modules = Array ();
 			if ($dir = @opendir(MODULES_PATH)) {
 				while (($file = readdir($dir)) !== false) {
 					if ($file != '.' && $file != '..') {
 						$module_folder = MODULES_PATH . '/' . $file;
 						if (is_dir($module_folder) && $this->_isModule($module_folder)) {
 							// this is module -> check if it's installed already
 							if (!$modules_helper->moduleInstalled($file)) {
 								$install_order = trim( file_get_contents($module_folder . '/install/install_order.txt') );
 								$modules[$install_order] = $file;
 							}
 						}
 					}
 				}
 
 				closedir($dir);
 			}
 
 			// allows to control module install order
 			ksort($modules, SORT_NUMERIC);
 			return $modules;
 		}
 
 		/**
 		 * Checks, that given folder is module root folder
 		 *
 		 * @param string $folder
 		 * @return bool
 		 */
 		function _isModule($folder)
 		{
 			return file_exists($folder . '/install.php') && file_exists($folder . '/install/install_schema.sql');
 		}
 	}
\ No newline at end of file
Index: branches/5.1.x/core/install/install_toolkit.php
===================================================================
--- branches/5.1.x/core/install/install_toolkit.php	(revision 13188)
+++ branches/5.1.x/core/install/install_toolkit.php	(revision 13189)
@@ -1,858 +1,866 @@
 <?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!');
 
 	/**
 	 * Upgrade sqls are located using this mask
 	 *
 	 */
 	define('UPGRADES_FILE', FULL_PATH.'/%sinstall/upgrades.%s');
 
 	/**
 	 * Prerequisit check classes are located using this mask
 	 *
 	 */
 	define('PREREQUISITE_FILE', FULL_PATH.'/%sinstall/prerequisites.php');
 
 	/**
 	 * Format of version identificator in upgrade files (normal, beta, release candidate)
 	 *
 	 */
 	define('VERSION_MARK', '# ===== v ([\d]+\.[\d]+\.[\d]+|[\d]+\.[\d]+\.[\d]+-B[\d]+|[\d]+\.[\d]+\.[\d]+-RC[\d]+) =====');
 
 	if (!defined('GET_LICENSE_URL')) {
 		/**
 		 * Url used for retrieving user licenses from Intechnic licensing server
 		 *
 		 */
 		define('GET_LICENSE_URL', 'http://www.intechnic.com/myaccount/license.php');
 	}
 
 	/**
 	 * Misc functions, that are required during installation, when
 	 *
 	 */
 	class kInstallToolkit {
 
 		/**
 		 * Reference to kApplication class object
 		 *
 		 * @var kApplication
 		 */
 		var $Application = null;
 
 		/**
 		 * Connection to database
 		 *
 		 * @var kDBConnection
 		 */
 		var $Conn = null;
 
 		/**
 		 * Path to config.php
 		 *
 		 * @var string
 		 */
 		var $INIFile = '';
 
 		/**
 		 * Parsed data from config.php
 		 *
 		 * @var Array
 		 */
 		var $systemConfig = Array ();
 
 		/**
 		 * Installator instance
 		 *
 		 * @var kInstallator
 		 */
 		var $_installator = null;
 
 		function kInstallToolkit()
 		{
 			if (class_exists('kApplication')) {
 				// auto-setup in case of separate module install
 				$this->Application =& kApplication::Instance();
 				$this->Conn =& $this->Application->GetADODBConnection();
 			}
 
 			$this->INIFile = FULL_PATH . DIRECTORY_SEPARATOR . 'config.php';
 
 			$this->systemConfig = $this->ParseConfig(true);
 		}
 
 		/**
 		 * Sets installator
 		 *
 		 * @param kInstallator $instance
 		 */
 		function setInstallator(&$instance)
 		{
 			$this->_installator =& $instance;
 		}
 
 		/**
 		 * Checks prerequisities before module install or upgrade
 		 *
 		 * @param string $module_path
 		 * @param string $versions
 		 * @param string $mode upgrade mode = {install, standalone, upgrade}
 		 */
 		function CheckPrerequisites($module_path, $versions, $mode)
 		{
 			static $prerequisit_classes = Array ();
 
 			$prerequisites_file = sprintf(PREREQUISITE_FILE, $module_path);
 			if (!file_exists($prerequisites_file) || !$versions) {
 				return Array ();
 			}
 
 			if (!isset($prerequisit_classes[$module_path])) {
 				// save class name, because 2nd time
 				// (in after call $prerequisite_class variable will not be present)
 				include_once $prerequisites_file;
 				$prerequisit_classes[$module_path] = $prerequisite_class;
 			}
 
 			$prerequisite_object = new $prerequisit_classes[$module_path]();
 			if (method_exists($prerequisite_object, 'setToolkit')) {
 				$prerequisite_object->setToolkit($this);
 			}
 
 			// some errors possible
 			return $prerequisite_object->CheckPrerequisites($versions, $mode);
 		}
 
 		/**
 		 * Processes one license, received from server
 		 *
 		 * @param string $file_data
 		 */
 		function processLicense($file_data)
 		{
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			$file_data = explode('Code==:', $file_data);
 			$file_data[0] = str_replace('In-Portal License File - do not edit!' . "\n", '', $file_data[0]);
 			$file_data = array_map('trim', $file_data);
 
 			if ($modules_helper->verifyLicense($file_data[0])) {
 				$this->setSystemConfig('Intechnic', 'License', $file_data[0]);
 				if (array_key_exists(1, $file_data)) {
 					$this->setSystemConfig('Intechnic', 'LicenseCode', $file_data[1]);
 				}
 				else {
 					$this->setSystemConfig('Intechnic', 'LicenseCode');
 				}
 				$this->SaveConfig();
 			}
 			else {
 				// invalid license received from licensing server
 				$this->_installator->errorMessage = 'Invalid License File';
 			}
 		}
 
 		/**
 		 * Saves given configuration values to database
 		 *
 		 * @param Array $config
 		 */
 		function saveConfigValues($config)
 		{
 			foreach ($config as $config_var => $value) {
 				$sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues
 						SET VariableValue = ' . $this->Conn->qstr($value) . '
 						WHERE VariableName = ' . $this->Conn->qstr($config_var);
 				$this->Conn->Query($sql);
 			}
 		}
 
 		/**
 		 * Sets module version to passed
 		 *
 		 * @param string $module_name
+		 * @param string $module_path
 		 * @param string $version
 		 */
-		function SetModuleVersion($module_name, $version = false)
+		function SetModuleVersion($module_name, $module_path = false, $version = false)
 		{
 			if ($version === false) {
-				$version = $this->GetMaxModuleVersion($module_name);
+				if (!$module_path) {
+					trigger_error('Module path must be given to "SetModuleVersion" method to auto-detect version', E_USER_ERROR);
+					return ;
+				}
+
+				$version = $this->GetMaxModuleVersion($module_path);
 			}
 
 			// get table prefix from config, because application may not be available here
 			$table_prefix = $this->getSystemConfig('Database', 'TablePrefix');
 
 			if ($module_name == 'kernel') {
 				$module_name = 'in-portal';
 			}
 
 			// don't use "adodb_mktime" here, because it's not yet included
 			$sql = 'UPDATE ' . $table_prefix . 'Modules
 					SET Version = "' . $version . '", BuildDate = ' . time() . '
 					WHERE LOWER(Name) = "' . strtolower($module_name) . '"';
 			$this->Conn->Query($sql);
 		}
 
 		/**
 		 * Sets module root category to passed
 		 *
 		 * @param string $module_name
 		 * @param string $category_id
 		 */
 		function SetModuleRootCategory($module_name, $category_id = 0)
 		{
 			// get table prefix from config, because application may not be available here
 			$table_prefix = $this->getSystemConfig('Database', 'TablePrefix');
 
 			if ($module_name == 'kernel') {
 				$module_name = 'in-portal';
 			}
 
 			$sql = 'UPDATE ' . $table_prefix . 'Modules
 					SET RootCat = ' . $category_id . '
 					WHERE LOWER(Name) = "' . strtolower($module_name) . '"';
 			$this->Conn->Query($sql);
 		}
 
 		/**
 		 * Returns maximal version of given module by scanning it's upgrade scripts
 		 *
 		 * @param string $module_name
 		 * @return string
 		 */
-		function GetMaxModuleVersion($module_name)
+		function GetMaxModuleVersion($module_path)
 		{
-			$upgrades_file = sprintf(UPGRADES_FILE, mb_strtolower($module_name).'/', 'sql');
+			$module_path = rtrim(mb_strtolower($module_path), '/');
+			$upgrades_file = sprintf(UPGRADES_FILE, $module_path . '/', 'sql');
+
 			if (!file_exists($upgrades_file)) {
 				// no upgrade file
 				return '5.0.0';
 			}
 
 			$sqls = file_get_contents($upgrades_file);
 			$versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs);
 			if (!$versions_found) {
 				// upgrades file doesn't contain version definitions
 				return '5.0.0';
 			}
 
 			return end($regs[1]);
 		}
 
 		/**
 		 * Runs SQLs from file
 		 *
 		 * @param string $filename
 		 * @param mixed $replace_from
 		 * @param mixed $replace_to
 		 */
 		function RunSQL($filename, $replace_from = null, $replace_to = null)
 		{
 			if (!file_exists(FULL_PATH.$filename)) {
 				return ;
 			}
 
 			$sqls = file_get_contents(FULL_PATH.$filename);
 			if (!$this->RunSQLText($sqls, $replace_from, $replace_to)) {
 				if (is_object($this->_installator)) {
 					$this->_installator->Done();
 				}
 				else {
 					if (isset($this->Application)) {
 						$this->Application->Done();
 					}
 
 					exit;
 				}
 			}
 		}
 
 		/**
 		 * Runs SQLs from string
 		 *
 		 * @param string $sqls
 		 * @param mixed $replace_from
 		 * @param mixed $replace_to
 		 */
 		function RunSQLText(&$sqls, $replace_from = null, $replace_to = null, $start_from=0)
 		{
 			$table_prefix = $this->getSystemConfig('Database', 'TablePrefix');
 
 			// add prefix to all tables
 			if (strlen($table_prefix) > 0) {
 				$replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
 				foreach ($replacements as $replacement) {
 					$sqls = str_replace($replacement, $replacement . $table_prefix, $sqls);
 				}
 			}
 
 			$sqls = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sqls);
 			$sqls = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sqls);
 			$sqls = str_replace('<%TABLE_PREFIX%>', $table_prefix, $sqls);
 
 			if (isset($replace_from) && isset($replace_to)) {
 				// replace something additionally, e.g. module root category
 				$sqls = str_replace($replace_from, $replace_to, $sqls);
 			}
 
 			$sqls = str_replace("\r\n", "\n", $sqls);  // convert to linux line endings
 			$no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", '', $sqls); // remove all comments "#" on new lines
 
 			if ($no_comment_sqls === null) {
 				// "ini.pcre.backtrack-limit" reached and error happened
 				$sqls = explode(";\n", $sqls . "\n"); // ensures that last sql won't have ";" in it
 				$sqls = array_map('trim', $sqls);
 
 				// remove all comments "#" on new lines (takes about 2 seconds for 53000 sqls)
 				$sqls = preg_replace("/#\s([^;]*?)/", '', $sqls);
 			}
 			else {
 				$sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it
 				$sqls = array_map('trim', $sqls);
 			}
 
 			$sql_count = count($sqls);
 			$db_collation = $this->getSystemConfig('Database', 'DBCollation');
 
 			for ($i = $start_from; $i < $sql_count; $i++) {
 				$sql = $sqls[$i];
 				if (!$sql || (substr($sql, 0, 1) == '#')) {
 					continue; // usually last line
 				}
 
 				if (substr($sql, 0, 13) == 'CREATE TABLE ' && $db_collation) {
 					// it is CREATE TABLE statement -> add collation
 					$sql .= ' COLLATE \'' . $db_collation . '\'';
 				}
 
 				$this->Conn->Query($sql);
 				if ($this->Conn->getErrorCode() != 0) {
 					if (is_object($this->_installator)) {
 		  				$this->_installator->errorMessage = 'Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg().'<br /><br />Last Database Query:<br /><textarea cols="70" rows="10" readonly>'.htmlspecialchars($sql).'</textarea>';
 		  				$this->_installator->LastQueryNum = $i + 1;
 					}
 	  				return false;
 	    		}
 			}
 			return true;
 		}
 
 		/**
 		 * Performs clean language import from given xml file
 		 *
 		 * @param string $lang_file
 		 * @param bool $upgrade
 		 * @todo Import for "core/install/english.lang" (322KB) takes 18 seconds to work on Windows
 		 */
 		function ImportLanguage($lang_file, $upgrade = false)
 		{
 			$lang_file = FULL_PATH.$lang_file.'.lang';
 			if (!file_exists($lang_file)) {
 				return ;
 			}
 
 			$language_import_helper =& $this->Application->recallObject('LanguageImportHelper');
 			/* @var $language_import_helper LanguageImportHelper */
 
 			$language_import_helper->performImport($lang_file, '|0|1|2|', '', $upgrade ? LANG_SKIP_EXISTING : LANG_OVERWRITE_EXISTING);
 		}
 
 		/**
 		 * Converts module version in format X.Y.Z[-BN/-RCM] to signle integer
 		 *
 		 * @param string $version
 		 * @return int
 		 */
 		function ConvertModuleVersion($version)
 		{
 			if (preg_match('/(.*)-(B|RC)([\d]+)/', $version, $regs)) {
 				// -B<M> or RC-<N>
 				$parts = explode('.', $regs[1]);
 
 				$parts[] = $regs[2] == 'B' ? 1 : 2; // B reliases goes before RC releases
 				$parts[] = $regs[3];
 			}
 			else {
 				// releases without B/RC marks go after any B/RC releases
 				$parts = explode('.', $version . '.3.100');
 			}
 
 			$bin = '';
 
 			foreach ($parts as $part_index => $part) {
 				if ($part_index == 3) {
 					// version type only can be 1/2/3 (11 in binary form), so don't use padding at all
 					$pad_count = 2;
 				}
 				else {
 					$pad_count = 8;
 				}
 
 				$bin .= str_pad(decbin($part), $pad_count, '0', STR_PAD_LEFT);
 			}
 
 			return bindec($bin);
 		}
 
 		/**
 		 * Returns themes, found in system
 		 *
 		 * @param bool $rebuild
 		 * @return int
 		 */
 		function getThemes($rebuild = false)
 		{
 			if ($rebuild) {
 				$this->rebuildThemes();
 			}
 
 			$id_field = $this->Application->getUnitOption('theme', 'IDField');
 			$table_name = $this->Application->getUnitOption('theme', 'TableName');
 
 			$sql = 'SELECT Name, ' . $id_field . '
 					FROM ' . $table_name . '
 					ORDER BY Name ASC';
 			return $this->Conn->GetCol($sql, $id_field);
 		}
 
 		function ParseConfig($parse_section = false)
 		{
 			if (!file_exists($this->INIFile)) {
 				return Array();
 			}
 
 			if( file_exists($this->INIFile) && !is_readable($this->INIFile) ) {
 				die('Could Not Open Ini File');
 			}
 
 			$contents = file($this->INIFile);
 
 			$retval = Array();
 			$section = '';
 			$ln = 1;
 			$resave = false;
 			foreach ($contents as $line) {
 				if ($ln == 1 && $line != '<'.'?'.'php die() ?'.">\n") {
 					$resave = true;
 				}
 
 				$ln++;
 				$line = trim($line);
 				$line = preg_replace('/;[.]*/', '', $line);
 				if (strlen($line) > 0) {
 					//echo $line . " - ";
 					if (preg_match('/^\[[a-z]+\]$/i', str_replace(' ', '', $line))) {
 						//echo 'section';
 						$section = mb_substr($line, 1, (mb_strlen($line) - 2));
 						if ($parse_section) {
 							$retval[$section] = array();
 						}
 						continue;
 					} elseif (strpos($line, '=') !== false) {
 						//echo 'main element';
 						list ($key, $val) = explode(' = ', $line);
 						if (!$parse_section) {
 							$retval[trim($key)] = str_replace('"', '', $val);
 						}
 						else {
 							$retval[$section][trim($key)] = str_replace('"', '', $val);
 						}
 					}
 				}
 			}
 
 			if ($resave) {
 				$fp = fopen($this->INIFile, 'w');
 				reset($contents);
 				fwrite($fp,'<'.'?'.'php die() ?'.">\n\n");
 				foreach ($contents as $line) {
 					fwrite($fp,"$line");
 				}
 				fclose($fp);
 			}
 
 			return $retval;
 		}
 
 		function SaveConfig($silent = false)
 		{
 			if (!is_writable($this->INIFile) && !is_writable(dirname($this->INIFile))) {
 				trigger_error('Cannot write to "' . $this->INIFile . '" file.', $silent ? E_USER_NOTICE : E_USER_ERROR);
 				return ;
 			}
 
 			$fp = fopen($this->INIFile, 'w');
 			fwrite($fp,'<'.'?'.'php die() ?'.">\n\n");
 
 			foreach ($this->systemConfig as $section_name => $section_data) {
 				fwrite($fp, '['.$section_name."]\n");
 				foreach ($section_data as $key => $value) {
 					fwrite($fp, $key.' = "'.$value.'"'."\n");
 				}
 				fwrite($fp, "\n");
 			}
 			fclose($fp);
 		}
 
 		/**
 		 * Sets value to system config (yet SaveConfig must be called to write it to file)
 		 *
 		 * @param string $section
 		 * @param string $key
 		 * @param string $value
 		 */
 		function setSystemConfig($section, $key, $value = null)
 		{
 			if (isset($value)) {
 				if (!array_key_exists($section, $this->systemConfig)) {
 					// create section, when missing
 					$this->systemConfig[$section] = Array ();
 				}
 
 				// create key in section
 				$this->systemConfig[$section][$key] = $value;
 				return ;
 			}
 
 			unset($this->systemConfig[$section][$key]);
 		}
 
 		/**
 		 * Returns information from system config
 		 *
 		 * @return string
 		 */
 		function getSystemConfig($section, $key)
 		{
 			if (!array_key_exists($section, $this->systemConfig)) {
 				return false;
 			}
 
 			if (!array_key_exists($key, $this->systemConfig[$section])) {
 				return false;
 			}
 
 			return $this->systemConfig[$section][$key] ? $this->systemConfig[$section][$key] : false;
 		}
 
 		/**
 		 * Checks if system config is present and is not empty
 		 *
 		 * @return bool
 		 */
 		function systemConfigFound()
 		{
 			return file_exists($this->INIFile) && $this->systemConfig;
 		}
 
 		/**
 		 * Checks if given section is present in config
 		 *
 		 * @param string $section
 		 * @return bool
 		 */
 		function sectionFound($section)
 		{
 			return array_key_exists($section, $this->systemConfig);
 		}
 
 		/**
 		 * Returns formatted module name based on it's root folder
 		 *
 		 * @param string $module_folder
 		 * @return string
 		 */
 		function getModuleName($module_folder)
 		{
 			return implode('-', array_map('ucfirst', explode('-', $module_folder)));
 		}
 
 		/**
 		 * Returns information about module (based on "install/module_info.xml" file)
 		 *
 		 * @param string $module_folder
 		 * @return Array
 		 */
 		function getModuleInfo($module_folder)
 		{
 			$info_file = MODULES_PATH . '/' . $module_folder . '/install/module_info.xml';
 
 			if (!file_exists($info_file)) {
 				return Array ();
 			}
 
 			$xml_helper =& $this->Application->recallObject('kXMLHelper');
 			/* @var $xml_helper kXMLHelper */
 
 			$root_node =& $xml_helper->Parse( file_get_contents($info_file) );
 
 			if (!is_object($root_node) || !preg_match('/^kxmlnode/i', get_class($root_node)) || ($root_node->Name == 'ERROR')) {
 				// non-valid xml file
 				return Array ();
 			}
 
 			$ret = Array ();
 			$current_node =& $root_node->firstChild;
 
 			do {
 				$ret[ strtolower($current_node->Name) ] = trim($current_node->Data);
 			} while (($current_node =& $current_node->NextSibling()));
 
 			return $ret;
 		}
 
 		/**
 		 * Returns nice module string to be used on install/upgrade screens
 		 *
 		 * @param string $module_folder
 		 * @param string $version_string
 		 * @return string
 		 */
 		function getModuleString($module_folder, $version_string)
 		{
 			// image (if exists) <description> (<name> <version>)
 
 			$ret = Array ();
 			$module_info = $this->getModuleInfo($module_folder);
 
 			if (array_key_exists('name', $module_info) && $module_info['name']) {
 				$module_name = $module_info['name'];
 			}
 			else {
 				$module_name = $this->getModuleName($module_folder);
 			}
 
 			if (array_key_exists('image', $module_info) && $module_info['image']) {
 				$image_src = $module_info['image'];
 
 				if (!preg_match('/^(http|https):\/\//', $image_src)) {
 					// local image -> make absolute url
 					$image_src = $this->Application->BaseURL() . $image_src;
 				}
 
 				$ret[] = '<img src="' . $image_src . '" alt="' . htmlspecialchars($module_name) . '" title="' . htmlspecialchars($module_name) . '" style="vertical-align:middle; margin: 3px 0 3px 5px"/>';
 			}
 
 			if (array_key_exists('description', $module_info) && $module_info['description']) {
 				$ret[] = $module_info['description'];
 			}
 			else {
 				$ret[] = $module_name;
 			}
 
 			$ret[] = '(' . $module_name . ' ' . $version_string . ')';
 
 			return implode(' ', $ret);
 		}
 
 		/**
 		 * Creates module root category in "Home" category using given data and returns it
 		 *
 		 * @param string $name
 		 * @param string $description
 		 * @param string $category_template
 		 * @param string $category_icon
 		 * @return kDBItem
 		 */
 		function &createModuleCategory($name, $description, $category_template = null, $category_icon = null)
 		{
 			static $fields = null;
 
 			if (!isset($fields)) {
 				$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 
 				$fields['name'] = $ml_formatter->LangFieldName('Name');
 				$fields['description'] = $ml_formatter->LangFieldName('Description');
 			}
 
 			$category =& $this->Application->recallObject('c', null, Array ('skip_autoload' => true));
 			/* @var $category kDBItem */
 
 			$category_fields = Array (
 				$fields['name'] => $name, 'Filename' => $name, 'AutomaticFilename' => 1,
 				$fields['description'] => $description, 'Status' => STATUS_ACTIVE, 'Priority' => -9999,
 			);
 
 			$category_fields['ParentId'] = $this->Application->findModule('Name', 'Core', 'RootCat');
 
 			if (isset($category_template)) {
 				$category_fields['Template'] = $category_template;
 				$category_fields['CachedTemplate'] = $category_template;
 			}
 
 			if (isset($category_icon)) {
 				$category_fields['UseMenuIconUrl'] = 1;
 				$category_fields['MenuIconUrl'] = $category_icon;
 			}
 
 			$category->Clear();
 			$category->SetDBFieldsFromHash($category_fields);
 
 			$category->Create();
 
 			$priority_helper =& $this->Application->recallObject('PriorityHelper');
 			/* @var $priority_helper kPriorityHelper */
 
 			$event = new kEvent('c:OnListBuild');
 
 			// ensure, that newly created category has proper value in Priority field
 			$priority_helper->recalculatePriorities($event, 'ParentId = ' . $category_fields['ParentId']);
 
 			// update Priority field in object, becase "CategoriesItem::Update" method will be called
 			// from "kInstallToolkit::setModuleItemTemplate" and otherwise will set 0 to Priority field
 			$sql = 'SELECT Priority
 					FROM ' . $category->TableName . '
 					WHERE ' . $category->IDField . ' = ' . $category->GetID();
 			$category->SetDBField('Priority', $this->Conn->GetOne($sql));
 
 			return $category;
 		}
 
 		/**
 		 * Sets category item template into custom field for given prefix
 		 *
 		 * @param kDBItem $category
 		 * @param string $prefix
 		 * @param string $item_template
 		 */
 		function setModuleItemTemplate(&$category, $prefix, $item_template)
 		{
 			$this->Application->removeObject('c-cdata');
 
 			// recreate all fields, because custom fields are added during install script
 			$category->defineFields();
 			$category->prepareConfigOptions(); // creates ml fields
 
 			$category->SetDBField('cust_' . $prefix  .'_ItemTemplate', $item_template);
 			$category->Update();
 		}
 
 		/**
 		 * Link custom field records with search config records + create custom field columns
 		 *
 		 * @param string $module_folder
 		 * @param int $item_type
 		 */
 		function linkCustomFields($module_folder, $prefix, $item_type)
 		{
 			$module_folder = strtolower($module_folder);
 			$module_name = $module_folder;
 
 			if ($module_folder == 'kernel') {
 				$module_name = 'in-portal';
 				$module_folder = 'core';
 			}
 
 			$db =& $this->Application->GetADODBConnection();
 
 			$sql = 'SELECT FieldName, CustomFieldId
 					FROM ' . TABLE_PREFIX . 'CustomField
 					WHERE Type = ' . $item_type . ' AND IsSystem = 0'; // config is not read here yet :( $this->Application->getUnitOption('p', 'ItemType');
 			$custom_fields = $db->GetCol($sql, 'CustomFieldId');
 
 			foreach ($custom_fields as $cf_id => $cf_name) {
 				$sql = 'UPDATE ' . TABLE_PREFIX . 'SearchConfig
 						SET CustomFieldId = ' . $cf_id . '
 						WHERE (TableName = "CustomField") AND (LOWER(ModuleName) = "' . $module_name . '") AND (FieldName = ' . $db->qstr($cf_name) . ')';
 				$db->Query($sql);
 			}
 
 			$this->Application->refreshModuleInfo(); // this module configs are now processed
 
 			// because of configs was read only from installed before modules (in-portal), then reread configs
 			$unit_config_reader =& $this->Application->recallObject('kUnitConfigReader');
 			/* @var $unit_config_reader kUnitConfigReader */
 
 			$unit_config_reader->scanModules(MODULES_PATH . DIRECTORY_SEPARATOR . $module_folder);
 
 			// create correct columns in CustomData table
 			$ml_helper =& $this->Application->recallObject('kMultiLanguageHelper');
 			$ml_helper->createFields($prefix . '-cdata', true);
 		}
 
 		/**
 		 * Deletes cache, useful after separate module install and installator last step
 		 *
 		 */
 		function deleteCache($refresh_permissions = false)
 		{
 			$this->Application->HandleEvent($event, 'adm:OnResetConfigsCache');
 			$this->Application->HandleEvent($event, 'c:OnResetCMSMenuCache');
 
 			if ($refresh_permissions) {
 				if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) {
 					// refresh permission without progress bar
 					$updater =& $this->Application->recallObject('kPermCacheUpdater');
 					/* @var $updater kPermCacheUpdater */
 
 					$updater->OneStepRun();
 				}
 				else {
 					// refresh permissions with ajax progress bar (when available)
 					$this->Application->setDBCache('ForcePermCacheUpdate', 1);
 				}
 			}
 		}
 
 		/**
 		 * Perform redirect after separate module install
 		 *
 		 * @param string $module_folder
 		 * @param bool $refresh_permissions
 		 */
 		function finalizeModuleInstall($module_folder, $refresh_permissions = false)
 		{
+			$this->SetModuleVersion(basename($module_folder), $module_folder);
+
 			if (!$this->Application->GetVar('redirect')) {
 				return ;
 			}
 
-			$this->SetModuleVersion($module_folder);
-
 			$this->deleteCache($refresh_permissions);
 
 			$url_params = Array (
 				'pass' => 'm', 'admin' => 1,
 				'RefreshTree' => 1, 'index_file' => 'index.php',
 			);
 			$this->Application->Redirect('modules/modules_list', $url_params);
 		}
 
 		/**
 		 * Performs rebuild of themes
 		 *
 		 */
 		function rebuildThemes()
 		{
 			$this->Application->HandleEvent($themes_event, 'adm:OnRebuildThemes');
 		}
 
 		/**
 		 * Checks that file is writable by group or others
 		 *
 		 * @param string $file
 		 * @return boolean
 		 */
 		function checkWritePermissions($file)
 		{
 			if (DIRECTORY_SEPARATOR == '\\') {
 				// windows doen't allow to check permissions (always returns null)
 				return null;
 			}
 
 			$permissions = fileperms($file);
 			return $permissions & 0x0010 || $permissions & 0x0002;
 		}
 	}
\ No newline at end of file
Index: branches/5.1.x/core/install/step_templates/choose_modules.tpl
===================================================================
--- branches/5.1.x/core/install/step_templates/choose_modules.tpl	(revision 13188)
+++ branches/5.1.x/core/install/step_templates/choose_modules.tpl	(revision 13189)
@@ -1,61 +1,61 @@
 <?php
 	ob_start();
 ?>
 <tr class="table-color2">
 	<td class="text" colspan="2" valign="middle">
 		<table cellpadding="0" cellspacing="0">
 			<tr>
 				<td valign="top">
 					<input type="checkbox" %3$s name="modules[]" id="module_%1$s" value="%1$s" style="margin-top: 8px;"/>
 				</td>
 				<td valign="top">
 					<label for="module_%1$s" style="">%2$s</label>
 					<div style="font-weight: bold; color: red;">%4$s</div>
 				</td>
 			</tr>
 		</table>
 	</td>
 </tr>
 <?php
 	$module_tpl = ob_get_clean();
 	echo '<br/><p>&nbsp;<strong>Please select Modules you would like to install:</strong></p>';
 	$first_time = $this->GetVar('step') != $this->currentStep; // data from this step was not submitted yet
 	$selected = $this->GetVar('modules');
 	if (!$selected) {
 		// preselect interface modules
 		$selected = Array ('core');
 	}
 
 	$modules_helper =& $this->Application->recallObject('ModulesHelper');
 	/* @var $modules_helper kModulesHelper */
 
 	$licensed_modules = array_map('strtolower', $modules_helper->_GetModules());
 
 	$modules = $this->ScanModules();
 	foreach ($modules as $module) {
-		$module_version = $this->toolkit->GetMaxModuleVersion($module);
-		$prerequisites_errors = $this->toolkit->CheckPrerequisites($module . '/', Array ($module_version), 'install');
+		$module_version = $this->toolkit->GetMaxModuleVersion('modules/' . $module . '/');
+		$prerequisites_errors = $this->toolkit->CheckPrerequisites('modules/' . $module . '/', Array ($module_version), 'install');
 
 		$license_module = $module;
 
 		if (!in_array(strtolower($license_module), $licensed_modules)) {
 			// when module isn't licensed user can't install it
 			continue; // option #1: don't show non-licensed modules
 			array_unshift($prerequisites_errors, 'Module not licensed'); // option #2: show warning under module name
 		}
 
 		if ($prerequisites_errors) {
 			// disable checkbox, when some of prerequisites not passed
 			$checked = 'disabled';
 		}
 		else {
 			// preserve user selected checked status
 			$checked = in_array($module, $selected) || $first_time ? 'checked="checked"' : '';
 		}
 
 		$error_msg = $prerequisites_errors ? implode('<br />', $prerequisites_errors) : '';
 		$module_string = $this->toolkit->getModuleString($module, $module_version);
 
 		echo sprintf($module_tpl, $module, $module_string, $checked, $error_msg);
 	}
 ?>
\ No newline at end of file
Index: branches/5.1.x/core/install.php
===================================================================
--- branches/5.1.x/core/install.php	(revision 13188)
+++ branches/5.1.x/core/install.php	(revision 13189)
@@ -1,1451 +1,1448 @@
 <?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.
 */
 	ini_set('display_errors', 1);
 	error_reporting(E_ALL);
 
 	define('IS_INSTALL', 1);
 	define('ADMIN', 1);
 	define('FULL_PATH', realpath(dirname(__FILE__).'/..') );
 	define('REL_PATH', '/core');
 
 	// run installator
 	$install_engine = new kInstallator();
 	$install_engine->Init();
 	$install_engine->Run();
 	$install_engine->Done();
 
 	class kInstallator {
 
 		/**
 		 * Reference to kApplication class object
 		 *
 		 * @var kApplication
 		 */
 		var $Application = null;
 
 		/**
 		 * Connection to database
 		 *
 		 * @var kDBConnection
 		 */
 		var $Conn = null;
 
 		/**
 		 * XML file containing steps information
 		 *
 		 * @var string
 		 */
 		var $StepDBFile = '';
 
 		/**
 		 * Step name, that currently being processed
 		 *
 		 * @var string
 		 */
 		var $currentStep = '';
 
 		/**
 		 * Steps list (preset) to use for current installation
 		 *
 		 * @var string
 		 */
 		var $stepsPreset = '';
 
 		/**
 		 * Installtion steps to be done
 		 *
 		 * @var Array
 		 */
 		var $steps = Array (
 			'fresh_install'		=>	Array ('check_paths', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'),
 			'clean_reinstall'	=>	Array ('check_paths', 'clean_db', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'),
 			'already_installed'	=>	Array ('check_paths', 'install_setup'),
 
 			'upgrade'			=>	Array ('check_paths', 'install_setup', 'upgrade_modules', 'security', 'finish'),
 			'update_license'	=>	Array ('check_paths', 'install_setup', 'select_license', /*'download_license',*/ 'select_domain', 'security', 'finish'),
 			'db_reconfig'		=>	Array ('check_paths', 'install_setup', 'db_reconfig', 'security', 'finish'),
 			'fix_paths'			=>	Array ('check_paths', 'install_setup', 'fix_paths', 'security', 'finish'),
 		);
 
 		/**
 		 * Steps, that doesn't required admin to be logged-in to proceed
 		 *
 		 * @var Array
 		 */
 		var $skipLoginSteps = Array ('check_paths', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish', -1);
 
 		/**
 		 * Steps, on which kApplication should not be initialized, because of missing correct db table structure
 		 *
 		 * @var Array
 		 */
 		var $skipApplicationSteps = Array ('check_paths', 'clean_db', 'db_config', 'db_reconfig' /*, 'install_setup'*/); // remove install_setup when application will work separately from install
 
 		/**
 		 * Folders that should be writeable to continue installation. $1 - main writeable folder from config.php ("/system" by default)
 		 *
 		 * @var Array
 		 */
 		var $writeableFolders = Array (
 			'$1',
 			'$1/images',
 			'$1/images/resized',
 			'$1/images/pending',
 			'$1/images/pending/resized',
 			'$1/images/emoticons', // for "In-Bulletin"
 			'$1/images/manufacturers', // for "In-Commerce"
 			'$1/images/manufacturers/resized', // for "In-Commerce"
 			'$1/images/polls', // for "In-Bulletin"
 			'$1/images/polls/resized', // for "In-Bulletin"
 			'$1/backupdata',
 			'$1/export',
 			'$1/stylesheets',
 			'$1/user_files',
 			'$1/cache',
 			'/themes',
 		);
 
 		/**
 		 * Contains last error message text
 		 *
 		 * @var string
 		 */
 		var $errorMessage = '';
 
 		/**
 		 * Base path for includes in templates
 		 *
 		 * @var string
 		 */
 		var $baseURL = '';
 
 		/**
 		 * Holds number of last executed query in the SQL
 		 *
 		 * @var int
 		 */
 		var $LastQueryNum = 0;
 
 		/**
 		 * Common tools required for installation process
 		 *
 		 * @var kInstallToolkit
 		 */
 		var $toolkit = null;
 
 		function Init()
 		{
 			include_once(FULL_PATH . REL_PATH . '/kernel/utility/multibyte.php');	// emulating multi-byte php extension
 			require_once(FULL_PATH . REL_PATH . '/install/install_toolkit.php'); // toolkit required for module installations to installator
 			$this->toolkit = new kInstallToolkit();
 			$this->toolkit->setInstallator($this);
 
 			$this->StepDBFile = FULL_PATH.'/'.REL_PATH.'/install/steps_db.xml';
 
 			$base_path = rtrim(preg_replace('/'.preg_quote(rtrim(REL_PATH, '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/');
 			$this->baseURL = 'http://'.$_SERVER['HTTP_HOST'].$base_path.'/core/install/';
 
 			set_error_handler( Array(&$this, 'ErrorHandler') );
 
 			if (file_exists($this->toolkit->INIFile)) {
 				// if config.php found, then check his write permission too
 				$this->writeableFolders[] = '/config.php';
 			}
 			else {
 				$this->writeableFolders[] = '/';
 			}
 
 			if (!$this->toolkit->getSystemConfig('Misc', 'WriteablePath')) {
 				// set global writable folder when such setting is missing
 				$this->toolkit->setSystemConfig('Misc', 'WriteablePath', DIRECTORY_SEPARATOR . 'system');
 				$this->toolkit->SaveConfig(true); // immediately save, because this path will be used in Application later
 			}
 
 			$this->currentStep = $this->GetVar('step');
 
 			// can't check login on steps where no application present anyways :)
 			$this->skipLoginSteps = array_unique(array_merge($this->skipLoginSteps, $this->skipApplicationSteps));
 
 			$this->SelectPreset();
 
 			if (!$this->currentStep) {
 				$this->SetFirstStep(); // sets first step of current preset
 			}
 
 			$this->InitStep();
 		}
 
 		function SetFirstStep()
 		{
 			reset($this->steps[$this->stepsPreset]);
 			$this->currentStep = current($this->steps[$this->stepsPreset]);
 		}
 
 		/**
 		 * Selects preset to proceed based on various criteria
 		 *
 		 */
 		function SelectPreset()
 		{
 			$preset = $this->GetVar('preset');
 			if ($this->toolkit->systemConfigFound()) {
 				// only at installation first step
 				$status = $this->CheckDatabase(false);
 				if ($status && $this->AlreadyInstalled()) {
 					// if already installed, then all future actions need login to work
 					$this->skipLoginSteps = Array ('check_paths', -1);
 					if (!$preset) {
 						$preset = 'already_installed';
 						$this->currentStep = '';
 					}
 				}
 			}
 			if ($preset === false) {
 				$preset = 'fresh_install'; // default preset
 			}
 
 			$this->stepsPreset = $preset;
 		}
 
 		function GetVar($name)
 		{
 			return array_key_exists($name, $_REQUEST) ? $_REQUEST[$name] : false;
 		}
 
 		function SetVar($name, $value)
 		{
 			$_REQUEST[$name] = $value;
 		}
 
 		/**
 		 * Performs needed intialization of data, that step requires
 		 *
 		 */
 		function InitStep()
 		{
 			$require_login = !in_array($this->currentStep, $this->skipLoginSteps);
 			$this->InitApplication($require_login);
 
 			if ($require_login) {
 				// step require login to proceed
 				if (!$this->Application->LoggedIn()) {
 					$this->stepsPreset = 'already_installed';
 					$this->currentStep = 'install_setup'; // manually set 2nd step, because 'check_paths' step doesn't contain login form
 //					$this->SetFirstStep();
 				}
 			}
 
 			switch ($this->currentStep) {
 				case 'check_paths':
 					$writeable_base = $this->toolkit->getSystemConfig('Misc', 'WriteablePath');
 					foreach ($this->writeableFolders as $folder_path) {
 						$file_path = FULL_PATH . str_replace('$1', $writeable_base, $folder_path);
 						if (file_exists($file_path) && !is_writable($file_path)) {
 							$this->errorMessage = '<br/>Installation can not continue until all required permissions are set correctly';
 							break;
 						}
 					}
 					break;
 
 				case 'clean_db':
 					// don't use Application, because all tables will be erased and it will crash
 					$sql = 'SELECT Path
 							FROM ' . TABLE_PREFIX . 'Modules';
 					$modules = $this->Conn->GetCol($sql);
 
 					foreach ($modules as $module_folder) {
 						$remove_file = '/' . $module_folder . 'install/remove_schema.sql';
 						if (file_exists(FULL_PATH . $remove_file)) {
 							$this->toolkit->RunSQL($remove_file);
 						}
 					}
 
 					$this->currentStep = $this->GetNextStep();
 					break;
 
 				case 'db_config':
 				case 'db_reconfig':
 					$fields = Array (
 						'DBType', 'DBHost', 'DBName', 'DBUser',
 						'DBUserPassword', 'DBCollation', 'TablePrefix'
 					);
 
 					// set fields
 					foreach ($fields as $field_name) {
 						$submit_value = $this->GetVar($field_name);
 
 						if ($submit_value !== false) {
 							$this->toolkit->setSystemConfig('Database', $field_name, $submit_value);
 						}
 						/*else {
 							$this->toolkit->setSystemConfig('Database', $field_name, '');
 						}*/
 					}
 			        break;
 
 				case 'download_license':
 					$license_source = $this->GetVar('license_source');
 					if ($license_source !== false && $license_source != 1) {
 						// previous step was "Select License" and not "Download from Intechnic" option was selected
 						$this->currentStep = $this->GetNextStep();
 					}
 					break;
 
 				case 'choose_modules':
 					// if no modules found, then proceed to next step
 					$modules = $this->ScanModules();
 					if (!$modules) {
 						$this->currentStep = $this->GetNextStep();
 					}
 					break;
 
 				case 'select_theme':
 					// put available theme list in database
 					$this->toolkit->rebuildThemes();
 					break;
 
 				case 'upgrade_modules':
 					// get installed modules from db and compare their versions to upgrade script
 					$modules = $this->GetUpgradableModules();
 					if (!$modules) {
 						$this->currentStep = $this->GetNextStep();
 					}
 					break;
 
 				case 'install_setup':
 					$next_preset = $this->Application->GetVar('next_preset');
 					if ($next_preset !== false) {
 						if ($this->Application->GetVar('login') == 'root') {
 							// verify "root" user using configuration settings
 							$login_event = new kEvent('u.current:OnLogin');
 							$this->Application->HandleEvent($login_event);
 
 							if ($login_event->status != erSUCCESS) {
 								$user =& $this->Application->recallObject('u.current');
 								/* @var $user UsersItem */
 
 								$this->errorMessage = $user->GetErrorMsg('ValidateLogin') . '. If you don\'t know your username or password, contact Intechnic Support';
 							}
 						}
 						else {
 							// non "root" user -> verify using licensing server
 							$url_params = Array (
 								'login=' . md5( $this->GetVar('login') ),
 								'password=' . md5( $this->GetVar('password') ),
 								'action=check',
 								'license_code=' . base64_encode( $this->toolkit->getSystemConfig('Intechnic', 'LicenseCode') ),
-								'version=' . '4.3.0',//$this->toolkit->GetMaxModuleVersion('In-Portal'),
+								'version=' . '4.3.0',//$this->toolkit->GetMaxModuleVersion('core/'),
 								'domain=' . base64_encode($_SERVER['HTTP_HOST']),
 							);
 
 							$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
 							$file_data = curl_post($license_url, '', null, 'GET');
 
 							if (substr($file_data, 0, 5) == 'Error') {
 								$this->errorMessage = substr($file_data, 6) . ' If you don\'t know your username or password, contact Intechnic Support';
 							}
 
 							if ($this->errorMessage == '') {
 								$user_id = -1;
 								$session =& $this->Application->recallObject('Session');
 								$session->SetField('PortalUserId', $user_id);
 								$this->Application->SetVar('u.current_id', $user_id);
 								$this->Application->StoreVar('user_id', $user_id);
 							}
 						}
 
 						if ($this->errorMessage == '') {
 							// processed with redirect to selected step preset
 							if (!isset($this->steps[$next_preset])) {
 								$this->errorMessage = 'Preset "'.$next_preset.'" not yet implemented';
 							}
 							else {
 								$this->stepsPreset = $next_preset;
 							}
 						}
 					}
 					else {
 						// if preset was not choosen, then raise error
 						$this->errorMessage = 'Please select action to perform';
 					}
 					break;
 
 				case 'security':
 					// perform write check
 					if ($this->Application->GetVar('skip_security_check')) {
 						// administrator intensionally skips security checks
 						break;
 					}
 
 					$write_check = true;
 					$check_paths = Array ('/', '/index.php', '/config.php', ADMIN_DIRECTORY . '/index.php');
 					foreach ($check_paths as $check_path) {
 						$path_check_status = $this->toolkit->checkWritePermissions(FULL_PATH . $check_path);
 
 						if (is_bool($path_check_status) && $path_check_status) {
 							$write_check = false;
 							break;
 						}
 					}
 
 					// script execute check
 					if (file_exists(WRITEABLE . '/install_check.php')) {
 						unlink(WRITEABLE . '/install_check.php');
 					}
 
 					$fp = fopen(WRITEABLE . '/install_check.php', 'w');
 					fwrite($fp, "<?php\n\techo 'OK';\n");
 					fclose($fp);
 
 					$curl_helper =& $this->Application->recallObject('CurlHelper');
 					/* @var $curl_helper kCurlHelper */
 
 					$output = $curl_helper->Send($this->Application->BaseURL(WRITEBALE_BASE) . 'install_check.php');
 					unlink(WRITEABLE . '/install_check.php');
 					$execute_check = ($output !== 'OK');
 
 					$directive_check = true;
 					$ini_vars = Array ('register_globals' => false, 'open_basedir' => true, 'allow_url_fopen' => false);
 					foreach ($ini_vars as $var_name => $var_value) {
 						$current_value = ini_get($var_name);
 
 						if (($var_value && !$current_value) || (!$var_value && $current_value)) {
 							$directive_check = false;
 							break;
 						}
 					}
 
 					if (!$write_check || !$execute_check || !$directive_check) {
 						$this->errorMessage = true;
 					}
 					/*else {
 						$this->currentStep = $this->GetNextStep();
 					}*/
 					break;
 			}
 
 			$this->PerformValidation(); // returns validation status (just in case)
 		}
 
 		/**
 		 * Validates data entered by user
 		 *
 		 * @return bool
 		 */
 		function PerformValidation()
 		{
 			if ($this->GetVar('step') != $this->currentStep) {
 				// just redirect from previous step, don't validate
 				return true;
 			}
 
 			$status = true;
 
 			switch ($this->currentStep) {
 				case 'db_config':
 				case 'db_reconfig':
 					// 1. check if required fields are filled
 					$section_name = 'Database';
 					$required_fields = Array ('DBType', 'DBHost', 'DBName', 'DBUser', 'DBCollation');
 					foreach ($required_fields as $required_field) {
 						if (!$this->toolkit->getSystemConfig($section_name, $required_field)) {
 							$status = false;
 							$this->errorMessage = 'Please fill all required fields';
 							break;
 						}
 					}
 					if (!$status) break;
 
 					// 2. check permissions, that use have in this database
 					$status = $this->CheckDatabase(($this->currentStep == 'db_config') && !$this->GetVar('UseExistingSetup'));
 					break;
 
 				case 'select_license':
 					$license_source = $this->GetVar('license_source');
 					if ($license_source == 2) {
 						// license from file -> file must be uploaded
 						$upload_error = $_FILES['license_file']['error'];
 						if ($upload_error != UPLOAD_ERR_OK) {
 							$this->errorMessage = 'Missing License File';
 						}
 					}
 					elseif (!is_numeric($license_source)) {
 						$this->errorMessage = 'Please select license';
 					}
 
 					$status = $this->errorMessage == '';
 					break;
 
 				case 'root_password':
 					// check, that password & verify password match
 					$password = $this->Application->GetVar('root_password');
 					$password_verify = $this->Application->GetVar('root_password_verify');
 
 					if ($password != $password_verify) {
 						$this->errorMessage = 'Passwords does not match';
 					}
 					elseif (mb_strlen($password) < 4) {
 						$this->errorMessage = 'Root Password must be at least 4 characters';
 					}
 
 					$status = $this->errorMessage == '';
 					break;
 
 				case 'choose_modules':
 					break;
 
 				case 'upgrade_modules':
 					$modules = $this->Application->GetVar('modules');
 					if (!$modules) {
 						$modules = Array ();
 						$this->errorMessage = 'Please select module(-s) to ' . ($this->currentStep == 'choose_modules' ? 'install' : 'upgrade');
 					}
 
 					// check interface module
 					$upgrade_data = $this->GetUpgradableModules();
 
 					if (array_key_exists('core', $upgrade_data) && !in_array('core', $modules)) {
 						// core can be upgraded, but isn't selected
 						$this->errorMessage = 'Please select "Core" as interface module';
 					}
 
 					$status = $this->errorMessage == '';
 					break;
 			}
 
 			return $status;
 		}
 
 		/**
 		 * Perform installation step actions
 		 *
 		 */
 		function Run()
 		{
 			if ($this->errorMessage) {
 				// was error during data validation stage
 				return ;
 			}
 
 			switch ($this->currentStep) {
 				case 'db_config':
 				case 'db_reconfig':
 					// store db configuration
 					$sql = 'SHOW COLLATION
 							LIKE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\'';
 					$collation_info = $this->Conn->Query($sql);
 					if ($collation_info) {
 						$this->toolkit->setSystemConfig('Database', 'DBCharset', $collation_info[0]['Charset']);
 
 						// database is already connected, that's why set collation on the fly
 						$this->Conn->Query('SET NAMES \''.$this->toolkit->getSystemConfig('Database', 'DBCharset').'\' COLLATE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\'');
 					}
 
 					$this->toolkit->SaveConfig();
 
 					if ($this->currentStep == 'db_config') {
 						if ($this->GetVar('UseExistingSetup')) {
 							// abort clean install and redirect to already_installed
 							$this->stepsPreset = 'already_installed';
 							break;
 						}
 
 						// import base data into new database, not for db_reconfig
 						$this->toolkit->RunSQL('/core/install/install_schema.sql');
 						$this->toolkit->RunSQL('/core/install/install_data.sql');
 
 						// create category using sql, because Application is not available here
 						$table_name = $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'IdGenerator';
 						$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
 						$resource_id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
 						if ($resource_id === false) {
 							$this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)');
 							$resource_id = 2;
 						}
 
 						$fields_hash = Array (
 							'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0,
 							'CreatedById' => -1, 'CreatedOn' => time(), 'ResourceId' => $resource_id - 1,
 							'l1_Description' => 'Content', 'Status' => 4,
 						);
 
 						$this->Conn->doInsert($fields_hash, $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'Category');
 
 						$this->toolkit->SetModuleRootCategory('Core', $this->Conn->getInsertID());
 
 						// set module "Core" version after install (based on upgrade scripts)
-						$this->toolkit->SetModuleVersion('Core');
+						$this->toolkit->SetModuleVersion('Core', 'core/');
 
 						// for now we set "In-Portal" module version to "Core" module version (during clean install)
-						$this->toolkit->SetModuleVersion('In-Portal', $this->toolkit->GetMaxModuleVersion('Core'));
+						$this->toolkit->SetModuleVersion('In-Portal', 'core/');
 					}
 					break;
 
 				case 'select_license':
 					$license_source = $this->GetVar('license_source');
 					switch ($license_source) {
 						case 1: // Download from Intechnic
 
 							break;
 
 						case 2: // Upload License File
 							$file_data = array_map('trim', file($_FILES['license_file']['tmp_name']));
 							if ((count($file_data) == 3) && $file_data[1]) {
 								$modules_helper =& $this->Application->recallObject('ModulesHelper');
 								/* @var $modules_helper kModulesHelper */
 
 								if ($modules_helper->verifyLicense($file_data[1])) {
 									$this->toolkit->setSystemConfig('Intechnic', 'License', $file_data[1]);
 									$this->toolkit->setSystemConfig('Intechnic', 'LicenseCode', $file_data[2]);
 									$this->toolkit->SaveConfig();
 								}
 								else {
 									$this->errorMessage = 'Invalid License File';
 								}
 							}
 							else {
 								$this->errorMessage = 'Invalid License File';
 							}
 							break;
 
 						case 3: // Use Existing License
 							$license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License');
 							if ($license_hash) {
 								$modules_helper =& $this->Application->recallObject('ModulesHelper');
 								/* @var $modules_helper kModulesHelper */
 
 								if (!$modules_helper->verifyLicense($license_hash)) {
 									$this->errorMessage = 'Invalid or corrupt license detected';
 								}
 							}
 							else {
 								// happens, when browser's "Back" button is used
 								$this->errorMessage = 'Missing License File';
 							}
 							break;
 
 						case 4: // Skip License (Local Domain Installation)
 							if ($this->toolkit->sectionFound('Intechnic')) {
 								// remove any previous license information
 								$this->toolkit->setSystemConfig('Intechnic', 'License');
 								$this->toolkit->setSystemConfig('Intechnic', 'LicenseCode');
 								$this->toolkit->SaveConfig();
 							}
 							break;
 					}
 					break;
 
 				case 'download_license':
 					$license_login = $this->GetVar('login');
 					$license_password = $this->GetVar('password');
 					$license_id = $this->GetVar('licenses');
 
 					if (strlen($license_login) && strlen($license_password) && !$license_id) {
 						// Here we determine weather login is ok & check available licenses
 						$url_params = Array (
 							'login=' . md5($license_login),
 							'password=' . md5($license_password),
-							'version=' . $this->toolkit->GetMaxModuleVersion('In-Portal'),
+							'version=' . $this->toolkit->GetMaxModuleVersion('core/'),
 							'domain=' . base64_encode($_SERVER['HTTP_HOST']),
 						);
 
 						$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
 						$file_data = curl_post($license_url, '', null, 'GET');
 						if (!$file_data) {
 							// error connecting to licensing server
 							$this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!';
 						}
 						else {
 							if (substr($file_data, 0, 5) == 'Error') {
 								// after processing data server returned error
 								$this->errorMessage = substr($file_data, 6);
 							}
 							else {
 								// license received
 								if (substr($file_data, 0, 3) == 'SEL') {
 									// we have more, then one license -> let user choose
 									$this->SetVar('license_selection', base64_encode( substr($file_data, 4) )); // we received html with radio buttons with names "licenses"
 									$this->errorMessage = 'Please select which license to use';
 								}
 								else {
 									// we have one license
 									$this->toolkit->processLicense($file_data);
 								}
 							}
 						}
 					}
 					else if (!$license_id) {
 						// licenses were not queried AND user/password missing
 						$this->errorMessage = 'Incorrect Username or Password. If you don\'t know your username or password, contact Intechnic Support';
 					}
 					else {
 						// Here we download license
 						$url_params = Array (
 							'license_id=' . md5($license_id),
 							'dlog=' . md5($license_login),
 							'dpass=' . md5($license_password),
-							'version=' . $this->toolkit->GetMaxModuleVersion('In-Portal'),
+							'version=' . $this->toolkit->GetMaxModuleVersion('core/'),
 							'domain=' . base64_encode($_SERVER['HTTP_HOST']),
 						);
 
 						$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
 						$file_data = curl_post($license_url, '', null, 'GET');
 
 						if (!$file_data) {
 							// error connecting to licensing server
 							$this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!';
 						}
 						else {
 							if (substr($file_data, 0, 5) == 'Error') {
 								// after processing data server returned error
 								$this->errorMessage = substr($file_data, 6);
 							}
 							else {
 								$this->toolkit->processLicense($file_data);
 							}
 						}
 					}
 					break;
 
 				case 'select_domain':
 					$modules_helper =& $this->Application->recallObject('ModulesHelper');
 					/* @var $modules_helper kModulesHelper */
 
 					$license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License');
 					if ($license_hash) {
 						// when license present, then extract domain from it
 						$license_hash = base64_decode($license_hash);
 						list ( , , $license_keys) = $modules_helper->_ParseLicense($license_hash);
 						$license_domain = $license_keys[0]['domain'];
 					}
 					else {
 						// when license missing, then use current domain
 						$license_domain = $_SERVER['HTTP_HOST'];
 					}
 
 					$domain = $this->GetVar('domain') == 1 ? $_SERVER['HTTP_HOST'] : str_replace(' ', '', $this->GetVar('other'));
 
 					if ($domain != '') {
 						if (strstr($domain, $license_domain) || $modules_helper->_IsLocalSite($domain)) {
 							$this->toolkit->setSystemConfig('Misc', 'Domain', $domain);
 							$this->toolkit->SaveConfig();
 						}
 						else {
 							$this->errorMessage = 'Domain name entered does not match domain name in the license!';
 						}
 					}
 					else {
 						$this->errorMessage = 'Please enter valid domain!';
 					}
 					break;
 
 				case 'root_password':
 					// update root password in database
 					$password = md5( md5($this->Application->GetVar('root_password')) . 'b38');
 
 					$config_values = Array (
 						'RootPass' => $password,
 						'Site_Path' => BASE_PATH.'/', // set Site_Path (for SSL & old in-portal code)
 						'Backup_Path' => FULL_PATH . $this->toolkit->getSystemConfig('Misc', 'WriteablePath') . DIRECTORY_SEPARATOR . 'backupdata',
 						'Smtp_AdminMailFrom' => 'portal@' . $this->toolkit->getSystemConfig('Misc', 'Domain')
 					);
 
 					$this->toolkit->saveConfigValues($config_values);
 
 					// login as "root", when no errors on password screen
 					$this->Application->SetVar('login', 'root');
 					$this->Application->SetVar('password', $this->Application->GetVar('root_password'));
 
 					$login_event = new kEvent('u.current:OnLogin');
 					$this->Application->HandleEvent($login_event);
 
 					// import base language for core (english)
 					$this->toolkit->ImportLanguage('/core/install/english');
 
 					// make sure imported language is set as active in session, created during installation
 					$this->Application->Session->SetField('Language', 1);
 
 					// set imported language as primary
 					$lang =& $this->Application->recallObject('lang.-item', null, Array('skip_autoload' => true));
 					/* @var $lang LanguagesItem */
 
 					$lang->Load(1); // fresh install => ID=1
 					$lang->setPrimary(true); // for Front-End
 					break;
 
 				case 'choose_modules':
 					// run module install scripts
 					$modules = $this->Application->GetVar('modules');
 					if ($modules) {
 						foreach ($modules as $module) {
 							$install_file = MODULES_PATH.'/'.$module.'/install.php';
 							if (file_exists($install_file)) {
 								include_once($install_file);
-
-								// set module version after install (based on upgrade scripts)
-								$this->toolkit->SetModuleVersion($module);
 							}
 						}
 					}
 
 					// update category cache
 					$updater =& $this->Application->recallObject('kPermCacheUpdater');
 					/* @var $updater kPermCacheUpdater */
 
 					$updater->OneStepRun();
 					break;
 
 				case 'post_config':
 					$this->toolkit->saveConfigValues( $this->GetVar('config') );
 					break;
 
 				case 'select_theme':
 					// 1. mark theme, that user is selected
 					$theme_id = $this->GetVar('theme');
 					$theme_table = $this->Application->getUnitOption('theme', 'TableName');
 					$theme_idfield = $this->Application->getUnitOption('theme', 'IDField');
 
 					$sql = 'UPDATE ' . $theme_table . '
 							SET Enabled = 1, PrimaryTheme = 1
 							WHERE ' . $theme_idfield . ' = ' . $theme_id;
 					$this->Conn->Query($sql);
 
 					$this->toolkit->rebuildThemes(); // rescan theme to create structure after theme is enabled !!!
 
 					if ($this->Application->isModuleEnabled('In-Portal')) {
 						// 2. compile theme stylesheets (only In-Portal uses them)
 						$css_table = $this->Application->getUnitOption('css', 'TableName');
 						$css_idfield = $this->Application->getUnitOption('css', 'IDField');
 
 						$sql = 'SELECT LOWER(Name) AS Name, ' . $css_idfield . '
 								FROM ' . $css_table;
 						$css_hash = $this->Conn->GetCol($sql, $css_idfield);
 
 						$css_item =& $this->Application->recallObject('css', null, Array('skip_autoload' => true));
 						/* @var $css_item StyleshetsItem */
 
 						foreach ($css_hash as $stylesheet_id => $theme_name) {
 							$css_item->Load($stylesheet_id);
 							$css_item->Compile();
 
 							$sql = 'UPDATE ' . $theme_table . '
 									SET StylesheetId = ' . $stylesheet_id . '
 									WHERE LOWER(Name) = ' .  $this->Conn->qstr($theme_name);
 							$this->Conn->Query($sql);
 						}
 					}
 
 					// install theme dependent demo data
 					if ($this->Application->GetVar('install_demo_data')) {
 						$sql = 'SELECT Name
 								FROM ' . $theme_table . '
 								WHERE ' . $theme_idfield . ' = ' . $theme_id;
 						$theme_name = $this->Conn->GetOne($sql);
 
 						foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 							if ($module_name == 'In-Portal') {
 								continue;
 							}
 
 							$this->toolkit->RunSQL('/themes' . '/' . $theme_name . '/' . $module_info['TemplatePath'] . '_install/install_data.sql', '{ThemeId}', $theme_id);
 						}
 					}
 					break;
 
 				case 'upgrade_modules':
 					// get installed modules from db and compare their versions to upgrade script
 					$modules = $this->Application->GetVar('modules');
 					if ($modules) {
 						$upgrade_data = $this->GetUpgradableModules();
 
 						$start_from_module = $this->GetVar('continue_from_module');
 						$start_from_query = $this->GetVar('continue_from_query');
 						if (!$start_from_query) $start_from_query = 0;
 						foreach ($modules as $module_name) {
 							if ($start_from_module && $module_name != $start_from_module) {
 								continue;
 							}
 							else {
 								$start_from_module = false; //otherwise it will skip all modules after the one we start with!
 							}
 							$module_info = $upgrade_data[$module_name];
 							$upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql');
 
 							$sqls = file_get_contents($upgrades_file);
 							$version_mark = preg_replace('/(\(.*?\))/', $module_info['FromVersion'], VERSION_MARK);
 
 							// get only sqls from next (relative to current) version to end of file
 							$start_pos = strpos($sqls, $version_mark);
 							$sqls = substr($sqls, $start_pos);
 
 							preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs);
 
 							if (!$start_from_module) {
 								$this->RunUpgrades($module_info['Path'], $regs[1], 'before');
 							}
 							if (!$this->toolkit->RunSQLText($sqls, null, null, $start_from_query)) {
 								$this->errorMessage .= '<input type="hidden" name="continue_from_module" value="'.$module_name.'">';
 								$this->errorMessage .= '<input type="hidden" name="continue_from_query" value="'.$this->LastQueryNum.'">';
 								$this->errorMessage .= '<br/>Click Continue button below to skip this query and go further<br/>';
 								$this->Done();
 							}
 							$start_from_query = 0; // so that next module start from the beggining
 
 							$this->toolkit->ImportLanguage('/' . $module_info['Path'] . 'install/english', true);
 							$this->RunUpgrades($module_info['Path'], $regs[1], 'after'); // upgrade script could operate resulting language pack
 
 							// after upgrade sqls are executed update version and upgrade language pack
 							$this->toolkit->SetModuleVersion($module_name, $module_info['ToVersion']);
 						}
 
 						// for now we set "In-Portal" module version to "Core" module version (during upgrade)
 						if (in_array('core', $modules)) {
 							$this->toolkit->SetModuleVersion('In-Portal', $upgrade_data['core']['ToVersion']);
 						}
 					}
 					break;
 
 				case 'fix_paths':
 					$this->toolkit->saveConfigValues( $this->Application->GetVar('config') );
 					break;
 
 				case 'finish':
 					// delete cache
 					$this->toolkit->deleteCache();
 
 					// set installation finished mark
 					if ($this->Application->ConfigValue('InstallFinished') === false) {
 						$fields_hash = Array (
 							'VariableName'	=>	'InstallFinished',
 							'VariableValue'	=>	1,
 						);
 						$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'ConfigurationValues');
 					}
 					break;
 			}
 
 			if ($this->errorMessage) {
 				// was error during run stage
 				return ;
 			}
 
 			$this->currentStep = $this->GetNextStep();
 			$this->InitStep(); // init next step (that will be shown now)
 
 			$this->InitApplication();
 
 			if ($this->currentStep == -1) {
 				// step after last step -> redirect to admin
 				$this->Application->Redirect('index', null, '', 'index.php');
 			}
 		}
 
 		/**
 		 * Run upgrade PHP scripts for module with specified path
 		 *
 		 * @param string $module_path
 		 * @param Array $versions
 		 * @param string $mode upgrade mode = {before,after}
 		 */
 		function RunUpgrades($module_path, $versions, $mode)
 		{
 			static $upgrade_classes = Array ();
 
 			$upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php');
 			if (!file_exists($upgrades_file) || !$versions) {
 				return ;
 			}
 
 			if (!isset($upgrade_classes[$module_path])) {
 				// save class name, because 2nd time
 				// (in after call $upgrade_class variable will not be present)
 				include_once $upgrades_file;
 				$upgrade_classes[$module_path] = $upgrade_class;
 			}
 
 			$upgrade_object = new $upgrade_classes[$module_path]();
 			if (method_exists($upgrade_object, 'setToolkit')) {
 				$upgrade_object->setToolkit($this->toolkit);
 			}
 
 			foreach ($versions as $version) {
 				$upgrade_method = 'Upgrade_'.str_replace(Array ('.', '-'), '_', $version);
 				if (method_exists($upgrade_object, $upgrade_method)) {
 					$upgrade_object->$upgrade_method($mode);
 				}
 			}
 		}
 
 		/**
 		 * Initialize kApplication
 		 *
 		 * @param bool $force initialize in any case
 		 */
 		function InitApplication($force = false)
 		{
 			if (($force || !in_array($this->currentStep, $this->skipApplicationSteps)) && !isset($this->Application)) {
 				// step is allowed for application usage & it was not initialized in previous step
 				global $start, $debugger, $dbg_options, $vars;
 				include_once(FULL_PATH.'/core/kernel/startup.php');
 
 				$this->Application =& kApplication::Instance();
 				$this->toolkit->Application =& kApplication::Instance();
 
 				$this->Application->Init();
 
 				$this->Conn =& $this->Application->GetADODBConnection();
 				$this->toolkit->Conn =& $this->Application->GetADODBConnection();
 			}
 		}
 
 		/**
 		 * Show next step screen
 		 *
 		 */
 		function Done($error_message = null)
 		{
 			if (isset($error_message)) {
 				$this->errorMessage = $error_message;
 			}
 
 			include_once (FULL_PATH.'/'.REL_PATH.'/install/incs/install.tpl');
 
 			if (isset($this->Application)) {
 				$this->Application->Done();
 			}
 
 			exit;
 		}
 
 		function ConnectToDatabase()
 		{
 			include_once FULL_PATH . '/core/kernel/db/db_connection.php';
 
 			$required_keys = Array ('DBType', 'DBUser', 'DBName');
 			foreach ($required_keys as $required_key) {
 				if (!$this->toolkit->getSystemConfig('Database', $required_key)) {
 					// one of required db connection settings missing -> abort connection
 					return false;
 				}
 			}
 
 			$this->Conn = new kDBConnection($this->toolkit->getSystemConfig('Database', 'DBType'), Array(&$this, 'DBErrorHandler'));
 			$this->Conn->Connect(
 				$this->toolkit->getSystemConfig('Database', 'DBHost'),
 				$this->toolkit->getSystemConfig('Database', 'DBUser'),
 				$this->toolkit->getSystemConfig('Database', 'DBUserPassword'),
 				$this->toolkit->getSystemConfig('Database', 'DBName')
 			);
 
 			// setup toolkit too
 			$this->toolkit->Conn =& $this->Conn;
 
 			return $this->Conn->errorCode == 0;
 		}
 
 		/**
 		 * Checks if core is already installed
 		 *
 		 * @return bool
 		 */
 		function AlreadyInstalled()
 		{
 			$table_prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix');
 
 			$sql = 'SELECT VariableValue
 					FROM ' . $table_prefix . 'ConfigurationValues
 					WHERE VariableName = "InstallFinished"';
 
 			return $this->TableExists('ConfigurationValues') && $this->Conn->GetOne($sql);
 		}
 
 		function CheckDatabase($check_installed = true)
 		{
 			// perform various check type to database specified
 			// 1. user is allowed to connect to database
 			// 2. user has all types of permissions in database
 
 
 			if (mb_strlen($this->toolkit->getSystemConfig('Database', 'TablePrefix')) > 7) {
 				$this->errorMessage = 'Table prefix should not be longer than 7 characters';
 				return false;
 			}
 
 			// connect to database
 			$status = $this->ConnectToDatabase();
 			if ($status) {
 				// if connected, then check if all sql statements work
 				$sql_tests[] = 'DROP TABLE IF EXISTS test_table';
 				$sql_tests[] = 'CREATE TABLE test_table(test_col mediumint(6))';
 				$sql_tests[] = 'LOCK TABLES test_table WRITE';
 				$sql_tests[] = 'INSERT INTO test_table(test_col) VALUES (5)';
 				$sql_tests[] = 'UPDATE test_table SET test_col = 12';
 				$sql_tests[] = 'UNLOCK TABLES';
 				$sql_tests[] = 'ALTER TABLE test_table ADD COLUMN new_col varchar(10)';
 				$sql_tests[] = 'SELECT * FROM test_table';
 				$sql_tests[] = 'DELETE FROM test_table';
 				$sql_tests[] = 'DROP TABLE IF EXISTS test_table';
 
 				foreach ($sql_tests as $sql_test) {
 					$this->Conn->Query($sql_test);
 					if ($this->Conn->getErrorCode() != 0) {
 						$status = false;
 						break;
 					}
 				}
 
 				if ($status) {
 					// if statements work & connection made, then check table existance
 					if ($check_installed && $this->AlreadyInstalled()) {
 						$this->errorMessage = 'An In-Portal Database already exists at this location';
 						return false;
 					}
 				}
 				else {
 					// user has insufficient permissions in database specified
 					$this->errorMessage = 'Permission Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg();
 					return false;
 				}
 			}
 			else {
 				// was error while connecting
 				if (!$this->Conn) return false;
 				$this->errorMessage = 'Connection Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg();
 				return false;
 			}
 
 			return true;
 		}
 
 		/**
 		 * Checks if all passed tables exists
 		 *
 		 * @param string $tables comma separated tables list
 		 * @return bool
 		 */
 		function TableExists($tables)
 		{
 			$prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix');
 
 			$all_found = true;
 			$tables = explode(',', $tables);
 			foreach ($tables as $table_name) {
 				$sql = 'SHOW TABLES LIKE "'.$prefix.$table_name.'"';
 				if (count($this->Conn->Query($sql)) == 0) {
 					$all_found = false;
 					break;
 				}
 			}
 
 			return $all_found;
 		}
 
 		/**
 		 * Returns modules list found in modules folder
 		 *
 		 * @return Array
 		 */
 		function ScanModules()
 		{
 			static $modules = null;
 
 			if (!isset($modules)) {
 				$modules = Array();
 				$fh = opendir(MODULES_PATH);
 				while ( ($sub_folder = readdir($fh)) ) {
 					$folder_path = MODULES_PATH . '/'.$sub_folder;
 					if ($sub_folder != '.' && $sub_folder != '..' && is_dir($folder_path)) {
 						// this is folder in MODULES_PATH directory
 						if (file_exists($folder_path.'/install.php') && file_exists($folder_path.'/install/install_schema.sql')) {
 							$install_order = trim( file_get_contents($folder_path . '/install/install_order.txt') );
 							$modules[$install_order] = $sub_folder;
 						}
 					}
 				}
 			}
 
 			// allows to control module install order
 			ksort($modules, SORT_NUMERIC);
 			return $modules;
 		}
 
 		/**
 		 * Returns list of modules, that can be upgraded
 		 *
 		 */
 		function GetUpgradableModules()
 		{
 			$ret = Array ();
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				if ($module_name == 'In-Portal') {
 					// don't show In-Portal, because it shares upgrade scripts with Core module
 					continue;
 				}
 
 				$upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql');
 				if (!file_exists($upgrades_file)) {
 					// no upgrade file
 					continue;
 				}
 
 				$sqls = file_get_contents($upgrades_file);
 				$versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs);
 				if (!$versions_found) {
 					// upgrades file doesn't contain version definitions
 					continue;
 				}
 
 				$to_version = end($regs[1]);
 				$this_version = $this->toolkit->ConvertModuleVersion($module_info['Version']);
 				if ($this->toolkit->ConvertModuleVersion($to_version) > $this_version) {
 					// destination version is greather then current
 					foreach ($regs[1] as $version) {
 						if ($this->toolkit->ConvertModuleVersion($version) > $this_version) {
 							$from_version = $version;
 							break;
 						}
 					}
 
 					$version_info = Array (
 						'FromVersion'	=>	$from_version,
 						'ToVersion'		=>	$to_version,
 					);
 
 					$ret[ strtolower($module_name) ] = array_merge_recursive2($module_info, $version_info);
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Returns content to show for current step
 		 *
 		 * @return string
 		 */
 		function GetStepBody()
 		{
 			$step_template = FULL_PATH.'/core/install/step_templates/'.$this->currentStep.'.tpl';
 			if (file_exists($step_template)) {
 				ob_start();
 				include_once ($step_template);
 				return ob_get_clean();
 			}
 
 			return '{step template "'.$this->currentStep.'" missing}';
 		}
 
 		/**
 		 * Parses step information file, cache result for current step ONLY & return it
 		 *
 		 * @return Array
 		 */
 		function &_getStepInfo()
 		{
 			static $info = Array('help_title' => null, 'step_title' => null, 'help_body' => null, 'queried' => false);
 
 			if (!$info['queried']) {
 				$fdata = file_get_contents($this->StepDBFile);
 
 				$parser = xml_parser_create();
 				xml_parse_into_struct($parser, $fdata, $values, $index);
 				xml_parser_free($parser);
 
 				foreach ($index['STEP'] as $section_index) {
 					$step_data =& $values[$section_index];
 
 					if ($step_data['attributes']['NAME'] == $this->currentStep) {
 						$info['step_title'] = $step_data['attributes']['TITLE'];
 						if (isset($step_data['attributes']['HELP_TITLE'])) {
 							$info['help_title'] = $step_data['attributes']['HELP_TITLE'];
 						}
 						else {
 							// if help title not set, then use step title
 							$info['help_title'] = $step_data['attributes']['TITLE'];
 						}
 						$info['help_body'] = trim($step_data['value']);
 						break;
 					}
 				}
 
 				$info['queried'] = true;
 			}
 
 			return $info;
 		}
 
 		/**
 		 * Returns particular information abou current step
 		 *
 		 * @param string $info_type
 		 * @return string
 		 */
 		function GetStepInfo($info_type)
 		{
 			$step_info =& $this->_getStepInfo();
 
 			if (isset($step_info[$info_type])) {
 				return $step_info[$info_type];
 			}
 
 			return '{step "'.$this->currentStep.'"; param "'.$info_type.'" missing}';
 		}
 
 		/**
 		 * Returns passed steps titles
 		 *
 		 * @param Array $steps
 		 * @return Array
 		 * @see kInstaller:PrintSteps
 		 */
 		function _getStepTitles($steps)
 		{
 			$fdata = file_get_contents($this->StepDBFile);
 
 			$parser = xml_parser_create();
 			xml_parse_into_struct($parser, $fdata, $values, $index);
 			xml_parser_free($parser);
 
 			$ret = Array ();
 			foreach ($index['STEP'] as $section_index) {
 				$step_data =& $values[$section_index];
 				if (in_array($step_data['attributes']['NAME'], $steps)) {
 					$ret[ $step_data['attributes']['NAME'] ] = $step_data['attributes']['TITLE'];
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Returns current step number in active steps_preset.
 		 * Value can't be cached, because same step can have different number in different presets
 		 *
 		 * @return int
 		 */
 		function GetStepNumber()
 		{
 			return array_search($this->currentStep, $this->steps[$this->stepsPreset]) + 1;
 		}
 
 		/**
 		 * Returns step name to process next
 		 *
 		 * @return string
 		 */
 		function GetNextStep()
 		{
 			$next_index = $this->GetStepNumber();
 			if ($next_index > count($this->steps[$this->stepsPreset]) - 1) {
 				return -1;
 			}
 
 			return $this->steps[$this->stepsPreset][$next_index];
 		}
 
 		/**
 		 * Returns step name, that was processed before this step
 		 *
 		 * @return string
 		 */
 		function GetPreviousStep()
 		{
 			$next_index = $this->GetStepNumber() - 1;
 			if ($next_index < 0) {
 				$next_index = 0;
 			}
 
 			return $this->steps[$this->stepsPreset][$next_index];
 		}
 
 		/**
 		 * Prints all steps from active steps preset and highlights current step
 		 *
 		 * @param string $active_tpl
 		 * @param string $passive_tpl
 		 * @return string
 		 */
 		function PrintSteps($active_tpl, $passive_tpl)
 		{
 			$ret = '';
 			$step_titles = $this->_getStepTitles($this->steps[$this->stepsPreset]);
 
 			foreach ($this->steps[$this->stepsPreset] as $step_name) {
 				$template = $step_name == $this->currentStep ? $active_tpl : $passive_tpl;
 				$ret .= sprintf($template, $step_titles[$step_name]);
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Installation error handler for sql errors
 		 *
 		 * @param int $code
 		 * @param string $msg
 		 * @param string $sql
 		 * @return bool
 		 * @access private
 		 */
 		function DBErrorHandler($code, $msg, $sql)
 		{
 			$this->errorMessage = 'Query: <br />'.htmlspecialchars($sql).'<br />execution result is error:<br />['.$code.'] '.$msg;
 			return true;
 		}
 
 		/**
 		 * Installation error handler
 		 *
 		 * @param int $errno
 		 * @param string $errstr
 		 * @param string $errfile
 		 * @param int $errline
 		 * @param Array $errcontext
 		 */
 		function ErrorHandler($errno, $errstr, $errfile = '', $errline = '', $errcontext = '')
 		{
 			if ($errno == E_USER_ERROR) {
 				// only react on user fatal errors
 				$this->Done($errstr);
 			}
 		}
 
 		/**
 		 * Checks, that given button should be visible on current installation step
 		 *
 		 * @param string $name
 		 * @return bool
 		 */
 		function buttonVisible($name)
 		{
 			$button_visibility = Array (
 				'continue' => $this->GetNextStep() != -1 || ($this->stepsPreset == 'already_installed'),
 				'refresh' => in_array($this->currentStep, Array ('check_paths', 'security')),
 				'back' => in_array($this->currentStep, Array (/*'select_license',*/ 'download_license', 'select_domain')),
 			);
 
 			if ($name == 'any') {
 				foreach ($button_visibility as $button_name => $button_visible) {
 					if ($button_visible) {
 						return true;
 					}
 				}
 
 				return false;
 			}
 
 			return array_key_exists($name, $button_visibility) ? $button_visibility[$name] : true;
 		}
 	}