Index: branches/5.2.x/core/units/categories/categories_tag_processor.php
===================================================================
--- branches/5.2.x/core/units/categories/categories_tag_processor.php	(revision 16307)
+++ branches/5.2.x/core/units/categories/categories_tag_processor.php	(revision 16308)
@@ -1,2259 +1,2282 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 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 CategoriesTagProcessor extends kDBTagProcessor {
 
 	function SubCatCount($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		if ( isset($params['today']) && $params['today'] ) {
 			$sql = 'SELECT COUNT(*)
 					FROM ' . $object->TableName . '
 					WHERE (ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%") AND (CreatedOn > ' . (adodb_mktime() - 86400) . ')';
 			return $this->Conn->GetOne($sql) - 1;
 		}
 
 		return $object->GetDBField('CachedDescendantCatsQty');
 	}
 
 	/**
 	 * Returns category count in system
 	 *
 	 * @param Array $params
 	 * @return int
 	 */
 	function CategoryCount($params)
 	{
 		$count_helper = $this->Application->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		$today_only = isset($params['today']) && $params['today'];
 		return $count_helper->CategoryCount($today_only);
 	}
 
 	function IsNew($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		return $object->GetDBField('IsNew') ? 1 : 0;
 	}
 
 	function IsPick($params)
 	{
 		return $this->IsEditorsPick($params);
 	}
 
 	/**
 	 * Returns item's editors pick status (using not formatted value)
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function IsEditorsPick($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		return $object->GetDBField('EditorsPick') == 1;
 	}
 
 	function ItemIcon($params)
 	{
 		$grids = $this->Application->getUnitOption($this->Prefix, 'Grids');
 		$grid = $grids[ $params['grid'] ];
 
 		if (!array_key_exists('Icons', $grid)) {
 			return '';
 		}
 
 		$icons = $grid['Icons'];
 		$icon_prefix = array_key_exists('icon_prefix', $params)? $params['icon_prefix'] : 'icon16_';
 
 		if (array_key_exists('name', $params)) {
 			$icon_name = $params['name'];
 			return array_key_exists($icon_name, $icons) ? $icons[$icon_name] : '';
 		}
 
 		$object = $this->getObject($params);
 		/* @var $object kDBList */
 
 		if ($object->GetDBField('ThemeId') > 0) {
 			if (!$object->GetDBField('IsMenu')) {
 				return $icon_prefix . 'section_menuhidden_system.png';
 			}
 			return $icon_prefix . 'section_system.png';
 		}
 
 		$status = $object->GetDBField('Status');
 
 		if ($status == STATUS_DISABLED) {
 			return $icon_prefix . 'section_disabled.png';
 		}
 
 		if (!$object->GetDBField('IsMenu')) {
 			return $icon_prefix . 'section_menuhidden.png';
 		}
 
 		if ($status == STATUS_PENDING) {
 			return $icon_prefix . 'section_pending.png';
 		}
 
 		if ($object->GetDBField('IsNew') && ($icon_prefix == 'icon16_')) {
 			return $icon_prefix . 'section_new.png'; // show gris icon only in grids
 		}
 
 		return $icon_prefix . 'section.png';
 	}
 
 	function ItemCount($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$ci_table = $this->Application->getUnitOption('ci', 'TableName');
 
 		$module_prefixes = implode(',', $this->Conn->qstrArray($this->_getModulePrefixes()));
 
 		$sql = 'SELECT COUNT(*)
 				FROM ' . $object->TableName . ' c
 				JOIN ' . $ci_table . ' ci ON c.CategoryId = ci.CategoryId
 				WHERE (c.TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight') . ') AND (ci.ItemPrefix IN (' . $module_prefixes . '))';
 
 		return $this->Conn->GetOne($sql);
 	}
 
 	function _getModulePrefixes()
 	{
 		$ret = Array ();
 
 		foreach ($this->Application->ModuleInfo as $module_info) {
 			$ret[] = $module_info['Var'];
 		}
 
 		return array_unique($ret);
 	}
 
 	function ListCategories($params)
 	{
 		return $this->PrintList2($params);
 	}
 
 	function RootCategoryName($params)
 	{
 		return $this->Application->ProcessParsedTag('m', 'RootCategoryName', $params);
 	}
 
 	function CheckModuleRoot($params)
 	{
 		$module_name = getArrayValue($params, 'module') ? $params['module'] : 'In-Commerce';
 		$module_root_cat = $this->Application->findModule('Name', $module_name, 'RootCat');
 
 		$additional_cats = $this->SelectParam($params, 'add_cats');
 		if ($additional_cats) {
 			$additional_cats = explode(',', $additional_cats);
 		}
 		else {
 			$additional_cats = array();
 		}
 
 		if ($this->Application->GetVar('m_cat_id') == $module_root_cat || in_array($this->Application->GetVar('m_cat_id'), $additional_cats)) {
 			$home_template = getArrayValue($params, 'home_template');
 
 			if ( !$home_template ) {
 				return;
 			}
 
 			$this->Application->Redirect($home_template, Array('pass'=>'all'));
 		};
 	}
 
 	function CategoryPath($params)
 	{
 		$navigation_bar = $this->Application->recallObject('kNavigationBar');
 		/* @var $navigation_bar kNavigationBar */
 
 		return $navigation_bar->build($params);
 	}
 
 	/**
 	 * Shows category path to specified category
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function FieldCategoryPath($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$field = $this->SelectParam($params, 'name,field');
 		$category_id = $object->GetDBField($field);
 
 		if ($category_id) {
 			$params['cat_id'] = $category_id;
 
 			$navigation_bar = $this->Application->recallObject('kNavigationBar');
 			/* @var $navigation_bar kNavigationBar */
 
 			return $navigation_bar->build($params);
 		}
 
 		return '';
 	}
 
 	function CurrentCategoryName($params)
 	{
 		$cat_object = $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List');
 		/* @var $cat_object kDBList */
 
 		$sql = 'SELECT '.$this->getTitleField().'
 				FROM '.$cat_object->TableName.'
 				WHERE CategoryId = '.(int)$this->Application->GetVar('m_cat_id');
 		return $this->Conn->GetOne($sql);
 	}
 
 	/**
 	 * Returns current category name
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo Find where it's used
 	 */
 	function CurrentCategory($params)
 	{
 		return $this->CurrentCategoryName($params);
 	}
 
 	function getTitleField()
 	{
 		$ml_formatter = $this->Application->recallObject('kMultiLanguage');
 		/* @var $ml_formatter kMultiLanguage */
 
 		return $ml_formatter->LangFieldName('Name');
 	}
 
 	/**
 	 * Returns symlinked category for given category
 	 *
 	 * @param int $category_id
 	 * @return int
 	 */
 	function getCategorySymLink($category_id)
 	{
 		if (!$category_id) {
 			// don't bother to get symlink for "Home" category
 			return $category_id;
 		}
 
 		$cache_key = 'category_symlinks[%CSerial%]';
 		$cache = $this->Application->getCache($cache_key);
 
 		if ($cache === false) {
 			$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 			// get symlinked categories, that are not yet deleted
 			$this->Conn->nextQueryCachable = true;
 			$sql = 'SELECT c1.SymLinkCategoryId, c1.' . $id_field . '
 					FROM ' . $table_name . ' c1
 					JOIN ' . $table_name . ' c2 ON c1.SymLinkCategoryId = c2.' . $id_field;
 			$cache = $this->Conn->GetCol($sql, $id_field);
 
 			$this->Application->setCache($cache_key, $cache);
 		}
 
 		return array_key_exists($category_id, $cache) ? $cache[$category_id] : $category_id;
 	}
 
 	function CategoryLink($params)
 	{
 		$category_id = getArrayValue($params, 'cat_id');
 
 		if ( $category_id === false ) {
 			$category_id = $this->Application->GetVar($this->getPrefixSpecial() . '_id');
 		}
 
 		if ( "$category_id" == 'Root' ) {
 			$category_id = $this->Application->findModule('Name', $params['module'], 'RootCat');
 		}
 		elseif ( "$category_id" == 'current' ) {
 			$category_id = $this->Application->GetVar('m_cat_id');
 		}
 
 		if ( !array_key_exists('direct_link', $params) || !$params['direct_link'] ) {
 			$category_id = $this->getCategorySymLink((int)$category_id);
 		}
 		else {
 			unset($params['direct_link']);
 		}
 
 		$virtual_template = $this->Application->getVirtualPageTemplate($category_id);
 
 		if ( ($virtual_template !== false) && preg_match('/external:(.*)/', $virtual_template, $rets) ) {
 			// external url (return here, instead of always replacing $params['t'] for kApplication::HREF to find it)
 			return $rets[1];
 		}
 
 		unset($params['cat_id'], $params['module']);
 		$new_params = Array ('pass' => 'm', 'm_cat_id' => $category_id, 'pass_category' => 1);
 		$params = array_merge($params, $new_params);
 
 		return $this->Application->ProcessParsedTag('m', 't', $params);
 	}
 
 	function CategoryList($params)
 	{
 		//$object = $this->Application->recallObject( $this->getPrefixSpecial() , $this->Prefix.'_List', $params );
 		$object =& $this->GetList($params);
 
 		if ($object->GetRecordsCount() == 0)
 		{
 			if (isset($params['block_no_cats'])) {
 				$params['name'] = $params['block_no_cats'];
 				return $this->Application->ParseBlock($params);
 			}
 			else {
 				return '';
 			}
 		}
 
 		if (isset($params['block'])) {
 			return $this->PrintList($params);
 		}
 		else {
 			$params['block'] = $params['block_main'];
 			if (isset($params['block_row_start'])) {
 				$params['row_start_block'] = $params['block_row_start'];
 			}
 
 			if (isset($params['block_row_end'])) {
 				$params['row_end_block'] = $params['block_row_end'];
 			}
 			return $this->PrintList2($params);
 		}
 	}
 
 	function Meta($params)
 	{
 		$object = $this->Application->recallObject($this->Prefix); // .'.-item'
 		/* @var $object CategoriesItem */
 
 		$meta_type = $params['name'];
 		if ($object->isLoaded()) {
 			// 1. get module prefix by current category
 			$category_helper = $this->Application->recallObject('CategoryHelper');
 			/* @var $category_helper CategoryHelper */
 
 			$category_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1));
-			$module_info = $category_helper->getCategoryModule($params,  $category_path);
-
-			// In-Edit & Proj-CMS module prefixes doesn't have custom field with item template
-			if ($module_info && $module_info['Var'] != 'adm' && $module_info['Var'] != 'st') {
+			$module_info = $category_helper->getCategoryModule($params, $category_path);
 
+			if ( $module_info ) {
 				// 2. get item template by current category & module prefix
 				$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
 				/* @var $rewrite_processor kRewriteUrlProcessor */
 
 				$category_params = Array (
 					'CategoryId' => $object->GetID(),
 					'ParentPath' => $object->GetDBField('ParentPath'),
 				);
 
 				$item_template = $rewrite_processor->GetItemTemplate($category_params, $module_info['Var']);
 
 				if ($this->Application->GetVar('t') == $item_template) {
 					// we are located on item's details page
 					$item = $this->Application->recallObject($module_info['Var']);
 					/* @var $item kCatDBItem */
 
 					// 3. get item's meta data
 					$value = $item->GetField('Meta'.$meta_type);
 					if ($value) {
 						return $value;
 					}
 				}
 
 				// 4. get category meta data
 				$value = $object->GetField('Meta'.$meta_type);
 				if ($value) {
 					return $value;
 				}
 			}
 		}
 
 		// 5. get default meta data
 		switch ($meta_type) {
 			case 'Description':
 				$config_name = 'Category_MetaDesc';
 				break;
 			case 'Keywords':
 				$config_name = 'Category_MetaKey';
 				break;
 		}
 
 		return $this->Application->ConfigValue($config_name);
 	}
 
 	function BuildListSpecial($params)
 	{
 		if (($this->Special != '') && !is_numeric($this->Special)) {
 			// When recursive category list is printed (like in sitemap), then special
 			// should be generated even if it's already present. Without it list on this
 			// level will erase list on previous level, because it will be stored in same object.
 			return $this->Special;
 		}
 
 		if ( isset($params['parent_cat_id']) ) {
 			$parent_cat_id = $params['parent_cat_id'];
 		}
 		else {
 			$parent_cat_id = $this->Application->GetVar($this->Prefix.'_id');
 			if (!$parent_cat_id) {
 				$parent_cat_id = $this->Application->GetVar('m_cat_id');
 			}
 			if (!$parent_cat_id) {
 				$parent_cat_id = 0;
 			}
 		}
 
 		$list_unique_key = $this->getUniqueListKey($params);
 		// check for "admin" variable, because we are parsing front-end template from admin when using template editor feature
 		if ($this->Application->GetVar('admin') || !$this->Application->isAdmin) {
 			// add parent category to special, when on Front-End,
 			// because there can be many category lists on same page
 			$list_unique_key .= $parent_cat_id;
 		}
 
 		if ($list_unique_key == '') {
 			return parent::BuildListSpecial($params);
 		}
 
 		return crc32($list_unique_key);
 	}
 
 	function IsCurrent($params)
 	{
 		$object = $this->getObject($params);
 		if ($object->GetID() == $this->Application->GetVar('m_cat_id')) {
 			return true;
 		}
 		else {
 			return false;
 		}
 	}
 
 	/**
 	 * Substitutes category in last template base on current category
 	 * This is required becasue when you navigate catalog using AJAX, last_template is not updated
 	 * but when you open item edit from catalog last_template is used to build opener_stack
 	 * So, if we don't substitute m_cat_id in last_template, after saving item we'll get redirected
 	 * to the first category we've opened, not the one we navigated to using AJAX
 	 *
 	 * @param Array $params
 	 */
 	function UpdateLastTemplate($params)
 	{
 		$category_id = $this->Application->GetVar('m_cat_id');
 
 		$wid = $this->Application->GetVar('m_wid');
 		list($index_file, $env) = explode('|', $this->Application->RecallVar(rtrim('last_template_'.$wid, '_')), 2);
 
 		$vars_backup = Array ();
 		$vars = $this->Application->processQueryString( str_replace('%5C', '\\', $env) );
 
 		foreach ($vars as $var_name => $var_value) {
 			$vars_backup[$var_name] = $this->Application->GetVar($var_name);
 			$this->Application->SetVar($var_name, $var_value);
 		}
 
 		// update required fields
 		$this->Application->SetVar('m_cat_id', $category_id);
 		$this->Application->Session->SaveLastTemplate($params['template']);
 
 		foreach ($vars_backup as $var_name => $var_value) {
 			$this->Application->SetVar($var_name, $var_value);
 		}
 	}
 
 	function GetParentCategory($params)
 	{
 		$parent_id = $this->Application->getBaseCategory();
 		$category_id = $this->Application->GetVar('m_cat_id');
 
 		if ($category_id != $parent_id) {
 			$sql = 'SELECT ParentId
 					FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName') . '
 					WHERE ' . $this->Application->getUnitOption($this->Prefix, 'IDField') . ' = ' . $category_id;
 			$parent_id = $this->Conn->GetOne($sql);
 		}
 
 		return $parent_id;
 	}
 
 	function InitCacheUpdater($params)
 	{
 		kUtil::safeDefine('CACHE_PERM_CHUNK_SIZE', 30);
 
 		$continue = $this->Application->GetVar('continue');
 		$total_cats = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'Categories');
 
 		if ( $continue === false ) {
 			$rebuild_mode = $this->Application->ConfigValue('CategoryPermissionRebuildMode');
 
 			if ( $rebuild_mode == CategoryPermissionRebuild::AUTOMATIC && $total_cats > CACHE_PERM_CHUNK_SIZE ) {
 				// first step, if category count > CACHE_PERM_CHUNK_SIZE, then ask for cache update
 				return true;
 			}
 
 			// if we don't have to ask, then assume user selected "Yes" in permcache update dialog
 			$continue = 1;
 		}
 
 		$updater = $this->Application->makeClass('kPermCacheUpdater', Array ($continue));
 		/* @var $updater kPermCacheUpdater */
 
 		if ( $continue === '0' ) { // No in dialog
 			$updater->clearData();
 			$this->Application->Redirect($params['destination_template']);
 		}
 
 		$ret = false; // don't ask for update
 
 		if ( $continue == 1 ) { // Initial run
 			$updater->setData();
 		}
 
 		if ( $continue == 2 ) { // Continuing
 			// called from AJAX request => returns percent
 			$needs_more = true;
 
 			while ( $needs_more && $updater->iteration <= CACHE_PERM_CHUNK_SIZE ) {
 				// until proceeded in this step category count exceeds category per step limit
 				$needs_more = $updater->DoTheJob();
 			}
 
 			if ( $needs_more ) {
 				// still some categories are left for next step
 				$updater->setData();
 			}
 			else {
 				// all done, update left tree and redirect
 				$updater->SaveData();
 
 				$this->Application->HandleEvent(new kEvent('c:OnResetCMSMenuCache'));
 
 				$this->Application->RemoveVar('PermCache_UpdateRequired');
 				$this->Application->StoreVar('RefreshStructureTree', 1);
 				$this->Application->Redirect($params['destination_template']);
 			}
 
 			$ret = $updater->getDonePercent();
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Parses warning block, but with style="display: none;". Used during permissions saving from AJAX
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function SaveWarning($params)
 	{
 		if ( $this->Prefix == 'st' ) {
 			// don't use this method for other prefixes then Categories, that use this tag processor
 			return parent::SaveWarning($params);
 		}
 
 		$main_prefix = getArrayValue($params, 'main_prefix');
 		if ( $main_prefix && $main_prefix != '$main_prefix' ) {
 			$top_prefix = $main_prefix;
 		}
 		else {
 			$top_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
 		}
 
 		$temp_tables = substr($this->Application->GetVar($top_prefix . '_mode'), 0, 1) == 't';
 		$modified = $this->Application->RecallVar($top_prefix . '_modified');
 
 		if ( !$temp_tables ) {
 			$this->Application->RemoveVar($top_prefix . '_modified');
 			return '';
 		}
 
 		$block_name = $this->SelectParam($params, 'render_as,name');
 		if ( $block_name ) {
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $block_name;
 			$block_params['edit_mode'] = $temp_tables ? 1 : 0;
 			$block_params['display'] = $temp_tables && $modified ? 1 : 0;
 			return $this->Application->ParseBlock($block_params);
 		}
 
 		return $temp_tables && $modified ? 1 : 0;
 	}
 
 	/**
 	 * Allows to detect if this prefix has something in clipboard
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function HasClipboard($params)
 	{
 		$clipboard = $this->Application->RecallVar('clipboard');
 		if ($clipboard) {
 			$clipboard = unserialize($clipboard);
 			foreach ($clipboard as $prefix => $clipboard_data) {
 				foreach ($clipboard_data as $mode => $ids) {
 					if ( count($ids) ) {
 						return 1;
 					}
 				}
 			}
 		}
 		return 0;
 	}
 
 	/**
 	 * Allows to detect if root category being edited
 	 *
 	 * @param Array $params
 	 */
 	function IsRootCategory($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object CategoriesItem */
 
 		return $object->IsRoot();
 	}
 
 	/**
 	 * Returns home category id
 	 *
 	 * @param Array $params
 	 * @return int
 	 */
 	function HomeCategory($params)
 	{
 		return $this->Application->getBaseCategory();
 	}
 
 	/**
 	 * Used for disabling "Home" and "Up" buttons in category list
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function ModuleRootCategory($params)
 	{
 		return $this->Application->GetVar('m_cat_id') == $this->Application->getBaseCategory();
 	}
 
 	function CatalogItemCount($params)
 	{
 		$params['skip_quering'] = true;
 		$object =& $this->GetList($params);
 
 		return $object->GetRecordsCount(false) != $object->GetRecordsCount() ? $object->GetRecordsCount().' / '.$object->GetRecordsCount(false) : $object->GetRecordsCount();
 	}
 
 	function InitCatalog($params)
 	{
 		$tab_prefixes = $this->Application->GetVar('tp'); // {all, <prefixes_list>, none}
 		if ( $tab_prefixes === false ) {
 			$tab_prefixes = 'all';
 		}
 
 		$skip_prefixes = isset($params['skip_prefixes']) && $params['skip_prefixes'] ? explode(',', $params['skip_prefixes']) : Array();
 		$replace_main = isset($params['replace_m']) && $params['replace_m'];
 
 		// get all prefixes available
 		$prefixes = Array();
 		foreach ($this->Application->ModuleInfo as $module_name => $module_data) {
 			$prefix = $module_data['Var'];
 
 			if ( $prefix == 'adm' /* || $prefix == 'm'*/ ) {
 				continue;
 			}
 
 			if ($prefix == 'm' && $replace_main) {
 				$prefix = 'c';
 			}
 
 			$prefixes[] = $prefix;
 		}
 
 		if ($tab_prefixes == 'none') {
 			$skip_prefixes = array_unique(array_merge($skip_prefixes, $prefixes));
 			unset($skip_prefixes[ array_search($replace_main ? 'c' : 'm', $skip_prefixes) ]);
 		}
 		elseif ($tab_prefixes != 'all') {
 			// prefix list here
 			$tab_prefixes = explode(',', $tab_prefixes); // list of prefixes that should stay
 			$skip_prefixes = array_unique(array_merge($skip_prefixes, array_diff($prefixes, $tab_prefixes)));
 		}
 
 		$params['name'] = $params['render_as'];
 		$params['skip_prefixes'] = implode(',', $skip_prefixes);
 		return $this->Application->ParseBlock($params);
 	}
 
 	/**
 	 * Determines, that printed category/menu item is currently active (will also match parent category)
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function IsActive($params)
 	{
 		static $current_path = null;
 
 		if ( !isset($current_path) ) {
 			$sql = 'SELECT ParentPath
 					FROM ' . TABLE_PREFIX . 'Categories
 					WHERE CategoryId = ' . (int)$this->Application->GetVar('m_cat_id');
 			$current_path = $this->Conn->GetOne($sql);
 		}
 
 		if ( array_key_exists('parent_path', $params) ) {
 			$test_path = $params['parent_path'];
 		}
 		else {
 			$template = isset($params['template']) ? $params['template'] : '';
 
 			if ( $template ) {
 				// when using from "c:CachedMenu" tag
 				$sql = 'SELECT ParentPath
 						FROM ' . TABLE_PREFIX . 'Categories
 						WHERE NamedParentPath = ' . $this->Conn->qstr('Content/' . $template);
 				$test_path = $this->Conn->GetOne($sql);
 			}
 			else {
 				// when using from "c:PrintList" tag
 				$cat_id = array_key_exists('cat_id', $params) && $params['cat_id'] ? $params['cat_id'] : false;
 				if ( $cat_id === false ) {
 					// category not supplied -> get current from PrintList
 					$category = $this->getObject($params);
 				}
 				else {
 					if ( "$cat_id" == 'Root' ) {
 						$cat_id = $this->Application->findModule('Name', $params['module'], 'RootCat');
 					}
 
 					$category = $this->Application->recallObject($this->Prefix . '.-c' . $cat_id, $this->Prefix, Array ('skip_autoload' => true));
 					/* @var $category CategoriesItem */
 
 					$category->Load($cat_id);
 				}
 
 				$test_path = $category->GetDBField('ParentPath');
 			}
 		}
 
 		return strpos($current_path, $test_path) !== false;
 	}
 
 	/**
 	 * Checks if user have one of required permissions
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function HasPermission($params)
 	{
 		$perm_helper = $this->Application->recallObject('PermissionsHelper');
 		/* @var $perm_helper kPermissionsHelper */
 
 		$params['raise_warnings'] = 0;
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$params['cat_id'] = $object->isLoaded() ? $object->GetDBField('ParentPath') : $this->Application->GetVar('m_cat_id');
 		return $perm_helper->TagPermissionCheck($params);
 	}
 
 	/**
 	 * Prepares name for field with event in it (used only on front-end)
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function SubmitName($params)
 	{
 		return 'events[' . $this->Prefix . '][' . $params['event'] . ']';
 	}
 
 	/**
 	 * Returns last modification date of items in category / system
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function LastUpdated($params)
 	{
 		$category_id = (int)$this->Application->GetVar('m_cat_id');
 		$local = array_key_exists('local', $params) && ($category_id > 0) ? $params['local'] : false;
 
 		$serial_name = $this->Application->incrementCacheSerial('c', $local ? $category_id : null, false);
 		$cache_key = 'category_last_updated[%' . $serial_name . '%]';
 
 		$row_data = $this->Application->getCache($cache_key);
 
 		if ( $row_data === false ) {
 			if ( $local && ($category_id > 0) ) {
 				// scan only current category & it's children
 				list ($tree_left, $tree_right) = $this->Application->getTreeIndex($category_id);
 
 				$sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate
 		        		FROM ' . TABLE_PREFIX . 'Categories
 		        		WHERE TreeLeft BETWEEN ' . $tree_left . ' AND ' . $tree_right;
 			}
 			else {
 				// scan all categories in system
 				$sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate
 		       			FROM ' . TABLE_PREFIX . 'Categories';
 			}
 
 			$this->Conn->nextQueryCachable = true;
 			$row_data = $this->Conn->GetRow($sql);
 			$this->Application->setCache($cache_key, $row_data);
 		}
 
 		if ( !$row_data ) {
 			return '';
 		}
 
 		$date = $row_data[$row_data['NewDate'] > $row_data['ModDate'] ? 'NewDate' : 'ModDate'];
 
 		// format date
 		$format = isset($params['format']) ? $params['format'] : '_regional_DateTimeFormat';
 
 		if ( preg_match("/_regional_(.*)/", $format, $regs) ) {
 			$lang = $this->Application->recallObject('lang.current');
 			/* @var $lang LanguagesItem */
 
 			if ( $regs[1] == 'DateTimeFormat' ) {
 				// combined format
 				$format = $lang->GetDBField('DateFormat') . ' ' . $lang->GetDBField('TimeFormat');
 			}
 			else {
 				// simple format
 				$format = $lang->GetDBField($regs[1]);
 			}
 		}
 
 		return adodb_date($format, $date);
 	}
 
 	function CategoryItemCount($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBList */
 
 		$params['cat_id'] = $object->GetID();
 
 		$count_helper = $this->Application->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		return $count_helper->CategoryItemCount($params['prefix'], $params);
 	}
 
 	/**
 	 * Returns prefix + any word (used for shared between categories per page settings)
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function VarName($params)
 	{
 		return $this->Prefix.'_'.$params['type'];
 	}
 
 	/**
 	 * Checks if current category is valid symbolic link to another category
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function IsCategorySymLink($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBList */
 
 		$sym_category_id = $object->GetDBField('SymLinkCategoryId');
 
 		if (is_null($sym_category_id))
 		{
 			return false;
 		}
 
 		$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
 		$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 		$sql = 'SELECT '.$id_field.'
 				FROM '.$table_name.'
 				WHERE '.$id_field.' = '.$sym_category_id;
 
 		return $this->Conn->GetOne($sql)? true : false;
 	}
 
 	/**
+	 * Builds link to a category in the Admin Tree.
+	 *
+	 * @param array $params Tag params.
+	 *
+	 * @return string
+	 */
+	protected function AdminTreeLink(array $params)
+	{
+		$params['direct_link'] = 1;
+		$params['pass'] = 'm';
+		$params['m_opener'] = 'r';
+
+		if ( $this->Application->ConfigValue('Catalog_PreselectModuleTab') ) {
+			$module_prefix = $this->GetModulePrefix($params);
+
+			if ( $module_prefix ) {
+				$params['anchor'] = 'tab-' . $module_prefix;
+			}
+		}
+
+		return $this->CategoryLink($params);
+	}
+
+	/**
 	 * Returns module prefix based on root category for given
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function GetModulePrefix($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$parent_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1));
 
 		$category_helper = $this->Application->recallObject('CategoryHelper');
 		/* @var $category_helper CategoryHelper */
 
 		$module_info = $category_helper->getCategoryModule($params, $parent_path);
-		return $module_info['Var'];
+
+		return $module_info ? $module_info['Var'] : 'c';
 	}
 
 	function ImageSrc($params)
 	{
 		list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial());
 		return $tag_processed ? $ret : false;
 	}
 
 	function PageLink($params)
 	{
 		$params['m_cat_page'] = $this->Application->GetVar($this->getPrefixSpecial() . '_Page');
 
 		return parent::PageLink($params);
 	}
 
 	/**
 	 * Returns spelling suggestions against search keyword
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function SpellingSuggestions($params)
 	{
 		$keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords')));
 		if ( !$keywords ) {
 			return '';
 		}
 
 		// 1. try to get already cached suggestion
 		$cache_key = 'search.suggestion[%SpellingDictionarySerial%]:' . $keywords;
 		$suggestion = $this->Application->getCache($cache_key);
 
 		if ( $suggestion !== false ) {
 			return $suggestion;
 		}
 
 		$table_name = $this->Application->getUnitOption('spelling-dictionary', 'TableName');
 
 		// 2. search suggestion in database
 		$this->Conn->nextQueryCachable = true;
 		$sql = 'SELECT SuggestedCorrection
 				FROM ' . $table_name . '
 				WHERE MisspelledWord = ' . $this->Conn->qstr($keywords);
 		$suggestion = $this->Conn->GetOne($sql);
 
 		if ( $suggestion !== false ) {
 			$this->Application->setCache($cache_key, $suggestion);
 
 			return $suggestion;
 		}
 
 		// 3. suggestion not found in database, ask webservice
 		$app_id = $this->Application->ConfigValue('YahooApplicationId');
 		$url = 'http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion?appid=' . $app_id . '&query=';
 
 		$curl_helper = $this->Application->recallObject('CurlHelper');
 		/* @var $curl_helper kCurlHelper */
 
 		$xml_data = $curl_helper->Send( $url . kUtil::escape($keywords, kUtil::ESCAPE_URL) );
 
 		$xml_helper = $this->Application->recallObject('kXMLHelper');
 		/* @var $xml_helper kXMLHelper */
 
 		$root_node =& $xml_helper->Parse($xml_data);
 		/* @var $root_node kXMLNode */
 
 		$result = $root_node->FindChild('RESULT');
 		/* @var $result kXMLNode */
 
 		if ( is_object($result) ) {
 			// webservice responded -> save in local database
 			$fields_hash = Array ('MisspelledWord' => $keywords, 'SuggestedCorrection' => $result->Data);
 
 			$this->Conn->doInsert($fields_hash, $table_name);
 			$this->Application->setCache($cache_key, $result->Data);
 
 			return $result->Data;
 		}
 
 		return '';
 	}
 
 	/**
 	 * Shows link for searching by suggested word
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function SuggestionLink($params)
 	{
 		$params['keywords'] = $this->SpellingSuggestions($params);
 
 		return $this->Application->ProcessParsedTag('m', 'Link', $params);
 	}
 
 	function InitCatalogTab($params)
 	{
 		$tab_params['mode'] = $this->Application->GetVar('tm'); // single/multi selection possible
 		$tab_params['special'] = $this->Application->GetVar('ts'); // use special for this tab
 		$tab_params['dependant'] = $this->Application->GetVar('td'); // is grid dependant on categories grid
 
 		// set default params (same as in catalog)
 		if ( $tab_params['mode'] === false ) {
 			$tab_params['mode'] = 'multi';
 		}
 
 		if ( $tab_params['special'] === false ) {
 			$tab_params['special'] = '';
 		}
 
 		if ( $tab_params['dependant'] === false ) {
 			$tab_params['dependant'] = 'yes';
 		}
 
 		// pass params to block with tab content
 		$params['name'] = $params['render_as'];
 		$special = $tab_params['special'] ? $tab_params['special'] : $this->Special;
 		$params['prefix'] = trim($this->Prefix.'.'.$special, '.');
 
 		$prefix_append = $this->Application->GetVar('prefix_append');
 		if ($prefix_append) {
 			$params['prefix'] .= $prefix_append;
 		}
 
 		$default_grid = array_key_exists('default_grid', $params) ? $params['default_grid'] : 'Default';
 		$radio_grid = array_key_exists('radio_grid', $params) ? $params['radio_grid'] : 'Radio';
 
 		$params['cat_prefix'] = trim('c.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.');
 		$params['tab_mode'] = $tab_params['mode'];
 		$params['grid_name'] = ($tab_params['mode'] == 'multi') ? $default_grid : $radio_grid;
 		$params['tab_dependant'] = $tab_params['dependant'];
 		$params['show_category'] = $tab_params['special'] == 'showall' ? 1 : 0; // this is advanced view -> show category name
 
 		if ($special == 'showall' || $special == 'user') {
 			$params['grid_name'] .= 'ShowAll';
 		}
 
 		// use $pass_params to be able to pass 'tab_init' parameter from m_ModuleInclude tag
 		return $this->Application->ParseBlock($params, 1);
 	}
 
 	/**
 	 * Show CachedNavbar of current item primary category
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function CategoryName($params)
 	{
 		// show category cachednavbar of
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$category_id = isset($params['cat_id']) ? $params['cat_id'] : $object->GetDBField('CategoryId');
 
 		$cache_key = 'category_paths[%CIDSerial:' . $category_id . '%][%PhrasesSerial%][Adm:' . (int)$this->Application->isAdmin . ']';
 		$category_path = $this->Application->getCache($cache_key);
 
 		if ($category_path === false) {
 			// not chached
 			if ($category_id > 0) {
 				$cached_navbar = $object->GetField('CachedNavbar');
 
 				if ($category_id == $object->GetDBField('ParentId')) {
 					// parent category cached navbar is one element smaller, then current ones
 					$cached_navbar = explode('&|&', $cached_navbar);
 					array_pop($cached_navbar);
 					$cached_navbar = implode('&|&', $cached_navbar);
 				}
 				else {
 					// no relation with current category object -> query from db
 					$language_id = (int)$this->Application->GetVar('m_lang');
 					if (!$language_id) {
 						$language_id = 1;
 					}
 
 					$sql = 'SELECT l' . $language_id . '_CachedNavbar
 							FROM ' . $object->TableName . '
 							WHERE ' . $object->IDField . ' = ' . $category_id;
 					$cached_navbar = $this->Conn->GetOne($sql);
 				}
 
 				$cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $cached_navbar);
 
 				$category_path = trim($this->CategoryName( Array('cat_id' => 0) ).' > '.str_replace('&|&', ' > ', $cached_navbar), ' > ');
 			}
 			else {
 				$category_path = $this->Application->Phrase(($this->Application->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name');
 			}
 
 			$this->Application->setCache($cache_key, $category_path);
 		}
 
 		return $category_path;
 	}
 
 	// structure related
 
 	/**
 	 * Returns page object based on requested params
 	 *
 	 * @param Array $params
 	 * @return CategoriesItem
 	 */
 	function &_getPage($params)
 	{
 		$page = $this->Application->recallObject($this->Prefix . '.' . $this->_getPageSpecial($params), null, $params);
 		/* @var $page kDBItem */
 
 		// 1. load by given id
 		$page_id = array_key_exists('page_id', $params) ? $params['page_id'] : 0;
 
 		if ( $page_id ) {
 			if ( $page_id != $page->GetID() ) {
 				// load if different
 				$page->Load($page_id);
 			}
 
 			return $page;
 		}
 
 		// 2. load by template
 		$template = array_key_exists('page', $params) ? $params['page'] : '';
 
 		if ( !$template ) {
 			$template = $this->Application->GetVar('t');
 		}
 
 		// different path in structure AND design template differs from requested template
 		$structure_path_match = mb_strtolower($page->GetDBField('NamedParentPath')) == mb_strtolower('Content/' . $template);
 		$design_match = $page->GetDBField('CachedTemplate') == $template;
 
 		if ( !$structure_path_match && !$design_match ) {
 			// Same sql like in "c:getPassedID". Load, when current page object doesn't match requested page object
 			$themes_helper = $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			$page_id = $themes_helper->getPageByTemplate($template);
 
 			$page->Load($page_id);
 		}
 
 		return $page;
 	}
 
 	/**
 	 * Returns unique special for each used page
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function _getPageSpecial($params)
 	{
 		$ret = Array ();
 		$page_id = array_key_exists('page_id', $params) ? $params['page_id'] : 0;
 		$template = array_key_exists('page', $params) ? $params['page'] : '';
 
 		if ( $page_id ) {
 			$ret[] = 'page_id=' . $page_id;
 		}
 
 		if ( $template ) {
 			$ret[] = 'page=' . $template;
 		}
 
 		return $ret ? '-virtual-' . kUtil::crc32(serialize($ret)) : '-virtual';
 	}
 
 	/**
 	 * Returns requested content block content of current or specified page
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function ContentBlock($params)
 	{
 		$num = getArrayValue($params, 'num');
 
 		if ( !$num ) {
 			$name = getArrayValue($params, 'name');
 
 			if ( $name ) {
 				$num = kUtil::crc32($name);
 			}
 		}
 
 		if ( !$num ) {
 			return 'NO CONTENT NUM SPECIFIED';
 		}
 
 		$page =& $this->_getPage($params);
 		/* @var $page kDBItem */
 
 		if ( !$page->isLoaded() ) {
 			// page is not created yet => all blocks are empty
 			return '';
 		}
 
 		$page_helper = $this->Application->recallObject('PageHelper');
 		/* @var $page_helper PageHelper */
 
 		$content = $this->Application->recallObject('content.-block', null, Array ('skip_autoload' => true));
 		/* @var $content kDBItem */
 
 		if ( !$page_helper->loadContentBlock($content, $page, $num) && EDITING_MODE ) {
 			$page_helper->createNewContentBlock($page->GetID(), $num);
 			$page_helper->loadContentBlock($content, $page, $num);
 		}
 
 		$edit_code_before = $edit_code_after = '';
 
 		if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
 			$button_code = $this->Application->ProcessParsedTag($content->getPrefixSpecial(), 'AdminEditButton', $params);
 
 			$edit_code_before = '
 				<div class="cms-edit-btn-container">
 					' . $button_code . '
 					<div class="cms-btn-content">';
 
 			$edit_code_after = '</div></div>';
 		}
 
 		if ( $this->Application->GetVar('_editor_preview_') == 1 ) {
 			$data = $this->Application->RecallVar('_editor_preview_content_');
 		}
 		else {
 			$data = $content->GetField('Content');
 		}
 
 		$data = $edit_code_before . $this->_transformContentBlockData($data, $params) . $edit_code_after;
 
 		if ( $data != '' ) {
 			$this->Application->Parser->DataExists = true;
 		}
 
 		return $data;
 	}
 
 	/**
 	 * Apply all kinds of content block data transformations without rewriting ContentBlock tag
 	 *
 	 * @param string $data
 	 * @param Array $params
 	 * @return string
 	 */
 	function _transformContentBlockData(&$data, $params)
 	{
 		return $data;
 	}
 
 	/**
 	 * Returns current page name or page based on page/page_id parameters
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo Used?
 	 */
 	function PageName($params)
 	{
 		$page =& $this->_getPage($params);
 
 		return $page->GetDBField('Name');
 	}
 
 	/**
 	 * Returns current/given page information
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function PageInfo($params)
 	{
 		$page =& $this->_getPage($params);
 
 		switch ($params['type']) {
 			case 'title':
 				// TODO: rename column to SectionTitle
 				$db_field = 'Name'; // "Section Title" - title to show on page (e.g. in <h1> tag)
 				break;
 
 			case 'htmlhead_title':
 				// TODO: rename column to HtmlTitle
 				$db_field = 'Title'; // "Title (on Page)" - in <title> html tag
 				break;
 
 			case 'meta_title':
 				$db_field = 'MetaTitle';
 				break;
 
 			case 'menu_title':
 				$db_field = 'MenuTitle'; // "Title (Menu Item)" - in menu and navigation bar
 				break;
 
 			case 'meta_keywords':
 				$db_field = 'MetaKeywords';
 				$cat_field = 'Keywords';
 				break;
 
 			case 'meta_description':
 				$db_field = 'MetaDescription';
 				$cat_field = 'Description';
 				break;
 
 			case 'tracking':
 			case 'index_tools':
 				if (!EDITING_MODE) {
 					$tracking = $page->GetDBField('IndexTools');
 					return $tracking ? $tracking : $this->Application->ConfigValue('cms_DefaultTrackingCode');
 				}
 				// no break here on purpose
 
 			default:
 				return '';
 		}
 
 		$default = isset($params['default']) ? $params['default'] : '';
 		$val = $page->GetField($db_field);
 		if (!$default) {
 			if ($this->Application->isModuleEnabled('In-Portal')) {
 				if (!$val && ($params['type'] == 'meta_keywords' || $params['type'] == 'meta_description')) {
 					// take category meta if it's not set for the page
 					return $this->Application->ProcessParsedTag('c', 'Meta', Array('name' => $cat_field));
 				}
 			}
 		}
 
 		if (isset($params['force_default']) && $params['force_default']) {
 			return $default;
 		}
 
 		if (preg_match('/^_Auto:/', $val)) {
 			$val = $default;
 
 			/*if ($db_field == 'Title') {
 				$page->SetDBField($db_field, $default);
 				$page->Update();
 			}*/
 		}
 		elseif ($page->GetID() == false) {
 			return $default;
 		}
 
 		return $val;
 	}
 
 	/**
 	 * Includes admin css and js, that are required for cms usage on Front-Edn
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function EditingScripts($params)
 	{
 		if ( $this->Application->GetVar('admin_scripts_included') || !EDITING_MODE ) {
 			return '';
 		}
 
 		$this->Application->SetVar('admin_scripts_included', 1);
 		$js_url = $this->Application->BaseURL() . 'core/admin_templates/js';
 
 		$minify_helper = $this->Application->recallObject('MinifyHelper');
 		/* @var $minify_helper MinifyHelper */
 
 		$to_compress = Array (
 			$js_url . '/jquery/thickbox/thickbox.css',
 			$js_url . '/../incs/cms.css',
 			$js_url . '/../img/toolbar/toolbar-sprite.css',
 		);
 
 		$css_compressed = $minify_helper->CompressScriptTag(Array ('files' => implode('|', $to_compress), 'templates_base' => $js_url . '/../'));
 
 		$ret = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\n";
 
 		$ret .= '	<!--[if IE]>
 					<link rel="stylesheet" href="' . $js_url . '/../incs/cms_ie.css' . '" type="text/css" media="screen"/>
 					<![endif]-->';
 
 		if ( EDITING_MODE == EDITING_MODE_DESIGN ) {
 			$ret .= '	<style type="text/css" media="all">
 							div.movable-element .movable-header { cursor: move; }
 						</style>';
 		}
 
 		$ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-1.9.1.min.js"></script>' . "\n";
 		$ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-ui-1.10.3.custom.min.js"></script>' . "\n";
 
 		$to_compress = Array (
 			$js_url . '/is.js',
 			$js_url . '/application.js',
 			$js_url . '/script.js',
 			$js_url . '/toolbar.js',
 			$js_url . '/jquery/thickbox/thickbox.js',
 			$js_url . '/template_manager.js',
 		);
 
 		$js_compressed = $minify_helper->CompressScriptTag( Array ('files' => implode('|', $to_compress)) );
 
 		$ret .= '<script type="text/javascript" src="' . $js_compressed . '"></script>' . "\n";
 		$ret .= '<script language="javascript">' . "\n";
 		$ret .= "TB.pathToImage = '" . $js_url . "/jquery/thickbox/loadingAnimation.gif';" . "\n";
 
 		$template = $this->Application->GetVar('t');
 		$theme_id = $this->Application->GetVar('m_theme');
 
 		$url_params = Array ('block' => '#BLOCK#', 'theme-file_event' => '#EVENT#', 'theme_id' => $theme_id, 'source' => $template, 'pass' => 'all,theme-file', 'front' => 1, 'm_opener' => 'd', '__NO_REWRITE__' => 1, 'no_amp' => 1);
 		$edit_template_url = $this->Application->HREF('themes/template_edit', ADMIN_DIRECTORY, $url_params, 'index.php');
 
 		$url_params = Array ('theme-file_event' => 'OnSaveLayout', 'source' => $template, 'pass' => 'all,theme-file', '__NO_REWRITE__' => 1, 'no_amp' => 1);
 		$save_layout_url = $this->Application->HREF('index', '', $url_params);
 
 		$page =& $this->_getPage($params);
 
 		$page_helper = $this->Application->recallObject('PageHelper');
 		/* @var $page_helper PageHelper */
 
 		$class_params = Array (
 			'pageId' => $page->GetID(),
 			'pageInfo' => $page->isLoaded() ? $page_helper->getPageInfo( $page->GetID() ) : Array (),
 			'editUrl' => $edit_template_url,
 			'browseUrl' => $this->Application->HREF('', '', Array ('editing_mode' => '#EDITING_MODE#', '__NO_REWRITE__' => 1, 'no_amp' => 1)),
 			'saveLayoutUrl' => $save_layout_url,
 			'editingMode' => (int)EDITING_MODE,
 		);
 
 		$site_name = strip_tags($this->Application->ConfigValue('Site_Name'));
 		$ret .= "var aTemplateManager = new TemplateManager(" . json_encode($class_params) . ");\n";
 		$ret .= "var main_title = '" . kUtil::escape($site_name, kUtil::ESCAPE_JS) . "';" . "\n";
 
 		$use_popups = (int)$this->Application->ConfigValue('UsePopups');
 		$ret .= "var \$use_popups = " . ($use_popups > 0 ? 'true' : 'false') . ";\n";
 		$ret .= "var \$modal_windows = " . ($use_popups == 2 ? 'true' : 'false') . ";\n";
 
 		if ( EDITING_MODE != EDITING_MODE_BROWSE ) {
 			$ret .= 'var $visible_toolbar_buttons = true' . ";\n";
 			$ret .= 'var $use_toolbarlabels = ' . ($this->Application->ConfigValue('UseToolbarLabels') ? 'true' : 'false') . ";\n";;
 
 			$ret .= "var base_url = '" . $this->Application->BaseURL() . "';" . "\n";
 			$ret .= 'TB.closeHtml = \'<img src="' . $js_url . '/../img/close_window15.gif" width="15" height="15" style="border-width: 0px;" alt="close"/><br/>\';' . "\n";
 
 			$url_params = Array ('m_theme' => '', 'pass' => 'm', 'm_opener' => 'r', '__NO_REWRITE__' => 1, 'no_amp' => 1);
 			$browse_url = $this->Application->HREF('catalog/catalog', ADMIN_DIRECTORY, $url_params, 'index.php');
 			$browse_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $browse_url);
 
 			$admin_title = strip_tags($this->Application->Phrase('la_AdministrativeConsole', false));
 
 			$ret .= '
 				set_window_title(document.title + \' - ' . kUtil::escape($admin_title, kUtil::ESCAPE_JS) . '\');
 
 				t = \'' . $this->Application->GetVar('t') . '\';
 
 				if (window.parent.frames["menu"] != undefined) {
 					if ( $.isFunction(window.parent.frames["menu"].SyncActive) ) {
 						window.parent.frames["menu"].SyncActive("' . $browse_url . '");
 					}
 				}
 			';
 		}
 
 		$ret .= '</script>' . "\n";
 
 		if ( EDITING_MODE != EDITING_MODE_BROWSE ) {
 			// add form, so admin scripts could work
 			$ret .= '<form id="kernel_form" name="kernel_form" enctype="multipart/form-data" method="post" action="' . $browse_url . '">
 						<input type="hidden" name="MAX_FILE_SIZE" id="MAX_FILE_SIZE" value="' . MAX_UPLOAD_SIZE . '" />
 						<input type="hidden" name="sid" id="sid" value="' . $this->Application->GetSID() . '" />
 					</form>';
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Prints "Edit Page" button on cms page
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function EditPage($params)
 	{
 		if ( $this->Application->GetVar('preview') ) {
 			// prevents draft preview function to replace last template in session and break page/content block editing process
 			$this->Application->SetVar('skip_last_template', 1);
 		}
 
 		if (!EDITING_MODE) {
 			return '';
 		}
 
 		$display_mode = array_key_exists('mode', $params) ? $params['mode'] : false;
 		unset($params['mode']);
 		$edit_code = '';
 
 		$page =& $this->_getPage($params);
 
 		if (!$page->isLoaded() || (($display_mode != 'end') && (EDITING_MODE == EDITING_MODE_BROWSE))) {
 			// when "EditingScripts" tag is not used, make sure, that scripts are also included
 			return $this->EditingScripts($params);
 		}
 
 		// show "EditPage" button only for pages, that exists in structure
 		if ($display_mode != 'end') {
 			$edit_btn = $edit_url = '';
 
 			if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
 				$item_prefix = isset($params['item_prefix']) ? $params['item_prefix'] : '';
 				unset($params['item_prefix']);
 
 				if ( $item_prefix ) {
 					$params['button_class'] = 'cms-section-properties-btn';
 					$edit_btn = $this->Application->ProcessParsedTag($item_prefix, 'AdminEditButton', $params) . "\n";
 				}
 				else {
 					$edit_btn = $this->AdminEditButton($params) . "\n"; // "st" object must be loaded before this
 				}
 			}
 			elseif ( EDITING_MODE == EDITING_MODE_DESIGN ) {
 				$url_params = Array(
 					'pass'			=>	'm,theme,theme-file',
 					'm_opener'		=>	'd',
 					'theme_id'		=>	$this->Application->GetVar('m_theme'),
 					'theme_mode'	=>	't',
 					'theme_event'	=>	'OnEdit',
 					'theme-file_id' =>	$this->_getThemeFileId(),
 					'front'			=>	1,
 					'__URLENCODE__'	=>	1,
 					'__NO_REWRITE__'=>	1,
 					'index_file' => 'index.php',
 				);
 
 				$edit_url = $this->Application->HREF('themes/file_edit', ADMIN_DIRECTORY, $url_params);
 
 				$button1_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/save_button.gif';
 				$button1_title = $this->Application->Phrase('la_btn_SaveChanges', false, true);
 				$button1_code = '<button style="background-image: url(' . $button1_icon . '); onclick="aTemplateManager.saveLayout(); return false;" class="cms-btn-new cms-save-layout-btn">' . $button1_title . '</button>';
 
 				$button2_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/cancel_button.gif';
 				$button2_title = $this->Application->Phrase('la_btn_Cancel', false, true);
 				$button2_code = '<button style="background-image: url(' . $button2_icon . '); onclick="aTemplateManager.cancelLayout(); return false;" class="cms-btn-new cms-cancel-layout-btn">' . $button2_title . '</button>';
 
 				$button3_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/section_properties.png';
 				$button3_title = $this->Application->Phrase('la_btn_SectionTemplate', false, true);
 				$button3_code = '<button style="background-image: url(' . $button3_icon . ');' . ($display_mode === false ? ' margin: 0px;' : '') . '" onclick="$form_name=\'kf_'.$page->GetID().'\'; std_edit_item(\'theme\', \'themes/file_edit\');" class="cms-btn-new cms-section-properties-btn">' . $button3_title . '</button>';
 
 				$edit_btn .= '<div class="cms-layout-btn-container"' . ($display_mode === false ? ' style="margin: 0px;"' : '') . '>' . $button1_code . $button2_code . '</div>' . $button3_code . "\n";
 			}
 
 			if ( $display_mode == 'start' ) {
 				// button with border around the page
 				if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
 					$tabs = "\n" . str_repeat("\t", 9);
 					$base_url = $this->Application->BaseURL();
 					$toolbar_hidden = $this->Application->GetVar('toolbar_hidden');
 
 					$edit_code .= '
 						<div>
 							<div id="cms-editing-notice">
 								<div class="top">
 									<a href="#" id="cms-close-editing-notice"></a>
 									<span prev_editors=""></span>
 								</div>
 								<div class="bottom"></div>
 							</div>
 
 							<div id="cms-revision-dropdown">
 								<div class="top"></div>
 								<div class="bottom"></div>
 							</div>
 						</div>';
 
 					if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') ) {
 						$edit_code .= '<div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '>
 											<div id="cms-revision-toolbar">
 												<script type="text/javascript">
 													var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/");
 													' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );';
 
 						if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) {
 							$edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );';
 						}
 
 						$edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs);
 
 						if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) {
 							$edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs);
 						}
 
 						$edit_code .= $tabs . 'a_toolbar.Render();' . "\n";
 
 						$revision = $this->Application->recallObject('page-revision.current');
 						/* @var $revision kDBItem */
 
 						if ( !$revision->GetDBField('IsDraft') ) {
 							$edit_code .= $tabs . 'a_toolbar.DisableButton("select");' . $tabs . 'a_toolbar.DisableButton("delete");' . $tabs . 'a_toolbar.DisableButton("preview");';
 						}
 
 						if ( $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
 							$edit_code .= $tabs . 'a_toolbar.DisableButton("approve");';
 						}
 
 						if ( $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
 							$edit_code .= $tabs . 'a_toolbar.DisableButton("decline");';
 						}
 
 						$publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true);
 
 						$edit_code .= substr($tabs, 0, -1) . '</script>
 
 									<div id="cms-current-revision-info">
 										<span class="revision-title"></span>
 										<div class="draft-saved"></div>
 									</div>
 
 									<a href="#" id="cms-close-toolbar"></a>
 									<div class="cms-clear"></div>
 								</div>
 
 								<a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a>
 							</div>' . "\n";
 					}
 				}
 
 				$edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">';
 
 			}
 			else {
 				// button without border around the page
 				$edit_code .= $edit_btn;
 			}
 		}
 
 		if ($display_mode == 'end') {
 			// draw border around the page
 			$edit_code .= '</div></div>';
 		}
 
 		if ($display_mode != 'end') {
 			if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
 				$url_params = Array(
 					'pass'			=>	'm',
 					'm_opener'		=>	'd',
 					'm_cat_id'		=>	$page->GetID(),
 					'__URLENCODE__'	=>	1,
 					'__NO_REWRITE__'=>	1,
 					'front'			=>	1,
 					'index_file' => 'index.php',
 				);
 
 				$revision = $this->Application->GetVar('revision');
 
 				if ( $revision ) {
 					$url_params['revision'] = $revision;
 				}
 
 				$page_admin_url = $this->Application->HREF('', ADMIN_DIRECTORY, $url_params);
 				$edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_revisions_'.$page->GetID().'" id="kf_revisions_'.$page->GetID().'" action="' . $page_admin_url . '">
 					<input type="hidden" name="revision" value="' . $this->Application->GetVar('revision', 0) . '"/>
 				</form>';
 			}
 
 			if ( $edit_url ) {
 				$edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_' . $page->GetID() . '" id="kf_' . $page->GetID() . '" action="' . $edit_url . '"></form>';
 			}
 
 			// when "EditingScripts" tag is not used, make sure, that scripts are also included
 			$edit_code .= $this->EditingScripts($params);
 		}
 
 		return $edit_code;
 	}
 
 	function toolbarButton($name, $title, $tabs)
 	{
 		$phrase = $this->Application->Phrase($title, false, true);
 
 		return $tabs . 'a_toolbar.AddButton( new ToolBarButton("' . $name . '", "' . kUtil::escape($phrase, kUtil::ESCAPE_HTML . '+' . kUtil::ESCAPE_JS) . '") );';
 	}
 
 	function _getThemeFileId()
 	{
 		$template = $this->Application->GetVar('t');
 
 		if (!$this->Application->TemplatesCache->TemplateExists($template) && !$this->Application->isAdmin) {
 			$cms_handler = $this->Application->recallObject($this->Prefix . '_EventHandler');
 			/* @var $cms_handler CategoriesEventHandler */
 
 			$template = ltrim($cms_handler->GetDesignTemplate(), '/');
 		}
 
 		$file_path = dirname($template) == '.' ? '' : '/' . dirname($template);
 		$file_name = basename($template);
 
 		$sql = 'SELECT FileId
 				FROM ' . TABLE_PREFIX . 'ThemeFiles
 				WHERE (ThemeId = ' . (int)$this->Application->GetVar('m_theme') . ') AND (FilePath = ' . $this->Conn->qstr($file_path) . ') AND (FileName = ' . $this->Conn->qstr($file_name . '.tpl') . ')';
 		return $this->Conn->GetOne($sql);
 	}
 
 	/**
 	 * Creates a button for editing item in Admin Console
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function AdminEditButton($params)
 	{
 		if ( EDITING_MODE != EDITING_MODE_CONTENT ) {
 			return '';
 		}
 
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$params['item_prefix'] = 'c';
 
 		if ( $this->Prefix == 'st' ) {
 			$params['button_icon'] = 'section_properties.png';
 			$params['button_class'] = 'cms-section-properties-btn';
 			$params['button_title'] = 'la_btn_SectionProperties';
 		}
 
 		return parent::AdminEditButton($params);
 	}
 
 	/**
 	 * Builds site menu
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function CachedMenu($params)
 	{
 		$menu_helper = $this->Application->recallObject('MenuHelper');
 		/* @var $menu_helper MenuHelper */
 
 		return $menu_helper->menuTag($this->getPrefixSpecial(), $params);
 	}
 
 	/**
 	 * Trick to allow some kind of output formatting when using CachedMenu tag
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function SplitColumn($params)
 	{
 		return $this->Application->GetVar($params['i']) > ceil($params['total'] / $params['columns']);
 	}
 
 	/**
 	 * Returns direct children count of given category
 	 *
 	 * @param Array $params
 	 * @return int
 	 */
 	function HasSubCats($params)
 	{
 		$sql = 'SELECT COUNT(*)
 				FROM ' . TABLE_PREFIX . 'Categories
 				WHERE ParentId = ' . $params['cat_id'];
 
 		return $this->Conn->GetOne($sql);
 	}
 
 	/**
 	 * Prints sub-pages of given/current page.
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo This could be reached by using "parent_cat_id" parameter. Only difference here is new block parameter "path". Need to rewrite.
 	 */
 	function PrintSubPages($params)
 	{
 		$list = $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List', $params);
 		/* @var $list kDBList */
 
 		$category_id = array_key_exists('category_id', $params) ? $params['category_id'] : $this->Application->GetVar('m_cat_id');
 
 		$list->addFilter('current_pages', TABLE_PREFIX . 'CategoryItems.CategoryId = ' . $category_id);
 		$list->Query();
 		$list->GoFirst();
 
 		$o = '';
 		$block_params = $this->prepareTagParams($params);
 		$block_params['name'] = $params['render_as'];
 
 		while (!$list->EOL()) {
 			$block_params['path'] = $list->GetDBField('Path');
 			$o .= $this->Application->ParseBlock($block_params);
 
 			$list->GoNext();
 		}
 
 		return $o;
 	}
 
 	/**
 	 * Builds link for browsing current page on Front-End
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function PageBrowseLink($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$themes_helper = $this->Application->recallObject('ThemesHelper');
 		/* @var $themes_helper kThemesHelper */
 
 		$site_config_helper = $this->Application->recallObject('SiteConfigHelper');
 		/* @var $site_config_helper SiteConfigHelper */
 
 		$settings = $site_config_helper->getSettings();
 
 		$url_params = Array (
 			'm_cat_id' => $object->GetID(),
 			'm_theme' => $themes_helper->getCurrentThemeId(),
 			'editing_mode' => $settings['default_editing_mode'],
 			'pass' => 'm',
 			'admin' => 1,
 		);
 
 		if ($this->Application->ConfigValue('UseModRewrite')) {
 			$url_params['__MOD_REWRITE__'] = 1;
 		}
 		else {
 			$url_params['index_file'] = 'index.php';
 		}
 
 		return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params);
 	}
 
 	/**
 	 * Builds a link for securely accessing a page later (even if it will not be publicly accessible)
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function DirectLink($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$themes_helper = $this->Application->recallObject('ThemesHelper');
 		/* @var $themes_helper kThemesHelper */
 
 		$url_params = Array (
 			'm_cat_id' => $object->GetID(),
 			'm_theme' => $themes_helper->getCurrentThemeId(),
 			'pass' => 'm',
 			'authkey' => $object->GetDBField('DirectLinkAuthKey'),
 			'__SSL__' => 0,
 			'__NO_SID__' => 0,
 		);
 
 		if ($this->Application->ConfigValue('UseModRewrite')) {
 			$url_params['__MOD_REWRITE__'] = 1;
 		}
 		else {
 			$url_params['index_file'] = 'index.php';
 		}
 
 		return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params);
 	}
 
 	/**
 	 * Builds link to category as a cms page
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function ContentPageLink($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$params['t'] = mb_strtolower($object->GetDBField('NamedParentPath'));
 		$params['m_cat_id'] = 0;
 
 		return $this->Application->ProcessParsedTag('m', 'Link', $params);
 	}
 
 	/**
 	 * Prepares cms page description for search result page
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function SearchDescription($params)
 	{
 		$object = $this->getObject($params);
 		$desc =  $object->GetField('MetaDescription');
 		if (!$desc) {
 			$sql = 'SELECT *
 					FROM ' . TABLE_PREFIX . 'PageContent
 					WHERE PageId = ' . $object->GetID() . ' AND ContentNum = 1';
 			$content = $this->Conn->GetRow($sql);
 
 			if ($content['l'.$this->Application->GetVar('m_lang').'_Content']) {
 				$desc = $content['l'.$this->Application->GetVar('m_lang').'_Content'];
 			}
 			else {
 				$desc = $content['l'.$this->Application->GetDefaultLanguageId().'_Content'];
 			}
 		}
 
 		return mb_substr($desc, 0, 300).(mb_strlen($desc) > 300 ? '...' : '');
 	}
 
 	/**
 	 * Simplified version of "c:CategoryLink" for "c:PrintList"
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo Used? Needs refactoring.
 	 */
 	function EnterCatLink($params)
 	{
 		$object = $this->getObject($params);
 
 		$url_params = Array ('pass' => 'm', 'm_cat_id' => $object->GetID());
 		return $this->Application->HREF($params['template'], '', $url_params);
 	}
 
 	/**
 	 * Simplified version of "c:CategoryPath", that do not use blocks for rendering
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo Used? Maybe needs to be removed.
 	 */
 	function PagePath($params)
 	{
 		$object = $this->getObject($params);
 		$path = $object->GetField('CachedNavbar');
 		if ($path) {
 			$items = explode('&|&', $path);
 			array_shift($items);
 			return implode(' -&gt; ', $items);
 		}
 
 		return '';
 	}
 
 	/**
 	 * Returns configuration variable value
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @todo Needs to be replaced with "m:GetConfig" tag; Not used now (were used on structure_edit.tpl).
 	 */
 	function AllowManualFilenames($params)
 	{
 		return $this->Application->ConfigValue('ProjCMSAllowManualFilenames');
 	}
 
 	/**
 	 * Draws path to current page (each page can be link to it)
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function CurrentPath($params)
 	{
 		$block_params = $this->prepareTagParams($params);
 		$block_params['name'] = $block_params['render_as'];
 
 		$object = $this->Application->recallObject($this->Prefix);
 		/* @var $object kDBItem */
 
 		$category_ids = explode('|', substr($object->GetDBField('ParentPath'), 1, -1));
 
 		$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
 		$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 		$language = (int)$this->Application->GetVar('m_lang');
 
 		if (!$language) {
 			$language = 1;
 		}
 
 		$sql = 'SELECT l'.$language.'_Name AS Name, NamedParentPath
 				FROM '.$table_name.'
 				WHERE '.$id_field.' IN ('.implode(',', $category_ids).')';
 		$categories_data = $this->Conn->Query($sql);
 
 		$ret = '';
 		foreach ($categories_data as $index => $category_data) {
 			if ($category_data['Name'] == 'Content') {
 				continue;
 			}
 			$block_params['title'] = $category_data['Name'];
 			$block_params['template'] = preg_replace('/^Content\//i', '', $category_data['NamedParentPath']);
 			$block_params['is_first'] = $index == 1; // because Content is 1st element
 			$block_params['is_last'] = $index == count($categories_data) - 1;
 
 			$ret .= $this->Application->ParseBlock($block_params);
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Synonim to PrintList2 for "onlinestore" theme
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function ListPages($params)
 	{
 		return $this->PrintList2($params);
 	}
 
 	/**
 	 * Returns information about parser element locations in template
 	 *
 	 * @param Array $params
 	 * @return mixed
 	 */
 	function BlockInfo($params)
 	{
 		if (!EDITING_MODE) {
 			return '';
 		}
 
 		$template_helper = $this->Application->recallObject('TemplateHelper');
 		/* @var $template_helper TemplateHelper */
 
 		return $template_helper->blockInfo( $params['name'] );
 	}
 
 	/**
 	 * Hide all editing tabs except permission tab, when editing "Home" (ID = 0) category
 	 *
 	 * @param Array $params
 	 */
 	function ModifyUnitConfig($params)
 	{
 		$root_category = $this->Application->RecallVar('IsRootCategory_' . $this->Application->GetVar('m_wid'));
 		if (!$root_category) {
 			return ;
 		}
 
 		$edit_tab_presets = $this->Application->getUnitOption($this->Prefix, 'EditTabPresets');
 		$edit_tab_presets['Default'] = Array (
 			'permissions' => $edit_tab_presets['Default']['permissions'],
 		);
 		$this->Application->setUnitOption($this->Prefix, 'EditTabPresets', $edit_tab_presets);
 	}
 
 	/**
 	 * Prints catalog export templates
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function PrintCatalogExportTemplates($params)
 	{
 		$prefixes = explode(',', $params['prefixes']);
 
 		$ret = Array ();
 		foreach ($prefixes as $prefix) {
 			if ($this->Application->prefixRegistred($prefix)) {
 				$module_path = $this->Application->getUnitOption($prefix, 'ModuleFolder') . '/';
 				$module_name = $this->Application->findModule('Path', $module_path, 'Name');
 
 				$ret[$prefix] = mb_strtolower($module_name) . '/export';
 			}
 		}
 
 		$json_helper = $this->Application->recallObject('JSONHelper');
 		/* @var $json_helper JSONHelper */
 
 		return $json_helper->encode($ret);
 	}
 
 	/**
 	 * Checks, that "view in browse mode" functionality available
 	 *
 	 * @param Array $params
 	 * @return bool
 	 */
 	function BrowseModeAvailable($params)
 	{
 		$valid_special = $params['Special'] != 'user';
 		$not_selector = $this->Application->GetVar('type') != 'item_selector';
 
 		return $valid_special && $not_selector;
 	}
 
 	/**
 	 * Returns a link for editing product
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function ItemEditLink($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBList */
 
 		$edit_template = $this->Application->getUnitOption($this->Prefix, 'AdminTemplatePath') . '/' . $this->Application->getUnitOption($this->Prefix, 'AdminTemplatePrefix') . 'edit';
 
 		$url_params = Array (
 			'm_opener'				=>	'd',
 			$this->Prefix.'_mode'	=>	't',
 			$this->Prefix.'_event'	=>	'OnEdit',
 			$this->Prefix.'_id'		=>	$object->GetID(),
 			'm_cat_id'				=>	$object->GetDBField('ParentId'),
 			'pass'					=>	'all,'.$this->Prefix,
 			'no_pass_through'		=>	1,
 		);
 
 		return $this->Application->HREF($edit_template,'', $url_params);
 	}
 
 	function RelevanceIndicator($params)
 	{
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		$search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
 		$sql = 'SELECT Relevance
 				FROM '.$search_results_table.'
 				WHERE ResourceId = '.$object->GetDBField('ResourceId');
 
     	$percents_off = (int)(100 - (100 * $this->Conn->GetOne($sql)));
     	$percents_off = ($percents_off < 0) ? 0 : $percents_off;
     	if ($percents_off) {
         	$params['percent_off'] = $percents_off;
     		$params['percent_on'] = 100 - $percents_off;
         	$params['name'] = $this->SelectParam($params, 'relevance_normal_render_as,block_relevance_normal');
     	}
     	else {
     		$params['name'] = $this->SelectParam($params, 'relevance_full_render_as,block_relevance_full');
     	}
     	return $this->Application->ParseBlock($params);
 	}
 
 	/**
 	 * Returns list of categories, that have category add/edit permission
 	 *
 	 * @param Array $params
 	 * @return string
 	 */
 	function AllowedCategoriesJSON($params)
 	{
 		if ($this->Application->RecallVar('user_id') == USER_ROOT) {
 			$categories = true;
 		}
 		else {
 			$object = $this->getObject($params);
 			/* @var $object kDBItem */
 
 			$perm_helper = $this->Application->recallObject('PermissionsHelper');
 			/* @var $perm_helper kPermissionsHelper */
 
 			$perm_prefix = $this->Application->getUnitOption($this->Prefix, 'PermItemPrefix');
 			$categories = $perm_helper->getPermissionCategories($perm_prefix . '.' . ($object->IsNewItem() ? 'ADD' : 'MODIFY'));
 		}
 
 		$json_helper = $this->Application->recallObject('JSONHelper');
 		/* @var $json_helper JSONHelper */
 
 		return $json_helper->encode($categories);
 	}
 
 	function PageEditable($params)
 	{
 		if ($this->Application->isDebugMode()) {
 			return true;
 		}
 
 		$object = $this->getObject($params);
 		/* @var $object kDBItem */
 
 		return !$object->GetDBField('Protected');
 	}
 
 	/**
 	 * Returns element for "__item__" navigation bar part
 	 *
 	 * @param Array $params
 	 * @return string
 	 * @access protected
 	 */
 	protected function CategoryItemElement($params)
 	{
 		$category_helper = $this->Application->recallObject('CategoryHelper');
 		/* @var $category_helper CategoryHelper */
 
 		$navigation_bar = $this->Application->recallObject('kNavigationBar');
 		/* @var $navigation_bar kNavigationBar */
 
 		$category_id = isset($params['cat_id']) ? $params['cat_id'] : $this->Application->GetVar('m_cat_id');
 		$parent_path = explode('|', substr($navigation_bar->getParentPath($category_id), 1, -1));
 		array_shift($parent_path); // remove "Content" category
 		$module_info = $category_helper->getCategoryModule($params, $parent_path);
 
 		if ( !$module_info ) {
 			return '';
 		}
 
 		$module_prefix = $module_info['Var'];
 
 		$object = $this->Application->recallObject($module_prefix);
 		/* @var $object kCatDBItem */
 
 		$title_field = $this->Application->getUnitOption($module_prefix, 'TitleField');
 		$block_params = $this->prepareTagParams($params);
 		$block_params['name'] = $params['render_as'];
 
 		$block_params['title'] = $object->GetField($title_field);
 		$block_params['prefix'] = $module_prefix;
 
 		return $this->Application->ParseBlock($block_params);
 	}
 }
Index: branches/5.2.x/core/units/categories/categories_event_handler.php
===================================================================
--- branches/5.2.x/core/units/categories/categories_event_handler.php	(revision 16307)
+++ branches/5.2.x/core/units/categories/categories_event_handler.php	(revision 16308)
@@ -1,3144 +1,3148 @@
 <?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 CategoriesEventHandler extends kDBEventHandler {
 
 		/**
 		 * Allows to override standard permission mapping
 		 *
 		 * @return void
 		 * @access protected
 		 * @see kEventHandler::$permMapping
 		 */
 		protected function mapPermissions()
 		{
 			parent::mapPermissions();
 
 			$permissions = Array (
 				'OnRebuildCache' => Array ('self' => 'add|edit'),
 				'OnCopy' => Array ('self' => true),
 				'OnCut' => Array ('self' => 'edit'),
 				'OnPasteClipboard' => Array ('self' => true),
 				'OnPaste' => Array ('self' => 'add|edit', 'subitem' => 'edit'),
 
 				'OnRecalculatePriorities' => Array ('self' => 'add|edit'), // category ordering
 				'OnItemBuild' => Array ('self' => true), // always allow to view individual categories (regardless of CATEGORY.VIEW right)
 				'OnUpdatePreviewBlock' => Array ('self' => true), // for FCKEditor integration
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Categories are sorted using special sorting event
 		 *
 		 */
 		function mapEvents()
 		{
 			parent::mapEvents();
 
 			$events_map = Array (
 				'OnMassMoveUp' => 'OnChangePriority',
 				'OnMassMoveDown' => 'OnChangePriority',
 			);
 
 			$this->eventMethods = array_merge($this->eventMethods, $events_map);
 		}
 
 		/**
 		 * Checks user permission to execute given $event
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(kEvent $event)
 		{
 			if ( $event->Name == 'OnResetCMSMenuCache' ) {
 				// events from "Tools -> System Tools" section are controlled via that section "edit" permission
 
 				$perm_helper = $this->Application->recallObject('PermissionsHelper');
 				/* @var $perm_helper kPermissionsHelper */
 
 				$perm_value = $this->Application->CheckPermission('in-portal:service.edit');
 
 				return $perm_helper->finalizePermissionCheck($event, $perm_value);
 			}
 
 			if ( !$this->Application->isAdmin ) {
 				if ( $event->Name == 'OnSetSortingDirect' ) {
 					// allow sorting on front event without view permission
 					return true;
 				}
 
 				if ( $event->Name == 'OnItemBuild' ) {
 					$category_id = $this->getPassedID($event);
 					if ( $category_id == 0 ) {
 						return true;
 					}
 				}
 			}
 
 			if ( in_array($event->Name, $this->_getMassPermissionEvents()) ) {
 				$items = $this->_getPermissionCheckInfo($event);
 
 				$perm_helper = $this->Application->recallObject('PermissionsHelper');
 				/* @var $perm_helper kPermissionsHelper */
 
 				if ( ($event->Name == 'OnSave') && array_key_exists(0, $items) ) {
 					// adding new item (ID = 0)
 					$perm_value = $perm_helper->AddCheckPermission($items[0]['ParentId'], $event->Prefix) > 0;
 				}
 				else {
 					// leave only items, that can be edited
 					$ids = Array ();
 					$check_method = in_array($event->Name, Array ('OnMassDelete', 'OnCut')) ? 'DeleteCheckPermission' : 'ModifyCheckPermission';
 					foreach ($items as $item_id => $item_data) {
 						if ( $perm_helper->$check_method($item_data['CreatedById'], $item_data['ParentId'], $event->Prefix) > 0 ) {
 							$ids[] = $item_id;
 						}
 					}
 
 					if ( !$ids ) {
 						// no items left for editing -> no permission
 						return $perm_helper->finalizePermissionCheck($event, false);
 					}
 
 					$perm_value = true;
 					$event->setEventParam('ids', $ids); // will be used later by "kDBEventHandler::StoreSelectedIDs" method
 				}
 
 				return $perm_helper->finalizePermissionCheck($event, $perm_value);
 			}
 
 			if ( $event->Name == 'OnRecalculatePriorities' ) {
 				$perm_helper = $this->Application->recallObject('PermissionsHelper');
 				/* @var $perm_helper kPermissionsHelper */
 
 				$category_id = $this->Application->GetVar('m_cat_id');
 
 				return $perm_helper->AddCheckPermission($category_id, $event->Prefix) || $perm_helper->ModifyCheckPermission(0, $category_id, $event->Prefix);
 			}
 
 			if ( $event->Name == 'OnPasteClipboard' ) {
 				// forces permission check to work by current category for "Paste In Category" operation
 				$category_id = $this->Application->GetVar('m_cat_id');
 				$this->Application->SetVar('c_id', $category_id);
 			}
 
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Returns events, that require item-based (not just event-name based) permission check
 		 *
 		 * @return Array
 		 */
 		function _getMassPermissionEvents()
 		{
 			return Array (
 				'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove',
 				'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown',
 				'OnCut',
 			);
 		}
 
 		/**
 		 * Returns category item IDs, that require permission checking
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function _getPermissionCheckIDs($event)
 		{
 			if ($event->Name == 'OnSave') {
 				$selected_ids = implode(',', $this->getSelectedIDs($event, true));
 				if (!$selected_ids) {
 					$selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave)
 				}
 			}
 			else {
 				// OnEdit, OnMassDelete events, when items are checked in grid
 				$selected_ids = implode(',', $this->StoreSelectedIDs($event));
 			}
 
 			return $selected_ids;
 		}
 
 		/**
 		 * Returns information used in permission checking
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 */
 		function _getPermissionCheckInfo($event)
 		{
 			// when saving data from temp table to live table check by data from temp table
 			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
 
 			if ($event->Name == 'OnSave') {
 				$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix);
 			}
 
 			$sql = 'SELECT ' . $id_field . ', CreatedById, ParentId
 					FROM ' . $table_name . '
 					WHERE ' . $id_field . ' IN (' . $this->_getPermissionCheckIDs($event) . ')';
 			$items = $this->Conn->Query($sql, $id_field);
 
 			if (!$items) {
 				// when creating new category, then no IDs are stored in session
 				$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 				list ($id, $fields_hash) = each($items_info);
 
 				if (array_key_exists('ParentId', $fields_hash)) {
 					$item_category = $fields_hash['ParentId'];
 				}
 				else {
 					$item_category = $this->Application->RecallVar('m_cat_id'); // saved in c:OnPreCreate event permission checking
 				}
 
 				$items[$id] = Array (
 					'CreatedById' => $this->Application->RecallVar('user_id'),
 					'ParentId' => $item_category,
 				);
 			}
 
 			return $items;
 		}
 
 		/**
 		 * Set's mark, that root category is edited
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnEdit(kEvent $event)
 		{
 			$category_id = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
 			$home_category = $this->Application->getBaseCategory();
 
 			$this->Application->StoreVar('IsRootCategory_' . $this->Application->GetVar('m_wid'), ($category_id === '0') || ($category_id == $home_category));
 
 			parent::OnEdit($event);
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				// keep "Section Properties" link (in browse modes) clean
 				$this->Application->DeleteVar('admin');
 			}
 		}
 
 		/**
 		 * Adds selected link to listing
 		 *
 		 * @param kEvent $event
 		 */
 		function OnProcessSelected($event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$selected_ids = $this->Application->GetVar('selected_ids');
 
 			$this->RemoveRequiredFields($object);
 			$object->SetDBField($this->Application->RecallVar('dst_field'), $selected_ids['c']);
 			$object->Update();
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Apply system filter to categories list
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(kEvent $event)
 		{
 			parent::SetCustomQuery($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			// don't show "Content" category in advanced view
 			$object->addFilter('system_categories', '%1$s.Status <> 4');
 
 			// show system templates from current theme only + all virtual templates
 			$object->addFilter('theme_filter', '%1$s.ThemeId = ' . $this->_getCurrentThemeId() . ' OR %1$s.ThemeId = 0');
 
 			if ($event->Special == 'showall') {
 				// if using recycle bin don't show categories from there
 				$recycle_bin = $this->Application->ConfigValue('RecycleBinFolder');
 				if ($recycle_bin) {
 					$sql = 'SELECT TreeLeft, TreeRight
 							FROM '.TABLE_PREFIX.'Categories
 							WHERE CategoryId = '.$recycle_bin;
 					$tree_indexes = $this->Conn->GetRow($sql);
 
 					$object->addFilter('recyclebin_filter', '%1$s.TreeLeft < '.$tree_indexes['TreeLeft'].' OR %1$s.TreeLeft > '.$tree_indexes['TreeRight']);
 				}
 			}
 
 			if ( (string)$event->getEventParam('parent_cat_id') !== '' ) {
 				$parent_cat_id = $event->getEventParam('parent_cat_id');
 
 				if ("$parent_cat_id" == 'Root') {
 					$module_name = $event->getEventParam('module') ? $event->getEventParam('module') : 'In-Commerce';
 					$parent_cat_id = $this->Application->findModule('Name', $module_name, 'RootCat');
 				}
 			}
 			else {
 				$parent_cat_id = $this->Application->GetVar('c_id');
 				if (!$parent_cat_id) {
 					$parent_cat_id = $this->Application->GetVar('m_cat_id');
 				}
 				if (!$parent_cat_id) {
 					$parent_cat_id = 0;
 				}
 			}
 
 			if ("$parent_cat_id" == '0') {
 				// replace "0" category with "Content" category id (this way template
 				$parent_cat_id = $this->Application->getBaseCategory();
 			}
 
 			if ("$parent_cat_id" != 'any') {
 				if ($event->getEventParam('recursive')) {
 					if ($parent_cat_id > 0) {
 						// not "Home" category
 						$tree_indexes = $this->Application->getTreeIndex($parent_cat_id);
 
 						$object->addFilter('parent_filter', '%1$s.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight']);
 					}
 				}
 				else {
 					$object->addFilter('parent_filter', '%1$s.ParentId = '.$parent_cat_id);
 				}
 			}
 
 			$this->applyViewPermissionFilter($object);
 
 			if (!$this->Application->isAdminUser)	{
 				// apply status filter only on front
 				$object->addFilter('status_filter', $object->TableName.'.Status = 1');
 			}
 
 			// process "types" and "except" parameters
 			$type_clauses = Array();
 
 			$types = $event->getEventParam('types');
 			$types = $types ? explode(',', $types) : Array ();
 
 			$except_types = $event->getEventParam('except');
 			$except_types = $except_types ? explode(',', $except_types) : Array ();
 
 			if (in_array('related', $types) || in_array('related', $except_types)) {
 				$related_to = $event->getEventParam('related_to');
 				if (!$related_to) {
 					$related_prefix = $event->Prefix;
 				}
 				else {
 					$sql = 'SELECT Prefix
 							FROM '.TABLE_PREFIX.'ItemTypes
 							WHERE ItemName = '.$this->Conn->qstr($related_to);
 					$related_prefix = $this->Conn->GetOne($sql);
 				}
 
 				$rel_table = $this->Application->getUnitOption('rel', 'TableName');
 				$item_type = (int)$this->Application->getUnitOption($event->Prefix, 'ItemType');
 
 				if ($item_type == 0) {
 					trigger_error('<strong>ItemType</strong> not defined for prefix <strong>' . $event->Prefix . '</strong>', E_USER_WARNING);
 				}
 
 				// process case, then this list is called inside another list
 				$prefix_special = $event->getEventParam('PrefixSpecial');
 				if (!$prefix_special) {
 					$prefix_special = $this->Application->Parser->GetParam('PrefixSpecial');
 				}
 
 				$id = false;
 				if ($prefix_special !== false) {
 					$processed_prefix = $this->Application->processPrefix($prefix_special);
 					if ($processed_prefix['prefix'] == $related_prefix) {
 						// printing related categories within list of items (not on details page)
 						$list = $this->Application->recallObject($prefix_special);
 						/* @var $list kDBList */
 
 						$id = $list->GetID();
 					}
 				}
 
 				if ($id === false) {
 					// printing related categories for single item (possibly on details page)
 					if ($related_prefix == 'c') {
 						$id = $this->Application->GetVar('m_cat_id');
 					}
 					else {
 						$id = $this->Application->GetVar($related_prefix . '_id');
 					}
 				}
 
 				$p_item = $this->Application->recallObject($related_prefix . '.current', null, Array('skip_autoload' => true));
 				/* @var $p_item kCatDBItem */
 
 				$p_item->Load( (int)$id );
 
 				$p_resource_id = $p_item->GetDBField('ResourceId');
 
 				$sql = 'SELECT SourceId, TargetId FROM '.$rel_table.'
 						WHERE
 							(Enabled = 1)
 							AND (
 									(Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.')
 									OR
 									(Type = 1
 										AND (
 												(SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.')
 												OR
 												(TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.')
 											)
 									)
 							)';
 
 				$related_ids_array = $this->Conn->Query($sql);
 				$related_ids = Array();
 
 				foreach ($related_ids_array as $key => $record) {
 					$related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ];
 				}
 
 				if (count($related_ids) > 0) {
 					$type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).')';
 					$type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).')';
 				}
 				else {
 					$type_clauses['related']['include'] = '0';
 					$type_clauses['related']['except'] = '1';
 				}
 
 				$type_clauses['related']['having_filter'] = false;
 			}
 
 			if (in_array('category_related', $type_clauses)) {
 				$object->removeFilter('parent_filter');
 				$resource_id = $this->Conn->GetOne('
 								SELECT ResourceId FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
 								WHERE CategoryId = '.$parent_cat_id
 							);
 
 				$sql = 'SELECT DISTINCT(TargetId) FROM '.TABLE_PREFIX.'CatalogRelationships
 						WHERE SourceId = '.$resource_id.' AND SourceType = 1';
 				$related_cats = $this->Conn->GetCol($sql);
 				$related_cats = is_array($related_cats) ? $related_cats : Array();
 
 				$sql = 'SELECT DISTINCT(SourceId) FROM '.TABLE_PREFIX.'CatalogRelationships
 						WHERE TargetId = '.$resource_id.' AND TargetType = 1 AND Type = 1';
 				$related_cats2 = $this->Conn->GetCol($sql);
 				$related_cats2 = is_array($related_cats2) ? $related_cats2 : Array();
 				$related_cats = array_unique( array_merge( $related_cats2, $related_cats ) );
 
 				if ($related_cats) {
 					$type_clauses['category_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')';
 					$type_clauses['category_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')';
 				}
 				else
 				{
 					$type_clauses['category_related']['include'] = '0';
 					$type_clauses['category_related']['except'] = '1';
 				}
 				$type_clauses['category_related']['having_filter'] = false;
 			}
 
 			if (in_array('product_related', $types)) {
 				$object->removeFilter('parent_filter');
 
 				$product_id = $event->getEventParam('product_id') ? $event->getEventParam('product_id') : $this->Application->GetVar('p_id');
 				$resource_id = $this->Conn->GetOne('
 								SELECT ResourceId FROM '.$this->Application->getUnitOption('p', 'TableName').'
 								WHERE ProductId = '.$product_id
 							);
 
 				$sql = 'SELECT DISTINCT(TargetId) FROM '.TABLE_PREFIX.'CatalogRelationships
 						WHERE SourceId = '.$resource_id.' AND TargetType = 1';
 				$related_cats = $this->Conn->GetCol($sql);
 				$related_cats = is_array($related_cats) ? $related_cats : Array();
 				$sql = 'SELECT DISTINCT(SourceId) FROM '.TABLE_PREFIX.'CatalogRelationships
 						WHERE TargetId = '.$resource_id.' AND SourceType = 1 AND Type = 1';
 				$related_cats2 = $this->Conn->GetCol($sql);
 				$related_cats2 = is_array($related_cats2) ? $related_cats2 : Array();
 				$related_cats = array_unique( array_merge( $related_cats2, $related_cats ) );
 
 				if ($related_cats) {
 					$type_clauses['product_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')';
 					$type_clauses['product_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')';
 				}
 				else {
 					$type_clauses['product_related']['include'] = '0';
 					$type_clauses['product_related']['except'] = '1';
 				}
 
 				$type_clauses['product_related']['having_filter'] = false;
 			}
 
 			$type_clauses['menu']['include'] = '%1$s.IsMenu = 1';
 			$type_clauses['menu']['except'] = '%1$s.IsMenu = 0';
 			$type_clauses['menu']['having_filter'] = false;
 
 			if (in_array('search', $types) || in_array('search', $except_types)) {
 				$event_mapping = Array (
 					'simple'		=>	'OnSimpleSearch',
 					'subsearch'		=>	'OnSubSearch',
 					'advanced'		=>	'OnAdvancedSearch'
 				);
 
 				$keywords = $event->getEventParam('keyword_string');
 				$type = $this->Application->GetVar('search_type', 'simple');
 
 				if ( $keywords ) {
 					// processing keyword_string param of ListProducts tag
 					$this->Application->SetVar('keywords', $keywords);
 					$type = 'simple';
 				}
 
 				$search_event = $event_mapping[$type];
 				$this->$search_event($event);
 
 				$object = $event->getObject();
 				/* @var $object kDBList */
 
 				$search_sql = '	FROM ' . TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search
 								search_result JOIN %1$s ON %1$s.ResourceId = search_result.ResourceId';
 				$sql = str_replace('FROM %1$s', $search_sql, $object->GetPlainSelectSQL());
 
 				$object->SetSelectSQL($sql);
 
 				$object->addCalculatedField('Relevance', 'search_result.Relevance');
 
 				$type_clauses['search']['include'] = '1';
 				$type_clauses['search']['except'] = '0';
 				$type_clauses['search']['having_filter'] = false;
 			}
 
 			$search_helper = $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$search_helper->SetComplexFilter($event, $type_clauses, implode(',', $types), implode(',', $except_types));
 		}
 
 		/**
 		 * Adds filter, that uses *.VIEW permissions to determine if an item should be shown to a user.
 		 *
 		 * @param kDBList $object Object.
 		 *
 		 * @return void
 		 * @access protected
 		 */
 		protected function applyViewPermissionFilter(kDBList $object)
 		{
 			if ( !$this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) {
 				return;
 			}
 
 			if ( $this->Application->RecallVar('user_id') == USER_ROOT ) {
 				// for "root" CATEGORY.VIEW permission is checked for items lists too
 				$view_perm = 1;
 			}
 			else {
 				$count_helper = $this->Application->recallObject('CountHelper');
 				/* @var $count_helper kCountHelper */
 
 				list ($view_perm, $view_filter) = $count_helper->GetPermissionClause($object->Prefix, 'perm');
 				$object->addFilter('perm_filter2', $view_filter);
 			}
 
 			$object->addFilter('perm_filter', 'perm.PermId = ' . $view_perm); // check for CATEGORY.VIEW permission
 		}
 
 		/**
 		 * Returns current theme id
 		 *
 		 * @return int
 		 */
 		function _getCurrentThemeId()
 		{
 			$themes_helper = $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			return (int)$themes_helper->getCurrentThemeId();
 		}
 
 		/**
 		 * Returns ID of current item to be edited
 		 * by checking ID passed in get/post as prefix_id
 		 * or by looking at first from selected ids, stored.
 		 * Returned id is also stored in Session in case
 		 * it was explicitly passed as get/post
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 * @access public
 		 */
 		public function getPassedID(kEvent $event)
 		{
 			if ( ($event->Special == 'page') || $this->_isVirtual($event) || ($event->Prefix == 'st') ) {
 				return $this->_getPassedStructureID($event);
 			}
 
 			if ( $this->Application->isAdmin ) {
 				return parent::getPassedID($event);
 			}
 
 			return $this->Application->GetVar('m_cat_id');
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function _getPassedStructureID($event)
 		{
 			static $page_by_template = Array ();
 
 			if ( $event->Special == 'current' ) {
 				return $this->Application->GetVar('m_cat_id');
 			}
 
 			$event->setEventParam('raise_warnings', 0);
 
 			$page_id = parent::getPassedID($event);
 
 			if ( $page_id === false ) {
 				$template = $event->getEventParam('page');
 				if ( !$template ) {
 					$template = $this->Application->GetVar('t');
 				}
 
 				// bug: when template contains "-" symbols (or others, that stripDisallowed will replace) it's not found
 				if ( !array_key_exists($template, $page_by_template) ) {
 					$template_crc = kUtil::crc32(mb_strtolower($template));
 
 					$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
 							FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 							WHERE
 								(
 									(NamedParentPathHash = ' . $template_crc . ') OR
 									(`Type` = ' . PAGE_TYPE_TEMPLATE . ' AND CachedTemplateHash = ' . $template_crc . ')
 								) AND (ThemeId = ' . $this->_getCurrentThemeId() . ' OR ThemeId = 0)';
 
 					$page_id = $this->Conn->GetOne($sql);
 				}
 				else {
 					$page_id = $page_by_template[$template];
 				}
 
 				if ( $page_id ) {
 					$page_by_template[$template] = $page_id;
 				}
 			}
 
 			if ( !$page_id && !$this->Application->isAdmin ) {
 				$page_id = $this->Application->GetVar('m_cat_id');
 			}
 
 			return $page_id;
 		}
 
 		function ParentGetPassedID($event)
 		{
 			return parent::getPassedID($event);
 		}
 
 		/**
 		 * Adds calculates fields for item statuses
 		 *
 		 * @param kCatDBItem $object
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function prepareObject(&$object, kEvent $event)
 		{
 			if ( $this->_isVirtual($event) ) {
 				return;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$object->addCalculatedField(
 				'IsNew',
 				'	IF(%1$s.NewItem = 2,
 						IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '.
 							$this->Application->ConfigValue('Category_DaysNew').
 							'*3600*24), 1, 0),
 						%1$s.NewItem
 				)');
 		}
 
 		/**
 		 * Checks, that this is virtual page
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 * @access protected
 		 */
 		protected function _isVirtual(kEvent $event)
 		{
 			return strpos($event->Special, '-virtual') !== false;
 		}
 
 		/**
 		 * Gets right special for configuring virtual page
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function _getCategorySpecial(kEvent $event)
 		{
 			return $this->_isVirtual($event) ? '-virtual' : $event->Special;
 		}
 
 		/**
 		 * Set correct parent path for newly created categories
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterCopyToLive(kEvent $event)
 		{
 			parent::OnAfterCopyToLive($event);
 
 			$object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true, 'live_table' => true));
 			/* @var $object CategoriesItem */
 
 			$parent_path = false;
 			$object->Load($event->getEventParam('id'));
 
 			if ( $event->getEventParam('temp_id') == 0 ) {
 				if ( $object->isLoaded() ) {
 					// update path only for real categories (not including "Home" root category)
 					$fields_hash = $object->buildParentBasedFields();
 					$this->Conn->doUpdate($fields_hash, $object->TableName, 'CategoryId = ' . $object->GetID());
 					$parent_path = $fields_hash['ParentPath'];
 				}
 			}
 			else {
 				$parent_path = $object->GetDBField('ParentPath');
 			}
 
 			if ( $parent_path ) {
 				$cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $parent_path));
 				/* @var $cache_updater kPermCacheUpdater */
 
 				$cache_updater->OneStepRun();
 			}
 		}
 
 		/**
 		 * Set cache modification mark if needed
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeDeleteFromLive(kEvent $event)
 		{
 			parent::OnBeforeDeleteFromLive($event);
 
 			$id = $event->getEventParam('id');
 
 			// loading anyway, because this object is needed by "c-perm:OnBeforeDeleteFromLive" event
 			$temp_object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $temp_object CategoriesItem */
 
 			$temp_object->Load($id);
 
 			if ( $id == 0 ) {
 				if ( $temp_object->isLoaded() ) {
 					// new category -> update cache (not loaded when "Home" category)
 					$this->Application->StoreVar('PermCache_UpdateRequired', 1);
 				}
 
 				return ;
 			}
 
 			// existing category was edited, check if in-cache fields are modified
 			$live_object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('live_table' => true, 'skip_autoload' => true));
 			/* @var $live_object CategoriesItem */
 
 			$live_object->Load($id);
 			$cached_fields = Array ('l' . $this->Application->GetDefaultLanguageId() . '_Name', 'Filename', 'Template', 'ParentId', 'Priority');
 
 			foreach ($cached_fields as $cached_field) {
 				if ( $live_object->GetDBField($cached_field) != $temp_object->GetDBField($cached_field) ) {
 					// use session instead of REQUEST because of permission editing in category can contain
 					// multiple submits, that changes data before OnSave event occurs
 					$this->Application->StoreVar('PermCache_UpdateRequired', 1);
 					break;
 				}
 			}
 
 			// remember category filename change between temp and live records
 			if ( $temp_object->GetDBField('Filename') != $live_object->GetDBField('Filename') ) {
 				$filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ());
 
 				$filename_changes[ $live_object->GetID() ] = Array (
 					'from' => $live_object->GetDBField('Filename'),
 					'to' => $temp_object->GetDBField('Filename')
 				);
 
 				$this->Application->SetVar($event->Prefix . '_filename_changes', $filename_changes);
 			}
 		}
 
 		/**
 		 * Calls kDBEventHandler::OnSave original event
 		 * Used in proj-cms:StructureEventHandler->OnSave
 		 *
 		 * @param kEvent $event
 		 */
 		function parentOnSave($event)
 		{
 			parent::OnSave($event);
 		}
 
 		/**
 		 * Reset root-category flag when new category is created
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreCreate(kEvent $event)
 		{
 			// 1. for permission editing of Home category
 			$this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid'));
 
 			parent::OnPreCreate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// 2. preset template
 			$category_id = $this->Application->GetVar('m_cat_id');
 			$root_category = $this->Application->getBaseCategory();
 
 			if ( $category_id == $root_category ) {
 				$object->SetDBField('Template', $this->_getDefaultDesign());
 			}
 
 			// 3. set default owner
 			$object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
 		}
 
 		/**
 		 * Checks cache update mark and redirect to cache if needed
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSave(kEvent $event)
 		{
 			// get data from live table before it is overwritten by parent OnSave method call
 			$ids = $this->getSelectedIDs($event, true);
 			$is_editing = implode('', $ids);
 			$old_statuses = $is_editing ? $this->_getCategoryStatus($ids) : Array ();
 
 			$object = $event->getObject();
 			/* @var $object CategoriesItem */
 
 			parent::OnSave($event);
 
 			if ( $event->status != kEvent::erSUCCESS ) {
 				return;
 			}
 
 			if ( $this->Application->RecallVar('PermCache_UpdateRequired') ) {
 				$this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid'));
 			}
 
 			$this->Application->StoreVar('RefreshStructureTree', 1);
 			$this->_resetMenuCache();
 
 			if ( $is_editing ) {
 				// send email event to category owner, when it's status is changed (from admin)
 				$object->SwitchToLive();
 				$new_statuses = $this->_getCategoryStatus($ids);
 				$process_statuses = Array (STATUS_ACTIVE, STATUS_DISABLED);
 
 				foreach ($new_statuses as $category_id => $new_status) {
 					if ( $new_status != $old_statuses[$category_id] && in_array($new_status, $process_statuses) ) {
 						$object->Load($category_id);
 						$email_event = $new_status == STATUS_ACTIVE ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY';
 						$this->Application->emailUser($email_event, $object->GetDBField('CreatedById'));
 					}
 				}
 			}
 
 			// change opener stack in case if edited category filename was changed
 			$filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ());
 
 			if ( $filename_changes ) {
 				$opener_stack = $this->Application->makeClass('kOpenerStack');
 				/* @var $opener_stack kOpenerStack */
 
 				list ($template, $params, $index_file) = $opener_stack->pop();
 
 				foreach ($filename_changes as $change_info) {
 					$template = str_ireplace($change_info['from'], $change_info['to'], $template);
 				}
 
 				$opener_stack->push($template, $params, $index_file);
 				$opener_stack->save();
 			}
 		}
 
 		/**
 		 * Returns statuses of given categories
 		 *
 		 * @param Array $category_ids
 		 * @return Array
 		 */
 		function _getCategoryStatus($category_ids)
 		{
 			$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 			$sql = 'SELECT Status, ' . $id_field . '
 					FROM ' . $table_name . '
 					WHERE ' . $id_field . ' IN (' . implode(',', $category_ids) . ')';
 			return $this->Conn->GetCol($sql, $id_field);
 		}
 
 		/**
 		 * Creates a new item in temp table and
 		 * stores item id in App vars and Session on success
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveCreated(kEvent $event)
 		{
 			$object = $event->getObject( Array ('skip_autoload' => true) );
 			/* @var $object CategoriesItem */
 
 			if ( $object->IsRoot() ) {
 				// don't create root category while saving permissions
 				return;
 			}
 
 			parent::OnPreSaveCreated($event);
 		}
 
 		/**
 		 * Deletes sym link to other category
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemDelete(kEvent $event)
 		{
 			parent::OnAfterItemDelete($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$sql = 'UPDATE ' . $object->TableName . '
 					SET SymLinkCategoryId = NULL
 					WHERE SymLinkCategoryId = ' . $object->GetID();
 			$this->Conn->Query($sql);
 
 			// delete direct subscriptions to category, that was deleted
 			$sql = 'SELECT SubscriptionId
 					FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions
 					WHERE CategoryId = ' . $object->GetID();
 			$ids = $this->Conn->GetCol($sql);
 
 			if ( $ids ) {
 				$temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler');
 				/* @var $temp_handler kTempTablesHandler */
 
 				$temp_handler->DeleteItems('system-event-subscription', '', $ids);
 			}
 		}
 
 		/**
 		 * Exclude root categories from deleting
 		 *
 		 * @param kEvent $event
 		 * @param string $type
 		 * @return void
 		 * @access protected
 		 */
 		protected function customProcessing(kEvent $event, $type)
 		{
 			if ( $event->Name == 'OnMassDelete' && $type == 'before' ) {
 				$ids = $event->getEventParam('ids');
 				if ( !$ids || $this->Application->ConfigValue('AllowDeleteRootCats') ) {
 					return;
 				}
 
 				$root_categories = Array ();
 
 				// get module root categories and exclude them
 				foreach ($this->Application->ModuleInfo as $module_info) {
 					$root_categories[] = $module_info['RootCat'];
 				}
 
 				$root_categories = array_unique($root_categories);
 
 				if ( $root_categories && array_intersect($ids, $root_categories) ) {
 					$event->setEventParam('ids', array_diff($ids, $root_categories));
 					$this->Application->StoreVar('root_delete_error', 1);
 				}
 			}
 		}
 
 		/**
 		 * Checks, that given template exists (physically) in given theme
 		 *
 		 * @param string $template
 		 * @param int $theme_id
 		 * @return bool
 		 */
 		function _templateFound($template, $theme_id = null)
 		{
 			static $init_made = false;
 
 			if (!$init_made) {
 				$this->Application->InitParser(true);
 				$init_made = true;
 			}
 
 			if (!isset($theme_id)) {
 				$theme_id = $this->_getCurrentThemeId();
 			}
 
 			$theme_name = $this->_getThemeName($theme_id);
 
 			return $this->Application->TemplatesCache->TemplateExists('theme:' . $theme_name . '/' . $template);
 		}
 
 		/**
 		 * Removes ".tpl" in template path
 		 *
 		 * @param string $template
 		 * @return string
 		 */
 		function _stripTemplateExtension($template)
 		{
 	//		return preg_replace('/\.[^.\\\\\\/]*$/', '', $template);
 
 			return preg_replace('/^[\\/]{0,1}(.*)\.tpl$/', "$1", $template);
 		}
 
 		/**
 		 * Deletes all selected items.
 		 * Automatically recourse into sub-items using temp handler, and deletes sub-items
 		 * by calling its Delete method if sub-item has AutoDelete set to true in its config file
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnMassDelete(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$to_delete = Array ();
 			$ids = $this->StoreSelectedIDs($event);
 			$recycle_bin = $this->Application->ConfigValue('RecycleBinFolder');
 
 			if ( $recycle_bin ) {
 				$rb = $this->Application->recallObject('c.recycle', null, Array ('skip_autoload' => true));
 				/* @var $rb CategoriesItem */
 
 				$rb->Load($recycle_bin);
 
 				$cat = $event->getObject(Array ('skip_autoload' => true));
 				/* @var $cat CategoriesItem */
 
 				foreach ($ids as $id) {
 					$cat->Load($id);
 
 					if ( preg_match('/^' . preg_quote($rb->GetDBField('ParentPath'), '/') . '/', $cat->GetDBField('ParentPath')) ) {
 						// already in "Recycle Bin" -> delete for real
 						$to_delete[] = $id;
 						continue;
 					}
 
 					// just move into "Recycle Bin" category
 					$cat->SetDBField('ParentId', $recycle_bin);
 					$cat->Update();
 				}
 
 				$ids = $to_delete;
 			}
 
 			$event->setEventParam('ids', $ids);
 			$this->customProcessing($event, 'before');
 			$ids = $event->getEventParam('ids');
 
 			if ( $ids ) {
 				$recursive_helper = $this->Application->recallObject('RecursiveHelper');
 				/* @var $recursive_helper kRecursiveHelper */
 
 				foreach ($ids as $id) {
 					$recursive_helper->DeleteCategory($id, $event->Prefix);
 				}
 			}
 
 			$this->clearSelectedIDs($event);
 
 			$this->_ensurePermCacheRebuild($event);
 		}
 
 		/**
 		 * Add selected items to clipboard with mode = COPY (CLONE)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCopy($event)
 		{
 			$this->Application->RemoveVar('clipboard');
 
 			$clipboard_helper = $this->Application->recallObject('ClipboardHelper');
 			/* @var $clipboard_helper kClipboardHelper */
 
 			$clipboard_helper->setClipboard($event, 'copy', $this->StoreSelectedIDs($event));
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Add selected items to clipboard with mode = CUT
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCut($event)
 		{
 			$this->Application->RemoveVar('clipboard');
 
 			$clipboard_helper = $this->Application->recallObject('ClipboardHelper');
 			/* @var $clipboard_helper kClipboardHelper */
 
 			$clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event));
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Controls all item paste operations. Can occur only with filled clipboard.
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPasteClipboard($event)
 		{
 			$clipboard = unserialize( $this->Application->RecallVar('clipboard') );
 			foreach ($clipboard as $prefix => $clipboard_data) {
 				$paste_event = new kEvent($prefix.':OnPaste', Array('clipboard_data' => $clipboard_data));
 				$this->Application->HandleEvent($paste_event);
 
 				$event->copyFrom($paste_event);
 			}
 		}
 
 		/**
 		 * Checks permission for OnPaste event
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function _checkPastePermission($event)
 		{
 			$perm_helper = $this->Application->recallObject('PermissionsHelper');
 			/* @var $perm_helper kPermissionsHelper */
 
 			$category_id = $this->Application->GetVar('m_cat_id');
 			if ($perm_helper->AddCheckPermission($category_id, $event->Prefix) == 0) {
 				// no items left for editing -> no permission
 				return $perm_helper->finalizePermissionCheck($event, false);
 			}
 
 			return true;
 		}
 
 		/**
 		 * Paste categories with sub-items from clipboard
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPaste($event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) || !$this->_checkPastePermission($event) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$clipboard_data = $event->getEventParam('clipboard_data');
 
 			if ( !$clipboard_data['cut'] && !$clipboard_data['copy'] ) {
 				return;
 			}
 
 			// 1. get ParentId of moved category(-es) before it gets updated!!!)
 			$source_category_id = 0;
 			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
 
 			if ( $clipboard_data['cut'] ) {
 				$sql = 'SELECT ParentId
 						FROM ' . $table_name . '
 						WHERE ' . $id_field . ' = ' . $clipboard_data['cut'][0];
 				$source_category_id = $this->Conn->GetOne($sql);
 			}
 
 			$recursive_helper = $this->Application->recallObject('RecursiveHelper');
 			/* @var $recursive_helper kRecursiveHelper */
 
 			if ( $clipboard_data['cut'] ) {
 				$recursive_helper->MoveCategories($clipboard_data['cut'], $this->Application->GetVar('m_cat_id'));
 			}
 
 			if ( $clipboard_data['copy'] ) {
 				// don't allow to copy/paste system OR theme-linked virtual pages
 
 				$sql = 'SELECT ' . $id_field . '
 						FROM ' . $table_name . '
 						WHERE ' . $id_field . ' IN (' . implode(',', $clipboard_data['copy']) . ') AND (`Type` = ' . PAGE_TYPE_VIRTUAL . ') AND (ThemeId = 0)';
 				$allowed_ids = $this->Conn->GetCol($sql);
 
 				if ( !$allowed_ids ) {
 					return;
 				}
 
 				foreach ($allowed_ids as $id) {
 					$recursive_helper->PasteCategory($id, $event->Prefix);
 				}
 			}
 
 			$priority_helper = $this->Application->recallObject('PriorityHelper');
 			/* @var $priority_helper kPriorityHelper */
 
 			if ( $clipboard_data['cut'] ) {
 				$ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $source_category_id);
 
 				if ( $ids ) {
 					$priority_helper->massUpdateChanged($event->Prefix, $ids);
 				}
 			}
 
 			// recalculate priorities of newly pasted categories in destination category
 			$parent_id = $this->Application->GetVar('m_cat_id');
 			$ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id);
 
 			if ( $ids ) {
 				$priority_helper->massUpdateChanged($event->Prefix, $ids);
 			}
 
 			if ( $clipboard_data['cut'] || $clipboard_data['copy'] ) {
 				$this->_ensurePermCacheRebuild($event);
 			}
 		}
 
 		/**
 		 * Ensures, that category permission cache is rebuild when category is added/edited/deleted
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function _ensurePermCacheRebuild(kEvent $event)
 		{
 			$this->Application->StoreVar('PermCache_UpdateRequired', 1);
 			$this->Application->StoreVar('RefreshStructureTree', 1);
 		}
 
 		/**
 		 * Occurs when pasting category
 		 *
 		 * @param kEvent $event
 		 */
 		/*function OnCatPaste($event)
 		{
 			$inp_clipboard = $this->Application->RecallVar('ClipBoard');
 			$inp_clipboard = explode('-', $inp_clipboard, 2);
 
 			if($inp_clipboard[0] == 'COPY')
 			{
 				$saved_cat_id = $this->Application->GetVar('m_cat_id');
 				$cat_ids = $event->getEventParam('cat_ids');
 
 				$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 				$table = $this->Application->getUnitOption($event->Prefix, 'TableName');
 				$ids_sql = 'SELECT '.$id_field.' FROM '.$table.' WHERE ResourceId IN (%s)';
 				$resource_ids_sql = 'SELECT ItemResourceId FROM '.TABLE_PREFIX.'CategoryItems WHERE CategoryId = %s AND PrimaryCat = 1';
 
 				$object = $this->Application->recallObject($event->Prefix.'.item', $event->Prefix, Array('skip_autoload' => true));
 
 				foreach($cat_ids as $source_cat => $dest_cat)
 				{
 					$item_resource_ids = $this->Conn->GetCol( sprintf($resource_ids_sql, $source_cat) );
 					if(!$item_resource_ids) continue;
 
 					$this->Application->SetVar('m_cat_id', $dest_cat);
 					$item_ids = $this->Conn->GetCol( sprintf($ids_sql, implode(',', $item_resource_ids) ) );
 
 					$temp = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 					if($item_ids) $temp->CloneItems($event->Prefix, $event->Special, $item_ids);
 				}
 
 				$this->Application->SetVar('m_cat_id', $saved_cat_id);
 			}
 		}*/
 
 		/**
 		 * Clears clipboard content
 		 *
 		 * @param kEvent $event
 		 */
 		function OnClearClipboard($event)
 		{
 			$this->Application->RemoveVar('clipboard');
 		}
 
 		/**
 		 * Sets correct status for new categories created on front-end
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemCreate(kEvent $event)
 		{
 			parent::OnBeforeItemCreate($event);
 
 			$object = $event->getObject();
 			/* @var $object CategoriesItem */
 
 			if ( $object->GetDBField('ParentId') <= 0 ) {
 				// no parent category - use current (happens during import)
 				$object->SetDBField('ParentId', $this->Application->GetVar('m_cat_id'));
 			}
 
 			$this->_beforeItemChange($event);
 
 			if ( $this->Application->isAdmin || $event->Prefix == 'st' ) {
 				// don't check category permissions when auto-creating structure pages
 				return ;
 			}
 
 			$perm_helper = $this->Application->recallObject('PermissionsHelper');
 			/* @var $perm_helper kPermissionsHelper */
 
 			$new_status = false;
 			$category_id = $this->Application->GetVar('m_cat_id');
 
 			if ( $perm_helper->CheckPermission('CATEGORY.ADD', 0, $category_id) ) {
 				$new_status = STATUS_ACTIVE;
 			}
 			else {
 				if ( $perm_helper->CheckPermission('CATEGORY.ADD.PENDING', 0, $category_id) ) {
 					$new_status = STATUS_PENDING;
 				}
 			}
 
 			if ( $new_status ) {
 				$object->SetDBField('Status', $new_status);
 
 				// don't forget to set Priority for suggested from Front-End categories
 				$min_priority = $this->_getNextPriority($object->GetDBField('ParentId'), $object->TableName);
 				$object->SetDBField('Priority', $min_priority);
 			}
 			else {
 				$event->status = kEvent::erPERM_FAIL;
 				return ;
 			}
 		}
 
 		/**
 		 * Returns next available priority for given category from given table
 		 *
 		 * @param int $category_id
 		 * @param string $table_name
 		 * @return int
 		 */
 		function _getNextPriority($category_id, $table_name)
 		{
 			$sql = 'SELECT MIN(Priority)
 					FROM ' . $table_name . '
 					WHERE ParentId = ' . $category_id;
 			return (int)$this->Conn->GetOne($sql) - 1;
 		}
 
 		/**
 		 * Sets correct status for new categories created on front-end
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(kEvent $event)
 		{
 			parent::OnBeforeItemUpdate($event);
 
 			$this->_beforeItemChange($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( $object->GetChangedFields() ) {
 				$object->SetDBField('ModifiedById', $this->Application->RecallVar('user_id'));
 			}
 		}
 
 		/**
 		 * Creates needed sql query to load item,
 		 * if no query is defined in config for
 		 * special requested, then use list query
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function ItemPrepareQuery(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$sqls = $object->getFormOption('ItemSQLs', Array ());
 			$category_special = $this->_getCategorySpecial($event);
 			$special = isset($sqls[$category_special]) ? $category_special : '';
 
 			// preferred special not found in ItemSQLs -> use analog from ListSQLs
 
 			return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event);
 		}
 
 		/**
 		 * Creates needed sql query to load list,
 		 * if no query is defined in config for
 		 * special requested, then use default
 		 * query
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function ListPrepareQuery(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$special = $this->_getCategorySpecial($event);
 			$sqls = $object->getFormOption('ListSQLs', Array ());
 
 			return $sqls[array_key_exists($special, $sqls) ? $special : ''];
 		}
 
 		/**
 		 * Performs redirect to correct suggest confirmation template
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnCreate(kEvent $event)
 		{
 			parent::OnCreate($event);
 
 			if ( $this->Application->isAdmin || $event->status != kEvent::erSUCCESS ) {
 				// don't sent email or rebuild cache directly after category is created by admin
 				return;
 			}
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $object->GetDBField('ParentPath')));
 			/* @var $cache_updater kPermCacheUpdater */
 
 			$cache_updater->OneStepRun();
 
 			$is_active = ($object->GetDBField('Status') == STATUS_ACTIVE);
 
 			$next_template = $is_active ? 'suggest_confirm_template' : 'suggest_pending_confirm_template';
 			$event->redirect = $this->Application->GetVar($next_template);
 			$event->SetRedirectParam('opener', 's');
 
 			// send email events
 			$perm_prefix = $this->Application->getUnitOption($event->Prefix, 'PermItemPrefix');
 
 			$event_suffix = $is_active ? 'ADD' : 'ADD.PENDING';
 			$this->Application->emailAdmin($perm_prefix . '.' . $event_suffix);
 			$this->Application->emailUser($perm_prefix . '.' . $event_suffix, $object->GetDBField('CreatedById'));
 		}
 
 		/**
 		 * Returns current per-page setting for list
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 * @access protected
 		 */
 		protected function getPerPage(kEvent $event)
 		{
 			if ( !$this->Application->isAdmin ) {
 				$same_special = $event->getEventParam('same_special');
 				$event->setEventParam('same_special', true);
 
 				$per_page = parent::getPerPage($event);
 
 				$event->setEventParam('same_special', $same_special);
 			}
 
 			return parent::getPerPage($event);
 		}
 
 		/**
 		 * Set's correct page for list based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetPagination(kEvent $event)
 		{
 			parent::SetPagination($event);
 
 			if ( !$this->Application->isAdmin ) {
 				$page_var = $event->getEventParam('page_var');
 
 				if ( $page_var !== false ) {
 					$page = $this->Application->GetVar($page_var);
 
 					if ( is_numeric($page) ) {
 						$object = $event->getObject();
 						/* @var $object kDBList */
 
 						$object->SetPage($page);
 					}
 				}
 			}
 		}
 
 		/**
 		 * Apply same processing to each item being selected in grid
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function iterateItems(kEvent $event)
 		{
 			if ( $event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline' ) {
 				parent::iterateItems($event);
 			}
 
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object CategoriesItem */
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			if ( $ids ) {
 				$status_field = $object->getStatusField();
 				$propagate_category_status = $this->Application->GetVar('propagate_category_status');
 
 				foreach ($ids as $id) {
 					$object->Load($id);
 					$object->SetDBField($status_field, $event->Name == 'OnMassApprove' ? 1 : 0);
 
 					if ( $object->Update() ) {
 						if ( $propagate_category_status ) {
 							$sql = 'UPDATE ' . $object->TableName . '
 									SET ' . $status_field . ' = ' . $object->GetDBField($status_field) . '
 									WHERE TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight');
 							$this->Conn->Query($sql);
 						}
 
 						$event->status = kEvent::erSUCCESS;
 
 						$email_event = $event->Name == 'OnMassApprove' ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY';
 						$this->Application->emailUser($email_event, $object->GetDBField('CreatedById'));
 					}
 					else {
 						$event->status = kEvent::erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 
 			$this->clearSelectedIDs($event);
 			$this->Application->StoreVar('RefreshStructureTree', 1);
 		}
 
 		/**
 		 * Checks, that currently loaded item is allowed for viewing (non permission-based)
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function checkItemStatus(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( !$object->isLoaded() ) {
 				return true;
 			}
 
 			if ( $object->GetDBField('Status') != STATUS_ACTIVE && $object->GetDBField('Status') != 4 ) {
 				if ( !$object->GetDBField('DirectLinkEnabled') || !$object->GetDBField('DirectLinkAuthKey') ) {
 					return false;
 				}
 
 				return $this->Application->GetVar('authkey') == $object->GetDBField('DirectLinkAuthKey');
 			}
 
 			return true;
 		}
 
 		/**
 		 * Set's correct sorting for list based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetSorting(kEvent $event)
 		{
 			$types = $event->getEventParam('types');
 			$types = $types ? explode(',', $types) : Array ();
 
 			if ( in_array('search', $types) ) {
 				$event->setPseudoClass('_List');
 
 				$object = $event->getObject();
 				/* @var $object kDBList */
 
 				// 1. no user sorting - sort by relevance
 				$default_sortings = parent::_getDefaultSorting($event);
 				$default_sorting = key($default_sortings['Sorting']) . ',' . current($default_sortings['Sorting']);
 
 				if ( $object->isMainList() ) {
 					$sort_by = $this->Application->GetVar('sort_by', '');
 
 					if ( !$sort_by ) {
 						$this->Application->SetVar('sort_by', 'Relevance,desc|' . $default_sorting);
 					}
 					elseif ( strpos($sort_by, 'Relevance,') !== false ) {
 						$this->Application->SetVar('sort_by', $sort_by . '|' . $default_sorting);
 					}
 				}
 				else {
 					$sorting_settings = $this->getListSetting($event, 'Sortings');
 					$sort_by = trim(getArrayValue($sorting_settings, 'Sort1') . ',' . getArrayValue($sorting_settings, 'Sort1_Dir'), ',');
 
 					if ( !$sort_by ) {
 						$event->setEventParam('sort_by', 'Relevance,desc|' . $default_sorting);
 					}
 					elseif ( strpos($sort_by, 'Relevance,') !== false ) {
 						$event->setEventParam('sort_by', $sort_by . '|' . $default_sorting);
 					}
 				}
 
 				$this->_removeForcedSortings($event);
 			}
 
 			parent::SetSorting($event);
 		}
 
 		/**
 		 * Removes forced sortings
 		 *
 		 * @param kEvent $event
 		 */
 		protected function _removeForcedSortings(kEvent $event)
 		{
 			$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
 			/* @var $list_sortings Array */
 
 			foreach ($list_sortings as $special => $sortings) {
 				unset($list_sortings[$special]['ForcedSorting']);
 			}
 
 			$this->Application->setUnitOption($event->Prefix, 'ListSortings', $list_sortings);
 		}
 
 		/**
 		 * Default sorting in search results only comes from relevance field
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 * @access protected
 		 */
 		protected function _getDefaultSorting(kEvent $event)
 		{
 			$types = $event->getEventParam('types');
 			$types = $types ? explode(',', $types) : Array ();
 
 			return in_array('search', $types) ? Array () : parent::_getDefaultSorting($event);
 		}
 
 		// ============= for cms page processing =======================
 
 		/**
 		 * Returns default design template
 		 *
 		 * @return string
 		 */
 		function _getDefaultDesign()
 		{
 			$default_design = trim($this->Application->ConfigValue('cms_DefaultDesign'), '/');
 
 			if (!$default_design) {
 				// theme-based alias for default design
 				return '#default_design#';
 			}
 
 			if (strpos($default_design, '#') === false) {
 				// real template, not alias, so prefix with "/"
 				return '/' . $default_design;
 			}
 
 			// alias
 			return $default_design;
 		}
 
 		/**
 		 * Returns default design based on given virtual template (used from kApplication::Run)
 		 *
 		 * @param string $t
 		 * @return string
 		 * @access public
 		 */
 		public function GetDesignTemplate($t = null)
 		{
 			if ( !isset($t) ) {
 				$t = $this->Application->GetVar('t');
 			}
 
 			$page = $this->Application->recallObject($this->Prefix . '.-virtual', null, Array ('page' => $t));
 			/* @var $page CategoriesItem */
 
 			if ( $page->isLoaded() ) {
 				$real_t = $page->GetDBField('CachedTemplate');
 				$this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId'));
 
 				if ( $page->GetDBField('FormId') ) {
 					$this->Application->SetVar('form_id', $page->GetDBField('FormId'));
 				}
 			}
 			else {
 				$not_found = $this->Application->ConfigValue('ErrorTemplate');
 				$real_t = $not_found ? $not_found : 'error_notfound';
 
 				$themes_helper = $this->Application->recallObject('ThemesHelper');
 				/* @var $themes_helper kThemesHelper */
 
 				$theme_id = $this->Application->GetVar('m_theme');
 				$category_id = $themes_helper->getPageByTemplate($real_t, $theme_id);
 				$this->Application->SetVar('m_cat_id', $category_id);
 
 				header('HTTP/1.0 404 Not Found');
 			}
 
 			// replace alias in form #alias_name# to actual template used in this theme
 			if ( $this->Application->isAdmin ) {
 				$themes_helper = $this->Application->recallObject('ThemesHelper');
 				/* @var $themes_helper kThemesHelper */
 
 				// only, used when in "Design Mode"
 				$this->Application->SetVar('theme.current_id', $themes_helper->getCurrentThemeId());
 			}
 
 			$theme = $this->Application->recallObject('theme.current');
 			/* @var $theme kDBItem */
 
 			$template = $theme->GetField('TemplateAliases', $real_t);
 
 			if ( $template ) {
 				return $template;
 			}
 
 			return $real_t;
 		}
 
 		/**
 		 * Sets category id based on found template (used from kApplication::Run)
 		 *
 		 * @deprecated
 		 */
 		/*function SetCatByTemplate()
 		{
 			$t = $this->Application->GetVar('t');
 			$page = $this->Application->recallObject($this->Prefix . '.-virtual');
 
 			if ( $page->isLoaded() ) {
 				$this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId'));
 			}
 		}*/
 
 		/**
 		 * Prepares template paths
 		 *
 		 * @param kEvent $event
 		 */
 		function _beforeItemChange($event)
 		{
 			$object = $event->getObject();
 			/* @var $object CategoriesItem */
 
 			$object->checkFilename();
 			$object->generateFilename();
 
 			$now = adodb_mktime();
 
 			if ( !$this->Application->isDebugMode() && strpos($event->Special, 'rebuild') === false ) {
 				$object->SetDBField('Type', $object->GetOriginalField('Type'));
 				$object->SetDBField('Protected', $object->GetOriginalField('Protected'));
 
 				if ( $object->GetDBField('Protected') ) {
 					// some fields are read-only for protected pages, when debug mode is off
 					$object->SetDBField('AutomaticFilename', $object->GetOriginalField('AutomaticFilename'));
 					$object->SetDBField('Filename', $object->GetOriginalField('Filename'));
 					$object->SetDBField('Status', $object->GetOriginalField('Status'));
 				}
 			}
 
 			$is_admin = $this->Application->isAdminUser;
 
 			if ( (!$object->IsTempTable() && !$is_admin) || ($is_admin && !$object->GetDBField('CreatedById')) ) {
 				$object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
 			}
 
 			if ($object->GetChangedFields()) {
 				$object->SetDBField('Modified_date', $now);
 				$object->SetDBField('Modified_time', $now);
 			}
 
 			$object->setRequired('PageCacheKey', $object->GetDBField('OverridePageCacheKey'));
 			$object->SetDBField('Template', $this->_stripTemplateExtension( $object->GetDBField('Template') ));
 
 			if ($object->GetDBField('Type') == PAGE_TYPE_TEMPLATE) {
 				if (!$this->_templateFound($object->GetDBField('Template'), $object->GetDBField('ThemeId'))) {
 					$object->SetError('Template', 'template_file_missing', 'la_error_TemplateFileMissing');
 				}
 			}
 
 			$this->_saveTitleField($object, 'Title');
 			$this->_saveTitleField($object, 'MenuTitle');
 
 			$root_category = $this->Application->getBaseCategory();
 
 			if ( file_exists(FULL_PATH . '/themes') && ($object->GetDBField('ParentId') == $root_category) && ($object->GetDBField('Template') == CATEGORY_TEMPLATE_INHERIT) ) {
 				// there are themes + creating top level category
 				$object->SetError('Template', 'no_inherit');
 			}
 
 			if ( !$this->Application->isAdminUser && $object->isVirtualField('cust_RssSource') ) {
 				// only administrator can set/change "cust_RssSource" field
 
 				if ($object->GetDBField('cust_RssSource') != $object->GetOriginalField('cust_RssSource')) {
 					$object->SetError('cust_RssSource', 'not_allowed', 'la_error_OperationNotAllowed');
 				}
 			}
 
 			if ( !$object->GetDBField('DirectLinkAuthKey') ) {
 				$key_parts = Array (
 					$object->GetID(),
 					$object->GetDBField('ParentId'),
 					$object->GetField('Name'),
 					'b38'
 				);
 
 				$object->SetDBField('DirectLinkAuthKey', substr( md5( implode(':', $key_parts) ), 0, 20 ));
 			}
 		}
 
 		/**
 		 * Sets page name to requested field in case when:
 		 * 1. page was auto created (through theme file rebuild)
 		 * 2. requested field is empty
 		 *
 		 * @param kDBItem $object
 		 * @param string $field
 		 * @author Alex
 		 */
 		function _saveTitleField(&$object, $field)
 		{
 			$value = $object->GetField($field, 'no_default'); // current value of target field
 
 			$ml_formatter = $this->Application->recallObject('kMultiLanguage');
 			/* @var $ml_formatter kMultiLanguage */
 
 			$src_field = $ml_formatter->LangFieldName('Name');
 			$dst_field = $ml_formatter->LangFieldName($field);
 
 			$dst_field_not_changed = $object->GetOriginalField($dst_field) == $value;
 
 			if ($value == '' || preg_match('/^_Auto: (.*)/', $value) || (($object->GetOriginalField($src_field) == $value) && $dst_field_not_changed)) {
 				// target field is empty OR target field value starts with "_Auto: " OR (source field value
 				// before change was equals to current target field value AND target field value wasn't changed)
 				$object->SetField($dst_field, $object->GetField($src_field));
 			}
 		}
 
 		/**
 		 * Don't allow to delete system pages, when not in debug mode
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemDelete(kEvent $event)
 		{
 			parent::OnBeforeItemDelete($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( $object->GetDBField('Protected') && !$this->Application->isDebugMode(false) ) {
 				$event->status = kEvent::erFAIL;
 			}
 		}
 
 		/**
 		 * Creates category based on given TPL file
 		 *
 		 * @param CategoriesItem $object
 		 * @param string $template
 		 * @param int $theme_id
 		 * @param int $system_mode
 		 * @param array $template_info
 		 * @return bool
 		 */
 		function _prepareAutoPage(&$object, $template, $theme_id = null, $system_mode = SMS_MODE_AUTO, $template_info = Array ())
 		{
 			$template = $this->_stripTemplateExtension($template);
 
 			if ($system_mode == SMS_MODE_AUTO) {
 				$page_type = $this->_templateFound($template, $theme_id) ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL;
 			}
 			else {
 				$page_type = $system_mode == SMS_MODE_FORCE ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL;
 			}
 
 			if (($page_type == PAGE_TYPE_TEMPLATE) && ($template_info === false)) {
 				// do not auto-create system pages, when browsing through site
 				return false;
 			}
 
 			if (!isset($theme_id)) {
 				$theme_id = $this->_getCurrentThemeId();
 			}
 
 			$root_category = $this->Application->getBaseCategory();
 			$page_category = $this->Application->GetVar('m_cat_id');
 			if (!$page_category) {
 				$page_category = $root_category;
 				$this->Application->SetVar('m_cat_id', $page_category);
 			}
 
 			if (($page_type == PAGE_TYPE_VIRTUAL) && (strpos($template, '/') !== false)) {
 				// virtual page, but have "/" in template path -> create it's path
 				$category_path = explode('/', $template);
 				$template = array_pop($category_path);
 
 				$page_category = $this->_getParentCategoryFromPath($category_path, $root_category, $theme_id);
 			}
 
 			$page_name = ($page_type == PAGE_TYPE_TEMPLATE) ? '_Auto: ' . $template : $template;
 			$page_description = '';
 
 			if ($page_type == PAGE_TYPE_TEMPLATE) {
 				$design_template = strtolower($template); // leading "/" not added !
 				if ($template_info) {
 					if (array_key_exists('name', $template_info) && $template_info['name']) {
 						$page_name = $template_info['name'];
 					}
 
 					if (array_key_exists('desc', $template_info) && $template_info['desc']) {
 						$page_description = $template_info['desc'];
 					}
 
 					if (array_key_exists('section', $template_info) && $template_info['section']) {
 						// this will override any global "m_cat_id"
 						$page_category = $this->_getParentCategoryFromPath(explode('||', $template_info['section']), $root_category, $theme_id);
 					}
 				}
 			}
 			else {
 				$design_template = $this->_getDefaultDesign(); // leading "/" added !
 			}
 
 			$object->Clear();
 			$object->SetDBField('ParentId', $page_category);
 			$object->SetDBField('Type', $page_type);
 			$object->SetDBField('Protected', 1); // $page_type == PAGE_TYPE_TEMPLATE
 
 			$object->SetDBField('IsMenu', 0);
 			$object->SetDBField('ThemeId', $theme_id);
 
 			// put all templates to then end of list (in their category)
 			$min_priority = $this->_getNextPriority($page_category, $object->TableName);
 			$object->SetDBField('Priority', $min_priority);
 
 			$object->SetDBField('Template', $design_template);
 			$object->SetDBField('CachedTemplate', $design_template);
 
 			$primary_language = $this->Application->GetDefaultLanguageId();
 			$current_language = $this->Application->GetVar('m_lang');
 			$object->SetDBField('l' . $primary_language . '_Name', $page_name);
 			$object->SetDBField('l' . $current_language . '_Name', $page_name);
 			$object->SetDBField('l' . $primary_language . '_Description', $page_description);
 			$object->SetDBField('l' . $current_language . '_Description', $page_description);
 
 			return $object->Create();
 		}
 
 		function _getParentCategoryFromPath($category_path, $base_category, $theme_id = null)
 		{
 			static $category_ids = Array ();
 
 			if (!$category_path) {
 				return $base_category;
 			}
 
 			if (array_key_exists(implode('||', $category_path), $category_ids)) {
 				return $category_ids[ implode('||', $category_path) ];
 			}
 
 			$backup_category_id = $this->Application->GetVar('m_cat_id');
 
 			$object = $this->Application->recallObject($this->Prefix . '.rebuild-path', null, Array ('skip_autoload' => true));
 			/* @var $object CategoriesItem */
 
 			$parent_id = $base_category;
 
 			$filenames_helper = $this->Application->recallObject('FilenamesHelper');
 			/* @var $filenames_helper kFilenamesHelper */
 
 			$safe_category_path = array_map(Array (&$filenames_helper, 'replaceSequences'), $category_path);
 
 			foreach ($category_path as $category_order => $category_name) {
 				$this->Application->SetVar('m_cat_id', $parent_id);
 
 				// get virtual category first, when possible
 				$sql = 'SELECT ' . $object->IDField . '
 						FROM ' . $object->TableName . '
 						WHERE
 							(
 								Filename = ' . $this->Conn->qstr($safe_category_path[$category_order]) . ' OR
 								Filename = ' . $this->Conn->qstr( $filenames_helper->replaceSequences('_Auto: ' . $category_name) ) . '
 							) AND
 							(ParentId = ' . $parent_id . ') AND
 							(ThemeId = 0 OR ThemeId = ' . $theme_id . ')
 						ORDER BY ThemeId ASC';
 				$parent_id = $this->Conn->GetOne($sql);
 
 				if ($parent_id === false) {
 					// page not found
 					$template = implode('/', array_slice($safe_category_path, 0, $category_order + 1));
 
 					// don't process system templates in sub-categories
 					$system = $this->_templateFound($template, $theme_id) && (strpos($template, '/') === false);
 
 					if (!$this->_prepareAutoPage($object, $category_name, $theme_id, $system ? SMS_MODE_FORCE : false)) {
 						// page was not created
 						break;
 					}
 
 					$parent_id = $object->GetID();
 				}
 			}
 
 			$this->Application->SetVar('m_cat_id', $backup_category_id);
 			$category_ids[ implode('||', $category_path) ] = $parent_id;
 
 			return $parent_id;
 		}
 
 		/**
 		 * Returns theme name by it's id. Used in structure page creation.
 		 *
 		 * @param int $theme_id
 		 * @return string
 		 */
 		function _getThemeName($theme_id)
 		{
 			static $themes = null;
 
 			if (!isset($themes)) {
 				$id_field = $this->Application->getUnitOption('theme', 'IDField');
 				$table_name = $this->Application->getUnitOption('theme', 'TableName');
 
 				$sql = 'SELECT Name, ' . $id_field . '
 						FROM ' . $table_name . '
 						WHERE Enabled = 1';
 				$themes = $this->Conn->GetCol($sql, $id_field);
 			}
 
 			return array_key_exists($theme_id, $themes) ? $themes[$theme_id] : false;
 		}
 
 		/**
 		 * Resets SMS-menu cache
 		 *
 		 * @param kEvent $event
 		 */
 		function OnResetCMSMenuCache($event)
 		{
 			if ($this->Application->GetVar('ajax') == 'yes') {
 				$event->status = kEvent::erSTOP;
 			}
 
 			$this->_resetMenuCache();
 			$event->SetRedirectParam('action_completed', 1);
 		}
 
 		/**
 		 * Performs reset of category-related caches (menu, structure dropdown, template mapping)
 		 *
 		 * @return void
 		 * @access protected
 		 */
 		protected function _resetMenuCache()
 		{
 			// reset cms menu cache (all variables are automatically rebuild, when missing)
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$this->Application->rebuildCache('master:cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime);
 				$this->Application->rebuildCache('master:StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime);
 				$this->Application->rebuildCache('master:template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime);
 			}
 			else {
 				$this->Application->rebuildDBCache('cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime);
 				$this->Application->rebuildDBCache('StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime);
 				$this->Application->rebuildDBCache('template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime);
 			}
 		}
 
 		/**
 		 * Updates structure config
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterConfigRead(kEvent $event)
 		{
 			parent::OnAfterConfigRead($event);
 
 			if (defined('IS_INSTALL') && IS_INSTALL) {
 				// skip any processing, because Categories table doesn't exists until install is finished
 				$this->addViewPermissionJoin($event);
 
 				return ;
 			}
 
 			$site_config_helper = $this->Application->recallObject('SiteConfigHelper');
 			/* @var $site_config_helper SiteConfigHelper */
 
 			$settings = $site_config_helper->getSettings();
 
 			$root_category = $this->Application->getBaseCategory();
 
 			// set root category
 			$section_adjustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments');
 
 			$section_adjustments['in-portal:browse'] = Array (
 				'url' => Array ('m_cat_id' => $root_category),
 				'late_load' => Array ('m_cat_id' => $root_category),
-				'onclick' => 'checkCatalog(' . $root_category . ')',
+				'onclick' => 'checkCatalog(' . $root_category . ', "c")',
 			);
 
+			if ( $this->Application->ConfigValue('Catalog_PreselectModuleTab') ) {
+				$section_adjustments['in-portal:browse']['url']['anchor'] = 'tab-c';
+			}
+
 			$section_adjustments['in-portal:browse_site'] = Array (
 				'url' => Array ('editing_mode' => $settings['default_editing_mode']),
 			);
 
 			$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_adjustments);
 
 			// prepare structure dropdown
 			$category_helper = $this->Application->recallObject('CategoryHelper');
 			/* @var $category_helper CategoryHelper */
 
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 
 			$fields['ParentId']['default'] = (int)$this->Application->GetVar('m_cat_id');
 			$fields['ParentId']['options'] = $category_helper->getStructureTreeAsOptions();
 
 			// limit design list by theme
 			$theme_id = $this->_getCurrentThemeId();
 			$design_sql = $fields['Template']['options_sql'];
 			$design_sql = str_replace('(tf.FilePath = "/designs")', '(' . implode(' OR ', $this->getDesignFolders()) . ')' . ' AND (t.ThemeId = ' . $theme_id . ')', $design_sql);
 			$fields['Template']['options_sql'] = $design_sql;
 
 			// adds "Inherit From Parent" option to "Template" field
 			$fields['Template']['options'] = Array (CATEGORY_TEMPLATE_INHERIT => $this->Application->Phrase('la_opt_InheritFromParent'));
 
 			$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
 
 			if ($this->Application->isAdmin) {
 				// don't sort by Front-End sorting fields
 				$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 				$remove_keys = Array ('DefaultSorting1Field', 'DefaultSorting2Field', 'DefaultSorting1Dir', 'DefaultSorting2Dir');
 				foreach ($remove_keys as $remove_key) {
 					unset($config_mapping[$remove_key]);
 				}
 				$this->Application->setUnitOption($event->Prefix, 'ConfigMapping', $config_mapping);
 			}
 			else {
 				// sort by parent path on Front-End only
 				$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
 				$list_sortings['']['ForcedSorting'] = Array ("CurrentSort" => 'asc');
 				$this->Application->setUnitOption($event->Prefix, 'ListSortings', $list_sortings);
 			}
 
 			$this->addViewPermissionJoin($event);
 
 			// add grids for advanced view (with primary category column)
 			$grids = $this->Application->getUnitOption($this->Prefix, 'Grids');
 			$process_grids = Array ('Default', 'Radio');
 			foreach ($process_grids as $process_grid) {
 				$grid_data = $grids[$process_grid];
 				$grid_data['Fields']['CachedNavbar'] = Array ('title' => 'la_col_Path', 'data_block' => 'grid_parent_category_td', 'filter_block' => 'grid_like_filter');
 				$grids[$process_grid . 'ShowAll'] = $grid_data;
 			}
 			$this->Application->setUnitOption($this->Prefix, 'Grids', $grids);
 		}
 
 		/**
 		 * Adds permission table table JOIN clause only, when advanced catalog view permissions enabled.
 		 *
 		 * @param kEvent $event Event.
 		 *
 		 * @return self
 		 * @access protected
 		 */
 		protected function addViewPermissionJoin(kEvent $event)
 		{
 			if ( $this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) {
 				$join_clause = 'LEFT JOIN ' . TABLE_PREFIX . 'CategoryPermissionsCache perm ON perm.CategoryId = %1$s.CategoryId';
 			}
 			else {
 				$join_clause = '';
 			}
 
 			$list_sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs');
 			/* @var $list_sqls array */
 
 			foreach ($list_sqls as $special => $list_sql) {
 				$list_sqls[$special] = str_replace('{PERM_JOIN}', $join_clause, $list_sql);
 			}
 
 			$this->Application->setUnitOption($event->Prefix, 'ListSQLs', $list_sqls);
 
 			return $this;
 		}
 
 		/**
 		 * Returns folders, that can contain design templates
 		 *
 		 * @return array
 		 * @access protected
 		 */
 		protected function getDesignFolders()
 		{
 			$ret = Array ('tf.FilePath = "/designs"', 'tf.FilePath = "/platform/designs"');
 
 			foreach ($this->Application->ModuleInfo as $module_info) {
 				$ret[] = 'tf.FilePath = "/' . $module_info['TemplatePath'] . 'designs"';
 			}
 
 			return array_unique($ret);
 		}
 
 		/**
 		 * Removes this item and it's children (recursive) from structure dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemLoad(kEvent $event)
 		{
 			parent::OnAfterItemLoad($event);
 
 			if ( !$this->Application->isAdmin ) {
 				// calculate priorities dropdown only for admin
 				return;
 			}
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// remove this category & it's children from dropdown
 			$sql = 'SELECT ' . $object->IDField . '
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%"';
 			$remove_categories = $this->Conn->GetCol($sql);
 
 			$options = $object->GetFieldOption('ParentId', 'options');
 			foreach ($remove_categories as $remove_category) {
 				unset($options[$remove_category]);
 			}
 			$object->SetFieldOption('ParentId', 'options', $options);
 		}
 
 		/**
 		 * Occurs after creating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemCreate(kEvent $event)
 		{
 			parent::OnAfterItemCreate($event);
 
 			$object = $event->getObject();
 			/* @var $object CategoriesItem */
 
 			// need to update path after category is created, so category is included in that path
 			$fields_hash = $object->buildParentBasedFields();
 			$this->Conn->doUpdate($fields_hash, $object->TableName, $object->IDField . ' = ' . $object->GetID());
 			$object->SetDBFieldsFromHash($fields_hash);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterRebuildThemes($event)
 		{
 			$sql = 'SELECT t.ThemeId, CONCAT( tf.FilePath, \'/\', tf.FileName ) AS Path, tf.FileMetaInfo
 					FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf
 					LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId
 					WHERE t.Enabled = 1 AND tf.FileType = 1
 					AND (
 						SELECT COUNT(CategoryId)
 						FROM ' . TABLE_PREFIX . 'Categories c
 						WHERE CONCAT(\'/\', c.Template, \'.tpl\') = CONCAT( tf.FilePath, \'/\', tf.FileName ) AND (c.ThemeId = t.ThemeId)
 					) = 0 ';
 			$files = $this->Conn->Query($sql, 'Path');
 			if ( !$files ) {
 				// all possible pages are already created
 				return;
 			}
 
 			kUtil::setResourceLimit();
 
 			$dummy = $this->Application->recallObject($event->Prefix . '.rebuild', NULL, Array ('skip_autoload' => true));
 			/* @var $dummy CategoriesItem */
 
 			$error_count = 0;
 			foreach ($files as $a_file => $file_info) {
 				$status = $this->_prepareAutoPage($dummy, $a_file, $file_info['ThemeId'], SMS_MODE_FORCE, unserialize($file_info['FileMetaInfo'])); // create system page
 				if ( !$status ) {
 					$error_count++;
 				}
 			}
 
 			if ( $this->Application->ConfigValue('CategoryPermissionRebuildMode') == CategoryPermissionRebuild::SILENT ) {
 				$updater = $this->Application->makeClass('kPermCacheUpdater');
 				/* @var $updater kPermCacheUpdater */
 
 				$updater->OneStepRun();
 			}
 
 			$this->_resetMenuCache();
 
 			if ( $error_count ) {
 				// allow user to review error after structure page creation
 				$event->MasterEvent->redirect = false;
 			}
 		}
 
 		/**
 		 * Processes OnMassMoveUp, OnMassMoveDown events
 		 *
 		 * @param kEvent $event
 		 */
 		function OnChangePriority($event)
 		{
 			$this->Application->SetVar('priority_prefix', $event->getPrefixSpecial());
 			$event->CallSubEvent('priority:' . $event->Name);
 
 			$this->Application->StoreVar('RefreshStructureTree', 1);
 			$this->_resetMenuCache();
 		}
 
 		/**
 		 * Completely recalculates priorities in current category
 		 *
 		 * @param kEvent $event
 		 */
 		function OnRecalculatePriorities($event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$this->Application->SetVar('priority_prefix', $event->getPrefixSpecial());
 			$event->CallSubEvent('priority:' . $event->Name);
 
 			$this->_resetMenuCache();
 		}
 
 		/**
 		 * Update Preview Block for FCKEditor
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdatePreviewBlock($event)
 		{
 			$event->status = kEvent::erSTOP;
 			$string = $this->Application->unescapeRequestVariable($this->Application->GetVar('preview_content'));
 
 			$category_helper = $this->Application->recallObject('CategoryHelper');
 			/* @var $category_helper CategoryHelper */
 
 			$string = $category_helper->replacePageIds($string);
 
 			$this->Application->StoreVar('_editor_preview_content_', $string);
 		}
 
 		/**
 		 * Makes simple search for categories
 		 * based on keywords string
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSimpleSearch($event)
 		{
 			$event->redirect = false;
 			$search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
 
 			$keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords')));
 
 			$query_object = $this->Application->recallObject('HTTPQuery');
 			/* @var $query_object kHTTPQuery */
 
 			$sql = 'SHOW TABLES LIKE "'.$search_table.'"';
 
 			if ( !isset($query_object->Get['keywords']) && !isset($query_object->Post['keywords']) && $this->Conn->Query($sql) ) {
 				// used when navigating by pages or changing sorting in search results
 				return;
 			}
 
 			if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length'))
 			{
 				$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table);
 				$this->Application->SetVar('keywords_too_short', 1);
 				return; // if no or too short keyword entered, doing nothing
 			}
 
 			$this->Application->StoreVar('keywords', $keywords);
 
 			$this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search
 
 			$keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_'));
 
 			$event->setPseudoClass('_List');
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$this->Application->SetVar($event->getPrefixSpecial().'_Page', 1);
 			$lang = $this->Application->GetVar('m_lang');
 			$items_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
 			$module_name = 'In-Portal';
 
 			$sql = 'SELECT *
 					FROM ' . $this->Application->getUnitOption('confs', 'TableName') . '
 					WHERE ModuleName = ' . $this->Conn->qstr($module_name) . ' AND SimpleSearch = 1';
 			$search_config = $this->Conn->Query($sql, 'FieldName');
 
 			$field_list = array_keys($search_config);
 
 			$join_clauses = Array();
 
 			// field processing
 			$weight_sum = 0;
 
 			$alias_counter = 0;
 
 			$custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields');
 			if ($custom_fields) {
 				$custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName');
 				$join_clauses[] = '	LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId';
 			}
 
 			// what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table))
 			$search_config_map = Array();
 
 			foreach ($field_list as $key => $field) {
 				$local_table = TABLE_PREFIX.$search_config[$field]['TableName'];
 				$weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause
 
 				// processing multilingual fields
 				if ( !$search_config[$field]['CustomFieldId'] && $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) {
 					$field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field;
 					$field_list[$key] = 'l'.$lang.'_'.$field;
 
 					if (!isset($search_config[$field]['ForeignField'])) {
 						$field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary'];
 						$search_config_map[ $field_list[$key.'_primary'] ] = $field;
 					}
 				}
 
 				// processing fields from other tables
 				$foreign_field = $search_config[$field]['ForeignField'];
 
 				if ( $foreign_field ) {
 					$exploded = explode(':', $foreign_field, 2);
 					if ($exploded[0] == 'CALC') {
 						// ignoring having type clauses in simple search
 						unset($field_list[$key]);
 						continue;
 					}
 					else {
 						$multi_lingual = false;
 						if ($exploded[0] == 'MULTI') {
 							$multi_lingual = true;
 							$foreign_field = $exploded[1];
 						}
 
 						$exploded = explode('.', $foreign_field);	// format: table.field_name
 						$foreign_table = TABLE_PREFIX.$exploded[0];
 
 						$alias_counter++;
 						$alias = 't'.$alias_counter;
 
 						if ($multi_lingual) {
 							$field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1];
 							$field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field;
 							$search_config_map[ $field_list[$key] ] = $field;
 							$search_config_map[ $field_list[$key.'_primary'] ] = $field;
 						}
 						else {
 							$field_list[$key] = $alias.'.'.$exploded[1];
 							$search_config_map[ $field_list[$key] ] = $field;
 						}
 
 						$join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']);
 						$join_clause = str_replace('{LocalTable}', $items_table, $join_clause);
 
 						$join_clauses[] = '	LEFT JOIN '.$foreign_table.' '.$alias.'
 											ON '.$join_clause;
 					}
 				}
 				else {
 					// processing fields from local table
 					if ($search_config[$field]['CustomFieldId']) {
 						$local_table = 'custom_data';
 
 						// search by custom field value on current language
 						$custom_field_id = array_search($field_list[$key], $custom_fields);
 						$field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id;
 
 						// search by custom field value on primary language
 						$field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id;
 						$search_config_map[ $field_list[$key.'_primary'] ] = $field;
 					}
 
 					$field_list[$key] = $local_table.'.'.$field_list[$key];
 					$search_config_map[ $field_list[$key] ] = $field;
 				}
 			}
 
 			// keyword string processing
 			$search_helper = $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$where_clause = Array ();
 			foreach ($field_list as $field) {
 				if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) {
 					// local real field
 					$filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false);
 					if ($filter_data) {
 						$where_clause[] = $filter_data['value'];
 					}
 				}
 				elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) {
 					$custom_field_name = 'cust_' . $search_config_map[$field];
 					$filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false);
 					if ($filter_data) {
 						$where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']);
 					}
 				}
 				else {
 					$where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field));
 				}
 			}
 
 			$where_clause = '((' . implode(') OR (', $where_clause) . '))'; // 2 braces for next clauses, see below!
 
 			$where_clause = $where_clause . ' AND (' . $items_table . '.Status = ' . STATUS_ACTIVE . ')';
 
 			if ($event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild') {
 				$sub_search_ids = $event->MasterEvent->getEventParam('ResultIds');
 
 				if ( $sub_search_ids !== false ) {
 					if ( $sub_search_ids ) {
 						$where_clause .= 'AND (' . $items_table . '.ResourceId IN (' . implode(',', $sub_search_ids) . '))';
 					}
 					else {
 						$where_clause .= 'AND FALSE';
 					}
 				}
 			}
 
 			// exclude template based sections from search results (ie. registration)
 			if ( $this->Application->ConfigValue('ExcludeTemplateSectionsFromSearch') ) {
 				$where_clause .= ' AND ' . $items_table . '.ThemeId = 0';
 			}
 
 			// making relevance clause
 			$positive_words = $search_helper->getPositiveKeywords($keywords);
 			$this->Application->StoreVar('highlight_keywords', serialize($positive_words));
 			$revelance_parts = Array();
 			reset($search_config);
 
 			foreach ($positive_words as $keyword_index => $positive_word) {
 				$positive_word = $search_helper->transformWildcards($positive_word);
 				$positive_words[$keyword_index] = $this->Conn->escape($positive_word);
 			}
 
 			foreach ($field_list as $field) {
 
 				if (!array_key_exists($field, $search_config_map)) {
 					$map_key = $search_config_map[$items_table . '.' . $field];
 				}
 				else {
 					$map_key = $search_config_map[$field];
 				}
 
 				$config_elem = $search_config[ $map_key ];
 				$weight = $config_elem['Priority'];
 
 				// search by whole words only ([[:<:]] - word boundary)
 				/*$revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.implode(' ', $positive_words).')[[:>:]]", '.$weight.', 0)';
 				foreach ($positive_words as $keyword) {
 					$revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.$keyword.')[[:>:]]", '.$weight.', 0)';
 				}*/
 
 				if ( count($positive_words) > 1 ) {
 					$condition = $field . ' LIKE "%' . implode(' ', $positive_words) . '%"';
 					$revelance_parts[] = 'IF(' . $condition . ', ' . $weight_sum . ', 0)';
 				}
 
 				// search by partial word matches too
 				foreach ( $positive_words as $keyword ) {
 					$revelance_parts[] = 'IF(' . $field . ' LIKE "%' . $keyword . '%", ' . $weight . ', 0)';
 				}
 			}
 
 			$revelance_parts = array_unique($revelance_parts);
 
 			$conf_postfix = $this->Application->getUnitOption($event->Prefix, 'SearchConfigPostfix');
 			$rel_keywords	= $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix)	/ 100;
 			$rel_pop		= $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix)		/ 100;
 			$rel_rating		= $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix)	/ 100;
 			$relevance_clause = '('.implode(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords;
 			if ($rel_pop && $object->isField('Hits')) {
 				$relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
 			}
 			if ($rel_rating && $object->isField('CachedRating')) {
 				$relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
 			}
 
 			// building final search query
 			if (!$this->Application->GetVar('do_not_drop_search_table')) {
 				$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event
 				$this->Application->SetVar('do_not_drop_search_table', true);
 			}
 
 			$search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"');
 			if ($search_table_exists) {
 				$select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) ';
 			}
 			else {
 				$select_intro = 'CREATE TABLE '.$search_table.' AS ';
 			}
 
 			$edpick_clause = $this->Application->getUnitOption($event->Prefix.'.EditorsPick', 'Fields') ? $items_table.'.EditorsPick' : '0';
 
 			$sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance,
 								'.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField').' AS ItemId,
 								'.$items_table.'.ResourceId,
 								'.$this->Application->getUnitOption($event->Prefix, 'ItemType').' AS ItemType,
 								 '.$edpick_clause.' AS EdPick
 						FROM '.$object->TableName.'
 						'.implode(' ', $join_clauses).'
 						WHERE '.$where_clause.'
 						GROUP BY '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField').' ORDER BY Relevance DESC';
 
 			$this->Conn->Query($sql);
 
 			if ( !$search_table_exists ) {
 				$sql = 'ALTER TABLE ' . $search_table . '
 						ADD INDEX (ResourceId),
 						ADD INDEX (Relevance)';
 				$this->Conn->Query($sql);
 			}
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSubSearch($event)
 		{
 			// keep search results from other items after doing a sub-search on current item type
 			$this->Application->SetVar('do_not_drop_search_table', true);
 
 			$ids = Array ();
 			$search_table = TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search';
 			$sql = 'SHOW TABLES LIKE "' . $search_table . '"';
 
 			if ( $this->Conn->Query($sql) ) {
 				$item_type = $this->Application->getUnitOption($event->Prefix, 'ItemType');
 
 				// 1. get ids to be used as search bounds
 				$sql = 'SELECT DISTINCT ResourceId
 						FROM ' . $search_table . '
 						WHERE ItemType = ' . $item_type;
 				$ids = $this->Conn->GetCol($sql);
 
 				// 2. delete previously found ids
 				$sql = 'DELETE FROM ' . $search_table . '
 						WHERE ItemType = ' . $item_type;
 				$this->Conn->Query($sql);
 			}
 
 			$event->setEventParam('ResultIds', $ids);
 			$event->CallSubEvent('OnSimpleSearch');
 		}
 
 		/**
 		 * Make record to search log
 		 *
 		 * @param string $keywords
 		 * @param int $search_type 0 - simple search, 1 - advanced search
 		 */
 		function saveToSearchLog($keywords, $search_type = 0)
 		{
 			// don't save keywords for each module separately, just one time
 			// static variable can't help here, because each module uses it's own class instance !
 			if (!$this->Application->GetVar('search_logged')) {
 				$sql = 'UPDATE '.TABLE_PREFIX.'SearchLogs
 						SET Indices = Indices + 1
 						WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search
 		        $this->Conn->Query($sql);
 		        if ($this->Conn->getAffectedRows() == 0) {
 		            $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type);
 		        	$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLogs');
 		        }
 
 		        $this->Application->SetVar('search_logged', 1);
 			}
 		}
 
 		/**
 		 * Load item if id is available
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function LoadItem(kEvent $event)
 		{
 			if ( !$this->_isVirtual($event) ) {
 				parent::LoadItem($event);
 				return;
 			}
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$id = $this->getPassedID($event);
 
 			if ( $object->isLoaded() && !is_array($id) && ($object->GetID() == $id) ) {
 				// object is already loaded by same id
 				return;
 			}
 
 			if ( $object->Load($id, null, true) ) {
 				$actions = $this->Application->recallObject('kActions');
 				/* @var $actions Params */
 
 				$actions->Set($event->getPrefixSpecial() . '_id', $object->GetID());
 			}
 			else {
 				$object->setID($id);
 			}
 		}
 
 		/**
 		 * Returns constrain for priority calculations
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @see PriorityEventHandler
 		 * @access protected
 		 */
 		protected function OnGetConstrainInfo(kEvent $event)
 		{
 			$constrain = ''; // for OnSave
 
 			$event_name = $event->getEventParam('original_event');
 			$actual_event_name = $event->getEventParam('actual_event');
 
 			if ( $actual_event_name == 'OnSavePriorityChanges' || $event_name == 'OnAfterItemLoad' || $event_name == 'OnAfterItemDelete' ) {
 				$object = $event->getObject();
 				/* @var $object kDBItem */
 
 				$constrain = 'ParentId = ' . $object->GetDBField('ParentId');
 			}
 			elseif ( $actual_event_name == 'OnPreparePriorities' ) {
 				$constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id');
 			}
 			elseif ( $event_name == 'OnSave' ) {
 				$constrain = '';
 			}
 			else {
 				$constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id');
 			}
 
 			$event->setEventParam('constrain_info', Array ($constrain, ''));
 		}
 
 		/**
 		 * Parses category part of url, build main part of url
 		 *
 		 * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE.
 		 * @param string $prefix Prefix, that listener uses for system integration
 		 * @param Array $params Params, that are used for url building or created during url parsing.
 		 * @param Array $url_parts Url parts to parse (only for parsing).
 		 * @param bool $keep_events Keep event names in resulting url (only for building).
 		 * @return bool|string|Array Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener.
 		 */
 		public function CategoryRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false)
 		{
 			if ($rewrite_mode == REWRITE_MODE_BUILD) {
 				return $this->_buildMainUrl($prefix, $params, $keep_events);
 			}
 
 			if ( $this->_parseFriendlyUrl($url_parts, $params) ) {
 				// friendly urls work like exact match only!
 				return false;
 			}
 
 			$this->_parseCategory($url_parts, $params);
 
 			return true;
 		}
 
 		/**
 		 * Build main part of every url
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return string
 		 */
 		protected function _buildMainUrl($prefix_special, &$params, $keep_events)
 		{
 			$ret = '';
 			list ($prefix) = explode('.', $prefix_special);
 
 			$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
 			/* @var $rewrite_processor kRewriteUrlProcessor */
 
 			$processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events);
 			if ($processed_params === false) {
 				return '';
 			}
 
 			// add language
 			if ($processed_params['m_lang'] && ($processed_params['m_lang'] != $rewrite_processor->primaryLanguageId)) {
 				$language_name = $this->Application->getCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]');
 				if ($language_name === false) {
 					$sql = 'SELECT PackName
 							FROM ' . TABLE_PREFIX . 'Languages
 							WHERE LanguageId = ' . $processed_params['m_lang'];
 					$language_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]', $language_name);
 				}
 
 				$ret .= $language_name . '/';
 			}
 
 			// add theme
 			if ($processed_params['m_theme'] && ($processed_params['m_theme'] != $rewrite_processor->primaryThemeId)) {
 				$theme_name = $this->Application->getCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]');
 				if ($theme_name === false) {
 					$sql = 'SELECT Name
 							FROM ' . TABLE_PREFIX . 'Themes
 							WHERE ThemeId = ' . $processed_params['m_theme'];
 					$theme_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]', $theme_name);
 
 				}
 
 				$ret .= $theme_name . '/';
 			}
 
 			// inject custom url parts made by other rewrite listeners just after language/theme url parts
 			if ($params['inject_parts']) {
 				$ret .= implode('/', $params['inject_parts']) . '/';
 			}
 
 			// add category
 			if ($processed_params['m_cat_id'] > 0 && $params['pass_category']) {
 				$category_filename = $this->Application->getCategoryCache($processed_params['m_cat_id'], 'filenames');
 
 				preg_match('/^Content\/(.*)/i', $category_filename, $regs);
 
 				if ($regs) {
 					$template = array_key_exists('t', $params) ? $params['t'] : false;
 
 					if (strtolower($regs[1]) == strtolower($template)) {
 						// we could have category path like "Content/<template_path>" in this case remove template
 						$params['pass_template'] = false;
 					}
 
 					$ret .= $regs[1] . '/';
 				}
 
 				$params['category_processed'] = true;
 			}
 
 			// reset category page
 			$force_page_adding = false;
 			if (array_key_exists('reset', $params) && $params['reset']) {
 				unset($params['reset']);
 
 				if ($processed_params['m_cat_id']) {
 					$processed_params['m_cat_page'] = 1;
 					$force_page_adding = true;
 				}
 			}
 
 			if ((array_key_exists('category_processed', $params) && $params['category_processed'] && ($processed_params['m_cat_page'] > 1)) || $force_page_adding) {
 				// category name was added before AND category page number found
 				$ret = rtrim($ret, '/') . '_' . $processed_params['m_cat_page'] . '/';
 			}
 
 			$template = array_key_exists('t', $params) ? $params['t'] : false;
 			$category_template = ($processed_params['m_cat_id'] > 0) && $params['pass_category'] ? $this->Application->getCategoryCache($processed_params['m_cat_id'], 'category_designs') : '';
 
 			if ((strtolower($template) == '__default__') && ($processed_params['m_cat_id'] == 0)) {
 				// for "Home" category set template to index when not set
 				$template = 'index';
 			}
 
 			// remove template from url if it is category index cached template
 			if ( ($template == $category_template) || (mb_strtolower($template) == '__default__') ) {
 				// given template is also default template for this category OR '__default__' given
 				$params['pass_template'] = false;
 			}
 
 			// remove template from url if it is site homepage on primary language & theme
 			if ( ($template == 'index') && $processed_params['m_lang'] == $rewrite_processor->primaryLanguageId && $processed_params['m_theme'] == $rewrite_processor->primaryThemeId ) {
 				// given template is site homepage on primary language & theme
 				$params['pass_template'] = false;
 			}
 
 			if ($template && $params['pass_template']) {
 				$ret .= $template . '/';
 			}
 
 			return mb_strtolower( rtrim($ret, '/') );
 		}
 
 		/**
 		 * Checks if whole url_parts matches a whole In-CMS page
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		protected function _parseFriendlyUrl($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$sql = 'SELECT CategoryId, NamedParentPath
 					FROM ' . TABLE_PREFIX . 'Categories
 					WHERE FriendlyURL = ' . $this->Conn->qstr(implode('/', $url_parts));
 			$friendly = $this->Conn->GetRow($sql);
 
 			$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
 			/* @var $rewrite_processor kRewriteUrlProcessor */
 
 			if ($friendly) {
 				$vars['m_cat_id'] = $friendly['CategoryId'];
 				$vars['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']);
 
 				while ($url_parts) {
 					$rewrite_processor->partParsed( array_shift($url_parts) );
 				}
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Extracts category part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		protected function _parseCategory($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$res = false;
 			$url_part = array_shift($url_parts);
 
 			$category_id = 0;
 			$last_category_info = false;
 			$category_path = $url_part == 'content' ? '' : 'content';
 
 			$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
 			/* @var $rewrite_processor kRewriteUrlProcessor */
 
 			do {
 				$category_path = trim($category_path . '/' . $url_part, '/');
 				// bb_<topic_id> -> forums/bb_2
 				if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $rets) ) {
 					$category_path = $rets[1];
 					$vars['m_cat_page'] = $rets[2];
 				}
 
 				$sql = 'SELECT CategoryId, SymLinkCategoryId, NamedParentPath
 						FROM ' . TABLE_PREFIX . 'Categories
 						WHERE (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)';
 				$category_info = $this->Conn->GetRow($sql);
 
 				if ($category_info !== false) {
 					$last_category_info = $category_info;
 					$rewrite_processor->partParsed($url_part);
 
 					$url_part = array_shift($url_parts);
 					$res = true;
 				}
 			} while ($category_info !== false && $url_part);
 
 			if ($last_category_info) {
 				// this category is symlink to other category, so use it's url instead
 				// (used in case if url prior to symlink adding was indexed by spider or was bookmarked)
 				if ($last_category_info['SymLinkCategoryId']) {
 					$sql = 'SELECT CategoryId, NamedParentPath
 							FROM ' . TABLE_PREFIX . 'Categories
 							WHERE (CategoryId = ' . $last_category_info['SymLinkCategoryId'] . ')';
 					$category_info = $this->Conn->GetRow($sql);
 
 					if ($category_info) {
 						// web symlinked category was found use it
 						// TODO: maybe 302 redirect should be made to symlinked category url (all other url parts should stay)
 						$last_category_info = $category_info;
 					}
 				}
 
 				// 1. Set virtual page as template, this will be replaced to physical template later in kApplication::Run.
 				// 2. Don't set CachedTemplate field as template here, because we will loose original page associated with it's cms blocks!
 				$vars['t'] = mb_strtolower( preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath']));
 
 				$vars['m_cat_id'] = $last_category_info['CategoryId'];
 				$vars['is_virtual'] = true; // for template from POST, strange code there!
 			}
 			/*else {
 				$vars['m_cat_id'] = 0;
 			}*/
 
 			return $res;
 		}
 
 		/**
 		 * Set's new unique resource id to user
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemValidate(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$resource_id = $object->GetDBField('ResourceId');
 
 			if ( !$resource_id ) {
 				$object->SetDBField('ResourceId', $this->Application->NextResourceId());
 			}
 		}
 
 		/**
 		 * Occurs before an item has been cloned
 		 * Id of newly created item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeClone(kEvent $event)
 		{
 			parent::OnBeforeClone($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$object->SetDBField('ResourceId', 0); // this will reset it
 
 		}
 	}
Index: branches/5.2.x/core/units/sections/sections_config.php
===================================================================
--- branches/5.2.x/core/units/sections/sections_config.php	(revision 16307)
+++ branches/5.2.x/core/units/sections/sections_config.php	(revision 16308)
@@ -1,238 +1,238 @@
 <?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!');
 
 	$config = Array (
 		'Prefix' => 'core-sections',
 		'EventHandlerClass' => Array ('class' => 'SiteConfigEventHandler', 'file' => 'site_config_eh.php', 'build_event' => 'OnBuild'),
 		'TagProcessorClass' => Array ('class' => 'SiteConfigTagProcessor', 'file' => 'site_config_tp.php', 'build_event' => 'OnBuild'),
 
 		'Hooks' => Array (
 			Array (
 				'Mode' => hBEFORE,
 				'Conditional' => false,
 				'HookToPrefix' => '*',
 				'HookToSpecial' => '*',
 				'HookToEvent' => Array ('OnAfterConfigRead'),
 				'DoPrefix' => '',
 				'DoSpecial' => '*',
 				'DoEvent' => 'OnApplySiteConfigChanges',
 			),
 		),
 
 		'Sections' => Array (
 			'in-portal:site' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'struct',
 				'label'			=>	'la_tab_Site_Structure',
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	1,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'c',
 			),
 
 			'in-portal:browse_site'	=> Array(
 				'parent'		=>	'in-portal:site',
 				'icon'			=>	'browse-site',
 				'label'			=>	'la_tab_BrowsePages',
 				'url'			=>	Array('t' => 'index', 'index_file' => '../index.php', 'admin' => 1, 'pass' => 'm'),
 				'permissions'	=>	Array('view'),
 				'priority'		=>	1,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:browse' => Array (
 				'parent'		=>	'in-portal:site',
 				'icon'			=>	'structure', // 'catalog'
 				'label'			=>	'la_title_Structure', // 'la_tab_Browse',
 				'url'			=>	Array ('t' => 'catalog/catalog', 'pass' => 'm'),
 				'late_load'		=>	Array ('t' => 'categories/xml/tree_categories', 'pass' => 'm', 'm_cat_id' => 0),
-				'onclick'		=>	'checkCatalog(0)',
+				'onclick'		=>	'checkCatalog(0, "c")',
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	2,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:reviews' => Array (
 				'parent'		=>	'in-portal:site',
 				'icon'			=>	'reviews',
 				'label'			=>	'la_tab_Reviews',
 				'url'			=>	Array ('t' => 'reviews/reviews', 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	3.5,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:users' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'user_management',
 				'label'			=>	'la_tab_Community',
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	3,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'u',
 			),
 
 			'in-portal:user_groups' => Array (
 				'parent'		=>	'in-portal:users',
 				'icon'			=>	'usergroups',
 				'label'			=>	'la_tab_User_Groups',
 				'url'			=>	Array ('t' => 'groups/groups_list', 'pass' => 'm'),
 				'permissions'	=>	Array ('view', 'add', 'edit', 'delete', 'advanced:send_email', 'advanced:manage_permissions'),
 				'SectionPrefix' => 'g',
 				'priority'		=>	3,
 				'type'			=>	stTREE,
 			),
 
 			// "Help" section
 			/*
 			'in-portal:help' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'help',
 				'label'			=>	'la_tab_Help',
 				'url'			=>	Array ('index_file' => 'help/manual.pdf', 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	7,
 				'type'			=>	stTREE,
 			),
 			*/
 
 			// "Logs & Reports" section
 			'in-portal:reports' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'summary_logs',
 				'label'			=>	'la_tab_LogsAndReports',
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	4,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'adm',
 			),
 
 			// "Configuration" section
 			'in-portal:system' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'conf',
 				'label'			=>	'la_tab_Sys_Config',
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	5,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'adm',
 			),
 
 			'in-portal:website_setting_folder' => Array (
 				'parent'		=>	'in-portal:system',
 				'icon'			=>	'conf_website',
 				'label'			=>	'la_title_Website',
 				'use_parent_header' => 1,
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	1,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'adm',
 			),
 
 			'in-portal:configure_general' => Array (
 				'parent'		=>	'in-portal:website_setting_folder',
 				'icon'			=>	'conf_general',
 				'label'			=>	'la_tab_General',
 				'url'			=>	Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view', 'add', 'edit'),
 				'priority'		=>	1,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:configure_advanced' => Array (
 				'parent'		=>	'in-portal:website_setting_folder',
 				'icon'			=>	'conf_advanced',
 				'label'			=>	'la_title_Advanced',
 				'url'			=>	Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view', 'add', 'edit'),
 				'priority'		=>	2,
 				'type'			=>	stTREE,
 			),
 
 			// "Tools" section
 			'in-portal:tools' => Array (
 				'parent'		=>	'in-portal:root',
 				'icon'			=>	'tools',
 				'label'			=>	'la_tab_Tools',
 				'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	6,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'SectionPrefix' => 'adm',
 			),
 
 			'in-portal:backup' => Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'backup',
 				'label'			=>	'la_tab_Backup',
 				'url'			=>	Array ('t' => 'tools/backup1', 'section' => 'in-portal:configure_general', 'module' => 'In-Portal',  'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	1,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:restore' => Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'restore',
 				'label'			=>	'la_tab_Restore',
 				'url'			=>	Array ('t' => 'tools/restore1', 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	2,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:main_import' => Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'import_data',
 				'label'			=>	'la_tab_ImportData',
 				'url'			=>	Array ('t' => 'tools/import1'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	3,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:sql_query' => Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'query_database',
 				'label'			=>	'la_tab_QueryDB',
 				'url'			=>	Array ('t' => 'tools/sql_query', 'pass' => 'm'),
 				'permissions'	=>	Array ('view', 'edit'),
 				'priority'		=>	4,
 				'type'			=>	stTREE,
 			),
 
 			'in-portal:server_info' => Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'server_info',
 				'label'			=>	'la_tab_ServerInfo',
 				'url'			=>	Array ('t' => 'tools/server_info', 'pass' => 'm'),
 				'permissions'	=>	Array ('view'),
 				'priority'		=>	5,
 				'type'			=>	stTREE,
 			),
 		),
-	);
\ No newline at end of file
+	);
Index: branches/5.2.x/core/units/helpers/category_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/category_helper.php	(revision 16307)
+++ branches/5.2.x/core/units/helpers/category_helper.php	(revision 16308)
@@ -1,381 +1,361 @@
 <?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 CategoryHelper extends kHelper {
 
 		/**
 		 * Structure tree for ParentId field in category or category items
 		 *
 		 * @var Array
 		 */
 		var $_structureTree = null;
 
 		/**
 		 * ID of primary language (only for caching)
 		 *
 		 * @var int
 		 */
 		var $_primaryLanguageId = false;
 
 		/**
-		 * Returns module information based on given module name or current category (relative to module root categories)
+		 * Returns module information based on given module name or current category
+		 * (relative to module root categories).
 		 *
-		 * @param Array $params
-		 * @param Array $category_ids category parent path (already as array)
-		 * @return Array
-		 * @access public
+		 * @param array $params        Tag params.
+		 * @param array $category_path Category parent path.
+		 *
+		 * @return array
 		 */
-		public function getCategoryModule($params, $category_ids)
+		public function getCategoryModule(array $params, array $category_path)
 		{
-			$module_info = Array ();
-
+			// Get module by name.
 			if ( isset($params['module']) ) {
-				// get module by name specified
-				$module_info = $this->Application->findModule('Name', $params['module']);
-
-			}
-			elseif ( $category_ids ) {
-				// get module by category path
-				$module_root_categories = $this->getModuleRootCategories();
-				$common_categories = array_intersect($category_ids, $module_root_categories);
-				$module_category_id = array_shift($common_categories); // get 1st common category
-				$module_info = $this->Application->findModule('RootCat', $module_category_id);
+				return $this->Application->findModule('Name', $params['module']);
 			}
 
-			return $module_info;
-		}
-
-		/**
-		 * Returns root categories from all modules
-		 *
-		 * @return Array
-		 * @access protected
-		 */
-		protected function getModuleRootCategories()
-		{
-			static $root_categories = null;
+			// Get module by category path.
+			if ( $category_path ) {
+				foreach ( array_reverse($category_path) as $module_category_id ) {
+					$module_info = $this->Application->findModule('RootCat', $module_category_id);
 
-			if ( !isset($root_categories) ) {
-				$root_categories = Array ();
-				foreach ($this->Application->ModuleInfo as $module_info) {
-					array_push($root_categories, $module_info['RootCat']);
+					if ( $module_info && $module_info['Var'] != 'adm' ) {
+						return $module_info;
+					}
 				}
-
-				$root_categories = array_unique($root_categories);
 			}
 
-			return $root_categories;
+			return array();
 		}
 
 		/**
 		 * Converts multi-dimensional category structure in one-dimensional option array (category_id=>category_name)
 		 *
 		 * @param Array $data
 		 * @param int $parent_category_id
 		 * @param int $language_id
 		 * @param int $theme_id
 		 * @param int $level
 		 * @return Array
 		 * @access protected
 		 */
 		protected function _printChildren(&$data, $parent_category_id, $language_id, $theme_id, $level = 0)
 		{
 			if ( $data['ThemeId'] != $theme_id && $data['ThemeId'] != 0 ) {
 				// don't show system templates from different themes
 				return Array ();
 			}
 
 			$category_language = $data['l' . $language_id . '_Name'] ? $language_id : $this->_primaryLanguageId;
 			$ret = Array ($parent_category_id => str_repeat('&mdash;', $level) . ' ' . $data['l' . $category_language . '_Name']);
 
 			if ( $data['children'] ) {
 				$level++;
 				foreach ($data['children'] as $category_id => $category_data) {
 					// numeric keys
 					$ret = kUtil::array_merge_recursive($ret, $this->_printChildren($data['children'][$category_id], $category_id, $language_id, $theme_id, $level));
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Returns information about children under parent path (recursive)
 		 *
 		 * @param int $parent_category_id
 		 * @param Array $languages
 		 * @return Array
 		 * @access protected
 		 */
 		protected function _getChildren($parent_category_id, $languages)
 		{
 			static $items_by_parent = null, $parent_mapping = null;
 
 			if ( !isset($items_by_parent) ) {
 				$fields = $items_by_parent = Array ();
 
 				foreach ($languages as $language_id) {
 					$fields[] = 'l' . $language_id . '_Name';
 				}
 
 				$sql = 'SELECT CategoryId AS id, ' . implode(', ', $fields) . ', ParentId, Template, ThemeId
 						FROM ' . TABLE_PREFIX . 'Categories
 						ORDER BY Priority DESC';
 				$items = $this->Conn->Query($sql, 'id');
 
 				foreach ($items as $item_id => $item_data) {
 					$item_parent_id = $item_data['ParentId'];
 					unset($item_data['ParentId']);
 
 					if ( !array_key_exists($item_parent_id, $items_by_parent) ) {
 						$items_by_parent[$item_parent_id] = Array ();
 					}
 
 					$item_data['children'] = false;
 					$parent_mapping[$item_id] = $item_parent_id;
 					$items_by_parent[$item_parent_id][$item_id] = $item_data;
 				}
 
 				$base_category = $this->Application->getBaseCategory(); // "Content" category
 
 				if ( isset($items_by_parent[$base_category]) ) {
 					$index_category = $this->findIndexCategoryId($items_by_parent[$base_category]);
 
 					// rename "Content" into "Home" keeping it's ID
 					$items_by_parent[$parent_mapping[$base_category]][$base_category]['l1_Name'] = $this->Application->Phrase('la_rootcategory_name');
 
 					if ( $index_category !== false ) {
 						// remove category of "index.tpl" template
 						unset($items_by_parent[$base_category][$index_category]);
 						unset($parent_mapping[$index_category]);
 					}
 				}
 			}
 
 			$data = $items_by_parent[$parent_mapping[$parent_category_id]][$parent_category_id];
 			$categories = array_key_exists($parent_category_id, $items_by_parent) ? $items_by_parent[$parent_category_id] : Array ();
 
 			foreach ($categories as $category_id => $category_data) {
 				if ( $category_id == $parent_category_id ) {
 					// don't process myself - prevents recursion
 					continue;
 				}
 
 				$data['children'][$category_id] = $this->_getChildren($category_id, $languages);
 			}
 
 			return $data;
 		}
 
 		/**
 		 * Finds "Home" category among given top level categories
 		 *
 		 * @param Array $top_categories
 		 * @return bool|int
 		 * @access protected
 		 */
 		protected function findIndexCategoryId($top_categories)
 		{
 			foreach ($top_categories as $category_id => $category_info) {
 				if ($category_info['Template'] == 'index') {
 					return $category_id;
 				}
 			}
 
 			return false;
 		}
 
 		/**
 		 * Generates OR retrieves from cache structure tree
 		 *
 		 * @return Array
 		 * @access protected
 		 */
 		protected function &_getStructureTree()
 		{
 			// get cached version of structure tree
 			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 				$data = $this->Application->getCache('master:StructureTree', false, CacheSettings::$structureTreeRebuildTime);
 			}
 			else {
 				$data = $this->Application->getDBCache('StructureTree', CacheSettings::$structureTreeRebuildTime);
 			}
 
 			if ( $data ) {
 				$data = unserialize($data);
 
 				return $data;
 			}
 
 			// generate structure tree from scratch
 			$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
 			/* @var $ml_helper kMultiLanguageHelper */
 
 			$languages = $ml_helper->getLanguages();
 			$root_category = $this->Application->getBaseCategory();
 			$data = $this->_getChildren($root_category, $languages);
 
 			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 				$this->Application->setCache('master:StructureTree', serialize($data));
 			}
 			else {
 				$this->Application->setDBCache('StructureTree', serialize($data));
 			}
 
 			return $data;
 		}
 
 		/**
 		 * Returns template mapping (between physical and virtual pages)
 		 *
 		 * @return Array
 		 * @access public
 		 */
 		public function getTemplateMapping()
 		{
 			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 				$data = $this->Application->getCache('master:template_mapping', false, CacheSettings::$templateMappingRebuildTime);
 			}
 			else {
 				$data = $this->Application->getDBCache('template_mapping', CacheSettings::$templateMappingRebuildTime);
 			}
 
 			if ( $data ) {
 				return unserialize($data);
 			}
 
 			$sql = 'SELECT
 						IF(c.`Type` = ' . PAGE_TYPE_TEMPLATE . ', CONCAT(c.Template, ":", c.ThemeId), CONCAT("id:", c.CategoryId)) AS SrcTemplate,
 						LOWER(
 							IF(
 								c.SymLinkCategoryId IS NOT NULL,
 								(SELECT cc.NamedParentPath FROM ' . TABLE_PREFIX . 'Categories AS cc WHERE cc.CategoryId = c.SymLinkCategoryId),
 							 	c.NamedParentPath
 							 )
 						) AS DstTemplate,
 						c.UseExternalUrl, c.ExternalUrl
 					FROM ' . TABLE_PREFIX . 'Categories AS c
 					WHERE c.Status = ' . STATUS_ACTIVE;
 			$pages = $this->Conn->Query($sql, 'SrcTemplate');
 
 			$mapping = Array ();
 			$base_url = $this->Application->BaseURL();
 
 			foreach ($pages as $src_template => $page) {
 				// process external url, before placing in cache
 				if ( $page['UseExternalUrl'] ) {
 					$external_url = $page['ExternalUrl'];
 
 					if ( !preg_match('/^(.*?):\/\/(.*)$/', $external_url) ) {
 						// url without protocol will be relative url to our site
 						$external_url = $base_url . $external_url;
 					}
 
 					$dst_template = 'external:' . $external_url;
 				}
 				else {
 					$dst_template = preg_replace('/^Content\//i', '', $page['DstTemplate']);
 				}
 
 				$mapping[$src_template] = $dst_template;
 			}
 
 			if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 				$data = $this->Application->setCache('master:template_mapping', serialize($mapping));
 			}
 			else {
 				$this->Application->setDBCache('template_mapping', serialize($mapping));
 			}
 
 			return $mapping;
 		}
 
 		/**
 		 * Returns category structure as field option list
 		 *
 		 * @return Array
 		 * @access public
 		 */
 		public function getStructureTreeAsOptions()
 		{
 			if ( (defined('IS_INSTALL') && IS_INSTALL) || !$this->Application->isAdmin ) {
 				// no need to create category structure during install
 				// OR on Front-End, because it's not used there
 				return Array ();
 			}
 
 			if ( isset($this->_structureTree) ) {
 				return $this->_structureTree;
 			}
 
 			$themes_helper = $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			$data = $this->_getStructureTree();
 
 			$theme_id = (int)$themes_helper->getCurrentThemeId();
 
 			$this->_primaryLanguageId = $this->Application->GetDefaultLanguageId();
 			$this->_structureTree = $this->_printChildren($data, $data['id'], $this->Application->GetVar('m_lang'), $theme_id);
 
 			return $this->_structureTree;
 		}
 
 		/**
 		 * Replace links like "@@ID@@" to actual template names in given text
 		 *
 		 * @param string $text
 		 * @return string
 		 * @access public
 		 */
 		public function replacePageIds($text)
 		{
 			if ( !preg_match_all('/@@(\\d+)@@/', $text, $regs) ) {
 				return $text;
 			}
 
 			$page_ids = $regs[1];
 
 			$sql = 'SELECT NamedParentPath, CategoryId
 					FROM ' . TABLE_PREFIX . 'Categories
 					WHERE CategoryId IN (' . implode(',', $page_ids) . ')';
 			$templates = $this->Conn->GetCol($sql, 'CategoryId');
 
 			$base_category = $this->Application->getBaseCategory();
 
 			if ( isset($templates[$base_category]) ) {
 				// "Content" category will act as "Home Page"
 				$templates[$base_category] .= '/Index';
 			}
 
 			foreach ($page_ids as $page_id) {
 				if ( !isset($templates[$page_id]) ) {
 					// internal page was deleted, but link to it was found in given content block data
 					continue;
 				}
 
 				$url_params = Array ('m_cat_id' => $page_id == $base_category ? 0 : $page_id, 'pass' => 'm');
 				$page_url = $this->Application->HREF(strtolower($templates[$page_id]), '', $url_params);
 				/*if ($this->Application->isAdmin) {
 					$page_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $page_url);
 				}*/
 
 				$text = str_replace('@@' . $page_id . '@@', $page_url, $text);
 			}
 
 			return $text;
 		}
-	}
\ No newline at end of file
+	}
Index: branches/5.2.x/core/units/configuration/configuration_event_handler.php
===================================================================
--- branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 16307)
+++ branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 16308)
@@ -1,568 +1,568 @@
 <?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 ConfigurationEventHandler extends kDBEventHandler  {
 
 		/**
 		 * Allows to override standard permission mapping
 		 *
 		 * @return void
 		 * @access protected
 		 * @see kEventHandler::$permMapping
 		 */
 		protected function mapPermissions()
 		{
 			parent::mapPermissions();
 
 			$permissions = Array (
 				'OnGenerateMaintenancePage' => Array ('self' => 'add|edit'),
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Changes permission section to one from REQUEST, not from config
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(kEvent $event)
 		{
 			$event->setEventParam('PermSection', $this->Application->GetVar('section'));
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(kEvent $event)
 		{
 			parent::SetCustomQuery($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$module = $this->Application->GetVar('module');
 			$section = $this->Application->GetVar('section');
 
 			$object->addFilter('module_filter', '%1$s.ModuleOwner = ' . $this->Conn->qstr($module));
 			$object->addFilter('section_filter', '%1$s.Section = ' . $this->Conn->qstr($section));
 
 			$can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange');
 
 			if ( !$can_change && !$this->Application->isDebugMode() ) {
 				$object->addFilter('interface_change_filter', '%1$s.VariableName NOT IN ("AdminConsoleInterface", "AllowAdminConsoleInterfaceChange")');
 			}
 
 			if ( defined('IS_INSTALL') && IS_INSTALL ) {
 				$object->addFilter('install_filter', '%1$s.Install = 1');
 			}
 
 			$object->addFilter('visible_filter', '%1$s.Heading <> ""');
 		}
 
 		/**
 		 * Presets new system setting fields
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreCreate(kEvent $event)
 		{
 			parent::OnPreCreate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$object->SetDBField('Section', $this->Application->GetVar('section'));
 			$object->SetDBField('ModuleOwner', $this->Application->GetVar('module'));
 		}
 
 		/**
 		 * Sets custom validation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemLoad(kEvent $event)
 		{
 			static $default_field_options = null;
 
 			parent::OnAfterItemLoad($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// ability to validate each configuration variable separately
 			if ( !isset($default_field_options) ) {
 				$default_field_options = $object->GetFieldOptions('VariableValue');
 			}
 
 			$new_field_options = $default_field_options;
 			$validation = $object->GetDBField('Validation');
 
 			if ( $validation ) {
 				$new_field_options = array_merge($new_field_options, unserialize($validation));
 			}
 
 			$object->SetFieldOptions('VariableValue', $new_field_options);
 		}
 
 		/**
 		 * Performs custom validation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemValidate(kEvent $event)
 		{
 			parent::OnBeforeItemValidate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// if password field is empty, then don't update
 			if ( $object->GetDBField('ElementType') == 'password' ) {
 				if ( trim($object->GetDBField('VariableValue')) != '' ) {
 					$password_formatter = $this->Application->recallObject('kPasswordFormatter');
 					/* @var $password_formatter kPasswordFormatter */
 
 					$object->SetDBField('VariableValue', $password_formatter->hashPassword($object->GetDBField('VariableValue')));
 				}
 			}
 
 			$this->_processCountryState($event);
 
 			$variable_name = $object->GetDBField('VariableName');
 			$new_value = $object->GetDBField('VariableValue');
 
 			if ( $variable_name == 'AdminConsoleInterface' ) {
 				$can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange');
 
 				if ( ($new_value != $object->GetOriginalField('VariableValue')) && !$can_change ) {
 					$object->SetError('VariableValue', 'not_allowed', 'la_error_OperationNotAllowed');
 				}
 			}
 			elseif ( $variable_name == 'HardMaintenanceTemplate' ) {
 				$compile = $event->MasterEvent->getEventParam('compile_maintenance_template');
 				$compile = $compile || $new_value != $object->GetOriginalField('VariableValue');
 
 				if ( $compile && !$this->_generateMaintenancePage($new_value) ) {
 					$object->SetError('VariableValue', 'template_file_missing', 'la_error_TemplateFileMissing');
 				}
 			}
 			elseif ( $variable_name == 'DefaultEmailRecipients' ) {
 				$email_event_data = $this->Application->GetVar('email-template_' . $event->Prefix);
 				$object->SetDBField('VariableValue', $email_event_data[0]['Recipients']);
 			}
 
 			$sections_helper = $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			$section = $object->GetDBField('Section');
 
 			if ( $section && !$sections_helper->getSectionData($section) ) {
 				$object->SetError('Section', 'unknown_section');
 			}
 		}
 
 		/**
 		 * Checks, that state belongs to selected country
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		protected function _processCountryState(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$country_iso = $this->_getCountryByState($event);
 			$state_name = $object->GetDBField('VariableValue');
 
 			if ( !$country_iso || !$state_name ) {
 				return;
 			}
 
 			$cs_helper = $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			$state_iso = $cs_helper->getStateIso($state_name, $country_iso);
 
 			if ( $state_iso !== false ) {
 				$object->SetDBField('VariableValue', $state_iso);
 			}
 			else {
 				// selected state doesn't belong to selected country
 				$object->SetError('VariableValue', 'invalid_state', 'la_InvalidState');
 			}
 		}
 
 		/**
 		 * Returns country iso code, that matches current state variable name
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function _getCountryByState(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$variable_name = $object->GetDBField('VariableName');
 
 			$state_country_hash = Array (
 				'Comm_State' => 'Comm_Country',
 				'Comm_Shipping_State' => 'Comm_Shipping_Country'
 			);
 
 			if ( !array_key_exists($variable_name, $state_country_hash) ) {
 				return false;
 			}
 
 			$field_values = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			$sql = 'SELECT VariableId
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE VariableName = ' . $this->Conn->qstr($state_country_hash[$variable_name]);
 			$country_variable_id = $this->Conn->GetOne($sql);
 
 			return $field_values[$country_variable_id]['VariableValue'];
 		}
 
 		/**
 		 * Does custom password setting processong
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(kEvent $event)
 		{
 			parent::OnBeforeItemUpdate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// if password field is empty, then don't update
 			if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) {
 				$object->SetFieldOption('VariableValue', 'skip_empty', 1);
 			}
 		}
 
 		/**
 		 * Occurs after updating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemUpdate(kEvent $event)
 		{
 			static $skin_deleted = false;
 
 			parent::OnAfterItemUpdate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) {
 				$object->SetFieldOption('VariableValue', 'skip_empty', 0);
 			}
 
 			// allows to check if variable's value was changed now
 			$variable_name = $object->GetDBField('VariableName');
 			$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 
 			if ( $object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue') ) {
 				$changed[] = $variable_name;
 				$this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed);
 
 				// update value in cache, so other code (during this script run) would use new value
 				$this->Application->SetConfigValue($variable_name, $object->GetDBField('VariableValue'), true);
 			}
 
 			if ( $variable_name == 'Require_AdminSSL' || $variable_name == 'AdminSSL_URL' ) {
 				// when administrative console is moved to SSL mode, then delete skin
 				if ( in_array($variable_name, $changed) && !$skin_deleted ) {
 					$skin_helper = $this->Application->recallObject('SkinHelper');
 					/* @var $skin_helper SkinHelper */
 
 					$skin_file = $skin_helper->getSkinPath();
 					if ( file_exists($skin_file) ) {
 						unlink($skin_file);
 					}
 
 					$skin_deleted = true;
 				}
 			}
 		}
 
 		/**
 		 * Updates kDBItem
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnUpdate(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			// 1. save user selected module root category
 			$new_category_id = getArrayValue($items_info, 'ModuleRootCategory', 'VariableValue');
 			if ( $new_category_id !== false ) {
 				unset($items_info['ModuleRootCategory']);
 			}
 
 			$object = $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			if ( $items_info ) {
 				$has_error = false;
 
 				foreach ($items_info as $id => $field_values) {
 					$object->Clear(); // clear validation errors from previous variable
 					$object->Load($id);
 					$object->SetFieldsFromHash($field_values);
 					$event->setEventParam('form_data', $field_values);
 
 					if ( !$object->Update($id) ) {
 						// don't stop when error found !
 						$has_error = true;
 					}
 				}
 
 				$event->status = $has_error ? kEvent::erFAIL : kEvent::erSUCCESS;
 			}
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$event->SetRedirectParam('action_completed', 1);
 
 				if ( $new_category_id !== false ) {
 					// root category was submitted
 					$module = $this->Application->GetVar('module');
 					$root_category_id = $this->Application->findModule('Name', $module, 'RootCat');
 
 					if ( $root_category_id != $new_category_id ) {
 						// root category differs from one in db
 						$fields_hash = Array ('RootCat' => $new_category_id);
 						$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', 'Name = ' . $this->Conn->qstr($module));
 					}
 				}
 
 				// reset cache
 				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
-				$require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups');
+				$require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups', 'Catalog_PreselectModuleTab');
 
 				$refresh_sections = array_intersect($require_refresh, $changed);
 				$require_full_refresh = Array ('Site_Name', 'AdminConsoleInterface');
 
 				if ( array_intersect($require_full_refresh, $changed) ) {
 					$event->SetRedirectParam('refresh_all', 1);
 				}
 				elseif ( $refresh_sections ) {
 					$event->SetRedirectParam('refresh_tree', 1);
 				}
 
 				if ( $refresh_sections ) {
 					// reset sections too, because of AdvancedUserManagement
 					$this->Application->DeleteSectionCache();
 				}
 
 				$this->Application->DeleteUnitCache($changed);
 			}
 			else{
 				$errors = $this->Application->GetVar('errors_' . $event->getPrefixSpecial());
 
 				if ( $errors ) {
 					// because we have list out there, and this is item
 					$this->Application->SetVar('first_error', key($errors));
 					$this->Application->removeObject($event->getPrefixSpecial());
 				}
 			}
 
 			// keeps module and section in REQUEST to ensure, that last admin template will work
 			$event->SetRedirectParam('module', $this->Application->GetVar('module'));
 			$event->SetRedirectParam('section', $this->Application->GetVar('section'));
 		}
 
 		/**
 		 * Process items from selector (selected_ids var, key - prefix, value - comma separated ids)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnProcessSelected($event)
 		{
 			$selected_ids = $this->Application->GetVar('selected_ids');
 			$this->Application->StoreVar('ModuleRootCategory', $selected_ids['c']);
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Generates maintenance page
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnGenerateMaintenancePage(kEvent $event)
 		{
 			$event->setEventParam('compile_maintenance_template', 1);
 
 			$event->CallSubEvent('OnUpdate');
 		}
 
 		/**
 		 * Generates HTML version of hard maintenance template
 		 *
 		 * @param string $template
 		 * @return bool
 		 * @access protected
 		 */
 		protected function _generateMaintenancePage($template = null)
 		{
 			if ( !isset($template) ) {
 				$template = $this->Application->ConfigValue('HardMaintenanceTemplate');
 			}
 
 			$curl_helper = $this->Application->recallObject('CurlHelper');
 			/* @var $curl_helper kCurlHelper */
 
 			$html = $curl_helper->Send($this->Application->BaseURL() . '?t=' . $template);
 
 			if ( $curl_helper->isGoodResponseCode() ) {
 				file_put_contents(WRITEABLE . DIRECTORY_SEPARATOR . 'maintenance.html', $html);
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Returns auto-complete values for ajax-dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSuggestValues(kEvent $event)
 		{
 			if ( !$this->Application->isAdminUser ) {
 				// very careful here, because this event allows to
 				// view every object field -> limit only to logged-in admins
 				return;
 			}
 
 			$event->status = kEvent::erSTOP;
 
 			$field = $this->Application->GetVar('field');
 			$cur_value = $this->Application->GetVar('cur_value');
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( !$field || !$cur_value || !$object->isField($field) ) {
 				return;
 			}
 
 			$limit = $this->Application->GetVar('limit');
 			if ( !$limit ) {
 				$limit = 20;
 			}
 
 			$sql = 'SELECT DISTINCT ' . $field . ', ModuleOwner
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE ' . $field . ' LIKE ' . $this->Conn->qstr('%' . $cur_value . '%') . '
 					ORDER BY ' . $field . ' ASC';
 			$raw_suggestions = $this->Conn->Query($sql);
 
 			$suggestions = Array ();
 			$this->Application->XMLHeader();
 
 			foreach ($raw_suggestions as $raw_suggestion) {
 				$suggestion = $raw_suggestion[$field];
 
 				if ( !isset($suggestions[$suggestion]) ) {
 					$suggestions[$suggestion] = Array ();
 				}
 
 				$suggestions[$suggestion][] = $raw_suggestion['ModuleOwner'];
 			}
 
 			array_splice($suggestions, $limit);
 
 			echo '<suggestions>';
 			$of_label = $this->Application->Phrase('la_From', false);
 
 			foreach ($suggestions as $suggestion_value => $suggestion_modules) {
 				$suggestion_module = in_array('In-Portal', $suggestion_modules) ? 'In-Portal' : implode(', ', $suggestion_modules);
 				$suggestion_title = $suggestion_value . ' <em style="color: grey;">' . $of_label . ' ' . $suggestion_module . '</em>';
 
 				echo '<item value="' . kUtil::escape($suggestion_value, kUtil::ESCAPE_HTML) . '">' . kUtil::escape($suggestion_title, kUtil::ESCAPE_HTML) . '</item>';
 			}
 
 			echo '</suggestions>';
 		}
 
 		/**
 		 * Prefills module dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterConfigRead(kEvent $event)
 		{
 			parent::OnAfterConfigRead($event);
 
 			$options = Array ();
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				if ( $module_name == 'Core' ) {
 					continue;
 				}
 
 				$options[$module_name] = $module_name;
 
 				if ( $module_name == 'In-Portal' ) {
 					$options['In-Portal:Users'] = 'In-Portal:Users';
 				}
 			}
 
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 			$fields['ModuleOwner']['options'] = $options;
 			$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
 		}
 	}
Index: branches/5.2.x/core/admin_templates/categories/xml/tree_categories.tpl
===================================================================
--- branches/5.2.x/core/admin_templates/categories/xml/tree_categories.tpl	(revision 16307)
+++ branches/5.2.x/core/admin_templates/categories/xml/tree_categories.tpl	(revision 16308)
@@ -1,20 +1,20 @@
 <inp2:m_XMLTemplate/>
 <inp2:m_DefineElement name="category">
 	<inp2:m_if check="Field" field="CachedDescendantCatsQty">
 		<folder
 			name="<inp2:Field name='Name'/>"
-			href="<inp2:CategoryLink template='catalog/catalog' direct_link='1' pass='m' m_opener='r'/>"
+			href="<inp2:AdminTreeLink template='catalog/catalog'/>"
 			icon="<inp2:c_ModulePath module='core'/>img/icons/<inp2:ItemIcon grid='Default' icon_prefix='icon24_'/>"
 			onclick="checkCatalog(<inp2:Field name='CategoryId' db="db"/>, '<inp2:GetModulePrefix/>')"
 			load_url="<inp2:CategoryLink direct_link="1" pass='m'/>">
 		</folder>
 	<inp2:m_else/>
 		<item
-			href="<inp2:CategoryLink template='catalog/catalog' direct_link='1' pass='m' m_opener='r'/>"
+			href="<inp2:AdminTreeLink template='catalog/catalog'/>"
 			icon="<inp2:c_ModulePath module='core'/>img/icons/<inp2:ItemIcon grid='Default' icon_prefix='icon24_'/>"
 			onclick="checkCatalog(<inp2:Field name='CategoryId' db="db"/>, '<inp2:GetModulePrefix/>')"><inp2:Field name="Name"/></item>
 	</inp2:m_if>
 </inp2:m_DefineElement>
 <tree>
 <inp2:c.tree_PrintList render_as="category" per_page="-1"/>
-</tree>
\ No newline at end of file
+</tree>
Index: branches/5.2.x/core/admin_templates/tree.tpl
===================================================================
--- branches/5.2.x/core/admin_templates/tree.tpl	(revision 16307)
+++ branches/5.2.x/core/admin_templates/tree.tpl	(revision 16308)
@@ -1,199 +1,199 @@
 <inp2:m_Set skip_last_template="1"/>
 <inp2:m_include t="incs/header" nobody="yes" noform="yes"/>
 <inp2:m_NoDebug/>
 
 <body class="tree-body" onresize="onTreeFrameResize();">
 
 <script type="text/javascript">
 	var $save_timer = null,
 		$last_width = parseInt('<inp2:m_GetConfig name="MenuFrameWidth"/>');
 
 	function credits(url) {
 		openwin(url, 'credits', 280, 520);
 	}
 
 	function onTreeFrameResize() {
 		var $menu_frame = $('#menu_frame', window.parent.document),
             $width;
 
 		if (!$menu_frame.length) {
 			return;
 		}
 
 		$width = $menu_frame.width();
 
         if (($width <= 0) || ($width == $last_width)) {
 			// don't save zero width
 			return;
 		}
 
 		clearTimeout($save_timer);
 		$save_timer = setTimeout( function() {saveFrameWidth($width);}, 2000);
 	}
 
 	function saveFrameWidth($width) {
 		getFrame('head').$FrameResizer.OpenWidth = $width;
 
 		$.get(
 			'<inp2:m_Link template="index" adm_event="OnSaveMenuFrameWidth" pass="m,adm" js_escape="1" no_amp="1"/>',
 			{width: $width}
 		);
 
 		$last_width = $width;
 	}
 </script>
 
 <script type="text/javascript" src="<inp2:m_Compress files='js/tree.js'/>"></script>
 
 <table style="height: 100%; width: 100%; border-right: 1px solid #777; border-bottom: 1px solid #777;">
 	<tr>
 		<td colspan="2" style="vertical-align: top; padding: 5px;">
 			<inp2:m_DefineElement name="xml_node" icon_module="">
 				<inp2:m_if check="m_ParamEquals" param="children_count" value="0">
 					<item href="<inp2:m_param name='section_url' html_escape='1'/>" priority="<inp2:m_param name='priority' html_escape='1'/>" onclick="<inp2:m_param name='onclick' html_escape='1'/>" icon="<inp2:$SectionPrefix_ModulePath module='$icon_module'/>img/icons/icon24_<inp2:m_param name='icon'/>.png"<inp2:m_if check="m_Param" name="debug_only"> debug_only="1"</inp2:m_if>><inp2:m_phrase name="$label" html_escape="1"/></item>
 				<inp2:m_else/>
 					<folder href="<inp2:m_param name='section_url' html_escape='1'/>" priority="<inp2:m_param name='priority' html_escape='1'/>" container="<inp2:m_param name='container' html_escape='1'/>" onclick="<inp2:m_param name='onclick' html_escape='1'/>" name="<inp2:m_phrase name='$label' html_escape='1'/>" icon="<inp2:$SectionPrefix_ModulePath module="$icon_module"/>img/icons/icon24_<inp2:m_param name='icon'/>.png" load_url="<inp2:m_param name='late_load' html_escape='1'/>"<inp2:m_if check="m_Param" name="debug_only"> debug_only="1"</inp2:m_if>><inp2:adm_PrintSections render_as="xml_node" section_name="$section_name"/></folder>
 				</inp2:m_if>
 			</inp2:m_DefineElement>
 
 			<table class="tree">
 				<tbody id="tree">
 				</tbody>
 			</table>
 			<script type="text/javascript">
 				var TREE_ICONS_PATH = 'img/tree';
 				var TREE_SHOW_PRIORITY = <inp2:m_if check="adm_ConstOn" name="DBG_SHOW_TREE_PRIORITY" debug_mode="1">1<inp2:m_else/>0</inp2:m_if>;
 				var TREE_LOADING_NODE = {
 					text: '<inp2:m_Phrase name="la_title_Loading" no_editing="1"/>',
 					icon: '<inp2:adm_ModulePath module="core"/>img/icons/icon24_loading.gif'
 				};
 
 				<inp2:m_DefineElement name="root_node">
 					var the_tree = new TreeFolder('tree', '<inp2:m_param name="label" js_escape="1"/>', '<inp2:m_param name="section_url" js_escape="1"/>', '<inp2:$SectionPrefix_ModulePath module="$icon_module"/>img/icons/icon24_<inp2:m_param name="icon"/>.png', null, null, '<inp2:m_param name="priority" js_escape='1'/>', '<inp2:m_param name="container" js_escape="1"/>');
 				</inp2:m_DefineElement>
 				<inp2:adm_PrintSection render_as="root_node" section_name="in-portal:root"/>
 
 				the_tree.AddFromXML('<tree><inp2:adm_PrintSections render_as="xml_node" section_name="in-portal:root" js_escape="1"/></tree>');
 
 				<inp2:m_if check="adm_MainFrameLink">
 					var fld = the_tree.locateItemByURL('<inp2:adm_MainFrameLink m_opener="r" no_amp="1"/>');
 					if (fld) {
 						fld.highLight();
 					}
 					else {
 						the_tree.highLight();
 					}
 				<inp2:m_else/>
 					the_tree.highLight();
 				</inp2:m_if>
 			</script>
 		</td>
 	</tr>
 </table>
 
 <script type="text/javascript">
-	function checkCatalog($cat_id) {
+	function checkCatalog($cat_id, $module_prefix) {
 		var $ret = checkEditMode(false);
 		var $right_frame = getFrame('main');
 
 		if ($ret && $right_frame.$is_catalog) {
-			$right_frame.$Catalog.go_to_cat($cat_id);
+			$right_frame.$Catalog.go_to_cat($cat_id, $module_prefix);
 			return 1; // this opens folder, but disables click
 		}
 
 		return $ret;
 	}
 
 	function setCatalogTab($prefix) {
 		var $ret = checkEditMode(false);
 
 		if ($ret) {
 			var $right_frame = getFrame('main');
 			var $catalog_type = (typeof $right_frame.$Catalog != 'undefined') ? $right_frame.$Catalog.type : '';
 
 			// highlight "Structure & Data" node, when one of it's shortcut nodes are clicked
 			<inp2:m_DefineElement name="section_url_element"><inp2:m_param name="section_url"/></inp2:m_DefineElement>
 			var $structure_node = the_tree.locateItemByURL('<inp2:adm_PrintSection render_as="section_url_element" section_name="in-portal:browse"/>');
 
 			if ($catalog_type == 'AdvancedView') {
 				$right_frame.$Catalog.switchTab($prefix);
 				return $structure_node; // this opens folder, but disables click
 			}
 
 			// this disabled click and causes other node to be highlighted
 			return $structure_node;
 		}
 
 		return $ret;
 	}
 
 	function checkEditMode($reset_toolbar)
 	{
 		if (!isset($reset_toolbar)) {
 			$reset_toolbar = true;
 		}
 
 		if ($reset_toolbar) {
 			getFrame('head').$('#extra_toolbar').html('');
 		}
 
 		var $phrase = '<inp2:m_Phrase label="la_EditingInProgress" js_escape="1"/>';
 
 		if (getFrame('main').$edit_mode) {
 			return confirm($phrase) ? true : false;
 		}
 
 		return true;
 	}
 
 	function ReloadFolder(url, with_late_load)
 	{
 		if (!with_late_load) with_late_load = false;
 
 		var fld = the_tree.locateItemByURL(url.replace(/&amp;/g, '&'), with_late_load);
 		if (fld) {
 			fld.reload();
 		}
 	}
 
 	function ShowStructure($url, $visible)
 	{
 		var fld = the_tree.locateItemByURL($url, true);
 		if (fld) {
 			if ($visible) {
 				fld.expand();
 			}
 			else {
 				fld.collapse();
 			}
 
 		}
 	}
 
 	function SyncActive(url) {
 		var fld = the_tree.locateItemByURL(url);
 		if (fld) {
 			fld.highLight();
 		}
 	}
 
 	<inp2:m_DefineElement name="container_node">
 		var $container_node = the_tree.locateItemByURL('<inp2:m_param name="section_url" js_escape="1"/>');
 		$container_node.Container = true;
 	</inp2:m_DefineElement>
 
 	<inp2:m_DefineElement name="container_node_fix">
 			<inp2:m_if check="m_GetConfig" name="$config_var">
 			<inp2:m_ifnot check="m_IsDebugMode">
 				<inp2:adm_PrintSection render_as="container_node" section_name="$section"/>
 			</inp2:m_ifnot>
 		</inp2:m_if>
 	</inp2:m_DefineElement>
 
 	<inp2:m_RenderElement name="container_node_fix" config_var="DebugOnlyFormConfigurator" section="in-portal:forms"/>
 	<inp2:m_RenderElement name="container_node_fix" config_var="DebugOnlyPromoBlockGroupConfigurator" section="in-portal:promo_block_groups"/>
 </script>
 
 <!--## when form is on top, then 100% height is broken ##-->
 <inp2:m_RenderElement name="kernel_form"/>
 <inp2:m_include t="incs/footer"/>