Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:08 AM

in-portal

Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php (revision 16562)
+++ branches/5.2.x/core/kernel/application.php (revision 16563)
@@ -1,3116 +1,3102 @@
<?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!');
/**
* Basic class for Kernel4-based Application
*
* This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
* The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
* <br>
* The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
* This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method,
* which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br>
* See singleton pattern by GOF.
*/
class kApplication implements kiCacheable {
/**
* Location of module helper class (used in installator too)
*/
const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php';
/**
* Is true, when Init method was called already, prevents double initialization
*
* @var bool
*/
public $InitDone = false;
/**
* Holds internal NParser object
*
* @var NParser
* @access public
*/
public $Parser;
/**
* Holds parser output buffer
*
* @var string
* @access protected
*/
protected $HTML = '';
/**
* The main Factory used to create
* almost any class of kernel and
* modules
*
* @var kFactory
* @access protected
*/
protected $Factory;
/**
* Template names, that will be used instead of regular templates
*
* @var Array
* @access public
*/
public $ReplacementTemplates = Array ();
/**
* Mod-Rewrite listeners used during url building and parsing
*
* @var Array
* @access public
*/
public $RewriteListeners = Array ();
/**
* Reference to debugger
*
* @var Debugger
* @access public
*/
public $Debugger = null;
/**
* Holds all phrases used
* in code and template
*
* @var PhrasesCache
* @access public
*/
public $Phrases;
/**
* Modules table content, key - module name
*
* @var Array
* @access public
*/
public $ModuleInfo = Array ();
/**
* Holds DBConnection
*
* @var IDBConnection
* @access public
*/
public $Conn = null;
/**
* Reference to event log
*
* @var Array|kLogger
* @access public
*/
protected $_logger = Array ();
// performance needs:
/**
* Holds a reference to httpquery
*
* @var kHttpQuery
* @access public
*/
public $HttpQuery = null;
/**
* Holds a reference to UnitConfigReader
*
* @var kUnitConfigReader
* @access public
*/
public $UnitConfigReader = null;
/**
* Holds a reference to Session
*
* @var Session
* @access public
*/
public $Session = null;
/**
* Holds a ref to kEventManager
*
* @var kEventManager
* @access public
*/
public $EventManager = null;
/**
* Holds a ref to kUrlManager
*
* @var kUrlManager
* @access public
*/
public $UrlManager = null;
/**
* Ref for TemplatesCache
*
* @var TemplatesCache
* @access public
*/
public $TemplatesCache = null;
/**
* Holds current NParser tag while parsing, can be used in error messages to display template file and line
*
* @var _BlockTag
* @access public
*/
public $CurrentNTag = null;
/**
* Object of unit caching class
*
* @var kCacheManager
* @access public
*/
public $cacheManager = null;
/**
* Tells, that administrator has authenticated in administrative console
* Should be used to manipulate data change OR data restrictions!
*
* @var bool
* @access public
*/
public $isAdminUser = false;
/**
* Tells, that admin version of "index.php" was used, nothing more!
* Should be used to manipulate data display!
*
* @var bool
* @access public
*/
public $isAdmin = false;
/**
* Instance of site domain object
*
* @var kDBItem
* @access public
* @todo move away into separate module
*/
public $siteDomain = null;
/**
* Prevent kApplication class to be created directly, only via Instance method
*
* @access private
*/
private function __construct()
{
}
final private function __clone() {}
/**
* Returns kApplication instance anywhere in the script.
*
* This method should be used to get single kApplication object instance anywhere in the
* Kernel-based application. The method is guaranteed to return the SAME instance of kApplication.
* Anywhere in the script you could write:
* <code>
* $application =& kApplication::Instance();
* </code>
* or in an object:
* <code>
* $this->Application =& kApplication::Instance();
* </code>
* to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
* To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
* BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
* create and return default KernelApplication instance.
*
* Pattern: Singleton
*
* @static
* @return kApplication
* @access public
*/
public static function &Instance()
{
static $instance = false;
if ( !$instance ) {
$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
$instance = new $class();
}
return $instance;
}
/**
* Initializes the Application
*
* @param string $factory_class
* @return bool Was Init actually made now or before
* @access public
* @see kHTTPQuery
* @see Session
* @see TemplatesCache
*/
public function Init($factory_class = 'kFactory')
{
if ( $this->InitDone ) {
return false;
}
if ( preg_match('/utf-8/i', CHARSET) ) {
setlocale(LC_ALL, 'en_US.UTF-8');
mb_internal_encoding('UTF-8');
}
$this->isAdmin = kUtil::constOn('ADMIN');
if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
ob_start(); // collect any output from method (other then tags) into buffer
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Init:');
}
$this->_logger = new kLogger($this->_logger);
$this->Factory = new $factory_class();
$this->registerDefaultClasses();
$system_config = new kSystemConfig(true);
$vars = $system_config->getData();
$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection');
$this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array ($this->_logger, 'handleSQLError')));
$this->Conn->setup($vars);
$this->cacheManager = $this->makeClass('kCacheManager');
$this->cacheManager->InitCache();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Before UnitConfigReader');
}
// init config reader and all managers
$this->UnitConfigReader = $this->makeClass('kUnitConfigReader');
$this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read
$this->registerModuleConstants();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('After UnitConfigReader');
}
define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
// start processing request
$this->HttpQuery = $this->recallObject('HTTPQuery');
$this->HttpQuery->process();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
}
$this->Session = $this->recallObject('Session');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed Session');
}
$this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
}
$this->cacheManager->LoadApplicationCache();
$site_timezone = $this->ConfigValue('Config_Site_Time');
if ( $site_timezone ) {
date_default_timezone_set($site_timezone);
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Loaded cache and phrases');
}
$this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
$this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed AfterConfigRead');
}
if ( $this->GetVar('m_cat_id') === false ) {
$this->SetVar('m_cat_id', 0);
}
if ( !$this->RecallVar('curr_iso') && !(defined('IS_INSTALL') && IS_INSTALL) ) {
$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
}
$visit_id = $this->RecallVar('visit_id');
if ( $visit_id !== false ) {
$this->SetVar('visits_id', $visit_id);
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->profileFinish('kernel4_startup');
}
$this->InitDone = true;
if ( PHP_SAPI !== 'cli' && !$this->isAdmin ) {
$this->HandleEvent(new kEvent('adm:OnStartup'));
}
return true;
}
/**
* Performs initialization of manager classes, that can be overridden from unit configs
*
* @return void
* @access public
* @throws Exception
*/
public function InitManagers()
{
if ( $this->InitDone ) {
throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
return;
}
$this->UrlManager = $this->makeClass('kUrlManager');
$this->EventManager = $this->makeClass('EventManager');
$this->Phrases = $this->makeClass('kPhraseCache');
$this->RegisterDefaultBuildEvents();
}
/**
* Returns module information. Searches module by requested field
*
* @param string $field
* @param mixed $value
* @param string $return_field field value to returns, if not specified, then return all fields
* @return Array
*/
public function findModule($field, $value, $return_field = null)
{
$found = $module_info = false;
foreach ($this->ModuleInfo as $module_info) {
if ( strtolower($module_info[$field]) == strtolower($value) ) {
$found = true;
break;
}
}
if ( $found ) {
return isset($return_field) ? $module_info[$return_field] : $module_info;
}
return false;
}
/**
* Refreshes information about loaded modules
*
* @return void
* @access public
*/
public function refreshModuleInfo()
{
if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) {
$this->registerModuleConstants();
return;
}
// use makeClass over recallObject, since used before kApplication initialization during installation
/** @var kModulesHelper $modules_helper */
$modules_helper = $this->makeClass('ModulesHelper');
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Modules
WHERE ' . $modules_helper->getWhereClause() . '
ORDER BY LoadOrder';
$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
$this->registerModuleConstants();
}
/**
* Checks if passed language id if valid and sets it to primary otherwise
*
* @return void
* @access public
*/
public function VerifyLanguageId()
{
/** @var LanguagesItem $lang */
$lang = $this->recallObject('lang.current');
if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) {
if ( !defined('IS_INSTALL') ) {
$this->ApplicationDie('Unknown or disabled language');
}
}
}
/**
* Checks if passed theme id if valid and sets it to primary otherwise
*
* @return void
* @access public
*/
public function VerifyThemeId()
{
if ( $this->isAdmin ) {
kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
return;
}
$path = $this->GetFrontThemePath();
if ( $path === false ) {
$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
}
kUtil::safeDefine('THEMES_PATH', $path);
}
/**
* Returns relative path to current front-end theme
*
* @param bool $force
* @return string
* @access public
*/
public function GetFrontThemePath($force = false)
{
static $path = null;
if ( !$force && isset($path) ) {
return $path;
}
/** @var ThemeItem $theme */
$theme = $this->recallObject('theme.current');
if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) {
return false;
}
// assign & then return, since it's static variable
$path = '/themes/' . $theme->GetDBField('Name');
return $path;
}
/**
* Returns primary front/admin language id
*
* @param bool $init
* @return int
* @access public
*/
public function GetDefaultLanguageId($init = false)
{
$cache_key = 'primary_language_info[%LangSerial%]';
$language_info = $this->getCache($cache_key);
if ( $language_info === false ) {
// cache primary language info first
$table = $this->getUnitOption('lang', 'TableName');
$id_field = $this->getUnitOption('lang', 'IDField');
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
FROM ' . $table . '
WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
if ( $language_info !== false ) {
$this->setCache($cache_key, $language_info);
}
}
$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) {
// get from cache
return $language_info[$language_key];
}
$language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false;
if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) {
$language_id = 1;
}
return $language_id;
}
/**
* Returns front-end primary theme id (even, when called from admin console)
*
* @param bool $force_front
* @return int
* @access public
*/
public function GetDefaultThemeId($force_front = false)
{
static $theme_id = 0;
if ( $theme_id > 0 ) {
return $theme_id;
}
if ( kUtil::constOn('DBG_FORCE_THEME') ) {
$theme_id = DBG_FORCE_THEME;
}
elseif ( !$force_front && $this->isAdmin ) {
$theme_id = 999;
}
else {
$cache_key = 'primary_theme[%ThemeSerial%]';
$theme_id = $this->getCache($cache_key);
if ( $theme_id === false ) {
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
FROM ' . $this->getUnitOption('theme', 'TableName') . '
WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
$theme_id = $this->Conn->GetOne($sql);
if ( $theme_id !== false ) {
$this->setCache($cache_key, $theme_id);
}
}
}
return $theme_id;
}
/**
* Returns site primary currency ISO code
*
* @return string
* @access public
* @todo Move into In-Commerce
*/
public function GetPrimaryCurrency()
{
$cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId');
$currency_iso = $this->getCache($cache_key);
if ( $currency_iso === false ) {
if ( $this->prefixRegistred('curr') ) {
$this->Conn->nextQueryCachable = true;
$currency_id = $this->siteDomainField('PrimaryCurrencyId');
$sql = 'SELECT ISO
FROM ' . $this->getUnitOption('curr', 'TableName') . '
WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1');
$currency_iso = $this->Conn->GetOne($sql);
}
else {
$currency_iso = 'USD';
}
$this->setCache($cache_key, $currency_iso);
}
return $currency_iso;
}
/**
* Returns site domain field. When none of site domains are found false is returned.
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return mixed
* @todo Move into separate module
*/
public function siteDomainField($field, $formatted = false, $format = null)
{
if ( $this->isAdmin ) {
// don't apply any filtering in administrative console
return false;
}
if ( !$this->siteDomain ) {
$this->siteDomain = $this->recallObject('site-domain.current', null, Array ('live_table' => true));
/** @var kDBItem $site_domain */
}
if ( $this->siteDomain->isLoaded() ) {
return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field);
}
return false;
}
/**
* Registers default classes such as kDBEventHandler, kUrlManager
*
* Called automatically while initializing kApplication
*
* @return void
* @access public
*/
public function RegisterDefaultClasses()
{
$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
$this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php');
$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php');
$this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php');
$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php');
$this->registerClass('kSubscriptionManager', KERNEL_PATH . '/managers/subscription_manager.php');
$this->registerClass('kSubscriptionItem', KERNEL_PATH . '/managers/subscription_manager.php');
$this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php');
$this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php');
$this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php');
$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php');
$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php');
$this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache');
$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
$this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php');
$this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php');
$this->registerClass('kLogger', KERNEL_PATH . '/utility/logger.php');
$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
$this->registerClass('PasswordHash', KERNEL_PATH . '/utility/php_pass.php');
// Params class descendants
$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery');
// session
$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage');
// template parser
$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor');
$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php');
$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php');
// database
$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php');
$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
// email sending
$this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php');
$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender');
$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
// do not move to config - this helper is used before configs are read
$this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper');
$this->registerClass('CKEditor', FULL_PATH . '/core/ckeditor/ckeditor_php5.php');
}
/**
* Registers default build events
*
* @return void
*/
public function RegisterDefaultBuildEvents()
{
$this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
}
/**
* Returns cached category information by given cache name. All given category
* information is recached, when at least one of 4 caches is missing.
*
* @param int $category_id
* @param string $name cache name = {filenames, category_designs, category_tree}
* @return string
* @access public
*/
public function getCategoryCache($category_id, $name)
{
return $this->cacheManager->getCategoryCache($category_id, $name);
}
/**
* Returns caching type (none, memory, temporary)
*
* @param int $caching_type
* @return bool
* @access public
*/
public function isCachingType($caching_type)
{
return $this->cacheManager->isCachingType($caching_type);
}
/**
* Increments serial based on prefix and it's ID (optional)
*
* @param string $prefix
* @param int $id ID (value of IDField) or ForeignKeyField:ID
* @param bool $increment
* @return string
* @access public
*/
public function incrementCacheSerial($prefix, $id = null, $increment = true)
{
return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
}
/**
* Returns cached $key value from cache named $cache_name
*
* @param int $key key name from cache
* @param bool $store_locally store data locally after retrieved
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds);
}
/**
* Stores new $value in cache with $key name
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function setCache($key, $value, $expiration = 0)
{
return $this->cacheManager->setCache($key, $value, $expiration);
}
/**
* Stores new $value in cache with $key name (only if it's not there)
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function addCache($key, $value, $expiration = 0)
{
return $this->cacheManager->addCache($key, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from cache
*
* @param string $key
* @return void
* @access public
*/
public function deleteCache($key)
{
$this->cacheManager->deleteCache($key);
}
/**
* Reset's all memory cache at once
*
* @return void
* @access public
*/
public function resetCache()
{
$this->cacheManager->resetCache();
}
/**
* Returns value from database cache
*
* @param string $name key name
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getDBCache($name, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getDBCache($name, $max_rebuild_seconds);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @return void
* @access public
*/
public function setDBCache($name, $value, $expiration = false)
{
$this->cacheManager->setDBCache($name, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from database cache
*
* @param string $name
* @return void
* @access public
*/
public function deleteDBCache($name)
{
$this->cacheManager->deleteDBCache($name);
}
/**
* Registers each module specific constants if any found
*
* @return bool
* @access protected
*/
protected function registerModuleConstants()
{
if ( file_exists(KERNEL_PATH . '/constants.php') ) {
kUtil::includeOnce(KERNEL_PATH . '/constants.php');
}
if ( !$this->ModuleInfo ) {
return false;
}
foreach ($this->ModuleInfo as $module_info) {
$constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
if ( file_exists($constants_file) ) {
kUtil::includeOnce($constants_file);
}
}
return true;
}
/**
* Performs redirect to hard maintenance template
*
* @return void
* @access public
*/
public function redirectToMaintenance()
{
$maintenance_page = WRITEBALE_BASE . '/maintenance.html';
$query_string = ''; // $this->isAdmin ? '' : '?next_template=' . kUtil::escape($_SERVER['REQUEST_URI'], kUtil::ESCAPE_URL);
if ( file_exists(FULL_PATH . $maintenance_page) ) {
header('Location: ' . BASE_PATH . $maintenance_page . $query_string);
exit;
}
}
/**
* Actually runs the parser against current template and stores parsing result
*
* This method gets 't' variable passed to the script, loads the template given in 't' variable and
* parses it. The result is store in {@link $this->HTML} property.
*
* @return void
* @access public
*/
public function Run()
{
// process maintenance mode redirect: begin
$maintenance_mode = $this->getMaintenanceMode();
if ( $maintenance_mode == MaintenanceMode::HARD ) {
$this->redirectToMaintenance();
}
elseif ( $maintenance_mode == MaintenanceMode::SOFT ) {
$maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate');
if ( $this->GetVar('t') != $maintenance_template ) {
$redirect_params = Array ();
if ( !$this->isAdmin ) {
$redirect_params['next_template'] = $_SERVER['REQUEST_URI'];
}
$this->Redirect($maintenance_template, $redirect_params);
}
}
// process maintenance mode redirect: end
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Run:');
}
if ( $this->isAdminUser ) {
// for permission checking in events & templates
$this->LinkVar('module'); // for common configuration templates
$this->LinkVar('module_key'); // for common search templates
$this->LinkVar('section'); // for common configuration templates
if ( $this->GetVar('m_opener') == 'p' ) {
$this->LinkVar('main_prefix'); // window prefix, that opened selector
$this->LinkVar('dst_field'); // field to set value choosed in selector
}
if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) {
// hide debug output from ajax requests automatically
kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
}
}
- elseif ( $this->GetVar('admin') ) {
- /** @var Session $admin_session */
- $admin_session = $this->recallObject('Session.admin');
- // store Admin Console User's ID to Front-End's session for cross-session permission checks
- $this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
-
- if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
- // user can edit cms blocks (when viewing front-end through admin's frame)
- $editing_mode = $this->GetVar('editing_mode');
- define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
- }
- }
-
- kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything
$this->Phrases->setPhraseEditing();
$this->EventManager->ProcessRequest();
$this->InitParser();
$t = $this->GetVar('render_template', $this->GetVar('t'));
if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) {
/** @var CategoriesEventHandler $cms_handler */
$cms_handler = $this->recallObject('st_EventHandler');
$t = ltrim($cms_handler->GetDesignTemplate(), '/');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
}
}
/*else {
$cms_handler->SetCatByTemplate();
}*/
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Parsing:');
}
$this->HTML = $this->Parser->Run($t);
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application after Parsing:');
}
}
/**
* Replaces current rendered template with given one.
*
* @param string|null $template Template.
*
* @return void
*/
public function QuickRun($template)
{
/** @var kThemesHelper $themes_helper */
$themes_helper = $this->recallObject('ThemesHelper');
// Set Web Request variables to affect link building on template itself.
$this->SetVar('t', $template);
$this->SetVar('m_cat_id', $themes_helper->getPageByTemplate($template));
$this->SetVar('passed', 'm');
// Replace current page content with given template.
$this->InitParser();
$this->Parser->Clear();
$this->HTML = $this->Parser->Run($template);
}
/**
* Performs template parser/cache initialization
*
* @param bool|string $theme_name
* @return void
* @access public
*/
public function InitParser($theme_name = false)
{
if ( !is_object($this->Parser) ) {
$this->Parser = $this->recallObject('NParser');
$this->TemplatesCache = $this->recallObject('TemplatesCache');
}
$this->TemplatesCache->forceThemeName = $theme_name;
}
/**
* Send the parser results to browser
*
* Actually send everything stored in {@link $this->HTML}, to the browser by echoing it.
*
* @return void
* @access public
*/
public function Done()
{
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
$debug_mode = defined('DEBUG_MODE') && $this->isDebugMode();
if ( $debug_mode ) {
if ( kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Done:');
}
$this->Session->SaveData(); // adds session data to debugger report
$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
}
else {
// send "Set-Cookie" header before any output is made
$this->Session->SetSession();
$this->HTML = ob_get_clean() . $this->HTML;
}
$this->_outputPage();
$this->cacheManager->UpdateApplicationCache();
if ( !$debug_mode ) {
$this->Session->SaveData();
}
$this->EventManager->runScheduledTasks();
if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) {
$this->_storeStatistics();
}
}
/**
* Outputs generated page content to end-user
*
* @return void
* @access protected
*/
protected function _outputPage()
{
$this->setContentType();
ob_start();
if ( $this->UseOutputCompression() ) {
$compression_level = $this->ConfigValue('OutputCompressionLevel');
if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) {
$compression_level = 7;
}
header('Content-Encoding: gzip');
echo gzencode($this->HTML, $compression_level);
}
else {
// when gzip compression not used connection won't be closed early!
echo $this->HTML;
}
// send headers to tell the browser to close the connection
header('Content-Length: ' . ob_get_length());
header('Connection: close');
// flush all output
ob_end_flush();
if ( ob_get_level() ) {
ob_flush();
}
flush();
// close current session
if ( session_id() ) {
session_write_close();
}
}
/**
* Stores script execution statistics to database
*
* @return void
* @access protected
*/
protected function _storeStatistics()
{
global $start;
$script_time = microtime(true) - $start;
$query_statistics = $this->Conn->getQueryStatistics(); // time & count
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'StatisticsCapture
WHERE TemplateName = ' . $this->Conn->qstr($this->GetVar('t'));
$data = $this->Conn->GetRow($sql);
if ( $data ) {
$this->_updateAverageStatistics($data, 'ScriptTime', $script_time);
$this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']);
$this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']);
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']);
}
else {
$data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time;
$data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time'];
$data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count'];
$data['TemplateName'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture');
}
}
/**
* Calculates average time for statistics
*
* @param Array $data
* @param string $field_prefix
* @param float $current_value
* @return void
* @access protected
*/
protected function _updateAverageStatistics(&$data, $field_prefix, $current_value)
{
$data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1);
if ( $current_value < $data[$field_prefix . 'Min'] ) {
$data[$field_prefix . 'Min'] = $current_value;
}
if ( $current_value > $data[$field_prefix . 'Max'] ) {
$data[$field_prefix . 'Max'] = $current_value;
}
}
/**
* Remembers slow query SQL and execution time into log
*
* @param string $slow_sql
* @param int $time
* @return void
* @access public
*/
public function logSlowQuery($slow_sql, $time)
{
$query_crc = kUtil::crc32($slow_sql);
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'SlowSqlCapture
WHERE QueryCrc = ' . $query_crc;
$data = $this->Conn->Query($sql, null, true);
if ( $data ) {
$this->_updateAverageStatistics($data, 'Time', $time);
$template_names = explode(',', $data['TemplateNames']);
array_push($template_names, $this->GetVar('t'));
$data['TemplateNames'] = implode(',', array_unique($template_names));
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']);
}
else {
$data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time;
$data['SqlQuery'] = $slow_sql;
$data['QueryCrc'] = $query_crc;
$data['TemplateNames'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture');
}
}
/**
* Checks if output compression options is available
*
* @return bool
* @access protected
*/
protected function UseOutputCompression()
{
if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
return false;
}
$accept_encoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($accept_encoding, 'gzip');
}
// Facade
/**
* Returns current session id (SID)
*
* @return int
* @access public
*/
public function GetSID()
{
/** @var Session $session */
$session = $this->recallObject('Session');
return $session->GetID();
}
/**
* Destroys current session
*
* @return void
* @access public
* @see UserHelper::logoutUser()
*/
public function DestroySession()
{
/** @var Session $session */
$session = $this->recallObject('Session');
$session->Destroy();
}
/**
* Returns variable passed to the script as GET/POST/COOKIE
*
* @param string $name Name of variable to retrieve
* @param mixed $default default value returned in case if variable not present
* @return mixed
* @access public
*/
public function GetVar($name, $default = false)
{
return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default;
}
/**
* Removes forceful escaping done to the variable upon Front-End submission.
*
* @param string|array $value Value.
*
* @return string|array
* @see kHttpQuery::StripSlashes
* @todo Temporary method for marking problematic places to take care of, when forceful escaping will be removed.
*/
public function unescapeRequestVariable($value)
{
return $this->HttpQuery->unescapeRequestVariable($value);
}
/**
* Returns variable passed to the script as $type
*
* @param string $name Name of variable to retrieve
* @param string $type Get/Post/Cookie
* @param mixed $default default value returned in case if variable not present
* @return mixed
* @access public
*/
public function GetVarDirect($name, $type, $default = false)
{
// $type = ucfirst($type);
$array = $this->HttpQuery->$type;
return isset($array[$name]) ? $array[$name] : $default;
}
/**
* Returns ALL variables passed to the script as GET/POST/COOKIE
*
* @return Array
* @access public
* @deprecated
*/
public function GetVars()
{
return $this->HttpQuery->GetParams();
}
/**
* Set the variable 'as it was passed to the script through GET/POST/COOKIE'
*
* This could be useful to set the variable when you know that
* other objects would relay on variable passed from GET/POST/COOKIE
* or you could use SetVar() / GetVar() pairs to pass the values between different objects.<br>
*
* @param string $var Variable name to set
* @param mixed $val Variable value
* @return void
* @access public
*/
public function SetVar($var,$val)
{
$this->HttpQuery->Set($var, $val);
}
/**
* Deletes kHTTPQuery variable
*
* @param string $var
* @return void
* @todo Think about method name
*/
public function DeleteVar($var)
{
$this->HttpQuery->Remove($var);
}
/**
* Deletes Session variable
*
* @param string $var
* @return void
* @access public
*/
public function RemoveVar($var)
{
$this->Session->RemoveVar($var);
}
/**
* Removes variable from persistent session
*
* @param string $var
* @return void
* @access public
*/
public function RemovePersistentVar($var)
{
$this->Session->RemovePersistentVar($var);
}
/**
* Restores Session variable to it's db version
*
* @param string $var
* @return void
* @access public
*/
public function RestoreVar($var)
{
$this->Session->RestoreVar($var);
}
/**
* Returns session variable value
*
* Return value of $var variable stored in Session. An optional default value could be passed as second parameter.
*
* @param string $var Variable name
* @param mixed $default Default value to return if no $var variable found in session
* @return mixed
* @access public
* @see Session::RecallVar()
*/
public function RecallVar($var,$default=false)
{
return $this->Session->RecallVar($var,$default);
}
/**
* Returns variable value from persistent session
*
* @param string $var
* @param mixed $default
* @return mixed
* @access public
* @see Session::RecallPersistentVar()
*/
public function RecallPersistentVar($var, $default = false)
{
return $this->Session->RecallPersistentVar($var, $default);
}
/**
* Stores variable $val in session under name $var
*
* Use this method to store variable in session. Later this variable could be recalled.
*
* @param string $var Variable name
* @param mixed $val Variable value
* @param bool $optional
* @return void
* @access public
* @see kApplication::RecallVar()
*/
public function StoreVar($var, $val, $optional = false)
{
/** @var Session $session */
$session = $this->recallObject('Session');
$this->Session->StoreVar($var, $val, $optional);
}
/**
* Stores variable to persistent session
*
* @param string $var
* @param mixed $val
* @param bool $optional
* @return void
* @access public
*/
public function StorePersistentVar($var, $val, $optional = false)
{
$this->Session->StorePersistentVar($var, $val, $optional);
}
/**
* Stores default value for session variable
*
* @param string $var
* @param string $val
* @param bool $optional
* @return void
* @access public
* @see Session::RecallVar()
* @see Session::StoreVar()
*/
public function StoreVarDefault($var, $val, $optional = false)
{
/** @var Session $session */
$session = $this->recallObject('Session');
$this->Session->StoreVarDefault($var, $val, $optional);
}
/**
* Links HTTP Query variable with session variable
*
* If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session.
* This method could be used for making sure that GetVar will return query or session value for given
* variable, when query variable should overwrite session (and be stored there for later use).<br>
* This could be used for passing item's ID into popup with multiple tab -
* in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id').
* After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session
*
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
* @param bool $optional
* @return void
* @access public
*/
public function LinkVar($var, $ses_var = null, $default = '', $optional = false)
{
if ( !isset($ses_var) ) {
$ses_var = $var;
}
if ( $this->GetVar($var) !== false ) {
$this->StoreVar($ses_var, $this->GetVar($var), $optional);
}
else {
$this->SetVar($var, $this->RecallVar($ses_var, $default));
}
}
/**
* Returns variable from HTTP Query, or from session if not passed in HTTP Query
*
* The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed.
* Returns the default value if variable does not exist in session and was not passed in HTTP Query
*
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
* @return mixed
* @access public
* @see LinkVar
*/
public function GetLinkedVar($var, $ses_var = null, $default = '')
{
$this->LinkVar($var, $ses_var, $default);
return $this->GetVar($var);
}
/**
* Renders given tag and returns it's output
*
* @param string $prefix
* @param string $tag
* @param Array $params
* @return mixed
* @access public
* @see kApplication::InitParser()
*/
public function ProcessParsedTag($prefix, $tag, $params)
{
/** @var kDBTagProcessor $processor */
$processor = $this->Parser->GetProcessor($prefix);
return $processor->ProcessParsedTag($tag, $params, $prefix);
}
/**
* Return object of IDBConnection interface
*
* Return object of IDBConnection interface already connected to the project database, configurable in config.php
*
* @return IDBConnection
* @access public
*/
public function &GetADODBConnection()
{
return $this->Conn;
}
/**
* Allows to parse given block name or include template
*
* @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
* @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter.
* @param bool $as_template
* @return string
* @access public
*/
public function ParseBlock($params, $pass_params = false, $as_template = false)
{
if ( substr($params['name'], 0, 5) == 'html:' ) {
return substr($params['name'], 5);
}
return $this->Parser->ParseBlock($params, $pass_params, $as_template);
}
/**
* Checks, that we have given block defined
*
* @param string $name
* @return bool
* @access public
*/
public function ParserBlockFound($name)
{
return $this->Parser->blockFound($name);
}
/**
* Allows to include template with a given name and given parameters
*
* @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
* @return string
* @access public
*/
public function IncludeTemplate($params)
{
return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0);
}
/**
* Return href for template
*
* @param string $t Template path
* @param string $prefix index.php prefix - could be blank, 'admin'
* @param Array $params
* @param string $index_file
* @return string
*/
public function HREF($t, $prefix = '', $params = Array (), $index_file = null)
{
return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
}
/**
* Returns theme template filename and it's corresponding page_id based on given seo template
*
* @param string $seo_template
* @return string
* @access public
*/
public function getPhysicalTemplate($seo_template)
{
return $this->UrlManager->getPhysicalTemplate($seo_template);
}
/**
* Returns template name, that corresponds with given virtual (not physical) page id
*
* @param int $page_id
* @return string|bool
* @access public
*/
public function getVirtualPageTemplate($page_id)
{
return $this->UrlManager->getVirtualPageTemplate($page_id);
}
/**
* Returns section template for given physical/virtual template
*
* @param string $template
* @param int $theme_id
* @return string
* @access public
*/
public function getSectionTemplate($template, $theme_id = null)
{
return $this->UrlManager->getSectionTemplate($template, $theme_id);
}
/**
* Returns variables with values that should be passed through with this link + variable list
*
* @param Array $params
* @return Array
* @access public
*/
public function getPassThroughVariables(&$params)
{
return $this->UrlManager->getPassThroughVariables($params);
}
/**
* Builds url
*
* @param string $t
* @param Array $params
* @param string $pass
* @param bool $pass_events
* @param bool $env_var
* @return string
* @access public
*/
public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true)
{
return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var);
}
/**
* Process QueryString only, create
* events, ids, based on config
* set template name and sid in
* desired application variables.
*
* @param string $env_var environment string value
* @param string $pass_name
* @return Array
* @access public
*/
public function processQueryString($env_var, $pass_name = 'passed')
{
return $this->UrlManager->plain->parse($env_var, $pass_name);
}
/**
* Parses rewrite url and returns parsed variables
*
* @param string $url
* @param string $pass_name
* @return Array
* @access public
*/
public function parseRewriteUrl($url, $pass_name = 'passed')
{
return $this->UrlManager->rewrite->parse($url, $pass_name);
}
/**
* Returns base part of all urls, build on website
*
* @param string $prefix
* @param bool $ssl
* @param bool $add_port
* @return string
* @access public
*/
public function BaseURL($prefix = '', $ssl = null, $add_port = true)
{
if ( $ssl === null ) {
// stay on same encryption level
return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
if ( $ssl ) {
// going from http:// to https://
$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
if ( !$base_url ) {
$ssl_url = $this->siteDomainField('SSLUrl');
$base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL');
}
return rtrim($base_url, '/') . $prefix . '/';
}
// going from https:// to http://
$domain = $this->siteDomainField('DomainName');
if ( $domain === false ) {
$domain = DOMAIN;
}
return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
/**
* Redirects user to url, that's build based on given parameters
*
* @param string $t
* @param Array $params
* @param string $prefix
* @param string $index_file
* @return void
* @access public
*/
public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
{
$js_redirect = getArrayValue($params, 'js_redirect');
if ( $t == '' || $t === true ) {
$t = $this->GetVar('t');
}
// pass prefixes and special from previous url
if ( array_key_exists('js_redirect', $params) ) {
unset($params['js_redirect']);
}
// allows to send custom responce code along with redirect header
if ( array_key_exists('response_code', $params) ) {
$response_code = (int)$params['response_code'];
unset($params['response_code']);
}
else {
$response_code = 302; // Found
}
if ( !array_key_exists('pass', $params) ) {
$params['pass'] = 'all';
}
if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) {
// redirects to the same template as current
$params['ajax'] = 'yes';
}
$location = $this->HREF($t, $prefix, $params, $index_file);
if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) {
$this->Debugger->appendTrace();
echo '<strong>Debug output above !!!</strong><br/>' . "\n";
if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
echo 'Referer: <strong>' . kUtil::escape($_SERVER['HTTP_REFERER'], kUtil::ESCAPE_HTML) . '</strong><br/>' . "\n";
}
echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n";
}
else {
if ( $js_redirect ) {
// show "redirect" template instead of redirecting,
// because "Set-Cookie" header won't work, when "Location"
// header is used later
$this->SetVar('t', 'redirect');
$this->SetVar('redirect_to', $location);
// make all additional parameters available on "redirect" template too
foreach ($params as $name => $value) {
$this->SetVar($name, $value);
}
return;
}
else {
if ( $this->GetVar('ajax') == 'yes' && ($t != $this->GetVar('t') || !$this->isSOPSafe($location, $t)) ) {
// redirection to other then current template during ajax request OR SOP violation
kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
echo '#redirect#' . $location;
}
elseif ( headers_sent() != '' ) {
// some output occurred -> redirect using javascript
echo '<script type="text/javascript">window.location.href = \'' . kUtil::escape($location, kUtil::ESCAPE_JS) . '\';</script>';
}
else {
// no output before -> redirect using HTTP header
// header('HTTP/1.1 302 Found');
header('Location: ' . $location, true, $response_code);
}
}
}
// session expiration is called from session initialization,
// that's why $this->Session may be not defined here
/** @var Session $session */
$session = $this->recallObject('Session');
if ( $this->InitDone ) {
// if redirect happened in the middle of application initialization don't call event,
// that presumes that application was successfully initialized
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
}
$session->SaveData();
ob_end_flush();
exit;
}
/**
* Determines if real redirect should be made within AJAX request.
*
* @param string $url Location.
* @param string $template Template.
*
* @return boolean
* @link http://en.wikipedia.org/wiki/Same-origin_policy
*/
protected function isSOPSafe($url, $template)
{
$parsed_url = parse_url($url);
if ( $parsed_url['scheme'] . '://' != PROTOCOL ) {
return false;
}
if ( $parsed_url['host'] != SERVER_NAME ) {
return false;
}
if ( defined('PORT') && isset($parsed_url['port']) && $parsed_url['port'] != PORT ) {
return false;
}
return true;
}
/**
* Returns translation of given label
*
* @param string $label
* @param bool $allow_editing return translation link, when translation is missing on current language
* @param bool $use_admin use current Admin Console language to translate phrase
* @return string
* @access public
*/
public function Phrase($label, $allow_editing = true, $use_admin = false)
{
return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin);
}
/**
* Replace language tags in exclamation marks found in text
*
* @param string $text
* @param bool $force_escape force escaping, not escaping of resulting string
* @return string
* @access public
*/
public function ReplaceLanguageTags($text, $force_escape = null)
{
return $this->Phrases->ReplaceLanguageTags($text, $force_escape);
}
/**
* Checks if user is logged in, and creates
* user object if so. User object can be recalled
* later using "u.current" prefix_special. Also you may
* get user id by getting "u.current_id" variable.
*
* @return void
* @access protected
*/
protected function ValidateLogin()
{
/** @var Session $session */
$session = $this->recallObject('Session');
$user_id = $session->GetField('PortalUserId');
if ( !$user_id && $user_id != USER_ROOT ) {
$user_id = USER_GUEST;
}
$this->SetVar('u.current_id', $user_id);
if ( !$this->isAdmin ) {
// needed for "profile edit", "registration" forms ON FRONT ONLY
$this->SetVar('u_id', $user_id);
}
$this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional
$this->isAdminUser = $this->isAdmin && $this->LoggedIn();
if ( $this->GetVar('expired') == 1 ) {
// this parameter is set only from admin
/** @var UsersItem $user */
$user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login'));
$user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired');
}
$this->HandleEvent(new kEvent('adm:OnLogHttpRequest'));
if ( $user_id != USER_GUEST ) {
// normal users + root
$this->LoadPersistentVars();
}
$user_timezone = $this->Session->GetField('TimeZone');
if ( $user_timezone ) {
date_default_timezone_set($user_timezone);
}
}
/**
* Loads current user persistent session data
*
* @return void
* @access public
*/
public function LoadPersistentVars()
{
$this->Session->LoadPersistentVars();
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
* @access public
*/
public function ConfigValue($name)
{
return $this->cacheManager->ConfigValue($name);
}
/**
* Changes value of individual configuration variable (+resets cache, when needed)
*
* @param string $name
* @param string $value
* @param bool $local_cache_only
* @return string
* @access public
*/
public function SetConfigValue($name, $value, $local_cache_only = false)
{
return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @param Array $params
* @param Array $specific_params
* @return void
* @access public
*/
public function HandleEvent($event, $params = null, $specific_params = null)
{
if ( isset($params) ) {
$event = new kEvent($params, $specific_params);
}
$this->EventManager->HandleEvent($event);
}
/**
* Notifies event subscribers, that event has occured
*
* @param kEvent $event
* @return void
*/
public function notifyEventSubscribers(kEvent $event)
{
$this->EventManager->notifySubscribers($event);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function eventImplemented(kEvent $event)
{
return $this->EventManager->eventImplemented($event);
}
/**
* Registers new class in the factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $file Filename in what $real_class is declared
* @param string $pseudo_class Name under this class object will be accessed using getObject method
* @return void
* @access public
*/
public function registerClass($real_class, $file, $pseudo_class = null)
{
$this->Factory->registerClass($real_class, $file, $pseudo_class);
}
/**
* Unregisters existing class from factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $pseudo_class Name under this class object is accessed using getObject method
* @return void
* @access public
*/
public function unregisterClass($real_class, $pseudo_class = null)
{
$this->Factory->unregisterClass($real_class, $pseudo_class);
}
/**
* Finds the absolute path to the file where the class is defined.
*
* @param string $class The name of the class.
*
* @return string|false
*/
public function findClassFile($class)
{
return $this->Factory->findClassFile($class);
}
/**
* Add new scheduled task
*
* @param string $short_name name to be used to store last maintenance run info
* @param string $event_string
* @param int $run_schedule run schedule like for Cron
* @param int $status
* @access public
*/
public function registerScheduledTask($short_name, $event_string, $run_schedule, $status = STATUS_ACTIVE)
{
$this->EventManager->registerScheduledTask($short_name, $event_string, $run_schedule, $status);
}
/**
* Registers Hook from subprefix event to master prefix event
*
* Pattern: Observer
*
* @param string $hook_event
* @param string $do_event
* @param int $mode
* @param bool $conditional
* @access public
*/
public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false)
{
$this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional);
}
/**
* Registers build event for given pseudo class
*
* @param string $pseudo_class
* @param string $event_name
* @access public
*/
public function registerBuildEvent($pseudo_class, $event_name)
{
$this->EventManager->registerBuildEvent($pseudo_class, $event_name);
}
/**
* Allows one TagProcessor tag act as other TagProcessor tag
*
* @param Array $tag_info
* @return void
* @access public
*/
public function registerAggregateTag($tag_info)
{
/** @var kArray $aggregator */
$aggregator = $this->recallObject('TagsAggregator', 'kArray');
$tag_data = Array (
$tag_info['LocalPrefix'],
$tag_info['LocalTagName'],
getArrayValue($tag_info, 'LocalSpecial')
);
$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data);
}
/**
* Returns object using params specified, creates it if is required
*
* @param string $name
* @param string $pseudo_class
* @param Array $event_params
* @param Array $arguments
* @return kBase
*/
public function recallObject($name, $pseudo_class = null, array $event_params = array(), array $arguments = array())
{
/*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) {
// first time, when object with "_prefix_here_" prefix is accessed
$this->Debugger->appendTrace();
}*/
return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
}
/**
* Returns tag processor for prefix specified
*
* @param string $prefix
* @return kDBTagProcessor
* @access public
*/
public function recallTagProcessor($prefix)
{
$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
return $this->recallObject($prefix . '_TagProcessor');
}
/**
* Checks if object with prefix passes was already created in factory
*
* @param string $name object pseudo_class, prefix
* @return bool
* @access public
*/
public function hasObject($name)
{
return $this->Factory->hasObject($name);
}
/**
* Removes object from storage by given name
*
* @param string $name Object's name in the Storage
* @return void
* @access public
*/
public function removeObject($name)
{
$this->Factory->DestroyObject($name);
}
/**
* Get's real class name for pseudo class, includes class file and creates class instance
*
* Pattern: Factory Method
*
* @param string $pseudo_class
* @param Array $arguments
* @return kBase
* @access public
*/
public function makeClass($pseudo_class, array $arguments = array())
{
return $this->Factory->makeClass($pseudo_class, $arguments);
}
/**
* Checks if application is in debug mode
*
* @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
* @return bool
* @author Alex
* @access public
*/
public function isDebugMode($check_debugger = true)
{
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
if ($check_debugger) {
$debug_mode = $debug_mode && is_object($this->Debugger);
}
return $debug_mode;
}
/**
* Apply url rewriting used by mod_rewrite or not
*
* @param bool|null $ssl Force ssl link to be build
* @return bool
* @access public
*/
public function RewriteURLs($ssl = false)
{
// case #1,#4:
// we want to create https link from http mode
// we want to create https link from https mode
// conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
// case #2,#3:
// we want to create http link from https mode
// we want to create http link from http mode
// conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
$allow_rewriting =
(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
|| // or allow rewriting for redirect TO httpS or when already in httpS
(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
return kUtil::constOn('MOD_REWRITE') && $allow_rewriting;
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $option
* @param mixed $default
* @return string
* @access public
*/
public function getUnitOption($prefix, $option, $default = false)
{
return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $option
* @param string $value
* @access public
*/
public function setUnitOption($prefix, $option, $value)
{
$this->UnitConfigReader->setUnitOption($prefix,$option,$value);
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
*/
public function getUnitOptions($prefix)
{
return $this->UnitConfigReader->getUnitOptions($prefix);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
public function prefixRegistred($prefix)
{
return $this->UnitConfigReader->prefixRegistred($prefix);
}
/**
* Splits any mixing of prefix and
* special into correct ones
*
* @param string $prefix_special
* @return Array
* @access public
*/
public function processPrefix($prefix_special)
{
return $this->Factory->processPrefix($prefix_special);
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @return void
* @access public
*/
public function setEvent($prefix_special, $event_name)
{
$this->EventManager->setEvent($prefix_special, $event_name);
}
/**
* SQL Error Handler
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
* @throws Exception
* @deprecated
*/
public function handleSQLError($code, $msg, $sql)
{
return $this->_logger->handleSQLError($code, $msg, $sql);
}
/**
* Returns & blocks next ResourceId available in system
*
* @return int
* @access public
*/
public function NextResourceId()
{
$table_name = TABLE_PREFIX . 'IdGenerator';
$this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE');
$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
$id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
if ( $id === false ) {
$this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)');
$id = 2;
}
$this->Conn->Query('UNLOCK TABLES');
return $id - 1;
}
/**
* Returns genealogical main prefix for sub-table prefix passes
* OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix
*
* @param string $current_prefix
* @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
* @return string
* @access public
*/
public function GetTopmostPrefix($current_prefix, $real_top = false)
{
// 1. get genealogical tree of $current_prefix
$prefixes = Array ($current_prefix);
while ($parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix')) {
if ( !$this->prefixRegistred($parent_prefix) ) {
// stop searching, when parent prefix is not registered
break;
}
$current_prefix = $parent_prefix;
array_unshift($prefixes, $current_prefix);
}
if ( $real_top ) {
return $current_prefix;
}
// 2. find what if parent is passed
$passed = explode(',', $this->GetVar('all_passed'));
foreach ($prefixes as $a_prefix) {
if ( in_array($a_prefix, $passed) ) {
return $a_prefix;
}
}
return $current_prefix;
}
/**
* Triggers email event of type Admin
*
* @param string $email_template_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access public
*/
public function emailAdmin($email_template_name, $to_user_id = null, $send_params = Array ())
{
return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_ADMIN, $to_user_id, $send_params);
}
/**
* Triggers email event of type User
*
* @param string $email_template_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access public
*/
public function emailUser($email_template_name, $to_user_id = null, $send_params = Array ())
{
return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_FRONTEND, $to_user_id, $send_params);
}
/**
* Triggers general email event
*
* @param string $email_template_name
* @param int $email_template_type (0 for User, 1 for Admin)
* @param int $to_user_id
* @param array $send_params associative array of direct send params,
* possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access protected
*/
protected function _email($email_template_name, $email_template_type, $to_user_id = null, $send_params = Array ())
{
/** @var kEmail $email */
$email = $this->makeClass('kEmail');
if ( !$email->findTemplate($email_template_name, $email_template_type) ) {
return false;
}
$email->setParams($send_params);
return $email->send($to_user_id);
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
* @access public
*/
public function LoggedIn()
{
// no session during expiration process
return is_null($this->Session) ? false : $this->Session->LoggedIn();
}
/**
* Determines if access permissions should not be checked.
*
* @param integer|null $user_id User ID.
*
* @return boolean
*/
public function permissionCheckingDisabled($user_id = null)
{
if ( !isset($user_id) ) {
$user_id = $this->RecallVar('user_id');
}
return $user_id == USER_ROOT;
}
/**
* Check current user permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckPermission($name, $type = 1, $cat_id = null)
{
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->recallObject('PermissionsHelper');
return $perm_helper->CheckPermission($name, $type, $cat_id);
}
/**
* Check current admin permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckAdminPermission($name, $type = 1, $cat_id = null)
{
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->recallObject('PermissionsHelper');
return $perm_helper->CheckAdminPermission($name, $type, $cat_id);
}
/**
* Set's any field of current visit
*
* @param string $field
* @param mixed $value
* @return void
* @access public
* @todo move to separate module
*/
public function setVisitField($field, $value)
{
if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) {
// admin logins are not registered in visits list
return;
}
/** @var kDBItem $visit */
$visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0));
if ( $visit->isLoaded() ) {
$visit->SetDBField($field, $value);
$visit->Update();
}
}
/**
* Allows to check if in-portal is installed
*
* @return bool
* @access public
*/
public function isInstalled()
{
return $this->InitDone && (count($this->ModuleInfo) > 0);
}
/**
* Allows to determine if module is installed & enabled
*
* @param string $module_name
* @return bool
* @access public
*/
public function isModuleEnabled($module_name)
{
return $this->findModule('Name', $module_name) !== false;
}
/**
* Returns Window ID of passed prefix main prefix (in edit mode)
*
* @param string $prefix
* @return int
* @access public
*/
public function GetTopmostWid($prefix)
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$mode = $this->GetVar($top_prefix . '_mode');
return $mode != '' ? substr($mode, 1) : '';
}
/**
* Get temp table name
*
* @param string $table
* @param mixed $wid
* @return string
* @access public
*/
public function GetTempName($table, $wid = '')
{
return $this->GetTempTablePrefix($wid) . $table;
}
/**
* Builds temporary table prefix based on given window id
*
* @param string $wid
* @return string
* @access public
*/
public function GetTempTablePrefix($wid = '')
{
if ( preg_match('/prefix:(.*)/', $wid, $regs) ) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
}
/**
* Checks if given table is a temporary table
*
* @param string $table
* @return bool
* @access public
*/
public function IsTempTable($table)
{
static $cache = Array ();
if ( !array_key_exists($table, $cache) ) {
$cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table);
}
return (bool)$cache[$table];
}
/**
* Checks, that given prefix is in temp mode
*
* @param string $prefix
* @param string $special
* @return bool
* @access public
*/
public function IsTempMode($prefix, $special = '')
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$var_names = Array (
$top_prefix,
rtrim($top_prefix . '_' . $special, '_'), // from post
rtrim($top_prefix . '.' . $special, '.'), // assembled locally
);
$var_names = array_unique($var_names);
$temp_mode = false;
foreach ($var_names as $var_name) {
$value = $this->GetVar($var_name . '_mode');
if ( $value && (substr($value, 0, 1) == 't') ) {
$temp_mode = true;
break;
}
}
return $temp_mode;
}
/**
* Return live table name based on temp table name
*
* @param string $temp_table
* @return string
*/
public function GetLiveName($temp_table)
{
if ( preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $temp_table, $rets) ) {
// cut wid from table end if any
return $rets[2];
}
else {
return $temp_table;
}
}
/**
* Stops processing of user request and displays given message
*
* @param string $message
* @access public
*/
public function ApplicationDie($message = '')
{
while ( ob_get_level() ) {
ob_end_clean();
}
if ( $this->isDebugMode() ) {
$message .= $this->Debugger->printReport(true);
}
$this->HTML = $message;
$this->_outputPage();
}
/**
* Returns comma-separated list of groups from given user
*
* @param int $user_id
* @return string
*/
public function getUserGroups($user_id)
{
switch ($user_id) {
case USER_ROOT:
$user_groups = $this->ConfigValue('User_LoggedInGroup');
break;
case USER_GUEST:
$user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup');
break;
default:
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . (int)$user_id;
$res = $this->Conn->GetCol($sql);
$user_groups = Array ($this->ConfigValue('User_LoggedInGroup'));
if ( $res ) {
$user_groups = array_merge($user_groups, $res);
}
$user_groups = implode(',', $user_groups);
}
return $user_groups;
}
/**
* Allows to detect if page is browsed by spider (293 scheduled_tasks supported)
*
* @return bool
* @access public
*/
/*public function IsSpider()
{
static $is_spider = null;
if ( !isset($is_spider) ) {
$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
$robots = file(FULL_PATH . '/core/robots_list.txt');
foreach ($robots as $robot_info) {
$robot_info = explode("\t", $robot_info, 3);
if ( $user_agent == trim($robot_info[2]) ) {
$is_spider = true;
break;
}
}
}
return $is_spider;
}*/
/**
* Allows to detect table's presence in database
*
* @param string $table_name
* @param bool $force
* @return bool
* @access public
*/
public function TableFound($table_name, $force = false)
{
return $this->Conn->TableFound($table_name, $force);
}
/**
* Returns counter value
*
* @param string $name counter name
* @param Array $params counter parameters
* @param string $query_name specify query name directly (don't generate from parameters)
* @param bool $multiple_results
* @return mixed
* @access public
*/
public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
{
/** @var kCountHelper $count_helper */
$count_helper = $this->recallObject('CountHelper');
return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
}
/**
* Resets counter, which are affected by one of specified tables
*
* @param string $tables comma separated tables list used in counting sqls
* @return void
* @access public
*/
public function resetCounters($tables)
{
if ( kUtil::constOn('IS_INSTALL') ) {
return;
}
/** @var kCountHelper $count_helper */
$count_helper = $this->recallObject('CountHelper');
$count_helper->resetCounters($tables);
}
/**
* Sends XML header + optionally displays xml heading
*
* @param string|bool $xml_version
* @return string
* @access public
* @author Alex
*/
public function XMLHeader($xml_version = false)
{
$this->setContentType('text/xml');
return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . CHARSET . '"?>' : '';
}
/**
* Returns category tree
*
* @param int $category_id
* @return Array
* @access public
*/
public function getTreeIndex($category_id)
{
$tree_index = $this->getCategoryCache($category_id, 'category_tree');
if ( $tree_index ) {
$ret = Array ();
list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
return $ret;
}
return false;
}
/**
* Base category of all categories
* Usually replaced category, with ID = 0 in category-related operations.
*
* @return int
* @access public
*/
public function getBaseCategory()
{
// same, what $this->findModule('Name', 'Core', 'RootCat') does
// don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade
return $this->ModuleInfo['Core']['RootCat'];
}
/**
* Deletes all data, that was cached during unit config parsing (excluding unit config locations)
*
* @param Array $config_variables
* @access public
*/
public function DeleteUnitCache($config_variables = null)
{
$this->cacheManager->DeleteUnitCache($config_variables);
}
/**
* Deletes cached section tree, used during permission checking and admin console tree display
*
* @return void
* @access public
*/
public function DeleteSectionCache()
{
$this->cacheManager->DeleteSectionCache();
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->Factory->setFromCache($data);
$this->UnitConfigReader->setFromCache($data);
$this->EventManager->setFromCache($data);
$this->ReplacementTemplates = $data['Application.ReplacementTemplates'];
$this->RewriteListeners = $data['Application.RewriteListeners'];
$this->ModuleInfo = $data['Application.ModuleInfo'];
}
/**
* Gets object data for caching
* The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
*
* @access public
* @return Array
*/
public function getToCache()
{
return array_merge(
$this->Factory->getToCache(),
$this->UnitConfigReader->getToCache(),
$this->EventManager->getToCache(),
Array (
'Application.ReplacementTemplates' => $this->ReplacementTemplates,
'Application.RewriteListeners' => $this->RewriteListeners,
'Application.ModuleInfo' => $this->ModuleInfo,
)
);
}
public function delayUnitProcessing($method, $params)
{
$this->cacheManager->delayUnitProcessing($method, $params);
}
/**
* Returns current maintenance mode state
*
* @param bool $check_ips
* @return int
* @access public
*/
public function getMaintenanceMode($check_ips = true)
{
$exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : '';
$setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT';
if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) {
$exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false;
if ( !$exception_ip ) {
return constant($setting_name);
}
}
return MaintenanceMode::NONE;
}
/**
* Sets content type of the page
*
* @param string $content_type
* @param bool $include_charset
* @return void
* @access public
*/
public function setContentType($content_type = 'text/html', $include_charset = null)
{
static $already_set = false;
if ( $already_set ) {
return;
}
$header = 'Content-type: ' . $content_type;
if ( !isset($include_charset) ) {
$include_charset = $content_type = 'text/html' || $content_type == 'text/plain' || $content_type = 'text/xml';
}
if ( $include_charset ) {
$header .= '; charset=' . CHARSET;
}
$already_set = true;
header($header);
}
/**
* Posts message to event log
*
* @param string $message
* @param int $code
* @param bool $write_now Allows further customization of log record by returning kLog object
* @return bool|int|kLogger
* @access public
*/
public function log($message, $code = null, $write_now = false)
{
$log = $this->_logger->prepare($message, $code)->addSource($this->_logger->createTrace(null, 1));
if ( $write_now ) {
return $log->write();
}
return $log;
}
/**
* Deletes log with given id from database or disk, when database isn't available
*
* @param int $unique_id
* @param int $storage_medium
* @return void
* @access public
* @throws InvalidArgumentException
*/
public function deleteLog($unique_id, $storage_medium = kLogger::LS_AUTOMATIC)
{
$this->_logger->delete($unique_id, $storage_medium);
}
/**
* Returns the client IP address.
*
* @return string The client IP address
* @access public
*/
public function getClientIp()
{
return $this->HttpQuery->getClientIp();
}
}
Index: branches/5.2.x/core/kernel/processors/main_processor.php
===================================================================
--- branches/5.2.x/core/kernel/processors/main_processor.php (revision 16562)
+++ branches/5.2.x/core/kernel/processors/main_processor.php (revision 16563)
@@ -1,1293 +1,1293 @@
<?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 kMainTagProcessor extends kTagProcessor {
public function __construct()
{
parent::__construct();
/** @var Params $actions */
$actions = $this->Application->recallObject('kActions');
$actions->Set('t', $this->Application->GetVar('t'));
$actions->Set('sid', $this->Application->GetSID());
$actions->Set('m_opener', $this->Application->GetVar('m_opener') );
}
/**
* Base folder for all template includes
*
* @param Array $params
* @return string
*/
function TemplatesBase($params)
{
static $cached = Array ();
$cache_key = crc32( serialize($params) );
if (!array_key_exists($cache_key, $cached)) {
$module = array_key_exists('module', $params) ? $params['module'] : 'core';
if ($this->Application->isAdmin) {
if ($module == 'in-portal') {
$module = 'kernel';
}
// remove leading slash + substitute module
$module_path = $this->Application->findModule('Name', $module, 'Path');
if ($module_path !== false) {
$path = $module_path . 'admin_templates';
}
else {
// remove leading slash + substitute module
$path = preg_replace('/\/(.*?)\/(.*)/', $module . '/\\2', THEMES_PATH);
}
}
else {
$path = mb_substr(THEMES_PATH, 1);
if (mb_strtolower($module) == 'in-portal') {
$module_folder = 'platform';
}
else {
$module_folder = $this->Application->findModule('Name', $module, 'TemplatePath');
}
$path .= rtrim('/' . trim($module_folder, '/'), '/') . '/';
}
$cached[$cache_key] = $this->Application->BaseURL() . $path;
}
return $cached[$cache_key];
}
/**
* Creates <base href ..> HTML tag for all templates
* affects future css, js files and href params of links
*
* @param Array $params
* @return string
* @access protected
* @see kMainTagProcessor::TemplatesBase
*/
protected function Base_Ref($params)
{
// tag TemplatesBase adds trailing "/" but only on Front-End
$base_href = rtrim($this->TemplatesBase($params), '/');
return '<base href="' . $base_href . '/" />';
}
/**
* Returns base url for web-site
*
* @return string
* @access public
*/
function BaseURL()
{
return $this->Application->BaseURL();
}
//for compatability with K3 tags
function Base($params)
{
return $this->TemplatesBase($params).'/';
}
function ProjectBase($params)
{
return $this->Application->BaseURL();
}
/*function Base($params)
{
return $this->Application->BaseURL().$params['add'];
}*/
/**
* Used to create link to any template.
* use "pass" paramter if "t" tag to specify
* prefix & special of object to be represented
* in resulting url
*
* @param Array $params
* @return string
* @access public
*/
function T($params)
{
// by default link to current template
$template = $this->SelectParam($params, 't,template');
$prefix = array_key_exists('prefix', $params) ? $params['prefix'] : '';
unset($params['t'], $params['template'], $params['prefix']);
$no_html_escape = false;
if ( isset($params['no_amp']) ) {
$no_html_escape = $params['no_amp'];
unset($params['no_amp']);
}
$ret = $this->Application->HREF($template, $prefix, $params);
if ( !$no_html_escape ) {
// most of the time links are placed into HTML document
// TODO: in future always do escaping according to current "escape context"
$ret = kUtil::escape($ret, kUtil::ESCAPE_HTML);
}
return $ret;
}
function Link($params)
{
// pass "m" prefix, instead of "all", that is by default on Front-End
if (!array_key_exists('pass', $params)) {
$params['pass'] = 'm';
}
return $this->T($params);
}
/**
* Performs redirect to provided template/url
*
* @param Array $params
* @return string
*/
function Redirect($params)
{
// By default link to current template.
$template = $this->SelectParam($params, 't,template');
$prefix = array_key_exists('prefix', $params) ? $params['prefix'] : '';
unset($params['t'], $params['template'], $params['prefix']);
// Pass "m" prefix, instead of "all", that is by default on Front-End.
if ( !array_key_exists('pass', $params) ) {
$params['pass'] = 'm';
}
$this->Application->Redirect($template, $params, $prefix);
return '';
}
/*function Env($params)
{
$t = $params['template'];
unset($params['template']);
return $this->Application->BuildEnv($t, $params, 'm', false, false);
}*/
function FormAction($params)
{
if (!array_key_exists('pass', $params)) {
$params['pass'] = 'all,m';
}
$params['pass_category'] = 1;
return $this->Application->HREF('', '', $params);
}
/*// NEEDS TEST
function Config($params)
{
return $this->Application->ConfigOption($params['var']);
}
function Object($params)
{
$name = $params['name'];
$method = $params['method'];
$tmp = $this->Application->recallObject($name);
if ($tmp != null) {
if (method_exists($tmp, $method))
return $tmp->$method($params);
else
echo "Method $method does not exist in object ".get_class($tmp)." named $name<br>";
}
else
echo "Object $name does not exist in the appliaction<br>";
}*/
/**
* Tag, that always returns true.
* For parser testing purposes
*
* @param Array $params
* @return bool
* @access public
*/
function True($params)
{
return true;
}
/**
* Tag, that always returns false.
* For parser testing purposes
*
* @param Array $params
* @return bool
* @access public
*/
function False($params)
{
return false;
}
/**
* Returns block parameter by name (used only as "check" parameter value for "m_if" tag!)
*
* @param Array $params
* @return stirng
* @access public
*/
function Param($params)
{
$name = $params['name'];
if (array_key_exists($name, $this->Application->Parser->Captures)) {
$capture_params = $params;
$capture_params['name'] = '__capture_' . $name;
$this->Application->Parser->SetParam($name, $this->Application->ParseBlock($capture_params));
}
$res = $this->Application->Parser->GetParam($name);
if ($res === false) {
$res = '';
}
if (array_key_exists('plus', $params)) {
$res += $params['plus'];
}
return $res;
}
/**
* Compares block parameter with value specified
*
* @param Array $params
* @return bool
* @access public
*/
function ParamEquals($params)
{
$name = $this->SelectParam($params, 'name,var,param');
$value = $params['value'];
return ($this->Application->Parser->GetParam($name) == $value);
}
/*function PHP_Self($params)
{
return $HTTP_SERVER_VARS['PHP_SELF'];
}
*/
/**
* Returns session variable value by name
*
* @param Array $params
* @return string
* @access public
*/
function Recall($params)
{
$var_name = $this->SelectParam($params,'name,var,param');
if (isset($params['persistent']) && $params['persistent']) {
$ret = $this->Application->RecallPersistentVar($var_name);
}
else {
$ret = $this->Application->RecallVar($var_name);
}
$ret = ($ret === false && isset($params['no_null'])) ? '' : $ret;
if (getArrayValue($params, 'special') || getArrayValue($params, 'htmlchars')) {
$ret = kUtil::escape($ret, kUtil::ESCAPE_HTML);
}
if (getArrayValue($params, 'urlencode')) {
$ret = kUtil::escape($ret, kUtil::ESCAPE_URL);
}
return $ret;
}
function RemoveVar($params)
{
$this->Application->RemoveVar( $this->SelectParam($params,'name,var,param') );
}
// bad style to store something from template to session !!! (by Alex)
// Used here only to test how session works, nothing more
function Store($params)
{
//echo"Store $params[name]<br>";
$name = $params['name'];
$value = $params['value'];
$this->Application->StoreVar($name,$value);
}
/**
* Links variable from request with variable from session
*
* @param Array $params
* @return string
* @access protected
*/
protected function LinkVar($params)
{
$var_name = $params['name'];
$session_var_name = isset($params['session_name']) ? $params['session_name'] : $var_name;
$default_value = isset($params['default']) ? $params['default'] : '';
$this->Application->LinkVar($var_name, $session_var_name, $default_value);
return '';
}
/**
* Links variable from request with variable from session and returns it's value
*
* @param Array $params
* @return string
* @access protected
*/
protected function GetLinkedVar($params)
{
$this->LinkVar($params);
return $this->Application->GetVar( $params['name'] );
}
/**
* Sets application variable value(-s)
*
* @param Array $params
* @access public
*/
function Set($params)
{
foreach ($params as $param => $value) {
$this->Application->SetVar($param, $value);
}
}
/**
* Increment application variable
* specified by number specified
*
* @param Array $params
* @access public
*/
function Inc($params)
{
$this->Application->SetVar($params['param'], $this->Application->GetVar($params['param']) + $params['by']);
}
/**
* Retrieves application variable
* value by name
*
* @param Array $params
* @return string
* @access public
*/
function Get($params)
{
$name = $this->SelectParam($params, 'name,var,param');
if ( strpos($name, '[') !== false ) {
preg_match('/([^\[\]]+)\[(.*)\]/', $name, $regs);
$function_params = explode('][', $regs[2]);
$ret = $this->Application->GetVar($regs[1], array());
kUtil::array_unshift_ref($function_params, $ret);
$ret = call_user_func_array('getArrayValue', $function_params);
}
else {
$ret = $this->Application->GetVar($name, '');
}
if ( array_key_exists('no_html_escape', $params) && $params['no_html_escape'] ) {
return $this->Application->isAdmin ? $ret : kUtil::unescape($ret, kUtil::ESCAPE_HTML);
}
return kUtil::escape($ret, kUtil::ESCAPE_HTML);
}
/**
* Retrieves application constant
* value by name
*
* @param Array $params
* @return string
* @access public
*/
function GetConst($params)
{
$constant_name = $this->SelectParam($params, 'name,const');
return defined($constant_name) ? constant($constant_name) : '';
}
/**
* Retrieves configuration variable value by name
*
* @param Array $params
* @return string
* @access public
*/
function GetConfig($params)
{
$config_name = $this->SelectParam($params, 'name,var');
$ret = $this->Application->ConfigValue($config_name);
if ( isset($params['formatted']) && $params['formatted'] ) {
$sql = 'SELECT ValueList
FROM ' . TABLE_PREFIX . 'SystemSettings
WHERE VariableName = ' . $this->Conn->qstr($config_name) . ' AND ElementType IN ("select", "radio")';
$value_list = $this->Conn->GetOne($sql);
if ( $value_list ) {
/** @var InpCustomFieldsHelper $helper */
$helper = $this->Application->recallObject('InpCustomFieldsHelper');
$options = $helper->GetValuesHash($value_list);
$ret = isset($options[$ret]) ? $options[$ret] : $ret;
}
}
if ( isset($params['as_label']) && $params['as_label'] ) {
$ret = $this->Application->Phrase($ret);
}
return $ret;
}
/**
* Compares configuration variable to a given value
*
* @param Array $params
* @return bool
* @deprecated
* @access protected
*/
protected function ConfigEquals($params)
{
$option = $this->SelectParam($params, 'name,option,var');
return $this->Application->ConfigValue($option) == $params['value'];
}
/**
* Creates all hidden fields
* needed for kernel_form
*
* @param Array $params
* @return string
* @access protected
*/
protected function DumpSystemInfo($params)
{
/** @var Params $actions */
$actions = $this->Application->recallObject('kActions');
$actions->Set('t', $this->Application->GetVar('t'));
$o = '';
$params = $actions->GetParams();
foreach ($params AS $name => $val) {
$o .= "<input type='hidden' name='$name' id='$name' value='$val'>\n";
}
return $o;
}
/**
* Used for search sidebox on front-end only
*
* @param Array $params
* @return string
* @access protected
*/
protected function GetFormHiddens($params)
{
$t = $this->SelectParam($params, 'template,t');
unset($params['template']);
$form_fields = Array ();
if ( $this->Application->RewriteURLs() ) {
/** @var Session $session */
$session = $this->Application->recallObject('Session');
if ( $session->NeedQueryString() ) {
$form_fields['sid'] = $this->Application->GetSID();
}
}
else {
$form_fields['env'] = $this->Application->BuildEnv($t, $params, 'm', false, false);
}
if ( $this->Application->GetVar('admin') == 1 ) {
$form_fields['admin'] = 1;
}
$ret = '';
$field_tpl = '<input type="hidden" name="%1$s" id="%1$s" value="%2$s"/>' . "\n";
foreach ($form_fields as $form_field => $field_value) {
$ret .= sprintf($field_tpl, $form_field, $field_value);
}
return $ret;
}
function Odd_Even($params)
{
$odd = $params['odd'];
$even = $params['even'];
if (!isset($params['var'])) {
$var = 'odd_even';
}
else {
$var = $params['var'];
}
if ($this->Application->GetVar($var) == 'even') {
if (!isset($params['readonly']) || !$params['readonly']) {
$this->Application->SetVar($var, 'odd');
}
return $even;
}
else {
if (!isset($params['readonly']) || !$params['readonly']) {
$this->Application->SetVar($var, 'even');
}
return $odd;
}
}
/**
* Returns phrase translation by name
*
* @param Array $params
* @return string
* @access public
*/
function Phrase($params)
{
$phrase_name = $this->SelectParam($params, 'label,name,title');
$default_translation = $this->SelectParam($params, 'default');
$no_editing = isset($params['no_editing']) && $params['no_editing'];
$translation = $this->Application->Phrase($phrase_name, !$no_editing);
$phrase_key = mb_strtoupper($phrase_name);
if ( $default_translation && strpos($translation, '!' . $phrase_key . '!') !== false ) {
/** @var kDBItem $phrase */
$phrase = $this->Application->recallObject('phrases.autocreate', null, Array ('skip_autoload' => true));
if ( !$phrase->Load($phrase_key, 'PhraseKey') ) {
$phrase->SetDBField('Phrase', $phrase_name);
/** @var kMultiLanguageHelper $ml_helper */
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
$languages = $ml_helper->getLanguages();
foreach ($languages AS $language_id) {
$phrase->SetDBField('l' . $language_id . '_Translation', $default_translation);
}
if ( $phrase->Create() ) {
$translation = $default_translation;
}
}
}
if ( isset($params['escape']) && $params['escape'] ) {
// html escaping here is redundant
$translation = kUtil::escape($translation, kUtil::ESCAPE_JS);
}
return $translation;
}
// for tabs
function is_active($params)
{
$test_templ = $this->SelectParam($params, 'templ,template,t');
if ( !getArrayValue($params, 'allow_empty') ) {
$if_true = getArrayValue($params, 'true') ? $params['true'] : 1;
$if_false = getArrayValue($params, 'false') ? $params['false'] : 0;
}
else {
$if_true = $params['true'];
$if_false = $params['false'];
}
$physical_template = $this->Application->getPhysicalTemplate($this->Application->GetVar('t'));
return preg_match('/^' . str_replace('/', '\/', $test_templ) . '/i', $physical_template) ? $if_true : $if_false;
}
function IsNotActive($params)
{
return !$this->is_active($params);
}
function IsActive($params)
{
return $this->is_active($params);
}
function is_t_active($params)
{
return $this->is_active($params);
}
function CurrentTemplate($params)
{
return $this->is_active($params);
}
/**
* Checks if session variable
* specified by name value match
* value passed as parameter
*
* @param Array $params
* @return string
* @access public
*/
function RecallEquals($params)
{
$name = $this->SelectParam($params, 'name,var');
$value = $params['value'];
if (isset($params['persistent']) && $params['persistent']) {
return $this->Application->RecallPersistentVar($name) == $value;
}
return ($this->Application->RecallVar($name) == $value);
}
/**
* Checks if application variable specified by name value match value passed as parameter
*
* @param Array $params
* @return bool
* @access protected
* @deprecated
*/
protected function GetEquals($params)
{
$name = $this->SelectParam($params, 'var,name,param');
return $this->Application->GetVar($name) == $params['value'];
}
function ModuleInclude($params)
{
$ret = '';
$included = Array ();
$block_params = array_merge($params, Array ('is_silent' => 2)); // don't make fatal errors in case if template is missing
$current_template = $this->Application->GetVar('t');
$replace_main = isset($params['replace_m']) && $params['replace_m'];
$skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array ();
$cms_mode = $this->Application->GetVar('admin');
foreach ($this->Application->ModuleInfo as $module_name => $module_data) {
$module_key = mb_strtolower($module_name);
if ( $module_name == 'In-Portal' ) {
if ( !$cms_mode && $this->Application->isAdmin ) {
// don't process In-Portal templates in admin
continue;
}
// Front-End still relies on In-Portal module
$module_prefix = $module_data['TemplatePath'];
}
elseif ( $this->Application->isAdmin && $module_data['Path'] != 'core/' ) {
$module_prefix = $module_key . '/'; // was $module_data['Path'];
}
else {
$module_prefix = $module_data['TemplatePath']; // always have trailing "/"
}
if ( in_array($module_prefix, $included) ) {
// template by this path was already included by other module (e.g. in-portal used core's template)
continue;
}
$block_params['t'] = $module_prefix . $this->SelectParam($params, $module_key . '_template,' . $module_key . '_t,template,t');
$check_prefix = $module_data['Var'];
if ( $check_prefix == 'adm' && $replace_main ) {
$check_prefix = 'c';
}
if ( $block_params['t'] == $current_template || in_array($check_prefix, $skip_prefixes) ) {
continue;
}
$no_data = $this->SelectParam($params, $module_key . '_block_no_data,block_no_data');
if ( $no_data ) {
$block_params['block_no_data'] = $module_prefix . '/' . $no_data;
}
$ret .= $this->Application->IncludeTemplate($block_params);
$included[] = $module_prefix;
}
return $ret;
}
function ModuleEnabled($params)
{
return $this->Application->isModuleEnabled( $params['module'] );
}
/**
* Checks if debug mode is on
*
* @param Array $params
* @return bool
* @access public
*/
function IsDebugMode($params)
{
return defined('DEBUG_MODE') && $this->Application->isDebugMode();
}
/*function MassParse($params)
{
$qty = $params['qty'];
$block = $params['block'];
$mode = $params['mode'];
$o = '';
if ($mode == 'func') {
$func = create_function('$params', '
$o = \'<tr>\';
$o.= \'<td>a\'.$params[\'param1\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param2\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param3\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param4\'].\'</td>\';
$o.= \'</tr>\';
return $o;
');
for ($i=1; $i<$qty; $i++) {
$block_params['param1'] = rand(1, 10000);
$block_params['param2'] = rand(1, 10000);
$block_params['param3'] = rand(1, 10000);
$block_params['param4'] = rand(1, 10000);
$o .= $func($block_params);
}
return $o;
}
$block_params['name'] = $block;
for ($i=0; $i<$qty; $i++) {
$block_params['param1'] = rand(1, 10000);
$block_params['param2'] = rand(1, 10000);
$block_params['param3'] = rand(1, 10000);
$block_params['param4'] = rand(1, 10000);
$block_params['passed'] = $params['passed'];
$block_params['prefix'] = 'm';
$o.= $this->Application->ParseBlock($block_params);
}
return $o;
}*/
function LoggedIn($params)
{
return $this->Application->LoggedIn();
}
/**
* Allows to check if permission exists directly in template and perform additional actions if required
*
* @param Array $params
* @return bool
*/
function CheckPermission($params)
{
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
return $perm_helper->TagPermissionCheck($params);
}
/**
* Checks if user is logged in and if not redirects it to template passed
*
* @param Array $params
*/
function RequireLogin($params)
{
$t = $this->Application->GetVar('t');
$next_t = getArrayValue($params, 'next_template');
if ( $next_t ) {
$t = $next_t;
}
// check by permissions: begin
if ((isset($params['perm_event']) && $params['perm_event']) ||
(isset($params['perm_prefix']) && $params['perm_prefix']) ||
(isset($params['permissions']) && $params['permissions'])) {
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$perm_status = $perm_helper->TagPermissionCheck($params);
if (!$perm_status) {
list($redirect_template, $redirect_params) = $perm_helper->getPermissionTemplate($params);
$this->Application->Redirect($redirect_template, $redirect_params);
}
else {
return ;
}
}
// check by permissions: end
// check by configuration value: begin
$condition = getArrayValue($params, 'condition');
if (!$condition) {
$condition = true;
}
else {
if (substr($condition, 0, 1) == '!') {
$condition = !$this->Application->ConfigValue(substr($condition, 1));
}
else {
$condition = $this->Application->ConfigValue($condition);
}
}
// check by configuration value: end
// check by belonging to group: begin
$group = $this->SelectParam($params, 'group');
$group_access = true;
if ($group) {
$sql = 'SELECT GroupId
FROM '.TABLE_PREFIX.'UserGroups
WHERE Name = '.$this->Conn->qstr($group);
$group_id = $this->Conn->GetOne($sql);
if ($group_id) {
$groups = explode(',', $this->Application->RecallVar('UserGroups'));
$group_access = in_array($group_id, $groups);
}
}
// check by belonging to group: end
if ((!$this->Application->LoggedIn() || !$group_access) && $condition) {
$redirect_params = $this->Application->HttpQuery->getRedirectParams(true);
if (MOD_REWRITE) {
// TODO: $next_t variable is ignored !!! (is anyone using m_RequireLogin tag with "next_template" parameter?)
$redirect_params = Array (
'm_cat_id' => 0,
'next_template' => 'external:' . $_SERVER['REQUEST_URI'],
);
}
else {
$redirect_params['next_template'] = $t;
}
if (array_key_exists('pass_category', $params)) {
$redirect_params['pass_category'] = $params['pass_category'];
}
if (array_key_exists('use_section', $params)) {
$redirect_params['use_section'] = $params['use_section'];
}
if ( $this->Application->LoggedIn() && !$group_access) {
$this->Application->Redirect($params['no_group_perm_template'], $redirect_params);
}
$this->Application->Redirect($params['login_template'], $redirect_params);
}
}
/**
* Checks, that user belongs to a group with a given name
*
* @param Array $params
* @return bool
*/
protected function IsMember($params)
{
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroups
WHERE Name = ' . $this->Conn->qstr($params['group']);
$group_id = $this->Conn->GetOne($sql);
if ( $group_id ) {
$groups = explode(',', $this->Application->RecallVar('UserGroups'));
return in_array($group_id, $groups);
}
return false;
}
/**
* Checks if SSL is on and redirects to SSL URL if needed
* If SSL_URL is not defined in config - the tag does not do anything
* If for_logged_in_only="1" exits if user is not logged in.
* If called without params forces https right away. If called with by_config="1" checks the
* Require SSL setting from General Config and if it is ON forces https
*
* @param Array $params
*/
protected function CheckSSL($params)
{
$ssl = $this->Application->isAdmin ? $this->Application->ConfigValue('AdminSSL_URL') : false;
if ( !$ssl ) {
// not in admin or admin ssl url is empty
$ssl_url = $this->Application->siteDomainField('SSLUrl');
$ssl = $ssl_url !== false ? $ssl_url : $this->Application->ConfigValue('SSL_URL');
}
if ( !$ssl || ($this->Application->TemplatesCache->forceThemeName !== false) ) {
// SSL URL is not set - no way to require SSL
// internal parsing (e.g. "TemplateParser::_parseTemplate") -> don't redirect
return;
}
$require = false;
if ( isset($params['mode']) && $params['mode'] == 'required' ) {
$require = true;
if ( isset($params['for_logged_in_only']) && $params['for_logged_in_only'] && !$this->Application->LoggedIn() ) {
$require = false;
}
if ( isset($params['condition']) ) {
if ( !$this->Application->ConfigValue($params['condition']) ) {
$require = false;
}
}
}
- if ( EDITING_MODE ) {
- // match SSL mode on front-end to one in administrative console, when browse modes are used
+ if ( defined('EDITING_MODE') && EDITING_MODE ) {
+ // Match SSL mode on front-end to one in administrative console, when browse modes are used.
$require = $this->Application->ConfigValue('Require_AdminSSL');
}
/** @var kHTTPQuery $http_query */
$http_query = $this->Application->recallObject('HTTPQuery');
$pass = $http_query->getRedirectParams();
$pass['pass_events'] = 1; // to make sure all events are passed when redirect happens
if ( $require ) {
if ( PROTOCOL == 'https://' ) {
$this->Application->SetVar('__KEEP_SSL__', 1);
return;
}
$pass['__SSL__'] = 1;
$this->Application->Redirect('', $pass);
}
else {
if ( PROTOCOL == 'https://' && $this->Application->ConfigValue('Force_HTTP_When_SSL_Not_Required') ) {
if ( $this->Application->GetVar('__KEEP_SSL__') ) {
return;
}
// $pass_more = Array ('pass' => 'm', 'm_cat_id' => 0, '__SSL__' => 0);
$pass['__SSL__'] = 0;
$this->Application->Redirect('', $pass); // $pass_more
}
}
}
function ConstOn($params)
{
$name = $this->SelectParam($params,'name,const');
return kUtil::constOn($name);
}
function SetDefaultCategory($params)
{
$category_id = $this->Application->findModule('Name', $params['module'], 'RootCat');
$this->Application->SetVar('m_cat_id', $category_id);
}
function XMLTemplate($params)
{
$this->NoDebug($params);
if ( isset($params['cache']) && $params['cache'] ) {
$nextyear = intval(date('Y') + 1);
$format = "D, d M Y H:i:s";
$expiration = gmdate($format, time() + $params['cache']) . ' GMT';
$last_modified = time();
header('Cache-Control: public, cache, max-age=' . $params['cache']);
header("Expires: $expiration");
header('Pragma: public');
// Getting headers sent by the client.
$headers = $this->Application->HttpQuery->getHeaders();
// Checking if the client is validating his cache and if it is current.
if ( isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) > $last_modified - $params['cache']) ) {
// Client's cache IS current, so we just respond '304 Not Modified'.
header('Last-Modified: ' . date($format, strtotime($headers['If-Modified-Since'])) . ' GMT', true, 304);
exit;
}
else {
// Image not cached or cache outdated, we respond '200 OK' and output the image.
header('Last-Modified: ' . gmdate($format, $last_modified) . ' GMT', true, 200);
}
}
// xml documents are usually long
kUtil::setResourceLimit();
if ( !$this->Application->GetVar('debug') ) {
return $this->Application->XMLHeader(getArrayValue($params, 'xml_version'));
}
return '';
}
function Header($params)
{
header($params['data']);
}
function NoDebug($params)
{
if ( !$this->Application->GetVar('debug') ) {
kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
}
}
/**
* Returns Home category name
*
* @param Array $params
* @return string
* @deprecated
*/
function RootCategoryName($params)
{
$no_editing = array_key_exists('no_editing', $params) && $params['no_editing'];
return $this->Application->Phrase('la_rootcategory_name', !$no_editing);
}
/**
* Allows to attach file directly from email event template
*
* @param Array $params
*/
function AttachFile($params)
{
$path = FULL_PATH . '/' . $params['path'];
$pseudo = isset($params['special']) ? 'EmailSender.' . $params['special'] : 'EmailSender';
/** @var kEmailSendingHelper $esender */
$esender = $this->Application->recallObject($pseudo);
if ( file_exists($path) && is_file($path) ) {
$esender->AddAttachment($path);
}
}
function CaptchaImage($params)
{
$this->NoDebug($params);
$this->Application->SetVar('skip_last_template', 1);
/** @var kCaptchaHelper $captcha_helper */
$captcha_helper = $this->Application->recallObject('CaptchaHelper');
// generate captcha code
$code = $captcha_helper->prepareCode( $this->Application->GetVar('var') );
$captcha_helper->GenerateCaptchaImage($code, $this->Application->GetVar('w'), $this->Application->GetVar('h'), true);
}
function SID($params)
{
return $this->Application->GetSID();
}
function ModuleInfo($params)
{
return $this->Application->findModule($params['key'], $params['value'], $params['return']);
}
function Random($params)
{
return rand(1, 100000000);
}
/**
* Prints parser params, available at current deep level
*
* @param Array $params
* @return string
*/
function PrintCurrentParams($params)
{
$current_params = $this->Application->Parser->Params;
foreach ($current_params as $param_name => $param_value) {
if ( is_object($param_value) && !method_exists($param_value, '__toString') ) {
$param_value = 'ClassName: ' . get_class($param_value);
}
$current_params[$param_name] = $param_name . ' = "' . $param_value . '"';
}
return '<pre>' . implode("\n", $current_params) . '</pre>';
}
/**
* Gets previously defined counter result
*
* @param Array $params
* @return int
*/
function GetCounter($params)
{
return $this->Application->getCounter($params['name'], $params);
}
/**
* Increments PageHit counter
*
* @param Array $params
* @return int
*/
function RegisterPageHit($params)
{
if ($this->Application->ConfigValue('UsePageHitCounter')) {
// get current counte
$sql = 'SELECT VariableValue
FROM '.TABLE_PREFIX.'SystemSettings
WHERE VariableName = "PageHitCounter"';
$page_counter = (int)$this->Conn->GetOne($sql);
$sql = 'UPDATE LOW_PRIORITY '.TABLE_PREFIX.'SystemSettings
SET VariableValue = '.($page_counter + 1).'
WHERE VariableName = "PageHitCounter"';
$this->Conn->Query($sql);
}
}
function Timestamp($params)
{
$format = isset($params['format']) ? $params['format'] : 'd.m.Y H:i:s';
return adodb_date($format);
}
function GetUrlHiddenFileds($params)
{
$vars = Array ('page', 'per_page', 'sort_by');
$ret = '<input type="hidden" name="main_list" value="1"/>';
if (array_key_exists('skip', $params)) {
$vars = array_diff($vars, $params['skip']);
}
foreach ($vars as $var_name) {
$var_value = $this->Application->GetVar($var_name);
if ($var_value) {
$ret .= '<input type="hidden" name="' . $var_name . '" value="' . $var_value . '"/>';
}
}
return $ret;
}
/**
* Returns current Page URL (without re-assembling it).
* "skip_query" param is optional and will remove the ?QUERY part from the result.
*
* @param Array $params
* @return string
* @access protected
*/
protected function CurrentPageLink($params)
{
if ( isset($params['skip_query']) && $params['skip_query'] ) {
return preg_replace('/\?' . preg_quote($_SERVER['QUERY_STRING'], '/') . '$/', '', $_SERVER['REQUEST_URI']);
}
return $_SERVER['REQUEST_URI'];
}
/**
* Returns current maintenance mode state
*
* @param Array $params
* @return int
* @access protected
*/
protected function MaintenanceMode($params)
{
$check_ips = isset($params['check_ips']) ? $params['check_ips'] : true;
return $this->Application->getMaintenanceMode($check_ips);
}
/**
* Checks if element with given name is defined
*
* @param Array $params
* @return int
* @access protected
*/
protected function ElementDefined($params)
{
return $this->Application->Parser->blockFound($params['name']);
}
}
Index: branches/5.2.x/core/units/categories/categories_config.php
===================================================================
--- branches/5.2.x/core/units/categories/categories_config.php (revision 16562)
+++ branches/5.2.x/core/units/categories/categories_config.php (revision 16563)
@@ -1,545 +1,556 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'c',
'ItemClass' => Array ('class' => 'CategoriesItem', 'file' => 'categories_item.php', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'CategoriesEventHandler', 'file' => 'categories_event_handler.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'CategoriesTagProcessor', 'file' => 'categories_tag_processor.php', 'build_event' => 'OnBuild'),
'RegisterClasses' => Array (
Array ('pseudo' => 'clsCachedPermissions', 'class' => 'clsCachedPermissions', 'file' => 'cache_updater.php', 'build_event' => ''),
Array ('pseudo' => 'clsRecursionStack', 'class' => 'clsRecursionStack', 'file' => 'cache_updater.php', 'build_event' => ''),
Array ('pseudo' => 'kPermCacheUpdater', 'class' => 'kPermCacheUpdater', 'file' => 'cache_updater.php', 'build_event' => ''),
),
'ConfigPriority' => 0,
'Hooks' => Array (
Array (
'Mode' => hAFTER,
'Conditional' => false,
'HookToPrefix' => 'adm', //self
'HookToSpecial' => '*',
'HookToEvent' => Array ('OnRebuildThemes'),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnAfterRebuildThemes',
),
Array (
'Mode' => hBEFORE,
'Conditional' => false,
'HookToPrefix' => '',
'HookToSpecial' => '*',
'HookToEvent' => Array ('OnAfterConfigRead'),
'DoPrefix' => 'cdata',
'DoSpecial' => '*',
'DoEvent' => 'OnDefineCustomFields',
),
Array (
'Mode' => hBEFORE,
'Conditional' => false,
'HookToPrefix' => 'rel',
'HookToSpecial' => '*',
'HookToEvent' => Array ('OnAfterConfigRead'),
'DoPrefix' => '',
'DoSpecial' => '*',
'DoEvent' => 'OnCloneSubItem',
),
Array (
'Mode' => hBEFORE,
'Conditional' => false,
'HookToPrefix' => 'img',
'HookToSpecial' => '*',
'HookToEvent' => Array ('OnAfterConfigRead'),
'DoPrefix' => '',
'DoSpecial' => '*',
'DoEvent' => 'OnCloneSubItem',
),
+
+ array(
+ 'Mode' => hAFTER,
+ 'Conditional' => false,
+ 'HookToPrefix' => 'adm',
+ 'HookToSpecial' => '',
+ 'HookToEvent' => array('OnStartup'),
+ 'DoPrefix' => '',
+ 'DoSpecial' => '',
+ 'DoEvent' => 'OnAfterStartupHook',
+ ),
),
'AutoLoad' => true,
'CatalogItem' => true,
'AdminTemplatePath' => 'categories',
'AdminTemplatePrefix' => 'categories_',
'SearchConfigPostfix' => 'category',
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
5 => 'mode',
),
'AggregateTags' => Array (
Array (
'AggregateTo' => 'm',
'AggregatedTagName' => 'CategoryLink',
'LocalTagName' => 'CategoryLink',
),
),
'IDField' => 'CategoryId',
'StatusField' => Array ('Status'), // 'Status'
'TitleField' => 'Name',
'TitlePhrase' => 'la_Text_Category',
'ItemType' => 1, // used for custom fields only
'StatisticsInfo' => Array (
'pending' => Array (
'icon' => 'icon16_cat_pending.gif',
'label' => 'la_tab_Categories',
'js_url' => '#url#',
'url' => Array ('t' => 'catalog/advanced_view', 'SetTab' => 'c', 'pass' => 'm,c.showall', 'c.showall_event' => 'OnSetFilterPattern', 'c.showall_filters' => 'show_active=0,show_pending=1,show_disabled=0,show_new=1,show_pick=1'),
'status' => STATUS_PENDING,
),
),
'TableName' => TABLE_PREFIX.'Categories',
'CustomDataTableName' => TABLE_PREFIX . 'CategoryCustomData',
'ViewMenuPhrase' => 'la_text_Categories',
'CatalogTabIcon' => 'icon16_sections.png',
'TitlePresets' => Array (
'default' => Array (
'new_status_labels' => Array ('c' => '!la_title_Adding_Category!'),
'edit_status_labels' => Array ('c' => '!la_title_Editing_Category!'),
'new_titlefield' => Array ('c' => '!la_title_New_Category!'),
),
'category_list' => Array ('prefixes' => Array ('c_List'), 'format' => "!la_title_Categories! (#c_recordcount#)"),
'catalog' => Array (
'prefixes' => Array (), 'format' => "<span id='category_path'>!la_title_Categories!</span>",
'toolbar_buttons' => Array ('select', 'cancel', 'upcat', 'homecat', 'new_cat', 'new_link', 'new_article', 'new_topic', 'new_product', 'edit', 'delete', 'new_listing', 'approve', 'decline', 'cut', 'copy', 'paste', 'move_up', 'move_down', 'tools', 'view', 'dbl-click')
),
'advanced_view' => Array (
'prefixes' => Array (), 'format' => "!la_title_AdvancedView!",
'toolbar_buttons' => Array ('select', 'cancel', 'new_cat', 'new_link', 'new_article', 'new_topic', 'new_product', 'edit', 'delete', 'new_listing', 'approve', 'decline', 'view', 'dbl-click'),
),
'reviews' => Array (
'prefixes' => Array (), 'format' => "!la_title_Reviews!",
'toolbar_buttons' => Array ('edit', 'delete', 'approve', 'decline', 'view', 'dbl-click',)
),
'review_edit' => Array ('prefixes' => Array (), 'format' => "!la_title_Editing_Review!"),
'categories_edit' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_General!",
),
'categories_properties' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Properties!",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'),
),
'categories_relations' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Relations!",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'approve', 'decline', 'view', 'dbl-click'),
),
'categories_related_searches' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_RelatedSearches!",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'move_up', 'move_down', 'approve', 'decline', 'view', 'dbl-click'),
),
'categories_images' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Images!",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'move_up', 'move_down', 'primary_image', 'view', 'dbl-click'),
),
'categories_permissions' => Array (
'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Permissions!",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'),
),
'categories_custom' => Array ('prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Custom!"),
'categories_update' => Array ('prefixes' => Array (), 'format' => "!la_title_UpdatingCategories!"),
'images_edit' => Array (
'prefixes' => Array ('c', 'c-img'),
'new_status_labels' => Array ('c-img' => '!la_title_Adding_Image!'),
'edit_status_labels' => Array ('c-img' => '!la_title_Editing_Image!'),
'new_titlefield' => Array ('c-img' => ''),
'format' => "#c_status# '#c_titlefield#' - #c-img_status# '#c-img_titlefield#'",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'),
),
'relations_edit' => Array (
'prefixes' => Array ('c', 'c-rel'),
'new_status_labels' => Array ('c-rel' => "!la_title_Adding_Relationship! '!la_title_New_Relationship!'"),
'edit_status_labels' => Array ('c-rel' => '!la_title_Editing_Relationship!'),
'format' => "#c_status# '#c_titlefield#' - #c-rel_status#",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'),
),
'related_searches_edit' => Array (
'prefixes' => Array ('c', 'c-search'),
'new_status_labels' => Array ('c-search' => "!la_title_Adding_RelatedSearch_Keyword!"),
'edit_status_labels' => Array ('c-search' => '!la_title_Editing_RelatedSearch_Keyword!'),
'format' => "#c_status# '#c_titlefield#' - #c-search_status#",
'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'),
),
'edit_content' => Array ('format' => '!la_EditingContent!'),
'tree_site' => Array ('format' => '!la_selecting_categories!'),
),
'EditTabPresets' => Array (
'Default' => Array (
'general' => Array ('title' => 'la_tab_General', 't' => 'categories/categories_edit', 'priority' => 1),
'properties' => Array ('title' => 'la_tab_Properties', 't' => 'categories/categories_edit_properties', 'priority' => 2),
'relations' => Array ('title' => 'la_tab_Relations', 't' => 'categories/categories_edit_relations', 'priority' => 3),
'related_searches' => Array ('title' => 'la_tab_Related_Searches', 't' => 'categories/categories_edit_related_searches', 'priority' => 4),
'images' => Array ('title' => 'la_tab_Images', 't' => 'categories/categories_edit_images', 'priority' => 5),
'permissions' => Array ('title' => 'la_tab_Permissions', 't' => 'categories/categories_edit_permissions', 'priority' => 6),
'custom' => Array ('title' => 'la_tab_Custom', 't' => 'categories/categories_edit_custom', 'priority' => 7),
),
),
'PermItemPrefix' => 'CATEGORY',
'PermSection' => Array ('main' => 'CATEGORY:in-portal:categories', /*'search' => 'in-portal:configuration_search',*/ 'custom' => 'in-portal:configuration_custom'),
'Sections' => Array (
'in-portal:configure_categories' => Array (
'parent' => 'in-portal:website_setting_folder',
'icon' => 'conf_output',
'label' => 'la_tab_ConfigOutput',
'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'),
'permissions' => Array ('view', 'add', 'edit'),
'priority' => 11.1,
'type' => stTREE,
),
'in-portal:configuration_search' => Array (
'parent' => 'in-portal:website_setting_folder',
'icon' => 'conf_search',
'label' => 'la_tab_ConfigSearch',
'url' => Array ('t' => 'config/config_search', 'module_key' => 'category', 'pass_section' => true, 'pass' => 'm'),
'permissions' => Array ('view', 'edit'),
'priority' => 11.2,
'type' => stTREE,
),
'in-portal:configuration_custom' => Array (
'parent' => 'in-portal:website_setting_folder',
'icon' => 'conf_customfields',
'label' => 'la_tab_ConfigCustom',
'url' => Array ('t' => 'custom_fields/custom_fields_list', 'cf_type' => 1, 'pass_section' => true, 'pass' => 'm,cf'),
'permissions' => Array ('view', 'add', 'edit', 'delete'),
'priority' => 11.3,
'type' => stTREE,
),
),
'FilterMenu' => Array (
'Groups' => Array (
Array ('mode' => 'AND', 'filters' => Array ('show_new'), 'type' => kDBList::HAVING_FILTER),
Array ('mode' => 'AND', 'filters' => Array ('show_pick'), 'type' => kDBList::WHERE_FILTER),
),
'Filters' => Array (
'show_new' => Array ('label' => 'la_Text_New', 'on_sql' => '', 'off_sql' => '`IsNew` != 1'),
'show_pick' => Array ('label' => 'la_prompt_EditorsPick', 'on_sql' => '', 'off_sql' => '`EditorsPick` != 1'),
)
),
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s
FROM %1$s
LEFT JOIN '.TABLE_PREFIX.'%3$sCatalogImages img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1
{PERM_JOIN}
LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryCustomData cust ON %1$s.ResourceId = cust.ResourceId',
'-virtual' => ' SELECT %1$s.* %2$s
FROM %1$s',
),
'SubItems' => Array ('c-rel', 'c-search', 'c-img', 'c-cdata', 'c-perm', 'content', 'page-revision'),
'ListSortings' => Array (
'' => Array (
'Sorting' => Array ('Priority' => 'desc', 'Name' => 'asc'),
)
),
'CalculatedFields' => Array (
'' => Array (
'CurrentSort' => "REPLACE(ParentPath, CONCAT('|', ".'%1$s'.".CategoryId, '|'), '')",
'AltName' => 'img.AltName',
'SameImages' => 'img.SameImages',
'LocalThumb' => 'img.LocalThumb',
'ThumbPath' => 'img.ThumbPath',
'ThumbUrl' => 'img.ThumbUrl',
'LocalImage' => 'img.LocalImage',
'LocalPath' => 'img.LocalPath',
'FullUrl' => 'img.Url',
),
'-virtual' => Array (),
),
'CacheModRewrite' => true,
'Fields' => Array (
'CategoryId' => Array ('type' => 'int', 'not_null' => 1,'default' => 0),
'Type' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Virtual', 2 => 'la_opt_Template'), 'use_phrases' => 1,
'not_null' => 1,'default' => 1
),
'SymLinkCategoryId' => Array ('type' => 'int', 'default' => NULL),
'ParentId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'not_null' => 1,'default' => 0, 'required' => 1),
'Name' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'required' => 1, 'default' => ''),
'Filename' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'AutomaticFilename' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1,
'default' => 1, 'not_null' => 1,
),
'Description' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => null),
'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'required' => 1, 'default' => '#NOW#'),
'EditorsPick' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1,
'default' => 0, 'not_null' => 1,
),
'Status' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Active', 2 => 'la_Pending', 0 => 'la_Disabled' ), 'use_phrases' => 1, 'not_null' => 1,'default' => 1),
'Priority' => Array ('type' => 'int', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => 0),
'MetaKeywords' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
'CachedDescendantCatsQty' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'CachedNavbar' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => null),
'CreatedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL),
'ResourceId' => Array ('type' => 'int', 'default' => null),
'ParentPath' => Array ('type' => 'string', 'default' => null),
'TreeLeft' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'TreeRight' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'NamedParentPath' => Array ('type' => 'string', 'default' => null),
'NamedParentPathHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0),
'MetaDescription' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
'HotItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2),
'NewItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2),
'PopItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2),
'Modified' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
'ModifiedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL),
'CachedTemplate' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'CachedTemplateHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0),
// fields from Pages
'Template' => Array (
'type' => 'string',
'formatter' => 'kOptionsFormatter',
'options_sql' => ' SELECT CONCAT(tf.Description, " :: ", FilePath, "/", TRIM(TRAILING ".tpl" FROM FileName) ) AS Title,
IF(tf.TemplateAlias <> "", tf.TemplateAlias, CONCAT(FilePath, "/", TRIM(TRAILING ".tpl" FROM FileName))) AS Value
FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf
LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId
WHERE (t.Enabled = 1) AND (tf.FileName NOT LIKE "%%.elm.tpl") AND (tf.FileName NOT LIKE "%%.des.tpl") AND (tf.FilePath = "/designs")
ORDER BY tf.Description ASC, tf.FileName ASC',
'option_key_field' => 'Value', 'option_title_field' => 'Title',
'error_msgs' => Array (
'no_inherit' => '!la_error_NoInheritancePossible!',
),
'required' => 1, 'not_null' => 1, 'default' => CATEGORY_TEMPLATE_INHERIT
),
'UseExternalUrl' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0,
),
'ExternalUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'UseMenuIconUrl' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0,
),
'MenuIconUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'Title' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => '', 'not_null'=>1),
'MenuTitle' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'default' => ''),
'MetaTitle' => Array ('type' => 'string', 'default' => null),
'IndexTools' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
'IsMenu' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Show', 0 => 'la_Hide'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1),
'Protected' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0),
'FormId' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array ('' => ''),
'options_sql' => 'SELECT Title, FormId FROM '.TABLE_PREFIX.'Forms ORDER BY Title',
'option_key_field' => 'FormId', 'option_title_field' => 'Title',
'default' => NULL
),
'FormSubmittedTemplate' => Array ('type' => 'string', 'default' => null),
'FriendlyURL' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ThemeId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'EnablePageCache' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'OverridePageCacheKey' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'PageCacheKey' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'PageExpiration' => Array ('type' => 'int', 'default' => NULL),
'LiveRevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1),
'DirectLinkEnabled' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 1
),
'DirectLinkAuthKey' => Array ('type' => 'string', 'max_len' => 20, 'not_null' => 1, 'default' => ''),
'PromoBlockGroupId' => Array (
'type' => 'int',
'options_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'PromoBlockGroups ORDER BY Title', 'option_title_field' => 'Title', 'option_key_field' => 'PromoBlockGroupId',
'not_null' => 1, 'default' => 0,
),
'RequireSSL' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'RequireLogin' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
),
'VirtualFields' => Array (
'Relevance' => Array ('type' => 'float', 'default' => 0),
'CurrentSort' => Array ('type' => 'string', 'default' => ''),
'IsNew' => Array ('type' => 'int', 'default' => 0),
'OldPriority' => Array ('type' => 'int', 'default' => 0),
// for primary image
'AltName' => Array ('type' => 'string', 'default' => ''),
'SameImages' => Array ('type' => 'string', 'default' => ''),
'LocalThumb' => Array ('type' => 'string', 'default' => ''),
'ThumbPath' => Array ('type' => 'string', 'default' => ''),
'ThumbUrl' => Array ('type' => 'string', 'default' => ''),
'LocalImage' => Array ('type' => 'string', 'default' => ''),
'LocalPath' => Array ('type' => 'string', 'default' => ''),
'FullUrl' => Array ('type' => 'string', 'default' => ''),
),
'Grids' => Array (
'Default' => Array (
'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status'
'default' => 'icon_section.png',
'1_0_0' => 'icon16_section_system.png', // system
'1_0_1' => 'icon16_section_system.png', // system
'1_1_1' => 'icon16_section_system.png', // system
'0_0_0' => 'icon16_section_disabled.png', // disabled
'0_0_1' => 'icon16_section_disabled.png', // disabled
'0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu
'0_2_0' => 'icon16_section_pending.png', // pending
'0_2_1' => 'icon16_section_pending.png', // pending
'NEW' => 'icon16_section_new.png', // section is new
),
'Fields' => Array (
'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55),
'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250),
'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65),
'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170),
'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220),
'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70),
'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
),
),
'Radio' => Array (
'Selector' => 'radio',
'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status'
'default' => 'icon_section.png',
'1_0_0' => 'icon16_section_system.png', // system
'1_0_1' => 'icon16_section_system.png', // system
'1_1_1' => 'icon16_section_system.png', // system
'0_0_0' => 'icon16_section_disabled.png', // disabled
'0_0_1' => 'icon16_section_disabled.png', // disabled
'0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu
'0_2_0' => 'icon16_section_pending.png', // pending
'0_2_1' => 'icon16_section_pending.png', // pending
'NEW' => 'icon16_section_new.png', // section is new
),
'Fields' => Array (
'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 55),
'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250),
'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65),
'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70),
'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170),
'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220),
'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
),
),
'Structure' => Array (
'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status'
'default' => 'icon_section.png',
'1_0_0' => 'icon16_section_system.png', // system
'1_0_1' => 'icon16_section_system.png', // system
'1_1_1' => 'icon16_section_system.png', // system
'0_0_0' => 'icon16_section_disabled.png', // disabled
'0_0_1' => 'icon16_section_disabled.png', // disabled
'0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu
'0_2_0' => 'icon16_section_pending.png', // pending
'0_2_1' => 'icon16_section_pending.png', // pending
'NEW' => 'icon16_section_new.png', // section is new
),
'Fields' => Array (
'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55),
'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250),
'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65),
'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70),
'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170),
'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220),
'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100),
),
),
),
'ConfigMapping' => Array (
'PerPage' => 'Perpage_Category',
'ShortListPerPage' => 'Perpage_Category_Short',
'DefaultSorting1Field' => 'Category_Sortfield',
'DefaultSorting2Field' => 'Category_Sortfield2',
'DefaultSorting1Dir' => 'Category_Sortorder',
'DefaultSorting2Dir' => 'Category_Sortorder2',
),
);
Index: branches/5.2.x/core/units/categories/categories_event_handler.php
===================================================================
--- branches/5.2.x/core/units/categories/categories_event_handler.php (revision 16562)
+++ branches/5.2.x/core/units/categories/categories_event_handler.php (revision 16563)
@@ -1,3156 +1,3190 @@
<?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
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$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);
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
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' ) {
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$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(
'OnStoreSelected', '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;
}
/**
+ * Creates "EDITING_MODE" constant.
+ *
+ * @param kEvent $event Event.
+ *
+ * @return void
+ */
+ protected function OnAfterStartupHook(kEvent $event)
+ {
+ if ( !$this->Application->GetVar('admin') ) {
+ // User can't edit anything.
+ kUtil::safeDefine('EDITING_MODE', '');
+
+ return;
+ }
+
+ /** @var Session $admin_session */
+ $admin_session = $this->Application->recallObject('Session.admin');
+
+ // Store Admin Console User's ID to Front-End's session for cross-session permission checks.
+ $this->Application->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
+
+ $base_category = $this->Application->getBaseCategory();
+
+ if ( $this->Application->CheckAdminPermission('CATEGORY.MODIFY', 0, $base_category) ) {
+ // User can edit cms blocks (when viewing front-end through admin's frame).
+ $editing_mode = $this->Application->GetVar('editing_mode');
+ define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
+ }
+
+ // User can't edit anything.
+ kUtil::safeDefine('EDITING_MODE', '');
+ }
+
+ /**
* 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)
{
/** @var kDBItem $object */
$object = $event->getObject();
$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);
/** @var kDBList $object */
$object = $event->getObject();
// 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)
/** @var kDBList $list */
$list = $this->Application->recallObject($prefix_special);
$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');
}
}
/** @var kCatDBItem $p_item */
$p_item = $this->Application->recallObject($related_prefix . '.current', null, Array('skip_autoload' => true));
$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);
/** @var kDBList $object */
$object = $event->getObject();
$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->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 {
/** @var kCountHelper $count_helper */
$count_helper = $this->Application->recallObject('CountHelper');
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()
{
/** @var kThemesHelper $themes_helper */
$themes_helper = $this->Application->recallObject('ThemesHelper');
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);
}
$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
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' ) {
$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
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');
$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
}
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;
}
/** @var kDBItem $object */
$object = $event->getObject(Array ('skip_autoload' => true));
$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);
/** @var CategoriesItem $object */
$object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true, 'live_table' => true));
$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 ) {
/** @var kPermCacheUpdater $cache_updater */
$cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $parent_path));
$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
/** @var CategoriesItem $temp_object */
$temp_object = $event->getObject(Array ('skip_autoload' => true));
$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
/** @var CategoriesItem $live_object */
$live_object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('live_table' => true, 'skip_autoload' => true));
$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);
/** @var kDBItem $object */
$object = $event->getObject();
// 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 ();
/** @var CategoriesItem $object */
$object = $event->getObject();
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 ) {
/** @var kOpenerStack $opener_stack */
$opener_stack = $this->Application->makeClass('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)
{
/** @var CategoriesItem $object */
$object = $event->getObject( Array ('skip_autoload' => true) );
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);
/** @var kDBItem $object */
$object = $event->getObject();
$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 ) {
/** @var kTempTablesHandler $temp_handler */
$temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', '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 ) {
/** @var CategoriesItem $rb */
$rb = $this->Application->recallObject('c.recycle', null, Array ('skip_autoload' => true));
$rb->Load($recycle_bin);
/** @var CategoriesItem $cat */
$cat = $event->getObject(Array ('skip_autoload' => true));
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 ) {
/** @var kRecursiveHelper $recursive_helper */
$recursive_helper = $this->Application->recallObject('RecursiveHelper');
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');
/** @var kClipboardHelper $clipboard_helper */
$clipboard_helper = $this->Application->recallObject('ClipboardHelper');
$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');
/** @var kClipboardHelper $clipboard_helper */
$clipboard_helper = $this->Application->recallObject('ClipboardHelper');
$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)
{
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$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);
}
/** @var kRecursiveHelper $recursive_helper */
$recursive_helper = $this->Application->recallObject('RecursiveHelper');
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);
}
}
/** @var kPriorityHelper $priority_helper */
$priority_helper = $this->Application->recallObject('PriorityHelper');
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);
/** @var CategoriesItem $object */
$object = $event->getObject();
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 ;
}
/** @var kPermissionsHelper $perm_helper */
$perm_helper = $this->Application->recallObject('PermissionsHelper');
$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);
/** @var kDBItem $object */
$object = $event->getObject();
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)
{
/** @var kDBItem $object */
$object = $event->getObject();
$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)
{
/** @var kDBItem $object */
$object = $event->getObject();
$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;
}
/** @var kDBItem $object */
$object = $event->getObject();
/** @var kPermCacheUpdater $cache_updater */
$cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $object->GetDBField('ParentPath')));
$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) ) {
/** @var kDBList $object */
$object = $event->getObject();
$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;
}
/** @var CategoriesItem $object */
$object = $event->getObject(Array ('skip_autoload' => true));
$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);
}
$email_event = $event->Name == 'OnMassApprove' ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY';
$this->Application->emailUser($email_event, $object->GetDBField('CreatedById'));
}
}
}
$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)
{
/** @var kDBItem $object */
$object = $event->getObject();
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');
/** @var kDBList $object */
$object = $event->getObject();
// 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)
{
/** @var Array $list_sortings */
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', 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');
}
/** @var CategoriesItem $page */
$page = $this->Application->recallObject($this->Prefix . '.-virtual', null, Array ('page' => $t));
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 ) {
/** @var kThemesHelper $themes_helper */
$themes_helper = $this->Application->recallObject('ThemesHelper');
// only, used when in "Design Mode"
$this->Application->SetVar('theme.current_id', $themes_helper->getCurrentThemeId());
}
/** @var kDBItem $theme */
$theme = $this->Application->recallObject('theme.current');
$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)
{
/** @var CategoriesItem $object */
$object = $event->getObject();
$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'));
}
}
$object->checkFilename();
$object->generateFilename();
// 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') ));
$category_type = $object->GetDBField('Type');
// Changing category type would associate/disassociate it to theme.
if ( $category_type != $object->GetOriginalField('Type') ) {
if ( $category_type == PAGE_TYPE_TEMPLATE ) {
$object->SetDBField('ThemeId', $this->_getCurrentThemeId());
}
else {
$object->SetDBField('ThemeId', 0);
}
}
if ( $category_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
/** @var kMultiLanguage $ml_formatter */
$ml_formatter = $this->Application->recallObject('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);
/** @var kDBItem $object */
$object = $event->getObject();
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');
/** @var CategoriesItem $object */
$object = $this->Application->recallObject($this->Prefix . '.rebuild-path', null, Array ('skip_autoload' => true));
$parent_id = $base_category;
/** @var kFilenamesHelper $filenames_helper */
$filenames_helper = $this->Application->recallObject('FilenamesHelper');
$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 ;
}
/** @var SiteConfigHelper $site_config_helper */
$site_config_helper = $this->Application->recallObject('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
/** @var CategoryHelper $category_helper */
$category_helper = $this->Application->recallObject('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 = '';
}
/** @var array $list_sqls */
$list_sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs');
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;
}
/** @var kDBItem $object */
$object = $event->getObject();
// 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);
/** @var CategoriesItem $object */
$object = $event->getObject();
// 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();
/** @var CategoriesItem $dummy */
$dummy = $this->Application->recallObject($event->Prefix . '.rebuild', NULL, Array ('skip_autoload' => true));
$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 ) {
/** @var kPermCacheUpdater $updater */
$updater = $this->Application->makeClass('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'));
/** @var CategoryHelper $category_helper */
$category_helper = $this->Application->recallObject('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;
$keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords')));
/** @var kHTTPQuery $query_object */
$query_object = $this->Application->recallObject('HTTPQuery');
/** @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'))
{
$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');
/** @var kDBList $object */
$object = $event->getObject();
$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.
$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);
/** @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;
}
/** @var kDBItem $object */
$object = $event->getObject();
$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) ) {
/** @var Params $actions */
$actions = $this->Application->recallObject('kActions');
$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' ) {
/** @var kDBItem $object */
$object = $event->getObject();
$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);
/** @var kRewriteUrlProcessor $rewrite_processor */
$rewrite_processor = $this->Application->recallObject('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);
/** @var kRewriteUrlProcessor $rewrite_processor */
$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
if ( $friendly ) {
$vars['is_friendly_url'] = true;
$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';
/** @var kRewriteUrlProcessor $rewrite_processor */
$rewrite_processor = $this->Application->recallObject('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)
{
/** @var kDBItem $object */
$object = $event->getObject();
$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);
/** @var kDBItem $object */
$object = $event->getObject();
$object->SetDBField('ResourceId', 0); // this will reset it
}
}

Event Timeline