Index: branches/5.0.x/core/kernel/db/cat_tag_processor.php
===================================================================
--- branches/5.0.x/core/kernel/db/cat_tag_processor.php	(revision 13385)
+++ branches/5.0.x/core/kernel/db/cat_tag_processor.php	(revision 13386)
@@ -1,834 +1,836 @@
 <?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 kCatDBTagProcessor extends kDBTagProcessor {
 
 		/**
 		 * Permission Helper
 		 *
 		 * @var kPermissionsHelper
 		 */
 		var $PermHelper = null;
 
 		function kCatDBTagProcessor()
 		{
 			parent::kDBTagProcessor();
 			$this->PermHelper = $this->Application->recallObject('PermissionsHelper');
 		}
 
 		function ItemIcon($params)
 		{
 			$grids = $this->Application->getUnitOption($this->Prefix, 'Grids');
 			$grid = $grids[ $params['grid'] ];
 
 			if (!array_key_exists('Icons', $grid)) {
 				return '';
 			}
 
 			$icons = $grid['Icons'];
 
 			if (array_key_exists('name', $params)) {
 				$icon_name = $params['name'];
 				return array_key_exists($icon_name, $icons) ? $icons[$icon_name] : '';
 			}
 
 			$status_fields = $this->Application->getUnitOption($this->Prefix, 'StatusField');
 
 			if (!$status_fields) {
 				return $icons['default'];
 			}
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
 			$value = $object->GetDBField($status_fields[0]); // sets base status icon
 
 			if ($value == STATUS_ACTIVE) {
 //				if( $object->HasField('IsPop') && $object->GetDBField('IsPop') ) $value = 'POP';
 //				if( $object->HasField('IsHot') && $object->GetDBField('IsHot') ) $value = 'HOT';
 				if( $object->HasField('IsNew') && $object->GetDBField('IsNew') ) $value = 'NEW';
 //				if( $object->HasField('EditorsPick') && $object->GetDBField('EditorsPick') ) $value = 'PICK';
 			}
 
 			return array_key_exists($value, $icons) ? $icons[$value] : $icons['default'];
 		}
 
 		/**
 		 * Allows to create valid mod-rewrite compatible link to module item
 		 *
 		 * @param Array $params
 		 * @param string $id_prefix
 		 * @return string
 		 */
 		function ItemLink($params, $id_prefix = null)
 		{
 			if (!isset($params['pass'])) {
 				$params['pass'] = 'm,'.$this->Prefix;
 			}
 
 			$item_id = isset($params[$id_prefix.'_id']) && $params[$id_prefix.'_id'];
 			if (!$item_id) {
 				$item_id = $this->Application->GetVar($this->getPrefixSpecial().'_id');
 				if (!$item_id) {
 					$item_id = $this->Application->GetVar($this->Prefix.'_id');
 				}
 			}
-			$params[$this->Prefix.'_id'] = $item_id;
 
 			$object =& $this->getObject($params);
-			$params['m_cat_id'] = $object->GetDBField('CategoryId');
+			/* @var $object kDBItem */
+
 			$params['m_cat_page'] = 1;
+			$params['m_cat_id'] = $object->GetDBField('CategoryId');
 			$params['pass_category'] = 1;
+			$params[$this->Prefix . '_id'] = $item_id ? $item_id : $object->GetID();
 
 			return $this->Application->ProcessParsedTag('m', 't', $params);
 		}
 
 		/**
 		 * Builds link for browsing current item on Front-End
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function PageBrowseLink($params)
 		{
 			$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();
 
 			$params['editing_mode'] = $settings['default_editing_mode'];
 			$params['m_theme'] = $themes_helper->getCurrentThemeId();
 			$params['index_file'] = 'index.php';
 			$params['prefix'] = '_FRONT_END_';
 			$params['admin'] = 1;
 
 			if ($this->Application->ConfigValue('UseModRewrite')) {
 				$params['__MOD_REWRITE__'] = 1;
 			}
 
 			return $this->ItemLink($params);
 		}
 
 		function CategoryPath($params)
 		{
 			if ($this->Application->isAdminUser) {
 				// path for module root category in admin
 				if (!isset($params['cat_id'])) {
 					$params['cat_id'] = $this->Application->RecallVar($params['session_var'], 0);
 				}
 			}
 			else {
 				// path for category item category in front-end
 				$object =& $this->getObject($params);
 				$params['cat_id'] = $object->GetDBField('CategoryId');
 			}
 
 			return $this->Application->ProcessParsedTag('c', 'CategoryPath', $params);
 		}
 
 		function BuildListSpecial($params)
 		{
 			if ($this->Special != '') return $this->Special;
 			if ( isset($params['parent_cat_id']) ) {
 				$parent_cat_id = $params['parent_cat_id'];
 			}
 			else {
 				$parent_cat_id = $this->Application->GetVar('c_id');
 				if (!$parent_cat_id) {
 					$parent_cat_id = $this->Application->GetVar('m_cat_id');
 				}
 			}
 
 			$recursive = isset($params['recursive']);
 
 			$list_unique_key = $this->getUniqueListKey($params).$recursive;
 			if ($list_unique_key == '') {
 				return parent::BuildListSpecial($params);
 			}
 
 			return crc32($parent_cat_id.$list_unique_key);
 		}
 
 		function CatalogItemCount($params)
 		{
 			$params['skip_quering'] = true;
 			$object =& $this->GetList($params);
 
 			if (!$object->Counted) {
 				$object->CountRecs();
 			}
 
 			return $object->NoFilterCount != $object->RecordsCount ? $object->RecordsCount.' / '.$object->NoFilterCount : $object->RecordsCount;
 		}
 
 		function ListReviews($params)
 		{
 			$prefix = $this->Prefix.'-rev';
 			$review_tag_processor =& $this->Application->recallObject($prefix.'.item_TagProcessor');
 			return $review_tag_processor->PrintList($params);
 		}
 
 		function ReviewCount($params)
 		{
 			$review_tag_processor =& $this->Application->recallObject('rev.item_TagProcessor');
 			return $review_tag_processor->TotalRecords($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.'.$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);
 			$category_id = isset($params['cat_id']) ? $params['cat_id'] : $object->GetDBField('CategoryId');
 
 			$category_path = $this->Application->getCache('category_paths', $category_id);
 			if ($category_path === false) {
 				// not chached
 				if ($category_id > 0) {
 					$cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $object->GetField('CachedNavbar'));
 					$category_path = trim($this->CategoryName( Array('cat_id' => 0) ).' > '.str_replace('&|&', ' > ', $cached_navbar), ' > ');
 				}
 				else {
 					$category_path = $this->Application->Phrase( $this->Application->ConfigValue('Root_Name') );
 				}
 				$this->Application->setCache('category_paths', $category_id, $category_path);
 			}
 			return $category_path;
 		}
 
 		/**
 		 * Allows to determine if original value should be shown
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function DisplayOriginal($params)
 		{
 			// original id found & greather then zero + show original
 			$display_original = isset($params['display_original']) && $params['display_original'];
 
 			$owner_field = $this->Application->getUnitOption($this->Prefix, 'OwnerField');
 			if (!$owner_field) {
 				$owner_field = 'CreatedById';
 			}
 
 			$object =& $this->getObject($params);
 			$perm_value = $this->PermHelper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $this->Prefix);
 
 			return $display_original && ($perm_value == 1) && $this->Application->GetVar($this->Prefix.'.original_id');
 		}
 
 		/**
 		 * 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 kCatDBItem */
 
 			// 1. category restriction
 			$params['cat_id'] = $object->isLoaded() ? $object->GetDBField('ParentPath') : $this->Application->GetVar('m_cat_id');
 
 			// 2. owner restriction
 			$owner_field = $this->Application->getUnitOption($this->Prefix, 'OwnerField');
 			if (!$owner_field) {
 				$owner_field = 'CreatedById';
 			}
 			$is_owner = $object->GetDBField($owner_field) == $this->Application->RecallVar('user_id');
 
 			return $perm_helper->TagPermissionCheck($params, $is_owner);
 		}
 
 		/**
 		 * Creates link to current category or to module root category, when current category is home
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function SuggestItemLink($params)
 		{
 			if (!isset($params['cat_id'])) {
 				$params['cat_id'] = $this->Application->GetVar('m_cat_id');
 			}
 
 			if ($params['cat_id'] == 0) {
 				$params['cat_id'] = $this->Application->findModule('Var', $this->Prefix, 'RootCat');
 			}
 
 			$params['m_cat_page'] = 1;
 
 			return $this->Application->ProcessParsedTag('c', 'CategoryLink', $params);
 		}
 
 		/**
 		 * Allows to detect if item has any additional images available
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function HasAdditionalImages($params)
 		{
 			$object =& $this->getObject($params);
 			$sql = 'SELECT ImageId
 					FROM '.$this->Application->getUnitOption('img', 'TableName').'
 					WHERE ResourceId = '.$object->GetDBField('ResourceId').' AND DefaultImg != 1 AND Enabled = 1';
 			return $this->Conn->GetOne($sql) ? 1 : 0;
 		}
 
 		/**
 		 * Checks that item is pending
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function IsPending($params)
 		{
 			$object =& $this->getObject($params);
 
 			$pending_status = Array (STATUS_PENDING, STATUS_PENDING_EDITING);
 			return in_array($object->GetDBField('Status'), $pending_status);
 		}
 
 		function IsFavorite($params)
 		{
 			static $favorite_status = Array ();
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBList */
 
 			if (!isset($favorite_status[$this->Special])) {
 				$resource_ids = $object->GetCol('ResourceId');
 
 				$user_id = $this->Application->RecallVar('user_id');
 				$sql = 'SELECT FavoriteId, ResourceId
 						FROM '.$this->Application->getUnitOption('fav', 'TableName').'
 						WHERE (PortalUserId = '.$user_id.') AND (ResourceId IN ('.implode(',', $resource_ids).'))';
 				$favorite_status[$this->Special] = $this->Conn->GetCol($sql, 'ResourceId');
 			}
 
 			return isset($favorite_status[$this->Special][$object->GetDBField('ResourceId')]);
 		}
 
 		/**
 		 * Returns item's editors pick status (using not formatted value)
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function IsEditorsPick($params)
 		{
 			$object =& $this->getObject($params);
 
 			return $object->GetDBField('EditorsPick') == 1;
 		}
 
 		function FavoriteToggleLink($params)
 		{
 			$fav_prefix = $this->Prefix.'-fav';
 
 			$params['pass'] = implode(',', Array('m', $this->Prefix, $fav_prefix));
 			$params[$fav_prefix.'_event'] = 'OnFavoriteToggle';
 
 			return $this->ItemLink($params);
 		}
 
 		/**
 		 * Checks if item is passed in url
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function ItemAvailable($params)
 		{
 			return $this->Application->GetVar($this->getPrefixSpecial().'_id') > 0;
 		}
 
 		function SortingSelected($params)
 		{
 			$list =& $this->GetList($params);
 			$user_sorting_start = $this->getUserSortIndex();
 
 			$sorting_field = $list->GetOrderField($user_sorting_start);
 			$sorting = strtolower($sorting_field . '|' . $list->GetOrderDirection($user_sorting_start));
 
 			$field_options = $list->GetFieldOptions($sorting_field);
 			if (array_key_exists('formatter', $field_options) && $field_options['formatter'] == 'kMultiLanguage') {
 				// remove language prefix
 				$sorting = preg_replace('/^l[\d]+_(.*)/', '\\1', $sorting);
 				$params['sorting'] = preg_replace('/^l[\d]+_(.*)/', '\\1', $params['sorting']);
 			}
 
 			return $sorting == strtolower($params['sorting']) ? $params['selected'] : '';
 		}
 
 		function CombinedSortingDropDownName($params)
 		{
 			return $this->Prefix.'_CombinedSorting';
 		}
 
 		/**
 		 * 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 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 we are viewing module root category
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function IsModuleHome($params)
 		{
 			$root_category = $this->Application->findModule('Var', $this->Prefix, 'RootCat');
 
 			return $root_category == $this->Application->GetVar('m_cat_id');
 		}
 
 		/**
 		 * Dynamic votes indicator
 		 *
 		 * @param Array $params
 		 *
 		 * @return string
 		 */
 		function VotesIndicator($params)
 		{
 			$object =& $this->getObject($params);
 			/* @var $object kDBItem */
 
 			$rating_helper =& $this->Application->recallObject('RatingHelper');
 			/* @var $rating_helper RatingHelper */
 
 			$small_style = array_key_exists('small_style', $params) ? $params['small_style'] : false;
 
 			return $rating_helper->ratingBar($object, true, '', $small_style);
 		}
 
 		function RelevanceIndicator($params)
 		{
 			$object =& $this->getObject($params);
 
 			$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);
 		}
 
 		function SearchResultField($params)
 		{
 			$ret = $this->Field($params);
 
 			$keywords = unserialize( $this->Application->RecallVar('highlight_keywords') );
 			$opening = $this->Application->ParseBlock( Array('name' => $this->SelectParam($params, 'highlight_opening_render_as,block_highlight_opening')) );
 			$closing = $this->Application->ParseBlock( Array('name' => $this->SelectParam($params, 'highlight_closing_render_as,block_highlight_closing')) );
 
 			foreach ($keywords as $index => $keyword) {
 				$keywords[$index] = preg_quote($keyword, '/');
 			}
 
 			return preg_replace('/('.implode('|', $keywords).')/i', $opening.'\\1'.$closing, $ret);
 		}
 
 		/**
 		 * Shows keywords, that user searched
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function SearchKeywords($params)
 		{
 			$keywords = $this->Application->GetVar('keywords');
 			$sub_search = $this->Application->GetVar('search_type') == 'subsearch';
 
 			return ($keywords !== false) && !$sub_search ? $keywords : $this->Application->RecallVar('keywords');
 		}
 
 		function AdvancedSearchForm($params)
 		{
 			$search_table = $this->Application->getUnitOption('confs', 'TableName');
 			$module_name = $this->Application->findModule('Var', $this->Prefix, 'Name');
 
 			$sql = 'SELECT *
 					FROM '.$search_table.'
 					WHERE (ModuleName = '.$this->Conn->qstr($module_name).') AND (AdvancedSearch = 1)
 					ORDER BY DisplayOrder';
 			$search_config = $this->Conn->Query($sql);
 
 			$ret = '';
 			foreach ($search_config as $record) {
 				$params['name'] = $this->SelectParam($params, 'and_or_render_as,and_or_block');
 				$params['field'] = $record['FieldName'];
 				$params['andor'] = $this->Application->ParseBlock($params);
 
 				$params['name'] = $this->SelectParam($params, $record['FieldType'].'_render_as,'.$record['FieldType'].'_block');
 				$params['caption'] = $this->Application->Phrase($record['DisplayName']);
 				$ret .= $this->Application->ParseBlock($params);
 			}
 			return $ret;
 		}
 
 		/**
 		 * Returns last modification date of items in category / system
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function LastUpdated($params)
 		{
 		    $category_id = $this->Application->GetVar('m_cat_id');
 		    $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 			if (isset($params['local']) && $params['local'] && $category_id > 0) {
 				// scan only current category & it's children
 		        $sql = 'SELECT TreeLeft, TreeRight
 		        		FROM ' . TABLE_PREFIX . 'Category
 		        		WHERE CategoryId = ' . (int)$category_id;
 		        $tree_info = $this->Conn->GetRow($sql);
 
 		        $sql = 'SELECT MAX(item_table.Modified) AS ModDate, MAX(item_table.CreatedOn) AS NewDate
 		        		FROM '.$table_name.' item_table
 		        		LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON (item_table.ResourceId = ci.ItemResourceId)
 		        		LEFT JOIN '.TABLE_PREFIX.'Category c ON c.CategoryId = ci.CategoryId
 		        		WHERE c.TreeLeft BETWEEN '.$tree_info['TreeLeft'].' AND '.$tree_info['TreeRight'];
 		    }
 			else {
 				// scan all categories in system
 				$sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate
 		       			FROM '.$table_name;
 		    }
 
 		    $row_data = $this->Conn->GetRow($sql);
 		    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');
 				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);
 		}
 
 		/**
 		 * Counts category item count in system (not category-dependent)
 		 *
 		 * @param Array $params
 		 * @return int
 		 */
 		function ItemCount($params)
 		{
 			$count_helper =& $this->Application->recallObject('CountHelper');
 			/* @var $count_helper kCountHelper */
 
 			$today_only = isset($params['today']) && $params['today'];
 			return $count_helper->ItemCount($this->Prefix, $today_only);
 		}
 
 		function CategorySelector($params)
 		{
 			$category_id = isset($params['category_id']) && is_numeric($params['category_id']) ? $params['category_id'] : false;
 			if ($category_id === false) {
 				// if category id not given use module root category
 				$category_id = $this->Application->findModule('Var', $this->Prefix, 'RootCat');
 			}
 
 			$id_field = $this->Application->getUnitOption('c', 'IDField');
 			$title_field = $this->Application->getUnitOption('c', 'TitleField');
 			$table_name = $this->Application->getUnitOption('c', 'TableName');
 
 			$count_helper =& $this->Application->recallObject('CountHelper');
 			/* @var $count_helper kCountHelper */
 
 			list ($view_perm, $view_filter) = $count_helper->GetPermissionClause('c', 'perm_cache');
 
 			// get category list (permission based)
 		  	$sql = 'SELECT c.'.$title_field.', c.'.$id_field.'
 		  			FROM '.$table_name.' c
 		    		INNER JOIN '.TABLE_PREFIX.'PermCache perm_cache ON c.CategoryId = perm_cache.CategoryId
 		    		WHERE (ParentId = '.$category_id.') AND ('.$view_filter.') AND (perm_cache.PermId = '.$view_perm.') AND (c.Status = '.STATUS_ACTIVE.')
 		    		ORDER BY c.'.$title_field.' ASC';
 			$categories = $this->Conn->GetCol($sql, $id_field);
 
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 			$block_params['strip_nl'] = 2;
 
 			$ret = '';
 			foreach ($categories as $category_id => $category_name) {
 				// print category
 				$block_params['separator'] = isset($params['category_id']) ? $params['separator'] : ''; // return original separator, remove separator for top level categories
 				$block_params['category_id'] = $category_id;
 				$block_params['category_name'] = $category_name;
 				$ret .= $this->Application->ParseBlock($block_params);
 
 				// print it's children
 				$block_params['separator'] = '&nbsp;&nbsp;&nbsp;'.$params['separator'];
 				$ret .= $this->CategorySelector($block_params);
 			}
 
 			return $ret;
 		}
 
 		function PrintMoreCategories($params)
 		{
 			$object =& $this->getObject();
 			/* @var $object kDBItem */
 
 			$category_ids = $this->Field($params);
 			if (!$category_ids) {
 				return '';
 			}
 
 			$category_ids = explode('|', substr($category_ids, 1, -1));
 
 			$id_field = $this->Application->getUnitOption('c', 'IDField');
 			$title_field = $this->Application->getUnitOption('c', 'TitleField');
 			$table_name = $this->Application->getUnitOption('c', 'TableName');
 
 			$sql = 'SELECT '.$title_field.', '.$id_field.'
 					FROM '.$table_name.'
 					WHERE '.$id_field.' IN ('.implode(',', $category_ids).')';
 			$categories = $this->Conn->GetCol($sql, $id_field);
 
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 
 			$ret = '';
 			foreach ($categories as $category_id => $category_name) {
 				$block_params['category_id'] = $category_id;
 				$block_params['category_name'] = $category_name;
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 
 			return $ret;
 		}
 
 		function DownloadFileLink($params)
 		{
 			$params[$this->getPrefixSpecial().'_event'] = 'OnDownloadFile';
 
 			return $this->ItemLink($params);
 		}
 
 		function ImageSrc($params)
 		{
 			list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial());
 			return $tag_processed ? $ret : false;
 		}
 
 		/**
 		 * Registers hit for item (one time per session)
 		 *
 		 * @param Array $params
 		 */
 		function RegisterHit($params)
 		{
 			$object =& $this->getObject();
 			/* @var $object kCatDBItem */
 
 			if ($object->isLoaded()) {
 				$object->RegisterHit();
 			}
 		}
 
 		/**
 		 * Returns link to item's author public profile
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function ProfileLink($params)
 		{
 			$object =& $this->getObject($params);
 			$owner_field = array_key_exists('owner_field', $params) ? $params['owner_field'] : 'CreatedById';
 			$params['user_id'] = $object->GetDBField($owner_field);
 			unset($params['owner_field']);
 
 			return $this->Application->ProcessParsedTag('m', 'Link', $params);
 		}
 
 		/**
 		 * Checks, that "view in browse mode" functionality available
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function BrowseModeAvailable($params)
 		{
 			$valid_special = $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();
 			/* @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('CategoryId'),
 				'pass'					=>	'all,'.$this->Prefix,
 				'no_pass_through'		=>	1,
 			);
 
 			return $this->Application->HREF($edit_template,'', $url_params);
 		}
 
 		function LanguageVisible($params)
 		{
 			$field = $this->SelectParam($params, 'name,field');
 
 			preg_match('/l([\d]+)_(.*)/', $field, $regs);
 			$params['name'] = $regs[2];
 
 			return $this->HasLanguageError($params) || $this->Application->GetVar('m_lang') == $regs[1];
 		}
 
 		function HasLanguageError($params)
 		{
 			static $languages = null;
 
 			if (!isset($languages)) {
 				$sql = 'SELECT ' . $this->Application->getUnitOption('lang', 'IDField') . '
 						FROM ' . $this->Application->getUnitOption('lang', 'TableName') . '
 						WHERE Enabled = 1';
 				$languages = $this->Conn->GetCol($sql);
 			}
 
 			$field = $this->SelectParam($params, 'name,field');
 
 			$object =& $this->getObject($params);
 			/* @var $object kDBItem */
 
 			foreach ($languages as $language_id) {
 				$check_field = 'l' . $language_id . '_' . $field;
 				if ($object->GetErrorMsg($check_field, false)) {
 					return true;
 				}
 			}
 
 			return false;
 		}
 	}
\ No newline at end of file