Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sun, Jan 5, 9:12 PM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/core/kernel/db/cat_tag_processor.php
===================================================================
--- branches/5.2.x/core/kernel/db/cat_tag_processor.php (revision 16373)
+++ branches/5.2.x/core/kernel/db/cat_tag_processor.php (revision 16374)
@@ -1,951 +1,954 @@
<?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;
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';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_results_table = $search_helper->getSearchTable();
$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);
}
/**
* Displays list of allowed categories on "Suggest Link" and similar forms.
*
* @param array $params Tag params.
*
* @return string
* @access protected
*/
protected 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');
// get category list (permission based)
$categories = $this->Conn->Query($this->getCategorySelectorQuery($category_id), $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;
}
/**
* Returns given category sub-categories, that user have rights to view.
*
* @param int $category_id Category.
*
* @return array
* @access protected
*/
protected function getCategorySelectorQuery($category_id)
{
$id_field = $this->Application->getUnitOption('c', 'IDField');
$title_field = $this->Application->getUnitOption('c', 'TitleField');
$where_clause = Array (
'c.ParentId = ' . $category_id,
'c.Status = ' . STATUS_ACTIVE,
);
$sql = 'SELECT c.' . $title_field . ' AS CategoryName,
c.' . $id_field . ',
c.l' . $this->Application->GetVar('m_lang') . '_CachedNavbar AS CachedNavbar
FROM ' . $this->Application->getUnitOption('c', 'TableName') . ' c';
$count_helper = $this->Application->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
list ($sql, $where_clause) = $count_helper->attachViewPermissionCheck('c', $sql, $where_clause);
return $sql . ' WHERE (' . implode(') AND (', $where_clause) . ') ORDER BY c.' . $title_field . ' ASC';
}
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);
}
- }
\ No newline at end of file
+ }
Index: branches/5.2.x/core/kernel/db/cat_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/cat_event_handler.php (revision 16373)
+++ branches/5.2.x/core/kernel/db/cat_event_handler.php (revision 16374)
@@ -1,3091 +1,3099 @@
<?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 kCatDBEventHandler extends kDBEventHandler {
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnSaveSettings' => Array ('self' => 'add|edit|advanced:import'),
'OnResetSettings' => Array ('self' => 'add|edit|advanced:import'),
'OnBeforeDeleteOriginal' => Array ('self' => 'edit|advanced:approve'),
'OnAfterDeleteOriginal' => Array ('self' => 'edit|advanced:approve'),
'OnCopy' => Array ('self' => true),
'OnDownloadFile' => Array ('self' => 'view'),
'OnCancelAction' => Array ('self' => true),
'OnItemBuild' => Array ('self' => true),
'OnMakeVote' => Array ('self' => true),
'OnReviewHelpful' => Array ('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$id = $this->getPassedID($event);
if ( $object->Load($id) ) {
$actions = $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set($event->getPrefixSpecial() . '_id', $object->GetID());
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ( $use_pending_editing && $event->Special != 'original' ) {
$this->Application->SetVar($event->Prefix . '.original_id', $object->GetDBField('OrgId'));
}
}
else {
$object->setID($id);
}
}
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( !$this->Application->isAdmin ) {
if ( $event->Name == 'OnSetSortingDirect' ) {
// allow sorting on front event without view permission
return true;
}
}
if ( $event->Name == 'OnExport' ) {
// save category_id before doing export
$this->Application->LinkVar('m_cat_id');
}
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]['CategoryId'], $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['CategoryId'], $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);
}
$export_events = array('OnSaveSettings', 'OnResetSettings', 'OnExportBegin');
if ( in_array($event->Name, $export_events) || ($event->Special == 'export' && $event->Name == 'OnNew') ) {
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$perm_value = $this->Application->CheckPermission('in-portal:main_import.view');
return $perm_helper->finalizePermissionCheck($event, $perm_value);
}
if ( $event->Name == 'OnProcessSelected' ) {
if ( $this->Application->RecallVar('dst_field') == 'ImportCategory' ) {
// when selecting target import category
return $this->Application->CheckPermission('in-portal:main_import.view');
}
}
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)
{
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
// when saving data from temp table to live table check by data from temp table
$item_ids = $this->_getPermissionCheckIDs($event);
$items = $perm_helper->GetCategoryItemData($event->Prefix, $item_ids, $event->Name == 'OnSave');
if (!$items) {
// when item not present in temp table, then permission is not checked, because there are no data in db to check
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list ($id, $fields_hash) = each($items_info);
if (array_key_exists('CategoryId', $fields_hash)) {
$item_category = $fields_hash['CategoryId'];
}
else {
$item_category = $this->Application->GetVar('m_cat_id');
}
$items[$id] = Array (
'CreatedById' => $this->Application->RecallVar('use_id'),
'CategoryId' => $item_category,
);
}
return $items;
}
/**
* Add selected items to clipboard with mode = COPY (CLONE)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected 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
* @return void
* @access protected
*/
protected 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);
}
/**
* 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;
}
/**
* Performs category item paste
*
* @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;
}
if ( $clipboard_data['copy'] ) {
$temp = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$this->Application->SetVar('ResetCatBeforeClone', 1); // used in "kCatDBEventHandler::OnBeforeClone"
$temp->CloneItems($event->Prefix, $event->Special, $clipboard_data['copy']);
}
if ( $clipboard_data['cut'] ) {
$object = $this->Application->recallObject($event->getPrefixSpecial() . '.item', $event->Prefix, Array ('skip_autoload' => true));
/* @var $object kCatDBItem */
foreach ($clipboard_data['cut'] as $id) {
$object->Load($id);
$object->MoveToCat();
}
}
}
/**
* Deletes all selected items.
* Automatically recurse 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;
}
$ids = $this->StoreSelectedIDs($event);
$to_delete = Array ();
$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);
$object = $this->Application->recallObject($event->Prefix . '.recycleitem', NULL, Array ('skip_autoload' => true));
/* @var $object kCatDBItem */
foreach ($ids as $id) {
$object->Load($id);
if ( preg_match('/^' . preg_quote($rb->GetDBField('ParentPath'), '/') . '/', $object->GetDBField('ParentPath')) ) {
$to_delete[] = $id;
continue;
}
$object->MoveToCat($recycle_bin);
}
$ids = $to_delete;
}
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$event->setEventParam('ids', $ids);
$this->customProcessing($event, 'before');
$ids = $event->getEventParam('ids');
if ( $ids ) {
$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
/**
* Return type clauses for list bulding on front
*
* @param kEvent $event
* @return Array
*/
function getTypeClauses($event)
{
$types = $event->getEventParam('types');
$types = $types ? explode(',', $types) : Array ();
$except_types = $event->getEventParam('except');
$except_types = $except_types ? explode(',', $except_types) : Array ();
$type_clauses = Array();
$user_id = $this->Application->RecallVar('user_id');
$owner_field = $this->getOwnerField($event->Prefix);
$type_clauses['my_items']['include'] = '%1$s.'.$owner_field.' = '.$user_id;
$type_clauses['my_items']['except'] = '%1$s.'.$owner_field.' <> '.$user_id;
$type_clauses['my_items']['having_filter'] = false;
$type_clauses['pick']['include'] = '%1$s.EditorsPick = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
$type_clauses['pick']['except'] = '%1$s.EditorsPick! = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
$type_clauses['pick']['having_filter'] = false;
$type_clauses['hot']['include'] = '`IsHot` = 1 AND PrimaryCat = 1';
$type_clauses['hot']['except'] = '`IsHot`! = 1 AND PrimaryCat = 1';
$type_clauses['hot']['having_filter'] = true;
$type_clauses['pop']['include'] = '`IsPop` = 1 AND PrimaryCat = 1';
$type_clauses['pop']['except'] = '`IsPop`! = 1 AND PrimaryCat = 1';
$type_clauses['pop']['having_filter'] = true;
$type_clauses['new']['include'] = '`IsNew` = 1 AND PrimaryCat = 1';
$type_clauses['new']['except'] = '`IsNew`! = 1 AND PrimaryCat = 1';
$type_clauses['new']['having_filter'] = true;
$type_clauses['displayed']['include'] = '';
$displayed = $this->Application->GetVar($event->Prefix.'_displayed_ids');
if ($displayed) {
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$type_clauses['displayed']['except'] = '%1$s.'.$id_field.' NOT IN ('.$displayed.')';
}
else {
$type_clauses['displayed']['except'] = '';
}
$type_clauses['displayed']['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';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_sql = ' FROM ' . $search_helper->getSearchTable() . ' 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'] = 'PrimaryCat = 1 AND ('.TABLE_PREFIX.'Categories.Status = '.STATUS_ACTIVE.')';
$type_clauses['search']['except'] = 'PrimaryCat = 1 AND ('.TABLE_PREFIX.'Categories.Status = '.STATUS_ACTIVE.')';
$type_clauses['search']['having_filter'] = false;
}
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 $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).') AND PrimaryCat = 1';
$type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).') AND PrimaryCat = 1';
}
else {
$type_clauses['related']['include'] = '0';
$type_clauses['related']['except'] = '1';
}
$type_clauses['related']['having_filter'] = false;
}
if (in_array('favorites', $types) || in_array('favorites', $except_types)) {
$sql = 'SELECT ResourceId
FROM '.$this->Application->getUnitOption('fav', 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id');
$favorite_ids = $this->Conn->GetCol($sql);
if ($favorite_ids) {
$type_clauses['favorites']['include'] = '%1$s.ResourceId IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1';
$type_clauses['favorites']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1';
}
else {
$type_clauses['favorites']['include'] = 0;
$type_clauses['favorites']['except'] = 1;
}
$type_clauses['favorites']['having_filter'] = false;
}
return $type_clauses;
}
/**
* Returns SQL clause, that will help to select only data from specified category & it's children
*
* @param int $category_id
* @return string
*/
function getCategoryLimitClause($category_id)
{
if (!$category_id) {
return false;
}
$tree_indexes = $this->Application->getTreeIndex($category_id);
if (!$tree_indexes) {
// id of non-existing category was given
return 'FALSE';
}
return TABLE_PREFIX.'Categories.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight'];
}
/**
* 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 kCatDBList */
// add category filter if needed
if ($event->Special != 'showall' && $event->Special != 'user') {
if ( (string)$event->getEventParam('parent_cat_id') !== '' ) {
$parent_cat_id = $event->getEventParam('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');
}
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 ((string)$parent_cat_id != 'any') {
if ($event->getEventParam('recursive')) {
$filter_clause = $this->getCategoryLimitClause($parent_cat_id);
if ($filter_clause !== false) {
$object->addFilter('category_filter', $filter_clause);
}
$object->addFilter('primary_filter', 'PrimaryCat = 1');
}
else {
$object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.CategoryId = '.$parent_cat_id );
}
}
else {
$object->addFilter('primary_filter', 'PrimaryCat = 1');
}
}
else {
$object->addFilter('primary_filter', 'PrimaryCat = 1');
// if using recycle bin don't show items from there
$recycle_bin = $this->Application->ConfigValue('RecycleBinFolder');
if ($recycle_bin) {
$object->addFilter('recyclebin_filter', TABLE_PREFIX.'CategoryItems.CategoryId <> '.$recycle_bin);
}
}
if ($event->Special == 'user') {
$editable_user = $this->Application->GetVar('u_id');
$object->addFilter('owner_filter', '%1$s.'.$this->getOwnerField($event->Prefix).' = '.$editable_user);
}
$this->applyViewPermissionFilter($object);
$types = $event->getEventParam('types');
$this->applyItemStatusFilter($object, $types);
$except_types = $event->getEventParam('except');
$type_clauses = $this->getTypeClauses($event);
$search_helper = $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->SetComplexFilter($event, $type_clauses, $types, $except_types);
}
/**
* Adds filter, that uses *.VIEW permissions to determine if an item should be shown to a user.
*
* @param kCatDBList $object Object.
*
* @return void
* @access protected
*/
protected function applyViewPermissionFilter(kCatDBList $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 {
// for any real user item list view permission is checked instead of CATEGORY.VIEW
$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);
}
/**
* Adds filter that filters out items with non-required statuses
*
* @param kDBList $object
* @param string $types
*/
function applyItemStatusFilter(&$object, $types)
{
// Link1 (before modifications) [Status = 1, OrgId = NULL], Link2 (after modifications) [Status = -2, OrgId = Link1_ID]
$pending_editing = $this->Application->getUnitOption($object->Prefix, 'UsePendingEditing');
if (!$this->Application->isAdminUser) {
$types = explode(',', $types);
if (in_array('my_items', $types)) {
$allow_statuses = Array (STATUS_ACTIVE, STATUS_PENDING, STATUS_PENDING_EDITING);
$object->addFilter('status_filter', '%1$s.Status IN ('.implode(',', $allow_statuses).')');
if ($pending_editing) {
$user_id = $this->Application->RecallVar('user_id');
$this->applyPendingEditingFilter($object, $user_id);
}
}
else {
$object->addFilter('status_filter', '(%1$s.Status = ' . STATUS_ACTIVE . ') AND (' . TABLE_PREFIX . 'Categories.Status = ' . STATUS_ACTIVE . ')');
if ($pending_editing) {
// if category item uses pending editing abilities, then in no cases show pending copies on front
$object->addFilter('original_filter', '%1$s.OrgId = 0 OR %1$s.OrgId IS NULL');
}
}
}
else {
if ($pending_editing) {
$this->applyPendingEditingFilter($object);
}
}
}
/**
* Adds filter, that removes live items if they have pending editing copies
*
* @param kDBList $object
* @param int $user_id
*/
function applyPendingEditingFilter(&$object, $user_id = NULL)
{
$sql = 'SELECT OrgId
FROM '.$object->TableName.'
WHERE Status = '.STATUS_PENDING_EDITING.' AND OrgId IS NOT NULL';
if (isset($user_id)) {
$owner_field = $this->getOwnerField($object->Prefix);
$sql .= ' AND '.$owner_field.' = '.$user_id;
}
$pending_ids = $this->Conn->GetCol($sql);
if ($pending_ids) {
$object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')');
}
}
/**
* Adds calculates fields for item statuses
*
* @param kDBItem|kDBList $object
* @param kEvent $event
* @return void
* @access protected
*/
protected function prepareObject(&$object, kEvent $event)
{
$this->prepareItemStatuses($event);
$object->addCalculatedField('CachedNavbar', 'l' . $this->Application->GetVar('m_lang') . '_CachedNavbar');
if ( $event->Special == 'export' || $event->Special == 'import' ) {
$export_helper = $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->prepareExportColumns($event);
}
}
/**
* Creates calculated fields for all item statuses based on config settings
*
* @param kEvent $event
*/
function prepareItemStatuses($event)
{
$object = $event->getObject( Array('skip_autoload' => true) );
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if (!$property_map) {
return ;
}
// new items
$object->addCalculatedField('IsNew', ' IF(%1$s.NewItem = 2,
IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '.
$this->Application->ConfigValue($property_map['NewDays']).
'*3600*24), 1, 0),
%1$s.NewItem
)');
// hot items (cache updated every hour)
if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
$serial_name = $this->Application->incrementCacheSerial($event->Prefix, NULL, false);
$hot_limit = $this->Application->getCache($property_map['HotLimit'] . '[%' . $serial_name . '%]');
}
else {
$hot_limit = $this->Application->getDBCache($property_map['HotLimit']);
}
if ($hot_limit === false) {
$hot_limit = $this->CalculateHotLimit($event);
}
$object->addCalculatedField('IsHot', ' IF(%1$s.HotItem = 2,
IF(%1$s.'.$property_map['ClickField'].' >= '.$hot_limit.', 1, 0),
%1$s.HotItem
)');
// popular items
$object->addCalculatedField('IsPop', ' IF(%1$s.PopItem = 2,
IF(%1$s.CachedVotesQty >= '.
$this->Application->ConfigValue($property_map['MinPopVotes']).
' AND %1$s.CachedRating >= '.
$this->Application->ConfigValue($property_map['MinPopRating']).
', 1, 0),
%1$s.PopItem)');
}
/**
* Calculates hot limit for current item's table
*
* @param kEvent $event
* @return float
* @access protected
*/
protected function CalculateHotLimit($event)
{
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if ( !$property_map ) {
return 0.00;
}
$click_field = $property_map['ClickField'];
$last_hot = $this->Application->ConfigValue($property_map['MaxHotNumber']) - 1;
$sql = 'SELECT ' . $click_field . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
ORDER BY ' . $click_field . ' DESC
LIMIT ' . $last_hot . ', 1';
$res = $this->Conn->GetCol($sql);
$hot_limit = (double)array_shift($res);
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$serial_name = $this->Application->incrementCacheSerial($event->Prefix, NULL, false);
$this->Application->setCache($property_map['HotLimit'] . '[%' . $serial_name . '%]', $hot_limit);
}
else {
$this->Application->setDBCache($property_map['HotLimit'], $hot_limit, 3600);
}
return $hot_limit;
}
/**
* Moves item to preferred category, updates item hits
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$object = $event->getObject();
/* @var $object kCatDBItem */
// update hits field
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if ( $property_map ) {
$click_field = $property_map['ClickField'];
if ( $this->Application->isAdminUser && ($this->Application->GetVar($click_field . '_original') !== false) && floor($this->Application->GetVar($click_field . '_original')) != $object->GetDBField($click_field) ) {
$sql = 'SELECT MAX(' . $click_field . ')
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
WHERE FLOOR(' . $click_field . ') = ' . $object->GetDBField($click_field);
$hits = ($res = $this->Conn->GetOne($sql)) ? $res + 0.000001 : $object->GetDBField($click_field);
$object->SetDBField($click_field, $hits);
}
}
// change category
$target_category = $object->GetDBField('CategoryId');
if ( $object->GetOriginalField('CategoryId') != $target_category ) {
$object->MoveToCat($target_category);
}
}
/**
* Occurs after loading item, 'id' parameter
* allows to get id of item that was loaded
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
$special = substr($event->Special, -6);
$object = $event->getObject();
/* @var $object kCatDBItem */
if ( $special == 'import' || $special == 'export' ) {
$image_data = $object->getPrimaryImageData();
if ( $image_data ) {
$thumbnail_image = $image_data[$image_data['LocalThumb'] ? 'ThumbPath' : 'ThumbUrl'];
if ( $image_data['SameImages'] ) {
$full_image = '';
}
else {
$full_image = $image_data[$image_data['LocalImage'] ? 'LocalPath' : 'Url'];
}
$object->SetDBField('ThumbnailImage', $thumbnail_image);
$object->SetDBField('FullImage', $full_image);
$object->SetDBField('ImageAlt', $image_data['AltName']);
}
}
// substituting pending status value for pending editing
if ( $object->HasField('OrgId') && $object->GetDBField('OrgId') > 0 && $object->GetDBField('Status') == -2 ) {
$new_options = Array ();
$options = $object->GetFieldOption('Status', 'options', false, Array ());
foreach ($options as $key => $val) {
if ( $key == 2 ) {
$key = -2;
}
$new_options[$key] = $val;
}
$object->SetFieldOption('Status', 'options', $new_options);
}
if ( !$this->Application->isAdmin ) {
// linking existing images for item with virtual fields
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$image_helper->LoadItemImages($object);
// linking existing files for item with virtual fields
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->LoadItemFiles($object);
}
if ( $object->isVirtualField('MoreCategories') ) {
// set item's additional categories to virtual field (used in editing)
$item_categories = $this->getItemCategories($object->GetDBField('ResourceId'));
$object->SetDBField('MoreCategories', $item_categories ? '|' . implode('|', $item_categories) . '|' : '');
}
}
/**
* Occurs after updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$this->CalculateHotLimit($event);
if ( substr($event->Special, -6) == 'import' ) {
$this->setCustomExportColumns($event);
}
$object = $event->getObject();
/* @var $object kCatDBItem */
if ( !$this->Application->isAdmin ) {
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
// process file upload in virtual fields
$file_helper->SaveItemFiles($object);
if ( $event->Special != '-item' ) {
// don't touch categories during cloning
$this->processAdditionalCategories($object, 'update');
}
}
$recycle_bin = $this->Application->ConfigValue('RecycleBinFolder');
if ( $this->Application->isAdminUser && $recycle_bin ) {
$sql = 'SELECT CategoryId
FROM ' . $this->Application->getUnitOption('ci', 'TableName') . '
WHERE ItemResourceId = ' . $object->GetDBField('ResourceId') . ' AND PrimaryCat = 1';
$primary_category = $this->Conn->GetOne($sql);
if ( $primary_category == $recycle_bin ) {
$event->CallSubEvent('OnAfterItemDelete');
}
}
if ( $object->GetChangedFields() ) {
$now = adodb_mktime();
$object->SetDBField('Modified_date', $now);
$object->SetDBField('Modified_time', $now);
$object->SetDBField('ModifiedById', $this->Application->RecallVar('user_id'));
}
}
/**
* Sets values for import process
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$object = $event->getObject();
/* @var $object kCatDBItem */
if ( substr($event->Special, -6) == 'import' ) {
$this->setCustomExportColumns($event);
}
$object->assignPrimaryCategory();
if ( !$this->Application->isAdmin ) {
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
// process file upload in virtual fields
$file_helper->SaveItemFiles($object);
if ( $event->Special != '-item' ) {
// don't touch categories during cloning
$this->processAdditionalCategories($object, 'create');
}
}
}
/**
* 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);
}
}
/**
* Makes simple search for category items
* 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 */
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_table = $search_helper->getSearchTable();
$sql = 'SHOW TABLES LIKE "'.$search_table.'"';
if(!isset($query_object->Get['keywords']) &&
!isset($query_object->Post['keywords']) &&
$this->Conn->Query($sql))
{
return; // used when navigating by pages or changing sorting in search results
}
if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length'))
{
- $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table);
+ $search_helper->ensureEmptySearchTable();
$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
$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 = $this->Application->findModule('Var', $event->Prefix, 'Name');
$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 */
-
+ // Keyword string processing.
$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!
$search_scope = $this->Application->GetVar('search_scope');
if ($search_scope == 'category') {
$category_id = $this->Application->GetVar('m_cat_id');
$category_filter = $this->getCategoryLimitClause($category_id);
if ($category_filter !== false) {
$join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'CategoryItems ON '.TABLE_PREFIX.'CategoryItems.ItemResourceId = '.$items_table.'.ResourceId';
$join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'Categories ON '.TABLE_PREFIX.'Categories.CategoryId = '.TABLE_PREFIX.'CategoryItems.CategoryId';
$where_clause = '('.$this->getCategoryLimitClause($category_id).') AND '.$where_clause;
}
}
$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';
}
}
}
// 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';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_table = $search_helper->getSearchTable();
$sql = 'SHOW TABLES LIKE "' . $search_table . '"';
+ $ids = array();
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');
}
/**
* Enter description here...
*
* @param kEvent $event
* @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!!
*/
function OnAdvancedSearch($event)
{
$query_object = $this->Application->recallObject('HTTPQuery');
/* @var $query_object kHTTPQuery */
if ( !isset($query_object->Post['andor']) ) {
// used when navigating by pages or changing sorting in search results
return;
}
$this->Application->RemoveVar('keywords');
$this->Application->RemoveVar('Search_Keywords');
$module_name = $this->Application->findModule('Var', $event->Prefix, 'Name');
$sql = 'SELECT *
FROM '.$this->Application->getUnitOption('confs', 'TableName').'
WHERE (ModuleName = '.$this->Conn->qstr($module_name).') AND (AdvancedSearch = 1)';
$search_config = $this->Conn->Query($sql);
$lang = $this->Application->GetVar('m_lang');
$object = $event->getObject();
/* @var $object kDBList */
$object->SetPage(1);
$items_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
$search_keywords = $this->Application->GetVar('value'); // will not be changed
$keywords = $this->Application->GetVar('value'); // will be changed down there
$verbs = $this->Application->GetVar('verb');
$glues = $this->Application->GetVar('andor');
$and_conditions = Array();
$or_conditions = Array();
$and_having_conditions = Array();
$or_having_conditions = Array();
$join_clauses = Array();
$highlight_keywords = Array();
$relevance_parts = Array();
$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';
}
$search_log = '';
$weight_sum = 0;
// processing fields and preparing conditions
foreach ($search_config as $record) {
$field = $record['FieldName'];
$join_clause = '';
$condition_mode = 'WHERE';
// field processing
$local_table = TABLE_PREFIX.$record['TableName'];
$weight_sum += $record['Priority']; // counting weight sum; used when making relevance clause
// processing multilingual fields
if ( $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) {
$field_name = 'l'.$lang.'_'.$field;
}
else {
$field_name = $field;
}
// processing fields from other tables
$foreign_field = $record['ForeignField'];
if ( $foreign_field ) {
$exploded = explode(':', $foreign_field, 2);
if($exploded[0] == 'CALC')
{
$user_groups = $this->Application->RecallVar('UserGroups');
$field_name = str_replace('{PREFIX}', TABLE_PREFIX, $exploded[1]);
$join_clause = str_replace('{PREFIX}', TABLE_PREFIX, $record['JoinClause']);
$join_clause = str_replace('{USER_GROUPS}', $user_groups, $join_clause);
$join_clause = ' LEFT JOIN '.$join_clause;
$condition_mode = 'HAVING';
}
else {
$exploded = explode('.', $foreign_field);
$foreign_table = TABLE_PREFIX.$exploded[0];
if($record['CustomFieldId']) {
$exploded[1] = 'l'.$lang.'_'.$exploded[1];
}
$alias_counter++;
$alias = 't'.$alias_counter;
$field_name = $alias.'.'.$exploded[1];
$join_clause = str_replace('{ForeignTable}', $alias, $record['JoinClause']);
$join_clause = str_replace('{LocalTable}', $items_table, $join_clause);
if($record['CustomFieldId'])
{
$join_clause .= ' AND '.$alias.'.CustomFieldId='.$record['CustomFieldId'];
}
$join_clause = ' LEFT JOIN '.$foreign_table.' '.$alias.'
ON '.$join_clause;
}
}
else
{
// processing fields from local table
if ($record['CustomFieldId']) {
$local_table = 'custom_data';
$field_name = 'l'.$lang.'_cust_'.array_search($field_name, $custom_fields);
}
$field_name = $local_table.'.'.$field_name;
}
$condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords);
if ($record['CustomFieldId'] && strlen($condition)) {
// search in primary value of custom field + value in current language
$field_name = $local_table.'.'.'l'.$this->Application->GetDefaultLanguageId().'_cust_'.array_search($field, $custom_fields);
$primary_condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords);
$condition = '('.$condition.' OR '.$primary_condition.')';
}
if ($condition) {
if ($join_clause) {
$join_clauses[] = $join_clause;
}
$relevance_parts[] = 'IF('.$condition.', '.$record['Priority'].', 0)';
if ($glues[$field] == 1) { // and
if ($condition_mode == 'WHERE') {
$and_conditions[] = $condition;
}
else {
$and_having_conditions[] = $condition;
}
}
else { // or
if ($condition_mode == 'WHERE') {
$or_conditions[] = $condition;
}
else {
$or_having_conditions[] = $condition;
}
}
// create search log record
$search_log_data = Array('search_config' => $record, 'verb' => getArrayValue($verbs, $field), 'value' => ($record['FieldType'] == 'range') ? $search_keywords[$field.'_from'].'|'.$search_keywords[$field.'_to'] : $search_keywords[$field]);
$search_log[] = $this->Application->Phrase('la_Field').' "'.$this->getHuman('Field', $search_log_data).'" '.$this->getHuman('Verb', $search_log_data).' '.$this->Application->Phrase('la_Value').' '.$this->getHuman('Value', $search_log_data).' '.$this->Application->Phrase($glues[$field] == 1 ? 'lu_And' : 'lu_Or');
}
}
if ($search_log) {
$search_log = implode('<br />', $search_log);
$search_log = preg_replace('/(.*) '.preg_quote($this->Application->Phrase('lu_and'), '/').'|'.preg_quote($this->Application->Phrase('lu_or'), '/').'$/is', '\\1', $search_log);
$this->saveToSearchLog($search_log, 1); // advanced search
}
$this->Application->StoreVar('highlight_keywords', serialize($highlight_keywords));
// making relevance clause
if($relevance_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(' + ', $relevance_parts).') / '.$weight_sum.' * '.$rel_keywords;
$relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
$relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
}
else
{
$relevance_clause = '0';
}
// building having clause
if($or_having_conditions)
{
$and_having_conditions[] = '('.implode(' OR ', $or_having_conditions).')';
}
$having_clause = implode(' AND ', $and_having_conditions);
$having_clause = $having_clause ? ' HAVING '.$having_clause : '';
// building where clause
if($or_conditions)
{
$and_conditions[] = '('.implode(' OR ', $or_conditions).')';
}
// $and_conditions[] = $items_table.'.Status = 1';
$where_clause = implode(' AND ', $and_conditions);
if(!$where_clause)
{
if($having_clause)
{
$where_clause = '1';
}
else
{
$where_clause = '0';
$this->Application->SetVar('adv_search_error', 1);
}
}
$where_clause .= ' AND '.$items_table.'.Status = 1';
- // building final search query
- $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+ // Building final search query.
+ $search_table = $search_helper->getSearchTable();
$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table);
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$pick_field = isset($fields['EditorsPick']) ? $items_table.'.EditorsPick' : '0';
$sql = ' CREATE TABLE '.$search_table.'
SELECT '.$relevance_clause.' AS Relevance,
'.$items_table.'.'.$id_field.' AS ItemId,
'.$items_table.'.ResourceId AS ResourceId,
11 AS ItemType,
'.$pick_field.' AS EdPick
FROM '.$items_table.'
'.implode(' ', $join_clauses).'
WHERE '.$where_clause.'
GROUP BY '.$items_table.'.'.$id_field.
$having_clause;
- $res = $this->Conn->Query($sql);
+ $this->Conn->Query($sql);
}
function getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, &$highlight_keywords)
{
$field = $record['FieldName'];
$condition_patterns = Array (
'any' => '%s LIKE %s',
'contains' => '%s LIKE %s',
'notcontains' => '(NOT (%1$s LIKE %2$s) OR %1$s IS NULL)',
'is' => '%s = %s',
'isnot' => '(%1$s != %2$s OR %1$s IS NULL)'
);
$condition = '';
switch ($record['FieldType']) {
case 'select':
$keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]);
if ($keywords[$field]) {
$condition = sprintf($condition_patterns['is'], $field_name, $this->Conn->qstr( $keywords[$field] ));
}
break;
case 'multiselect':
$keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]);
if ($keywords[$field]) {
$condition = Array ();
$values = explode('|', substr($keywords[$field], 1, -1));
foreach ($values as $selected_value) {
$condition[] = sprintf($condition_patterns['contains'], $field_name, $this->Conn->qstr('%|'.$selected_value.'|%'));
}
$condition = '('.implode(' OR ', $condition).')';
}
break;
case 'text':
$keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]);
if (mb_strlen($keywords[$field]) >= $this->Application->ConfigValue('Search_MinKeyword_Length')) {
$highlight_keywords[] = $keywords[$field];
if (in_array($verbs[$field], Array('any', 'contains', 'notcontains'))) {
$keywords[$field] = '%'.strtr($keywords[$field], Array('%' => '\\%', '_' => '\\_')).'%';
}
$condition = sprintf($condition_patterns[$verbs[$field]], $field_name, $this->Conn->qstr( $keywords[$field] ));
}
break;
case 'boolean':
if ($keywords[$field] != -1) {
$property_mappings = $this->Application->getUnitOption($this->Prefix, 'ItemPropertyMappings');
$items_table = $this->Application->getUnitOption($this->Prefix, 'TableName');
switch ($field) {
case 'HotItem':
$hot_limit_var = getArrayValue($property_mappings, 'HotLimit');
if ($hot_limit_var) {
$hot_limit = (int)$this->Application->getDBCache($hot_limit_var);
$condition = 'IF('.$items_table.'.HotItem = 2,
IF('.$items_table.'.Hits >= '.
$hot_limit.
', 1, 0), '.$items_table.'.HotItem) = '.$keywords[$field];
}
break;
case 'PopItem':
$votes2pop_var = getArrayValue($property_mappings, 'VotesToPop');
$rating2pop_var = getArrayValue($property_mappings, 'RatingToPop');
if ($votes2pop_var && $rating2pop_var) {
$condition = 'IF('.$items_table.'.PopItem = 2, IF('.$items_table.'.CachedVotesQty >= '.
$this->Application->ConfigValue($votes2pop_var).
' AND '.$items_table.'.CachedRating >= '.
$this->Application->ConfigValue($rating2pop_var).
', 1, 0), '.$items_table.'.PopItem) = '.$keywords[$field];
}
break;
case 'NewItem':
$new_days_var = getArrayValue($property_mappings, 'NewDays');
if ($new_days_var) {
$condition = 'IF('.$items_table.'.NewItem = 2,
IF('.$items_table.'.CreatedOn >= (UNIX_TIMESTAMP() - '.
$this->Application->ConfigValue($new_days_var).
'*3600*24), 1, 0), '.$items_table.'.NewItem) = '.$keywords[$field];
}
break;
case 'EditorsPick':
$condition = $items_table.'.EditorsPick = '.$keywords[$field];
break;
}
}
break;
case 'range':
$range_conditions = Array();
if ($keywords[$field.'_from'] && !preg_match("/[^0-9]/i", $keywords[$field.'_from'])) {
$range_conditions[] = $field_name.' >= '.$keywords[$field.'_from'];
}
if ($keywords[$field.'_to'] && !preg_match("/[^0-9]/i", $keywords[$field.'_to'])) {
$range_conditions[] = $field_name.' <= '.$keywords[$field.'_to'];
}
if ($range_conditions) {
$condition = implode(' AND ', $range_conditions);
}
break;
case 'date':
if ($keywords[$field]) {
if (in_array($keywords[$field], Array('today', 'yesterday'))) {
$current_time = getdate();
$day_begin = adodb_mktime(0, 0, 0, $current_time['mon'], $current_time['mday'], $current_time['year']);
$time_mapping = Array('today' => $day_begin, 'yesterday' => ($day_begin - 86400));
$min_time = $time_mapping[$keywords[$field]];
}
else {
$time_mapping = Array (
'last_week' => 604800, 'last_month' => 2628000, 'last_3_months' => 7884000,
'last_6_months' => 15768000, 'last_year' => 31536000,
);
$min_time = adodb_mktime() - $time_mapping[$keywords[$field]];
}
$condition = $field_name.' > '.$min_time;
}
break;
}
return $condition;
}
/**
* Returns human readable representation of searched data to be placed in search log
* @param string $type
* @param Array $search_data
* @return string
* @access protected
*/
protected function getHuman($type, $search_data)
{
// all 3 variables are retrieved from $search_data array
/* @var $search_config Array */
/* @var $verb string */
/* @var $value string */
$type = ucfirst(strtolower($type));
extract($search_data, EXTR_SKIP);
switch ($type) {
case 'Field':
return $this->Application->Phrase($search_config['DisplayName']);
break;
case 'Verb':
return $verb ? $this->Application->Phrase('lu_advsearch_'.$verb) : '';
break;
case 'Value':
switch ($search_config['FieldType']) {
case 'date':
$values = Array(0 => 'lu_comm_Any', 'today' => 'lu_comm_Today',
'yesterday' => 'lu_comm_Yesterday', 'last_week' => 'lu_comm_LastWeek',
'last_month' => 'lu_comm_LastMonth', 'last_3_months' => 'lu_comm_Last3Months',
'last_6_months' => 'lu_comm_Last6Months', 'last_year' => 'lu_comm_LastYear');
$ret = $this->Application->Phrase($values[$value]);
break;
case 'range':
$value = explode('|', $value);
return $this->Application->Phrase('lu_comm_From').' "'.$value[0].'" '.$this->Application->Phrase('lu_comm_To').' "'.$value[1].'"';
break;
case 'boolean':
$values = Array(1 => 'lu_comm_Yes', 0 => 'lu_comm_No', -1 => 'lu_comm_Both');
$ret = $this->Application->Phrase($values[$value]);
break;
default:
$ret = $value;
break;
}
return '"'.$ret.'"';
break;
}
return '';
}
/**
* 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)
{
$object = $event->getObject();
/* @var $object kDBList */
// get PerPage (forced -> session -> config -> 10)
$object->SetPerPage($this->getPerPage($event));
// main lists on Front-End have special get parameter for page
$page = $object->isMainList() ? $this->Application->GetVar('page') : false;
if ( !$page ) {
// page is given in "env" variable for given prefix
$page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page');
}
if ( !$page && $event->Special ) {
// when not part of env, then variables like "prefix.special_Page" are
// replaced (by PHP) with "prefix_special_Page", so check for that too
$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
}
if ( !$object->isMainList() ) {
// main lists doesn't use session for page storing
$this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional
if ( !$page ) {
if ( $this->Application->RewriteURLs() ) {
// when page not found by prefix+special, then try to search it without special at all
$page = $this->Application->GetVar($event->Prefix . '_Page');
if ( !$page ) {
// page not found in request -> get from session
$page = $this->Application->RecallVar($event->Prefix . '_Page');
}
if ( $page ) {
// page found in request -> store in session
$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional
}
}
else {
// page not found in request -> get from session
$page = $this->Application->RecallVar($event->getPrefixSpecial() . '_Page');
}
}
else {
// page found in request -> store in session
$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional
}
if ( !$event->getEventParam('skip_counting') ) {
// when stored page is larger, then maximal list page number
// (such case is also processed in kDBList::Query method)
$pages = $object->GetTotalPages();
if ( $page > $pages ) {
$page = 1;
$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', 1, true);
}
}
}
$object->SetPage($page);
}
/* === RELATED TO IMPORT/EXPORT: BEGIN === */
/**
* Shows export dialog
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExport(kEvent $event)
{
$selected_ids = $this->StoreSelectedIDs($event);
if ( implode(',', $selected_ids) == '' ) {
// K4 fix when no ids found bad selected ids array is formed
$selected_ids = false;
}
$selected_cats_ids = $this->Application->GetVar('export_categories');
$this->Application->StoreVar($event->Prefix . '_export_ids', $selected_ids ? implode(',', $selected_ids) : '');
$this->Application->StoreVar($event->Prefix . '_export_cats_ids', $selected_cats_ids);
$export_helper = $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$redirect_params = Array (
$this->Prefix . '.export_event' => 'OnNew',
'pass' => 'all,' . $this->Prefix . '.export'
);
$event->setRedirectParams($redirect_params);
}
/**
* Performs each export step & displays progress percent
*
* @param kEvent $event
*/
function OnExportProgress($event)
{
$export_object = $this->Application->recallObject('CatItemExportHelper');
/* @var $export_object kCatDBItemExportHelper */
$action_method = 'perform'.ucfirst($event->Special);
$field_values = $export_object->$action_method($event);
// finish code is done from JS now
if ($field_values['start_from'] == $field_values['total_records']) {
if ($event->Special == 'import') {
$this->Application->StoreVar('PermCache_UpdateRequired', 1);
$event->SetRedirectParam('m_cat_id', $this->Application->RecallVar('ImportCategory'));
$event->SetRedirectParam('anchor', 'tab-' . $event->Prefix);
$event->redirect = 'catalog/catalog';
}
elseif ($event->Special == 'export') {
$event->redirect = $export_object->getModuleName($event) . '/' . $event->Special . '_finish';
$event->SetRedirectParam('pass', 'all');
}
return ;
}
$export_options = $export_object->loadOptions($event);
echo $export_options['start_from'] * 100 / $export_options['total_records'];
$event->status = kEvent::erSTOP;
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
* @access protected
*/
public function getCustomExportColumns(kEvent $event)
{
return Array (
'__VIRTUAL__ThumbnailImage' => 'ThumbnailImage',
'__VIRTUAL__FullImage' => 'FullImage',
'__VIRTUAL__ImageAlt' => 'ImageAlt'
);
}
/**
* Sets non standart virtual fields (e.g. to other tables)
*
* @param kEvent $event
*/
function setCustomExportColumns($event)
{
$this->restorePrimaryImage($event);
}
/**
* Create/Update primary image record in info found in imported data
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function restorePrimaryImage($event)
{
$object = $event->getObject();
/* @var $object kCatDBItem */
if ( !$object->GetDBField('ThumbnailImage') && !$object->GetDBField('FullImage') ) {
return ;
}
$image_data = $object->getPrimaryImageData();
$image = $this->Application->recallObject('img', NULL, Array ('skip_autoload' => true));
/* @var $image kDBItem */
if ( $image_data ) {
$image->Load($image_data['ImageId']);
}
else {
$image->Clear();
$image->SetDBField('Name', 'main');
$image->SetDBField('DefaultImg', 1);
$image->SetDBField('ResourceId', $object->GetDBField('ResourceId'));
}
if ( $object->GetDBField('ImageAlt') ) {
$image->SetDBField('AltName', $object->GetDBField('ImageAlt'));
}
if ( $object->GetDBField('ThumbnailImage') ) {
$thumbnail_field = $this->isURL($object->GetDBField('ThumbnailImage')) ? 'ThumbUrl' : 'ThumbPath';
$image->SetDBField($thumbnail_field, $object->GetDBField('ThumbnailImage'));
$image->SetDBField('LocalThumb', $thumbnail_field == 'ThumbPath' ? 1 : 0);
}
if ( !$object->GetDBField('FullImage') ) {
$image->SetDBField('SameImages', 1);
}
else {
$image->SetDBField('SameImages', 0);
$full_field = $this->isURL($object->GetDBField('FullImage')) ? 'Url' : 'LocalPath';
$image->SetDBField($full_field, $object->GetDBField('FullImage'));
$image->SetDBField('LocalImage', $full_field == 'LocalPath' ? 1 : 0);
}
if ( $image->isLoaded() ) {
$image->Update();
}
else {
$image->Create();
}
}
/**
* Detects if image url is specified in a given path (instead of path on disk)
*
* @param string $path
* @return bool
* @access protected
*/
protected function isURL($path)
{
return preg_match('#(http|https)://(.*)#', $path);
}
/**
* Prepares item for import/export operations
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnNew(kEvent $event)
{
parent::OnNew($event);
if ( $event->Special == 'import' || $event->Special == 'export' ) {
$export_helper = $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->setRequiredFields($event);
}
}
/**
* Process items selected in item_selector
*
* @param kEvent $event
*/
function OnProcessSelected($event)
{
$dst_field = $this->Application->RecallVar('dst_field');
$selected_ids = $this->Application->GetVar('selected_ids');
if ( $dst_field == 'ItemCategory' ) {
// Item Edit -> Categories Tab -> New Categories
$object = $event->getObject();
/* @var $object kCatDBItem */
$category_ids = explode(',', $selected_ids['c']);
foreach ($category_ids as $category_id) {
$object->assignToCategory($category_id);
}
}
if ($dst_field == 'ImportCategory') {
// Tools -> Import -> Item Import -> Select Import Category
$this->Application->StoreVar('ImportCategory', $selected_ids['c']);
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', 0);
$event->SetRedirectParam($event->getPrefixSpecial() . '_event', 'OnExportBegin');
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Saves Import/Export settings to session
*
* @param kEvent $event
*/
function OnSaveSettings($event)
{
$event->redirect = false;
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( $items_info ) {
list($id, $field_values) = each($items_info);
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$object->setID($id);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
$field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!!
$field_values['ImportSource'] = 2;
$field_values['ImportLocalFilename'] = $object->GetDBField('ImportFilename');
$items_info[$id] = $field_values;
$this->Application->StoreVar($event->getPrefixSpecial() . '_ItemsInfo', serialize($items_info));
}
}
/**
* Saves Import/Export settings to session
*
* @param kEvent $event
*/
function OnResetSettings($event)
{
$this->Application->StoreVar('ImportCategory', $this->Application->getBaseCategory());
}
/**
* Cancels item editing
* @param kEvent $event
* @return void
* @todo Used?
*/
function OnCancelAction($event)
{
$event->redirect = $this->Application->GetVar('cancel_template');
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
}
/* === RELATED TO IMPORT/EXPORT: END === */
/**
* Stores item's owner login into separate field together with id
*
* @param kEvent $event
* @param string $id_field
* @param string $cached_field
*/
function cacheItemOwner($event, $id_field, $cached_field)
{
$object = $event->getObject();
/* @var $object kDBItem */
$object->SetDBField($cached_field, $object->GetField($id_field));
}
/**
* Saves edited item into temp table
* If there is no id, new item is created in temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSave(kEvent $event)
{
parent::OnPreSave($event);
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ( $event->status == kEvent::erSUCCESS && $use_pending_editing ) {
// decision: clone or not clone
$object = $event->getObject();
/* @var $object kCatDBItem */
if ( $object->GetID() == 0 || $object->GetDBField('OrgId') > 0 ) {
// new items or cloned items shouldn't be cloned again
return ;
}
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$owner_field = $this->getOwnerField($event->Prefix);
if ( $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix) == 2 ) {
// 1. clone original item
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array ($object->GetID()), NULL, NULL, NULL, true);
$ci_table = $this->Application->GetTempName(TABLE_PREFIX . 'CategoryItems');
// 2. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem
$sql = 'SELECT ResourceId
FROM ' . $object->TableName . '
WHERE ' . $object->IDField . ' = ' . $cloned_ids[0];
$clone_resource_id = $this->Conn->GetOne($sql);
$sql = 'DELETE FROM ' . $ci_table . '
WHERE ItemResourceId = ' . $clone_resource_id . ' AND PrimaryCat = 1';
$this->Conn->Query($sql);
// 3. copy main item categoryitems to cloned item
$sql = ' INSERT INTO ' . $ci_table . ' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename)
SELECT CategoryId, ' . $clone_resource_id . ' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename
FROM ' . $ci_table . '
WHERE ItemResourceId = ' . $object->GetDBField('ResourceId');
$this->Conn->Query($sql);
// 4. put cloned id to OrgId field of item being cloned
$sql = 'UPDATE ' . $object->TableName . '
SET OrgId = ' . $object->GetID() . '
WHERE ' . $object->IDField . ' = ' . $cloned_ids[0];
$this->Conn->Query($sql);
// 5. substitute id of item being cloned with clone id
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $cloned_ids[0]);
$selected_ids = $this->getSelectedIDs($event, true);
$selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0];
$this->StoreSelectedIDs($event, $selected_ids);
// 6. delete original item from temp table
$temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($object->GetID()));
}
}
}
/**
* Sets item's owner field
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent $event)
{
parent::OnPreCreate($event);
if ( $event->status != kEvent::erSUCCESS ) {
return ;
}
$object = $event->getObject();
/* @var $object kDBItem */
$owner_field = $this->getOwnerField($event->Prefix);
$object->SetDBField($owner_field, $this->Application->RecallVar('user_id'));
}
/**
* Occurs before original item of item in pending editing got deleted (for hooking only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteOriginal(kEvent $event)
{
}
/**
* Occurs after original item of item in pending editing got deleted (for hooking only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterDeleteOriginal(kEvent $event)
{
}
/**
* 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
if ( $this->Application->GetVar('ResetCatBeforeClone') ) {
$object->SetDBField('CategoryId', NULL);
}
}
/**
* Set status for new category item based on user permission in category
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$object = $event->getObject();
/* @var $object kCatDBItem */
$owner_field = $this->getOwnerField($event->Prefix);
// Don't allow creating records on behalf of another user.
if ( !$this->Application->isAdminUser && !defined('CRON') ) {
$object->SetDBField($owner_field, $object->GetOriginalField($owner_field));
}
// Auto-assign records to currently logged-in user.
if ( !$object->GetDBField($owner_field) ) {
$object->SetDBField($owner_field, $this->Application->RecallVar('user_id'));
}
if ( !$this->Application->isAdmin ) {
$this->setItemStatusByPermission($event);
}
}
/**
* Sets category item status based on user permissions (only on Front-end)
*
* @param kEvent $event
*/
function setItemStatusByPermission($event)
{
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if (!$use_pending_editing) {
return ;
}
$object = $event->getObject();
/* @var $object kCatDBItem */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$primary_category = $object->GetDBField('CategoryId') > 0 ? $object->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id');
$item_status = $perm_helper->AddCheckPermission($primary_category, $event->Prefix);
if ($item_status == STATUS_DISABLED) {
$event->status = kEvent::erFAIL;
}
else {
$object->SetDBField('Status', $item_status);
}
}
/**
* Creates category item & redirects to confirmation template (front-end only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
parent::OnCreate($event);
$this->SetFrontRedirectTemplate($event, 'suggest');
}
/**
* Returns item's categories (allows to exclude primary category)
*
* @param int $resource_id
* @param bool $with_primary
* @return Array
*/
function getItemCategories($resource_id, $with_primary = false)
{
$sql = 'SELECT CategoryId
FROM '.TABLE_PREFIX.'CategoryItems
WHERE (ItemResourceId = '.$resource_id.')';
if (!$with_primary) {
$sql .= ' AND (PrimaryCat = 0)';
}
return $this->Conn->GetCol($sql);
}
/**
* Adds new and removes old additional categories from category item
*
* @param kCatDBItem $object
* @param int $mode
*/
function processAdditionalCategories(&$object, $mode)
{
if ( !$object->isVirtualField('MoreCategories') ) {
// given category item doesn't require such type of processing
return ;
}
$process_categories = $object->GetDBField('MoreCategories');
if ($process_categories === '') {
// field was not in submit & have default value (when no categories submitted, then value is null)
return ;
}
if ($mode == 'create') {
// prevents first additional category to become primary
$object->assignPrimaryCategory();
}
$process_categories = $process_categories ? explode('|', substr($process_categories, 1, -1)) : Array ();
$existing_categories = $this->getItemCategories($object->GetDBField('ResourceId'));
$add_categories = array_diff($process_categories, $existing_categories);
foreach ($add_categories as $category_id) {
$object->assignToCategory($category_id);
}
$remove_categories = array_diff($existing_categories, $process_categories);
foreach ($remove_categories as $category_id) {
$object->removeFromCategory($category_id);
}
}
/**
* Creates category item & redirects to confirmation template (front-end only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
$use_pending = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ($this->Application->isAdminUser || !$use_pending) {
parent::OnUpdate($event);
$this->SetFrontRedirectTemplate($event, 'modify');
return ;
}
$object = $event->getObject(Array('skip_autoload' => true));
/* @var $object kCatDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ($items_info) {
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$owner_field = $this->getOwnerField($event->Prefix);
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$edit_perm = $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix);
if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) {
// pending editing enabled + not pending copy -> get/create pending copy & save changes to it
$original_id = $object->GetID();
$original_resource_id = $object->GetDBField('ResourceId');
$file_helper->PreserveItemFiles($field_values);
$object->Load($original_id, 'OrgId');
if (!$object->isLoaded()) {
// 1. user has no pending copy of live item -> clone live item
$cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($original_id), NULL, NULL, NULL, true);
$object->Load($cloned_ids[0]);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
// 1a. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem
$ci_table = $this->Application->getUnitOption('ci', 'TableName');
$sql = 'DELETE FROM '.$ci_table.'
WHERE ItemResourceId = '.$object->GetDBField('ResourceId').' AND PrimaryCat = 1';
$this->Conn->Query($sql);
// 1b. copy main item categoryitems to cloned item
$sql = 'INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename)
SELECT CategoryId, '.$object->GetDBField('ResourceId').' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename
FROM '.$ci_table.'
WHERE ItemResourceId = '.$original_resource_id;
$this->Conn->Query($sql);
// 1c. put cloned id to OrgId field of item being cloned
$object->SetDBField('Status', STATUS_PENDING_EDITING);
$object->SetDBField('OrgId', $original_id);
}
else {
// 2. user has pending copy of live item -> just update field values
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
}
// update id in request (used for redirect in mod-rewrite mode)
$this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID());
}
else {
// 3. already editing pending copy -> just update field values
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
}
if ($object->Update()) {
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
$this->SetFrontRedirectTemplate($event, 'modify');
}
/**
* Sets next template to one required for front-end after adding/modifying item
*
* @param kEvent $event
* @param string $template_key - {suggest,modify}
*/
function SetFrontRedirectTemplate($event, $template_key)
{
if ( $this->Application->isAdmin || $event->status != kEvent::erSUCCESS ) {
return;
}
// prepare redirect template
$object = $event->getObject();
/* @var $object kDBItem */
$is_active = ($object->GetDBField('Status') == STATUS_ACTIVE);
$next_template = $is_active ? 'confirm_template' : 'pending_confirm_template';
$event->redirect = $this->Application->GetVar($template_key . '_' . $next_template);
$event->SetRedirectParam('opener', 's');
// send email events
$perm_prefix = $this->Application->getUnitOption($event->Prefix, 'PermItemPrefix');
$owner_field = $this->getOwnerField($event->Prefix);
$owner_id = $object->GetDBField($owner_field);
switch ( $event->Name ) {
case 'OnCreate':
$event_suffix = $is_active ? 'ADD' : 'ADD.PENDING';
$this->Application->emailAdmin($perm_prefix . '.' . $event_suffix); // there are no ADD.PENDING event for admin :(
$this->Application->emailUser($perm_prefix . '.' . $event_suffix, $owner_id);
break;
case 'OnUpdate':
$event_suffix = $is_active ? 'MODIFY' : 'MODIFY.PENDING';
$user_id = is_numeric($object->GetDBField('ModifiedById')) ? $object->GetDBField('ModifiedById') : $owner_id;
$this->Application->emailAdmin($perm_prefix . '.' . $event_suffix); // there are no ADD.PENDING event for admin :(
$this->Application->emailUser($perm_prefix . '.' . $event_suffix, $user_id);
break;
}
}
/**
* 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 kCatDBItem */
$ids = $this->StoreSelectedIDs($event);
if ( $ids ) {
foreach ($ids as $id) {
$ret = true;
$object->Load($id);
switch ( $event->Name ) {
case 'OnMassApprove':
$ret = $object->ApproveChanges();
break;
case 'OnMassDecline':
$ret = $object->DeclineChanges();
break;
}
if ( !$ret ) {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
$this->clearSelectedIDs($event);
}
/**
* Deletes items & preserves clean env
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDelete(kEvent $event)
{
parent::OnDelete($event);
if ( $event->status == kEvent::erSUCCESS && !$this->Application->isAdmin ) {
$event->SetRedirectParam('pass', 'm');
$event->SetRedirectParam('m_cat_id', 0);
}
}
/**
* 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() ) {
if ( $event->Special != 'previous' && $event->Special != 'next' ) {
$this->_errorNotFound($event);
}
return true;
}
$status = $object->GetDBField('Status');
$user_id = $this->Application->RecallVar('user_id');
$owner_field = $this->getOwnerField($event->Prefix);
if ( ($status == STATUS_PENDING_EDITING || $status == STATUS_PENDING) && ($object->GetDBField($owner_field) == $user_id) ) {
return true;
}
return $status == STATUS_ACTIVE;
}
/**
* 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)
{
if ( !$this->Application->isAdmin ) {
$event->setEventParam('same_special', true);
}
$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);
}
}
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);
}
}
$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);
}
/**
* Returns current per-page setting for list
*
* @param kEvent $event
* @return int
* @access protected
*/
protected function getPerPage(kEvent $event)
{
if ( !$this->Application->isAdmin ) {
$event->setEventParam('same_special', true);
}
return parent::getPerPage($event);
}
/**
* Returns owner field for given prefix
*
* @param $prefix
* @return string
* @access protected
*/
protected function getOwnerField($prefix)
{
return $this->Application->getUnitOption($prefix, 'OwnerField', 'CreatedById');
}
/**
* Creates virtual image fields for item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
if (defined('IS_INSTALL') && IS_INSTALL) {
$this->addViewPermissionJoin($event);
return ;
}
if ( !$this->Application->isAdmin ) {
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->createItemFiles($event->Prefix, true); // create image fields
$file_helper->createItemFiles($event->Prefix, false); // create file fields
}
$this->changeSortings($event)->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_primary_category_td', 'filter_block' => 'grid_like_filter');
$grids[$process_grid . 'ShowAll'] = $grid_data;
}
$this->Application->setUnitOption($this->Prefix, 'Grids', $grids);
// add options for CategoryId field (quick way to select item's primary category)
$category_helper = $this->Application->recallObject('CategoryHelper');
/* @var $category_helper CategoryHelper */
$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
$virtual_fields['CategoryId']['default'] = (int)$this->Application->GetVar('m_cat_id');
$virtual_fields['CategoryId']['options'] = $category_helper->getStructureTreeAsOptions();
$this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields);
}
/**
* Changes default sorting according to system settings.
*
* @param kEvent $event Event.
*
* @return self
* @access protected
*/
protected function changeSortings(kEvent $event)
{
$remove_sortings = Array ();
if ( !$this->Application->isAdmin ) {
// remove Pick sorting on Front-end, when not required
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping', Array ());
if ( !isset($config_mapping['ForceEditorPick']) || !$this->Application->ConfigValue($config_mapping['ForceEditorPick']) ) {
$remove_sortings[] = 'EditorsPick';
}
}
else {
// remove all forced sortings in Admin Console
$remove_sortings = array_merge($remove_sortings, Array ('Priority', 'EditorsPick'));
}
if ( !$remove_sortings ) {
return $this;
}
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
/* @var $list_sortings Array */
foreach ($list_sortings as $special => $sorting_fields) {
foreach ($remove_sortings as $sorting_field) {
unset($list_sortings[$special]['ForcedSorting'][$sorting_field]);
}
}
$this->Application->setUnitOption($event->Prefix, 'ListSortings', $list_sortings);
return $this;
}
/**
* 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 = ' . TABLE_PREFIX . '%3$sCategoryItems.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 file contents associated with item
*
* @param kEvent $event
*/
function OnDownloadFile($event)
{
$object = $event->getObject();
/* @var $object kCatDBItem */
$event->status = kEvent::erSTOP;
$field = $this->Application->GetVar('field');
if (!preg_match('/^File([\d]+)/', $field)) {
return ;
}
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$filename = $object->GetField($field, 'full_path');
$file_helper->DownloadFile($filename);
}
/**
* Saves user's vote
*
* @param kEvent $event
*/
function OnMakeVote($event)
{
$event->status = kEvent::erSTOP;
if ($this->Application->GetVar('ajax') != 'yes') {
// this is supposed to call from AJAX only
return ;
}
$rating_helper = $this->Application->recallObject('RatingHelper');
/* @var $rating_helper RatingHelper */
$object = $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kCatDBItem */
$object->Load( $this->Application->GetVar('id') );
echo $rating_helper->makeVote($object);
}
/**
* Marks review as useful
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnReviewHelpful($event)
{
if ( $this->Application->GetVar('ajax') == 'yes' ) {
$event->status = kEvent::erSTOP;
}
$review_id = (int)$this->Application->GetVar('review_id');
if ( !$review_id ) {
return;
}
$spam_helper = $this->Application->recallObject('SpamHelper');
/* @var $spam_helper SpamHelper */
$spam_helper->InitHelper($review_id, 'ReviewHelpful', strtotime('+1 month') - strtotime('now'));
$field = (int)$this->Application->GetVar('helpful') ? 'HelpfulCount' : 'NotHelpfulCount';
$sql = 'SELECT ' . $field . '
FROM ' . $this->Application->getUnitOption('rev', 'TableName') . '
WHERE ' . $this->Application->getUnitOption('rev', 'IDField') . ' = ' . $review_id;
$count = $this->Conn->GetOne($sql);
if ( $spam_helper->InSpamControl() ) {
if ( $this->Application->GetVar('ajax') == 'yes' ) {
echo $count;
}
return;
}
$sql = 'UPDATE ' . $this->Application->getUnitOption('rev', 'TableName') . '
SET ' . $field . ' = ' . $field . ' + 1
WHERE ' . $this->Application->getUnitOption('rev', 'IDField') . ' = ' . $review_id;
$this->Conn->Query($sql);
if ( $this->Conn->getAffectedRows() ) {
// db was changed -> review with such ID exists
$spam_helper->AddToSpamControl();
}
if ( $this->Application->GetVar('ajax') == 'yes' ) {
echo $count + 1;
}
}
/**
* [HOOK] Allows to add cloned subitem to given prefix
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCloneSubItem(kEvent $event)
{
parent::OnCloneSubItem($event);
if ( $event->MasterEvent->Prefix == 'fav' ) {
$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
$subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix;
$clones[$subitem_prefix]['ParentTableKey'] = 'ResourceId';
$clones[$subitem_prefix]['ForeignKey'] = 'ResourceId';
$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
}
}
/**
* 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());
}
}
}
Index: branches/5.2.x/core/units/categories/categories_tag_processor.php
===================================================================
--- branches/5.2.x/core/units/categories/categories_tag_processor.php (revision 16373)
+++ branches/5.2.x/core/units/categories/categories_tag_processor.php (revision 16374)
@@ -1,2282 +1,2285 @@
<?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);
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($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 ? $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
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$curl_helper->SetRequestData(array(
'appid' => $this->Application->ConfigValue('YahooApplicationId'),
'query' => $keywords,
));
$xml_data = $curl_helper->Send('http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion');
$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);
$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);
$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)),
'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);
$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,
'__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(),
'__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)
{
+ /** @var kDBItem $object */
$object = $this->getObject($params);
- /* @var $object kDBItem */
- $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_results_table = $search_helper->getSearchTable();
$sql = 'SELECT Relevance
- FROM '.$search_results_table.'
- WHERE ResourceId = '.$object->GetDBField('ResourceId');
+ 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 16373)
+++ branches/5.2.x/core/units/categories/categories_event_handler.php (revision 16374)
@@ -1,3142 +1,3145 @@
<?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;
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
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';
+ $search_sql = ' FROM ' . $search_helper->getSearchTable() . ' 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 {
$this->Application->UrlManager->show404();
}
// 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'));
}
}
// Don't allow creating records on behalf of another user.
if ( !$this->Application->isAdminUser && !defined('CRON') ) {
$object->SetDBField('CreatedById', $object->GetOriginalField('CreatedById'));
}
// Auto-assign records to currently logged-in user.
if ( !$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 . ', "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 */
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_table = $search_helper->getSearchTable();
$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);
+ $search_helper->ensureEmptySearchTable();
$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 */
-
+ // Keyword string processing.
$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';
+ /** @var kSearchHelper $search_helper */
+ $search_helper = $this->Application->recallObject('SearchHelper');
+
+ $search_table = $search_helper->getSearchTable();
$sql = 'SHOW TABLES LIKE "' . $search_table . '"';
+ $ids = array();
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/helpers/search_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/search_helper.php (revision 16373)
+++ branches/5.2.x/core/units/helpers/search_helper.php (revision 16374)
@@ -1,805 +1,837 @@
<?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 kSearchHelper extends kHelper {
/**
* Perform Exact Search flag
*
* @var bool
* @access protected
*/
protected $_performExactSearch = true;
public function __construct()
{
parent::__construct();
$this->_performExactSearch = $this->Application->ConfigValue('PerformExactSearch');
}
/**
* Splits search phrase into keyword using quotes,plus and minus sings and spaces as split criteria
*
* @param string $keyword
* @return Array
* @access public
*/
public function splitKeyword($keyword)
{
if ( $this->Application->ConfigValue('CheckStopWords') ) {
$keyword_after_remove = $this->_removeStopWords($keyword);
if ( $keyword_after_remove ) {
// allow to search through stop word grid
$keyword = $keyword_after_remove;
}
}
$final = Array ();
$quotes_re = '/([+\-]?)"(.*?)"/';
$no_quotes_re = '/([+\-]?)([^ ]+)/';
preg_match_all($quotes_re, $keyword, $res);
foreach ($res[2] as $index => $kw) {
$final[$kw] = $res[1][$index];
}
$keyword = preg_replace($quotes_re, '', $keyword);
preg_match_all($no_quotes_re, $keyword, $res);
foreach ($res[2] as $index => $kw) {
$final[$kw] = $res[1][$index];
}
if ( $this->_performExactSearch ) {
foreach ($final AS $kw => $plus_minus) {
if ( !$plus_minus ) {
$final[$kw] = '+';
}
}
}
return $final;
}
function getPositiveKeywords($keyword)
{
$keywords = $this->splitKeyword($keyword);
$ret = Array();
foreach ($keywords as $keyword => $sign) {
if ($sign == '+' || $sign == '') {
$ret[] = $keyword;
}
}
return $ret;
}
/**
* Replace wildcards to match MySQL
*
* @param string $keyword
* @return string
*/
function transformWildcards($keyword)
{
return str_replace(Array ('%', '_', '*', '?') , Array ('\%', '\_', '%', '_'), $keyword);
}
function buildWhereClause($keyword, $fields)
{
$keywords = $this->splitKeyword( $this->transformWildcards($keyword) );
$normal_conditions = $plus_conditions = $minus_conditions = Array();
foreach ($keywords as $keyword => $sign) {
$keyword = $this->Conn->escape($keyword);
switch ($sign) {
case '+':
$plus_conditions[] = implode(" LIKE '%" . $keyword . "%' OR ", $fields) . " LIKE '%" . $keyword . "%'";
break;
case '-':
$condition = Array ();
foreach ($fields as $field) {
$condition[] = $field . " NOT LIKE '%" . $keyword . "%' OR " . $field . ' IS NULL';
}
$minus_conditions[] = '(' . implode(') AND (', $condition) . ')';
break;
case '':
$normal_conditions[] = implode(" LIKE '%" . $keyword . "%' OR ", $fields) . " LIKE '%" . $keyword . "%'";
break;
}
}
// building where clause
if ($normal_conditions) {
$where_clause = '(' . implode(') OR (', $normal_conditions) . ')';
}
else {
$where_clause = '1';
}
if ($plus_conditions) {
$where_clause = '(' . $where_clause . ') AND (' . implode(') AND (', $plus_conditions) . ')';
}
if ($minus_conditions) {
$where_clause = '(' . $where_clause . ') AND (' . implode(') AND (', $minus_conditions) . ')';
}
return $where_clause;
}
/**
* Returns additional information about search field
*
* @param kDBList $object
* @param string $field_name
* @return Array
*/
function _getFieldInformation(&$object, $field_name)
{
$sql_filter_type = $object->isVirtualField($field_name) ? 'having' : 'where';
$field_options = $object->GetFieldOptions($field_name);
$table_name = '';
$field_type = isset($field_options['type']) ? $field_options['type'] : 'string';
if (preg_match('/(.*)\.(.*)/', $field_name, $regs)) {
$table_name = '`'.$regs[1].'`.'; // field from external table
$field_name = $regs[2];
}
elseif ($sql_filter_type == 'where') {
$table_name = '`'.$object->TableName.'`.'; // field from local table
}
$table_name = ($sql_filter_type == 'where') ? $table_name : '';
// replace wid inside table name to WID_MARK constant value
$is_temp_table = preg_match('/(.*)'.TABLE_PREFIX.'ses_'.$this->Application->GetSID().'(_[\d]+){0,1}_edit_(.*)/', $table_name, $regs);
if ($is_temp_table) {
$table_name = $regs[1].TABLE_PREFIX.'ses_'.EDIT_MARK.'_edit_'.$regs[3]; // edit_mark will be replaced with sid[_main_wid] in AddFilters
}
return Array ($field_name, $field_type, $table_name, $sql_filter_type);
}
/**
* Removes stop words from keyword
*
* @param string $keyword
* @return string
*/
function _removeStopWords($keyword)
{
static $stop_words = Array ();
if (!$stop_words) {
$sql = 'SELECT StopWord
FROM ' . $this->Application->getUnitOption('stop-word', 'TableName') . '
ORDER BY LENGTH(StopWord) DESC, StopWord ASC';
$stop_words = $this->Conn->GetCol($sql);
foreach ($stop_words as $index => $stop_word) {
$stop_words[$index] = '/(^| )' . preg_quote($stop_word, '/') . '( |$)/';
}
}
$keyword = preg_replace($stop_words, ' ', $keyword);
return trim( preg_replace('/[ ]+/', ' ', $keyword) );
}
/**
* Performs new search on a given grid
*
* @param kEvent $event
* @return void
* @access public
*/
public function performSearch($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
// process search keyword
$search_keyword = $this->Application->GetVar($event->getPrefixSpecial(true) . '_search_keyword');
$this->Application->StoreVar($event->getPrefixSpecial() . '_search_keyword', $search_keyword);
$custom_filter = $this->processCustomFilters($event);
if ( !$search_keyword && $custom_filter === false ) {
$this->resetSearch($event);
return ;
}
if ( $search_keyword ) {
$this->processAutomaticFilters($event, $search_keyword, $custom_filter);
}
}
/**
* Creates filtering sql clauses based on given search restrictions
*
* @param kEvent $event
* @param string $search_keyword
* @param Array $custom_filter
* @return void
*/
function processAutomaticFilters($event, $search_keyword, $custom_filter)
{
$grid_name = $this->Application->GetVar('grid_name');
$grids = $this->Application->getUnitOption($event->Prefix, 'Grids');
$search_fields = array_keys($grids[$grid_name]['Fields']);
$search_filter = Array();
$object = $event->getObject();
/* @var $object kDBList */
foreach ($search_fields as $search_field) {
$custom_search = isset($custom_filter[$search_field]);
$filter_data = $this->getSearchClause($object, $search_field, $search_keyword, $custom_search);
if ($filter_data) {
$search_filter[$search_field] = $filter_data;
}
else {
unset($search_filter[$search_field]);
}
}
$this->Application->StoreVar($event->getPrefixSpecial().'_search_filter', serialize($search_filter) );
}
/**
* Returns search clause for any particular field
*
* @param kDBList $object
* @param string $field_name
* @param string $search_keyword what we are searching (false, when building custom filter clause)
* @param string $custom_search already found using custom filter
* @return Array
*/
function getSearchClause(&$object, $field_name, $search_keyword, $custom_search)
{
if ($object->isVirtualField($field_name) && !$object->isCalculatedField($field_name)) {
// Virtual field, that is shown in grid, but it doesn't have corresponding calculated field.
// Happens, when field value is calculated on the fly (during grid display) and it is not searchable.
return '';
}
$search_keywords = $this->splitKeyword($search_keyword);
list ($field_name, $field_type, $table_name, $sql_filter_type) = $this->_getFieldInformation($object, $field_name);
$filter_value = '';
// get field clause by formatter name and/or parameters
$field_options = $object->GetFieldOptions($field_name);
$formatter = getArrayValue($field_options, 'formatter');
switch ($formatter) {
case 'kOptionsFormatter':
$search_keys = Array();
if ($custom_search === false) {
// if keywords passed through simple search filter (on each grid)
$use_phrases = getArrayValue($field_options, 'use_phrases');
$multiple = array_key_exists('multiple', $field_options) && $field_options['multiple'];
foreach ($field_options['options'] as $key => $val) {
$match_to = mb_strtolower($use_phrases ? $this->Application->Phrase($val) : $val);
foreach ($search_keywords as $keyword => $sign) {
// doesn't support wildcards
if (strpos($match_to, mb_strtolower($keyword)) === false) {
if ($sign == '+') {
$filter_value = $table_name.'`'.$field_name.'` = NULL';
break;
}
else {
continue;
}
}
if ($sign == '+' || $sign == '') {
// don't add single quotes to found option ids when multiselect (but escape string anyway)
$search_keys[$key] = $multiple ? $this->Conn->escape($key) : $this->Conn->qstr($key);
}
elseif($sign == '-') {
// if same value if found as exclusive too, then remove from search result
unset($search_keys[$key]);
}
}
}
}
if ($search_keys) {
if ($multiple) {
$filter_value = $table_name.'`'.$field_name.'` LIKE "%|' . implode('|%" OR ' . $table_name.'`'.$field_name.'` LIKE "%|', $search_keys) . '|%"';
}
else {
$filter_value = $table_name.'`'.$field_name.'` IN ('.implode(',', $search_keys).')';
}
}
$field_processed = true;
break;
case 'kDateFormatter':
// if date is searched using direct filter, then do nothing here, otherwise search using LIKE clause
$field_processed = ($custom_search !== false) ? true : false;
break;
default:
$field_processed = false;
break;
}
// if not already processed by formatter, then get clause by field type
if (!$field_processed && $search_keywords) {
switch($field_type)
{
case 'int':
case 'integer':
case 'numeric':
$search_keys = Array();
foreach ($search_keywords as $keyword => $sign) {
if (!is_numeric($keyword) || ($sign == '-')) {
continue;
}
$search_keys[] = $this->Conn->qstr($keyword);
}
if ($search_keys) {
$filter_value = $table_name.'`'.$field_name.'` IN ('.implode(',', $search_keys).')';
}
break;
case 'double':
case 'float':
case 'real':
$search_keys = Array();
foreach ($search_keywords as $keyword => $sign) {
$keyword = str_replace(',', '.', $keyword);
if (!is_numeric($keyword) || ($sign == '-')) continue;
$search_keys[] = 'ABS('.$table_name.'`'.$field_name.'` - '.$this->Conn->qstr($keyword).') <= 0.0001';
}
if ($search_keys) {
$filter_value = '('.implode(') OR (', $search_keys).')';
}
break;
case 'string':
$filter_value = $this->buildWhereClause($search_keyword, Array($table_name.'`'.$field_name.'`'));
break;
}
}
if ($filter_value) {
return Array('type' => $sql_filter_type, 'value' => $filter_value);
}
return false;
}
/**
* Processes custom filters from submit
*
* @param kEvent $event
* @return Array|bool
*/
function processCustomFilters($event)
{
$grid_name = $this->Application->GetVar('grid_name');
// update "custom filter" with values from submit: begin
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name/*, ALLOW_DEFAULT_SETTINGS*/);
if ($custom_filters) {
$custom_filters = unserialize($custom_filters);
$custom_filter = isset($custom_filters[$grid_name]) ? $custom_filters[$grid_name] : Array ();
}
else {
$custom_filter = Array ();
}
// submit format custom_filters[prefix_special][field]
$submit_filters = $this->Application->GetVar('custom_filters');
if ($submit_filters) {
$submit_filters = getArrayValue($submit_filters, $event->getPrefixSpecial(), $grid_name);
if ($submit_filters) {
foreach ($submit_filters as $field_name => $field_options) {
list ($filter_type, $field_value) = each($field_options);
$is_empty = strlen(is_array($field_value) ? implode('', $field_value) : $field_value) == 0;
if ($is_empty) {
if (isset($custom_filter[$field_name])) {
// use isset, because non-existing key will cause "php notice"!
unset($custom_filter[$field_name][$filter_type]); // remove filter
if (!$custom_filter[$field_name]) {
// if no filters left for field, then delete record at all
unset($custom_filter[$field_name]);
}
}
}
else {
$custom_filter[$field_name][$filter_type]['submit_value'] = $field_value;
}
}
}
}
if ($custom_filter) {
$custom_filters[$grid_name] = $custom_filter;
}
else {
unset($custom_filters[$grid_name]);
}
// update "custom filter" with values from submit: end
if (!$custom_filter) {
// in case when no filters specified, there are nothing to process
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name, serialize($custom_filters) );
return false;
}
$object = $event->getObject(); // don't recall it each time in getCustomFilterSearchClause
$grid_info = $this->Application->getUnitOption($event->Prefix.'.'.$grid_name, 'Grids');
foreach ($custom_filter as $field_name => $field_options) {
list ($filter_type, $field_options) = each($field_options);
$field_options['grid_options'] = $grid_info['Fields'][$field_name];
$field_options = $this->getCustomFilterSearchClause($object, $field_name, $filter_type, $field_options);
if ($field_options['value']) {
unset($field_options['grid_options']);
$custom_filter[$field_name][$filter_type] = $field_options;
}
}
$custom_filters[$grid_name] = $custom_filter;
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name, serialize($custom_filters) );
return $custom_filter;
}
/**
* Checks, that range filters "To" part is defined for given grid
*
* @param string $prefix_special
* @param string $grid_name
* @return bool
*/
function rangeFiltersUsed($prefix_special, $grid_name)
{
static $cache = Array ();
$cache_key = $prefix_special . $grid_name;
if (array_key_exists($cache_key, $cache)) {
return $cache[$cache_key];
}
$view_name = $this->Application->RecallVar($prefix_special . '_current_view');
$custom_filters = $this->Application->RecallPersistentVar($prefix_special . '_custom_filter.' . $view_name/*, ALLOW_DEFAULT_SETTINGS*/);
if (!$custom_filters) {
// filters not defined for given prefix
$cache[$cache_key] = false;
return false;
}
$custom_filters = unserialize($custom_filters);
if (!is_array($custom_filters) || !array_key_exists($grid_name, $custom_filters)) {
// filters not defined for given grid
$cache[$cache_key] = false;
return false;
}
$range_filter_defined = false;
$custom_filter = $custom_filters[$grid_name];
foreach ($custom_filter as $field_name => $field_options) {
list ($filter_type, $field_options) = each($field_options);
if (strpos($filter_type, 'range') === false) {
continue;
}
$to_value = (string)$field_options['submit_value']['to'];
if ($to_value !== '') {
$range_filter_defined = true;
break;
}
}
$cache[$cache_key] = $range_filter_defined;
return $range_filter_defined;
}
/**
* Return numeric range filter value + checking that it's number
*
* @param Array $value array containing range filter value
* @return unknown
*/
function getRangeValue($value)
{
// fix user typing error, since MySQL only sees "." as decimal separator
$value = str_replace(',', '.', $value);
return strlen($value) && is_numeric($value) ? $this->Conn->qstr($value) : false;
}
/**
* Returns filter clause
*
* @param kDBItem $object
* @param string $field_name
* @param string $filter_type
* @param Array $field_options
* @return Array
*/
function getCustomFilterSearchClause(&$object, $field_name, $filter_type, $field_options)
{
// this is usually used for mutlilingual fields and date fields
if (isset($field_options['grid_options']['sort_field'])) {
$field_name = $field_options['grid_options']['sort_field'];
}
list ($field_name, $field_type, $table_name, $sql_filter_type) = $this->_getFieldInformation($object, $field_name);
$filter_value = '';
switch ($filter_type) {
case 'range':
$from = $this->getRangeValue($field_options['submit_value']['from']);
$to = $this->getRangeValue($field_options['submit_value']['to']);
if ( $from !== false && $to !== false ) {
// add range filter
$filter_value = $table_name . '`' . $field_name . '` >= ' . $from . ' AND ' . $table_name . '`' . $field_name . '` <= ' . $to;
}
elseif ( $field_type == 'int' || $field_type == 'integer' ) {
if ( $from !== false ) {
// add equals filter on $from
$filter_value = $table_name . '`' . $field_name . '` = ' . $from;
}
elseif ( $to !== false ) {
// add equals filter on $to
$filter_value = $table_name . '`' . $field_name . '` = ' . $to;
}
}
else {
// MySQL can't compare values in "float" type columns using "=" operator
if ( $from !== false ) {
// add equals filter on $from
$filter_value = 'ABS(' . $table_name . '`' . $field_name . '` - ' . $from . ') <= 0.0001';
}
elseif ( $to !== false ) {
// add equals filter on $to
$filter_value = 'ABS(' . $table_name . '`' . $field_name . '` - ' . $to . ') <= 0.0001';
}
}
break;
case 'date_range':
$from = $this->processRangeField($object, $field_name, $field_options['submit_value'], 'from');
$to = $this->processRangeField($object, $field_name, $field_options['submit_value'], 'to');
$day_seconds = 23 * 60 * 60 + 59 * 60 + 59;
if ($from !== false && $to === false) {
$from = strtotime(date('Y-m-d', $from) . ' 00:00:00', $from); // reset to morning
$to = $from + $day_seconds;
}
elseif ($from === false && $to !== false) {
$to = strtotime(date('Y-m-d', $to) . ' 23:59:59', $to); // reset to evening
$from = $to - $day_seconds;
}
if ($from !== false && $to !== false) {
$filter_value = $table_name.'`'.$field_name.'` >= '.$from.' AND '.$table_name.'`'.$field_name.'` <= '.$to;
}
break;
case 'equals':
case 'options':
$field_value = strlen($field_options['submit_value']) ? $this->Conn->qstr($field_options['submit_value']) : false;
if ($field_value) {
$filter_value = $table_name.'`'.$field_name.'` = '.$field_value;
}
break;
case 'picker':
$field_value = strlen($field_options['submit_value']) ? $this->Conn->escape($field_options['submit_value']) : false;
if ($field_value) {
$filter_value = $table_name.'`'.$field_name.'` LIKE "%|'.$field_value.'|%"';
}
break;
case 'multioptions':
$field_value = $field_options['submit_value'];
if ( $field_value ) {
$field_value = explode('|', substr($field_value, 1, -1));
$multiple = $object->GetFieldOption($field_name, 'multiple');
$field_value = $this->Conn->qstrArray($field_value, $multiple ? 'escape' : 'qstr');
if ( $multiple ) {
$filter_value = $table_name . '`' . $field_name . '` LIKE "%|' . implode('|%" OR ' . $table_name . '`' . $field_name . '` LIKE "%|', $field_value) . '|%"';
}
else {
$filter_value = $table_name . '`' . $field_name . '` IN (' . implode(',', $field_value) . ')';
}
}
break;
case 'like':
$filter_value = $this->buildWhereClause($field_options['submit_value'], Array($table_name.'`'.$field_name.'`'));
break;
default:
break;
}
$field_options['sql_filter_type'] = $sql_filter_type;
$field_options['value'] = $filter_value;
return $field_options;
}
/**
* Enter description here...
*
* @param kdbItem $object
* @param string $search_field
* @param string $value
* @param string $type
*/
function processRangeField(&$object, $search_field, $value, $type)
{
if ( !strlen($value[$type]) ) {
return false;
}
$options = $object->GetFieldOptions($search_field);
$dt_separator = array_key_exists('date_time_separator', $options) ? $options['date_time_separator'] : ' ';
$value[$type] = trim($value[$type], $dt_separator); // trim any
$tmp_value = explode($dt_separator, $value[$type], 2);
if ( count($tmp_value) == 1 ) {
$time_format = $this->_getInputTimeFormat($options);
if ( $time_format ) {
// time is missing, but time format available -> guess time and add to date
$time = ($type == 'from') ? adodb_mktime(0, 0, 0) : adodb_mktime(23, 59, 59);
$time = adodb_date($time_format, $time);
$value[$type] .= $dt_separator . $time;
}
}
$formatter = $this->Application->recallObject($options['formatter']);
/* @var $formatter kFormatter */
$value_ts = $formatter->Parse($value[$type], $search_field, $object);
if ( $object->GetErrorPseudo($search_field) ) {
// invalid format -> ignore this date in search
$object->RemoveError($search_field);
return false;
}
return $value_ts;
}
/**
* Returns InputTimeFormat using given field options
*
* @param Array $field_options
* @return string
*/
function _getInputTimeFormat($field_options)
{
if ( array_key_exists('input_time_format', $field_options) ) {
return $field_options['input_time_format'];
}
$lang_current = $this->Application->recallObject('lang.current');
/* @var $lang_current LanguagesItem */
return $lang_current->GetDBField('InputTimeFormat');
}
/**
* Resets current search
*
* @param kEvent $event
*/
function resetSearch($event)
{
$this->Application->RemoveVar($event->getPrefixSpecial().'_search_filter');
$this->Application->RemoveVar($event->getPrefixSpecial().'_search_keyword');
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$this->Application->RemovePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name);
}
/**
* Creates filters based on "types" & "except" parameters from PrintList
*
* @param kEvent $event
* @param Array $type_clauses
* @param string $types
* @param string $except_types
*/
function SetComplexFilter($event, &$type_clauses, $types, $except_types)
{
$includes_or_filter = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_OR));
/* @var $includes_or_filter kMultipleFilter */
$excepts_and_filter = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_AND));
/* @var $excepts_and_filter kMultipleFilter */
$includes_or_filter_h = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_OR));
/* @var $includes_or_filter_h kMultipleFilter */
$excepts_and_filter_h = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_AND));
/* @var $excepts_and_filter_h kMultipleFilter */
if ( $types ) {
$types = explode(',', $types);
foreach ($types as $type) {
$type = trim($type);
if ( isset($type_clauses[$type]) ) {
if ( $type_clauses[$type]['having_filter'] ) {
$includes_or_filter_h->addFilter('filter_' . $type, $type_clauses[$type]['include']);
}
else {
$includes_or_filter->addFilter('filter_' . $type, $type_clauses[$type]['include']);
}
}
}
}
if ( $except_types ) {
$except_types = explode(',', $except_types);
foreach ($except_types as $type) {
$type = trim($type);
if ( isset($type_clauses[$type]) ) {
if ( $type_clauses[$type]['having_filter'] ) {
$excepts_and_filter_h->addFilter('filter_' . $type, $type_clauses[$type]['except']);
}
else {
$excepts_and_filter->addFilter('filter_' . $type, $type_clauses[$type]['except']);
}
}
}
}
$object = $event->getObject();
/* @var $object kDBList */
$object->addFilter('includes_filter', $includes_or_filter);
$object->addFilter('excepts_filter', $excepts_and_filter);
$object->addFilter('includes_filter_h', $includes_or_filter_h, kDBList::HAVING_FILTER);
$object->addFilter('excepts_filter_h', $excepts_and_filter_h, kDBList::HAVING_FILTER);
}
- }
\ No newline at end of file
+
+ /**
+ * Ensures empty search table
+ *
+ * @return void
+ */
+ public function ensureEmptySearchTable()
+ {
+ $search_table = $this->getSearchTable();
+ $this->Conn->Query('DROP TABLE IF EXISTS ' . $search_table);
+ $sql = 'CREATE TABLE ' . $search_table . ' (
+ `Relevance` decimal(8,5) DEFAULT NULL,
+ `ItemId` int(11) NOT NULL DEFAULT 0,
+ `ResourceId` int(11) DEFAULT NULL,
+ `ItemType` int(1) NOT NULL DEFAULT 0,
+ `EdPick` tinyint(4) NOT NULL DEFAULT 0,
+ KEY `ResourceId` (`ResourceId`),
+ KEY `Relevance` (`Relevance`)
+ )';
+ $this->Conn->Query($sql);
+ }
+
+ /**
+ * Search table name
+ *
+ * @return string
+ */
+ public function getSearchTable()
+ {
+ return TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search';
+ }
+
+ }

Event Timeline