<?php
/**
* @version	$Id: cat_tag_processor.php 15360 2012-05-29 08:52:10Z alex $
* @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;

		public function __construct()
		{
			parent::__construct();

			$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 ($this->Application->isAdmin) {
				// link from Admin to Front-end
				$params['prefix'] = '_FRONT_END_';

				if ( $this->Application->ConfigValue('UseModRewrite') ) {
					$params['__MOD_REWRITE__'] = 1;
				}
				else {
					$params['index_file'] = 'index.php';
				}
			}

			if ( !isset($params['pass']) ) {
				$params['pass'] = 'm,'.$this->Prefix;
			}

			// set by PrintList2 tag
			$item_id = $this->Application->GetVar($this->getPrefixSpecial() . '_id');

			if ( !$item_id && ($this->Special != 'next' && $this->Special != 'previous') ) {
				// set from page url
				$item_id = $this->Application->GetVar($this->Prefix . '_id');
			}

			$object = $this->getObject($params);
			/* @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['admin'] = 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');
			}

			$navigation_bar = $this->Application->recallObject('kNavigationBar');
			/* @var $navigation_bar kNavigationBar */

			return $navigation_bar->build($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);

			return $object->GetRecordsCount(false) != $object->GetRecordsCount() ? $object->GetRecordsCount().' / '.$object->GetRecordsCount(false) : $object->GetRecordsCount();
		}

		function ListReviews($params)
		{
			$prefix = $this->Prefix . '-rev';

			$review_tag_processor = $this->Application->recallObject($prefix . '.item_TagProcessor');
			/* @var $review_tag_processor kDBTagProcessor */

			return $review_tag_processor->PrintList($params);
		}

		function ReviewCount($params)
		{
			$review_tag_processor = $this->Application->recallObject('rev.item_TagProcessor');
			/* @var $review_tag_processor kDBTagProcessor */

			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');

			$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 = 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->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name');
				}

				$this->Application->setCache($cache_key, $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);
			/* @var $object kDBItem */

			$cache_key = $object->Prefix . '_additional_images[%' . $this->Application->incrementCacheSerial($object->Prefix, $object->GetID(), false) . '%]';
			$ret = $this->Application->getCache($cache_key);

			if ($ret === false) {
				$this->Conn->nextQueryCachable = true;
				$sql = 'SELECT ImageId
						FROM ' . $this->Application->getUnitOption('img', 'TableName') . '
						WHERE ResourceId = ' . $object->GetDBField('ResourceId') . ' AND DefaultImg != 1 AND Enabled = 1';
				$ret = $this->Conn->GetOne($sql) ? 1 : 0;

				$this->Application->setCache($cache_key, $ret);
			}

			return $ret;
		}

		/**
		 * 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();

			// remove language prefix from $current_sorting_field
			$current_sorting_field = preg_replace('/^l[\d]+_(.*)/', '\\1', $list->GetOrderField($user_sorting_start));
			$current_sorting = $current_sorting_field . '|' . $list->GetOrderDirection($user_sorting_start);

			return strtolower($current_sorting) == strtolower($params['sorting']) ? $params['selected'] : '';
		}

		function CombinedSortingDropDownName($params)
		{
			$list =& $this->GetList($params);

			if ($list->isMainList()) {
				return parent::CombinedSortingDropDownName($params);
			}

			return $list->Prefix . '_CombinedSorting';
		}

		/**
		 * Prepares name for field with event in it (used only on front-end)
		 *
		 * @param Array $params
		 * @return string
		 */
		function SubmitName($params)
		{
			$list =& $this->GetList($params);

			if ($list->isMainList()) {
				return parent::SubmitName($params);
			}

			return 'events[' . $list->Prefix . '][' . $params['event'] . ']';
		}

		/**
		 * Returns prefix + any word (used for shared between categories per page settings)
		 *
		 * @param Array $params
		 * @return string
		 */
		function VarName($params)
		{
			$list =& $this->GetList($params);

			if ($list->isMainList()) {
				return parent::VarName($params);
			}

			return $list->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 = (int)$this->Application->GetVar('m_cat_id');
			$local = array_key_exists('local', $params) && ($category_id > 0) ? $params['local'] : false;

			$serial_name1 = $this->Application->incrementCacheSerial('c', $local ? $category_id : null, false);
			$serial_name2 = $this->Application->incrementCacheSerial($this->Prefix, null, false);
			$cache_key = 'categoryitems_last_updated[%' . $serial_name1 . '%][%' . $serial_name2 . '%]';

			$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(item_table.Modified) AS ModDate, MAX(item_table.CreatedOn) AS NewDate
			        		FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName') . ' item_table
			        		LEFT JOIN ' . TABLE_PREFIX . 'CategoryItems ci ON (item_table.ResourceId = ci.ItemResourceId)
			        		LEFT JOIN ' . TABLE_PREFIX . 'Categories c ON c.CategoryId = ci.CategoryId
			        		WHERE c.TreeLeft BETWEEN ' . $tree_left . ' AND ' . $tree_right;
				}
				else {
					// scan all categories in system
					$sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate
			       			FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName');
				}

				$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);
		}

		/**
		 * 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.' AS CategoryName, c.'.$id_field.', c.l' . $this->Application->GetVar('m_lang') . '_CachedNavbar AS CachedNavbar
		  			FROM '.$table_name.' c
		    		INNER JOIN '.TABLE_PREFIX.'CategoryPermissionsCache 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->Query($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_data) {
				// 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_data['CategoryName'];

				$cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $category_data['CachedNavbar']);
				$block_params['full_path'] = str_replace('&|&', ' > ', $cached_navbar);

				$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($params);
			/* @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.' AS CategoryName, '.$id_field.', l' . $this->Application->GetVar('m_lang') . '_CachedNavbar AS CachedNavbar
					FROM '.$table_name.'
					WHERE '.$id_field.' IN ('.implode(',', $category_ids).')';
			$categories = $this->Conn->Query($sql, $id_field);

			$block_params = $this->prepareTagParams($params);
			$block_params['name'] = $params['render_as'];

			$ret = '';
			foreach ($categories as $category_id => $category_data) {
				$block_params['category_id'] = $category_id;
				$block_params['category_name'] = $category_data['CategoryName'];

				$cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $category_data['CachedNavbar']);
				$block_params['full_path'] = str_replace('&|&', ' > ', $cached_navbar);

				$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($params);
			/* @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($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('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;
		}

		/**
		 * Returns list of categories, that have add/edit permission for current category item type
		 *
		 * @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);
		}
	}