Index: branches/5.3.x/core/kernel/application.php =================================================================== --- branches/5.3.x/core/kernel/application.php (revision 16170) +++ branches/5.3.x/core/kernel/application.php (revision 16171) @@ -1,3065 +1,3064 @@ <?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 + * Registered routers, that are used during url building and parsing. * - * @var Array - * @access public + * @var array */ - public $RewriteListeners = Array (); + public $routers = array(); /** * Reference to debugger * * @var Debugger * @access public */ public $Debugger = null; /** * Holds all phrases used * in code and template * * @var kPhraseCache * @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->UnitConfigReader->scanModules(MODULES_PATH); // Will also set routers. $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('kHTTPQuery'); $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 + $this->UnitConfigReader->AfterConfigRead(); 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') ) { $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; $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); } $this->UrlManager = $this->makeClass('kUrlManager'); $this->EventManager = $this->makeClass('kEventManager'); $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(); $this->Factory->configureAutoloader(); return; } // use makeClass over recallObject, since used before kApplication initialization during installation $modules_helper = $this->makeClass('kModulesHelper'); /* @var $modules_helper kModulesHelper */ $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(); $this->Factory->configureAutoloader(); } /** * 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 $language_config = $this->getUnitConfig('lang'); $table = $language_config->getTableName(); $id_field = $language_config->getIDField(); $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; $theme_config = $this->getUnitConfig('theme'); $sql = 'SELECT ' . $theme_config->getIDField() . ' FROM ' . $theme_config->getTableName() . ' 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->getUnitConfig('curr')->getTableName() . ' 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 $site_domain kDBItem */ } if ( $this->siteDomain->isLoaded() ) { return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field); } return false; } /** * Registers classes, that are used before unit configs (where class registration usually is done) are read. * * Called automatically while initializing kApplication. * * @return void * @access public */ public function RegisterDefaultClasses() { // Database. $this->registerClass('IDBConnection', KERNEL_PATH . '/db/i_db_connection.php'); $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'); // Cache. $this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php'); $this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php'); // Unit configs. $this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php'); $this->registerClass('kUnitConfigCloner', KERNEL_PATH . '/utility/unit_config_cloner.php'); // Urls. $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'); // Events. $this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php'); $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'); // Misc. $this->registerClass('kPhraseCache', KERNEL_PATH . '/languages/phrases_cache.php'); $this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH); // Aliased. $this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions'); $this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor'); $this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender'); } /** * Registers default build events * * @return void * @access protected */ protected 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') ) { $admin_session = $this->recallObject('Session.admin'); /* @var $admin_session Session */ // 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 ) { $cms_handler = $this->recallObject('st_EventHandler'); /* @var $cms_handler CategoriesEventHandler */ $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:'); } } /** * Only renders template * * @see kDBEventHandler::_errorNotFound() */ public function QuickRun() { // discard any half-parsed content ob_clean(); // replace current page content with 404 $this->InitParser(); $this->HTML = $this->Parser->Run($this->GetVar('t')); } /** * 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'] = time(); $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'] = time(); $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'] = time(); $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'] = time(); $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() { $session = $this->recallObject('Session'); /* @var $session Session */ return $session->GetID(); } /** * Destroys current session * * @return void * @access public * @see UserHelper::logoutUser() */ public function DestroySession() { $session = $this->recallObject('Session'); /* @var $session 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) { $session = $this->recallObject('Session'); /* @var $session 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) { $session = $this->recallObject('Session'); /* @var $session 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) { $processor = $this->Parser->GetProcessor($prefix); /* @var $processor kDBTagProcessor */ 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 seo template by physical template * * @param string $physical_template * @return string * @access public */ public function getSeoTemplate($physical_template) { return $this->UrlManager->getSeoTemplate($physical_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 $domain Domain override. * @param boolean $ssl_redirect Redirect to/from SSL. * * @return string */ public function BaseURL($domain = '', $ssl_redirect = null) { if ( $ssl_redirect === null ) { // stay on same encryption level return PROTOCOL . ($domain ? $domain : SERVER_NAME) . (defined('PORT') ? ':' . PORT : '') . BASE_PATH . '/'; } if ( $ssl_redirect ) { // going from http:// to https:// $protocol = 'https://'; $domain = $this->getSecureDomain(); } else { // going from https:// to http:// $protocol = 'http://'; $domain = $this->siteDomainField('DomainName'); if ( $domain === false ) { $domain = DOMAIN; // not on site domain } } return $protocol . $domain . (defined('PORT') ? ':' . PORT : '') . BASE_PATH . '/'; } /** * Returns secure domain. * * @return string */ public function getSecureDomain() { $ret = $this->isAdmin ? $this->ConfigValue('AdminSSLDomain') : false; if ( !$ret ) { $ssl_domain = $this->siteDomainField('SSLDomainName'); return strlen($ssl_domain) ? $ssl_domain : $this->ConfigValue('SSLDomain'); } return $ret; } /** * 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>' . $_SERVER['HTTP_REFERER'] . '</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 = \'' . $location . '\';</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 $session = $this->recallObject('Session'); /* @var $session 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() { $session = $this->recallObject('Session'); /* @var $session 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 $user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login')); /* @var $user UsersItem */ $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); } /** * 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 string $module * @param int $status * @access public */ public function registerScheduledTask($short_name, $event_string, $run_schedule, $module, $status = STATUS_ACTIVE) { $this->EventManager->registerScheduledTask($short_name, $event_string, $run_schedule, $module, $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) { $aggregator = $this->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator 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, $event_params = 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, $arguments = Array ()) { return $this->Factory->makeClass($pseudo_class, $arguments); } /** * Returns sub-classes of given ancestor class. * * @param string $ancestor_class Ancestor class. * @param boolean $concrete_only Return only non-abstract classes. * * @return array */ public function getSubClasses($ancestor_class, $concrete_only = true) { return $this->Factory->getSubClasses($ancestor_class, $concrete_only); } /** * 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; } /** * Returns unit config for given prefix * * @param string $prefix * @return kUnitConfig * @access public */ public function getUnitConfig($prefix) { return $this->UnitConfigReader->getUnitConfig($prefix); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix * @return bool */ public function prefixRegistred($prefix) { return $this->UnitConfigReader->prefixRegistered($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->getUnitConfig($current_prefix)->getParentPrefix()) { 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 ()) { $email = $this->makeClass('kEmail'); /* @var $email 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(); } /** * 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) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ 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) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ 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; } $visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0)); /* @var $visit kDBItem */ 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) { $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ 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; } $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $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->routers = $data['Application.Routers']; $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.Routers' => $this->routers, '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.3.x/core/kernel/managers/cache_manager.php =================================================================== --- branches/5.3.x/core/kernel/managers/cache_manager.php (revision 16170) +++ branches/5.3.x/core/kernel/managers/cache_manager.php (revision 16171) @@ -1,852 +1,852 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kCacheManager extends kBase implements kiCacheable { /** * Used variables from SystemSettings table * * @var Array * @access protected */ protected $configVariables = Array(); /** * Used variables from SystemSettings table retrieved from unit cache * * @var Array * @access protected */ protected $originalConfigVariables = Array (); /** * IDs of config variables used in current run (for caching) * * @var Array * @access protected */ protected $configIDs = Array (); /** * IDs of config variables retrieved from unit cache * * @var Array * @access protected */ protected $originalConfigIDs = Array (); /** * Object of memory caching class * * @var kCache * @access protected */ protected $cacheHandler = null; protected $temporaryCache = Array ( 'registerAggregateTag' => Array (), 'registerScheduledTask' => Array (), 'registerHook' => Array (), 'registerBuildEvent' => Array (), 'registerAggregateTag' => Array (), ); /** * Name of database table, where configuration settings are stored * * @var string * @access protected */ protected $settingTableName = ''; /** * Set's references to kApplication and DBConnection interface class instances * * @access public */ public function __construct() { parent::__construct(); $this->settingTableName = TABLE_PREFIX . 'SystemSettings'; if ( defined('IS_INSTALL') && IS_INSTALL ) { // table substitution required, so "root" can perform login to upgrade to 5.2.0, where setting table was renamed if ( !$this->Application->TableFound(TABLE_PREFIX . 'SystemSettings') ) { $this->settingTableName = TABLE_PREFIX . 'ConfigurationValues'; } } } /** * Creates caching manager instance * * @access public */ public function InitCache() { $this->cacheHandler = $this->Application->makeClass('kCache'); } /** * Returns cache key, used to cache phrase and configuration variable IDs used on current page * * @return string * @access protected */ protected function getCacheKey() { // TODO: maybe language part isn't required, since same phrase from different languages have one ID now return $this->Application->GetVar('t') . $this->Application->GetVar('m_theme') . $this->Application->GetVar('m_lang') . $this->Application->isAdmin; } /** * Loads phrases and configuration variables, that were used on this template last time * * @access public */ public function LoadApplicationCache() { $phrase_ids = $config_ids = Array (); $sql = 'SELECT PhraseList, ConfigVariables FROM ' . TABLE_PREFIX . 'PhraseCache WHERE Template = ' . $this->Conn->qstr( md5($this->getCacheKey()) ); $res = $this->Conn->GetRow($sql); if ($res) { if ( $res['PhraseList'] ) { $phrase_ids = explode(',', $res['PhraseList']); } if ( $res['ConfigVariables'] ) { $config_ids = array_diff( explode(',', $res['ConfigVariables']), $this->originalConfigIDs); } } $this->Application->Phrases->Init('phrases', '', null, $phrase_ids); $this->configIDs = $this->originalConfigIDs = $config_ids; $this->InitConfig(); } /** * Updates phrases and configuration variables, that were used on this template * * @access public */ public function UpdateApplicationCache() { $update = false; //something changed $update = $update || $this->Application->Phrases->NeedsCacheUpdate(); $update = $update || (count($this->configIDs) && $this->configIDs != $this->originalConfigIDs); if ($update) { $fields_hash = Array ( 'PhraseList' => implode(',', $this->Application->Phrases->Ids), 'CacheDate' => time(), 'Template' => md5( $this->getCacheKey() ), 'ConfigVariables' => implode(',', array_unique($this->configIDs)), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PhraseCache', 'REPLACE'); } } /** * Loads configuration variables, that were used on this template last time * * @access protected */ protected function InitConfig() { if (!$this->originalConfigIDs) { return ; } $sql = 'SELECT VariableValue, VariableName FROM ' . $this->settingTableName . ' WHERE VariableId IN (' . implode(',', $this->originalConfigIDs) . ')'; $config_variables = $this->Conn->GetCol($sql, 'VariableName'); $this->configVariables = array_merge($this->configVariables, $config_variables); } /** * Returns configuration option value by name * * @param string $name * @return string * @access public */ public function ConfigValue($name) { $site_domain_override = Array ( 'DefaultEmailSender' => 'AdminEmail', 'DefaultEmailRecipients' => 'DefaultEmailRecipients', ); if ( isset($site_domain_override[$name]) ) { $res = $this->Application->siteDomainField($site_domain_override[$name]); if ( $res ) { return $res; } } if ( array_key_exists($name, $this->configVariables) ) { return $this->configVariables[$name]; } if ( defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound($this->settingTableName, true) ) { return false; } $this->Conn->nextQueryCachable = true; $sql = 'SELECT VariableId, VariableValue FROM ' . $this->settingTableName . ' WHERE VariableName = ' . $this->Conn->qstr($name); $res = $this->Conn->GetRow($sql); if ( $res !== false ) { $this->configIDs[] = $res['VariableId']; $this->configVariables[$name] = $res['VariableValue']; return $res['VariableValue']; } trigger_error('Usage of undefined configuration variable "<strong>' . $name . '</strong>"', E_USER_NOTICE); return false; } /** * 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) { $this->configVariables[$name] = $value; if ( $local_cache_only ) { return; } $fields_hash = Array ('VariableValue' => $value); $this->Conn->doUpdate($fields_hash, $this->settingTableName, 'VariableName = ' . $this->Conn->qstr($name)); if ( array_key_exists($name, $this->originalConfigVariables) && $value != $this->originalConfigVariables[$name] ) { $this->DeleteUnitCache(); } } /** * Loads data, that was cached during unit config parsing * * @return bool * @access public */ public function LoadUnitCache() { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->getCache('master:configs_parsed', false, CacheSettings::$unitCacheRebuildTime); } else { $data = $this->Application->getDBCache('configs_parsed', CacheSettings::$unitCacheRebuildTime); } if ( $data ) { $cache = unserialize($data); // 126 KB all modules unset($data); $this->Application->InitManagers(); $this->Application->setFromCache($cache); $aggregator = $this->Application->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $aggregator->setFromCache($cache); $this->setFromCache($cache); unset($cache); return true; } return false; } /** * Empties factory and event manager cache (without storing changes) */ public function EmptyUnitCache() { // maybe discover keys automatically from corresponding classes $cache_keys = Array ( 'Factory.Files', 'Factory.ClassInfo', 'Factory.ClassTree', 'Factory.Namespaces', 'Factory.realClasses', 'ConfigReader.prefixFiles', 'ConfigCloner.clones', 'EventManager.beforeHooks', 'EventManager.afterHooks', 'EventManager.scheduledTasks', 'EventManager.buildEvents', - 'Application.ReplacementTemplates', 'Application.RewriteListeners', 'Application.ModuleInfo', + 'Application.ReplacementTemplates', 'Application.Routers', 'Application.ModuleInfo', 'Application.ConfigHash', 'Application.ConfigCacheIds', ); $empty_cache = Array (); foreach ($cache_keys as $cache_key) { $empty_cache[$cache_key] = Array (); } $this->Application->setFromCache($empty_cache); $this->setFromCache($empty_cache); // Otherwise kModulesHelper indirectly used from includeConfigFiles won't work. $this->Application->RegisterDefaultClasses(); } /** * Updates data, that was parsed from unit configs this time * * @access public */ public function UpdateUnitCache() { $aggregator = $this->Application->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $this->preloadConfigVars(); // preloading will put to cache $cache = array_merge( $this->Application->getToCache(), $aggregator->getToCache(), $this->getToCache() ); $cache_rebuild_by = SERVER_NAME . ' (' . $this->Application->getClientIp() . ') - ' . date('d/m/Y H:i:s'); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->setCache('master:configs_parsed', serialize($cache)); $this->Application->setCache('master:last_cache_rebuild', $cache_rebuild_by); } else { $this->Application->setDBCache('configs_parsed', serialize($cache)); $this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by); } } public function delayUnitProcessing($method, $params) { if ($this->Application->InitDone) { // init already done -> call immediately (happens during installation) $function = Array (&$this->Application, $method); call_user_func_array($function, $params); return ; } $this->temporaryCache[$method][] = $params; } public function applyDelayedUnitProcessing() { foreach ($this->temporaryCache as $method => $method_calls) { $function = Array (&$this->Application, $method); foreach ($method_calls as $method_call) { call_user_func_array($function, $method_call); } $this->temporaryCache[$method] = Array (); } } /** * 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) { if ( isset($config_variables) && !array_intersect(array_keys($this->originalConfigVariables), $config_variables) ) { // prevent cache reset, when given config variables are not in unit cache return; } if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } else { $this->rebuildDBCache('configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } } /** * Deletes cached section tree, used during permission checking and admin console tree display * * @return void * @access public */ public function DeleteSectionCache() { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } else { $this->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } } /** * Preloads 21 widely used configuration variables, so they will get to cache for sure * * @access protected */ protected function preloadConfigVars() { $config_vars = Array ( // session related 'SessionTimeout', 'SessionCookieName', 'SessionCookieDomains', 'SessionBrowserSignatureCheck', 'SessionIPAddressCheck', 'CookieSessions', 'KeepSessionOnBrowserClose', 'User_GuestGroup', 'User_LoggedInGroup', 'RegistrationUsernameRequired', // output related 'UseModRewrite', 'UseContentLanguageNegotiation', 'UseOutputCompression', 'OutputCompressionLevel', 'Config_Site_Time', 'SystemTagCache', 'DefaultGridPerPage', // tracking related 'UseChangeLog', 'UseVisitorTracking', 'ModRewriteUrlEnding', 'ForceModRewriteUrlEnding', 'RunScheduledTasksFromCron', ); $escaped_config_vars = $this->Conn->qstrArray($config_vars); $sql = 'SELECT VariableId, VariableName, VariableValue FROM ' . $this->settingTableName . ' WHERE VariableName IN (' . implode(',', $escaped_config_vars) . ')'; $data = $this->Conn->Query($sql, 'VariableId'); foreach ($data as $variable_id => $variable_info) { $this->configIDs[] = $variable_id; $this->configVariables[ $variable_info['VariableName'] ] = $variable_info['VariableValue']; } } /** * Sets data from cache to object * * Used for cases, when ConfigValue is called before LoadApplicationCache method (e.g. session init, url engine init) * * @param Array $data * @access public */ public function setFromCache(&$data) { $this->configVariables = $this->originalConfigVariables = $data['Application.ConfigHash']; $this->configIDs = $this->originalConfigIDs = $data['Application.ConfigCacheIds']; } /** * 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 ( 'Application.ConfigHash' => $this->configVariables, 'Application.ConfigCacheIds' => $this->configIDs, // not in use, since it only represents template specific values, not global ones // 'Application.Caches.ConfigVariables' => $this->originalConfigIDs, ); } /** * Returns caching type (none, memory, temporary) * * @param int $caching_type * @return bool * @access public */ public function isCachingType($caching_type) { return $this->cacheHandler->getCachingType() == $caching_type; } /** * 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->cacheHandler->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->cacheHandler->setCache($key, $value, $expiration); } /** * Stores new $value in cache with $key name (only if not there already) * * @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->cacheHandler->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->cacheHandler->rebuildCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from cache * * @param string $key * @return void * @access public */ public function deleteCache($key) { $this->cacheHandler->delete($key); } /** * Reset's all memory cache at once * * @return void * @access public */ public function resetCache() { $this->cacheHandler->reset(); } /** * 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) { // no serials in cache key OR cache is outdated $rebuilding = false; $wait_seconds = $max_rebuild_seconds; while (true) { $cached_data = $this->_getDBCache(Array ($name, $name . '_rebuilding', $name . '_rebuild')); if ( $cached_data[$name . '_rebuild'] ) { // cache rebuild requested -> rebuild now $this->deleteDBCache($name . '_rebuild'); if ( $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M1]') ) { return false; } } $cache = $cached_data[$name]; $rebuilding = $cached_data[$name . '_rebuilding']; if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) { // cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M2' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } elseif ( $cache !== false ) { // cache present -> return it $this->cacheHandler->storeStatistics($name, $rebuilding ? 'h' : 'H'); return $cache; } $wait_seconds -= kCache::WAIT_STEP; sleep(kCache::WAIT_STEP); } $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M3' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } /** * Returns value from database cache * * @param string|Array $names key name * @return mixed * @access protected */ protected function _getDBCache($names) { $res = Array (); $names = (array)$names; $this->Conn->nextQueryCachable = true; $sql = 'SELECT Data, Cached, LifeTime, VarName FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName IN (' . implode(',', $this->Conn->qstrArray($names)) . ')'; $cached_data = $this->Conn->Query($sql, 'VarName'); foreach ($names as $name) { if ( !isset($cached_data[$name]) ) { $res[$name] = false; continue; } $lifetime = (int)$cached_data[$name]['LifeTime']; // in seconds if ( ($lifetime > 0) && ($cached_data[$name]['Cached'] + $lifetime < time()) ) { // delete expired $this->Conn->nextQueryCachable = true; $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = ' . $this->Conn->qstr($name); $this->Conn->Query($sql); $res[$name] = false; continue; } $res[$name] = $cached_data[$name]['Data']; } return count($res) == 1 ? array_pop($res) : $res; } /** * 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->cacheHandler->storeStatistics($name, 'WU'); $this->deleteDBCache($name . '_rebuilding'); $this->_setDBCache($name, $value, $expiration); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @param string $insert_type * @return bool * @access protected */ protected function _setDBCache($name, $value, $expiration = false, $insert_type = 'REPLACE') { if ( (int)$expiration <= 0 ) { $expiration = -1; } $fields_hash = Array ( 'VarName' => $name, 'Data' => &$value, 'Cached' => time(), 'LifeTime' => (int)$expiration, ); $this->Conn->nextQueryCachable = true; return $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SystemCache', $insert_type); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @return bool * @access protected */ protected function _addDBCache($name, $value, $expiration = false) { return $this->_setDBCache($name, $value, $expiration, 'INSERT'); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @param string $miss_type * @return bool * @access public */ public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0, $miss_type = 'M') { if ( !isset($mode) || $mode == kCache::REBUILD_NOW ) { $this->cacheHandler->storeStatistics($name, $miss_type); if ( !$max_rebuilding_time ) { return true; } if ( !$this->_addDBCache($name . '_rebuilding', 1, $max_rebuilding_time) ) { $this->cacheHandler->storeStatistics($name, 'l'); return false; } $this->deleteDBCache($name . '_rebuild'); $this->cacheHandler->storeStatistics($name, 'L'); } elseif ( $mode == kCache::REBUILD_LATER ) { $this->_setDBCache($name . '_rebuild', 1, 0); $this->deleteDBCache($name . '_rebuilding'); } return true; } /** * Deletes key from database cache * * @param string $name * @return void * @access public */ public function deleteDBCache($name) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = ' . $this->Conn->qstr($name); $this->Conn->Query($sql); } /** * 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) { $pascal_case_prefix = implode('', array_map('ucfirst', explode('-', $prefix))); $serial_name = $pascal_case_prefix . (isset($id) ? 'IDSerial:' . $id : 'Serial'); if ($increment) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Incrementing serial: <strong>' . $serial_name . '</strong>.'); } $this->setCache($serial_name, (int)$this->getCache($serial_name) + 1); if (!defined('IS_INSTALL') || !IS_INSTALL) { // delete cached mod-rewrite urls related to given prefix and id $delete_clause = isset($id) ? $prefix . ':' . $id : $prefix; $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls WHERE Prefixes LIKE ' . $this->Conn->qstr('%|' . $delete_clause . '|%'); $this->Conn->Query($sql); } } return $serial_name; } /** * Returns cached category informaton 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) { $serial_name = '[%CIDSerial:' . $category_id . '%]'; $cache_key = $name . $serial_name; $ret = $this->getCache($cache_key); if ($ret === false) { if (!$category_id) { // don't query database for "Home" category (ID = 0), because it doesn't exist in database return false; } // this allows to save 2 sql queries for each category $this->Conn->nextQueryCachable = true; $sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . (int)$category_id; $category_data = $this->Conn->GetRow($sql); if ($category_data !== false) { // only direct links to category pages work (symlinks, container pages and so on won't work) $this->setCache('filenames' . $serial_name, $category_data['NamedParentPath']); $this->setCache('category_designs' . $serial_name, ltrim($category_data['CachedTemplate'], '/')); $this->setCache('category_tree' . $serial_name, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']); } } return $this->getCache($cache_key); } } Index: branches/5.3.x/core/kernel/managers/url_processor.php =================================================================== --- branches/5.3.x/core/kernel/managers/url_processor.php (revision 16170) +++ branches/5.3.x/core/kernel/managers/url_processor.php (revision 16171) @@ -1,127 +1,133 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); abstract class kUrlProcessor extends kBase { /** * Reference to kUrlProcessor class instance * * @var kUrlManager */ protected $manager = null; public function __construct(&$manager) { parent::__construct(); $this->setManager($manager); } /** * Sets reference to url manager * * @param kUrlManager $manager * @return void * @access public */ public function setManager(&$manager) { $this->manager =& $manager; } /** * Returns sorted array of passed prefixes (to build url from) * * @param string $pass * @return Array * @access protected */ protected function getPassInfo($pass = 'all') { if ( !$pass ) { $pass = 'all'; } $pass = trim( preg_replace( '/(?<=,|\\A)all(?=,|\\z)/', trim($this->Application->GetVar('passed'), ','), trim($pass, ',') ), ','); if ( !$pass ) { return Array (); } $pass_info = array_unique(explode(',', $pass)); // array( prefix[.special], prefix[.special] ... // we need to keep that sorting despite the sorting below, because this sorts prefixes with same priority by name sort($pass_info, SORT_STRING); // to be prefix1,prefix1.special1,prefix1.special2,prefix3.specialX foreach ($pass_info as $prefix) { list ($prefix_only,) = explode('.', $prefix, 2); - $sorted[$prefix] = $this->Application->getUnitConfig($prefix_only)->getRewritePriority(0); + + if ( isset($this->Application->routers[$prefix_only]) ) { + $sorted[$prefix] = (int)$this->Application->routers[$prefix_only]['priority']; + } + else { + $sorted[$prefix] = 0; + } } asort($sorted, SORT_NUMERIC); $pass_info = array_keys($sorted); // ensure that "m" prefix is at the beginning $main_index = array_search('m', $pass_info); if ( $main_index !== false ) { unset($pass_info[$main_index]); array_unshift($pass_info, 'm'); } return $pass_info; } /** * Builds url * * @param string $t * @param Array $params * @param string $pass * @param bool $pass_events * @param bool $env_var * @return string * @access public */ abstract public function build($t, $params, $pass='all', $pass_events = false, $env_var = true); /** * Parses given string into a set of variables * * @abstract * @param string $string * @param string $pass_name * @return Array * @access public */ abstract public function parse($string, $pass_name = 'passed'); /** * Builds env part that corresponds prefix passed * * @param string $prefix_special item's prefix & [special] * @param Array $params url params * @param bool $pass_events * @return string * @access protected */ abstract protected function BuildModuleEnv($prefix_special, &$params, $pass_events = false); -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/managers/rewrite_url_processor.php =================================================================== --- branches/5.3.x/core/kernel/managers/rewrite_url_processor.php (revision 16170) +++ branches/5.3.x/core/kernel/managers/rewrite_url_processor.php (revision 16171) @@ -1,1080 +1,1060 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kRewriteUrlProcessor extends kUrlProcessor { /** * Holds a reference to httpquery * * @var kHttpQuery * @access protected */ protected $HTTPQuery = null; /** - * Urls parts, that needs to be matched by rewrite listeners + * Urls parts, that needs to be matched by routers. * - * @var Array - * @access protected + * @var array */ - protected $_partsToParse = Array (); + protected $_partsToParse = array(); /** * Category item prefix, that was found * * @var string|boolean * @access protected */ protected $modulePrefix = false; /** * Template aliases for current theme * * @var Array * @access protected */ protected $_templateAliases = null; /** * Domain-based primary language id * * @var int * @access public */ public $primaryLanguageId = false; /** * Domain-based primary theme id * * @var int * @access public */ public $primaryThemeId = false; /** * Possible url endings from ModRewriteUrlEnding configuration variable * * @var Array * @access protected */ protected $_urlEndings = Array ('.html', '/', ''); /** - * Factory storage sub-set, containing mod-rewrite listeners, used during url building and parsing + * Factory storage sub-set, containing routers, used during url building and parsing. * - * @var Array - * @access protected + * @var array */ - protected $rewriteListeners = Array (); + protected $routers = array(); /** * Constructor of kRewriteUrlProcessor class * * @param $manager * @return kRewriteUrlProcessor */ public function __construct(&$manager) { parent::__construct($manager); $this->HTTPQuery = $this->Application->recallObject('kHTTPQuery'); // domain based primary language $this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId'); if (!$this->primaryLanguageId) { // when domain-based language not found -> use site-wide language $this->primaryLanguageId = $this->Application->GetDefaultLanguageId(); } // domain based primary theme $this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId'); if (!$this->primaryThemeId) { // when domain-based theme not found -> use site-wide theme $this->primaryThemeId = $this->Application->GetDefaultThemeId(true); } - $this->_initRewriteListeners(); + $this->initRouters(); } /** * Sets module prefix. * * @param string $prefix Unit config prefix. * * @return void */ public function setModulePrefix($prefix) { $this->modulePrefix = $prefix; } /** * Parses url * * @return void */ public function parseRewriteURL() { $url = $this->Application->GetVar('_mod_rw_url_'); if ( $url ) { $this->_redirectToDefaultUrlEnding($url); $url = $this->_removeUrlEnding($url); } $cached = $this->_getCachedUrl($url); if ( $cached !== false ) { $vars = $cached['vars']; $passed = $cached['passed']; } else { $vars = $this->parse($url); $passed = $vars['pass']; // also used in bottom of this method unset($vars['pass']); if ( !$this->_partsToParse ) { // don't cache 404 Not Found $this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed)); } if ( $this->Application->GetVarDirect('t', 'Post') ) { // template from POST overrides template from URL. $vars['t'] = $this->Application->GetVarDirect('t', 'Post'); if ( isset($vars['is_virtual']) && $vars['is_virtual'] ) { $vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS) } } unset($vars['is_virtual']); } foreach ($vars as $name => $value) { $this->HTTPQuery->Set($name, $value); } $this->_initAll(); // also will use parsed language to load phrases from it $this->HTTPQuery->finalizeParsing($passed); } /** * Detects url ending of given url * * @param string $url * @return string * @access protected */ protected function _findUrlEnding($url) { if ( !$url ) { return ''; } foreach ($this->_urlEndings as $url_ending) { if ( mb_substr($url, mb_strlen($url) - mb_strlen($url_ending)) == $url_ending ) { return $url_ending; } } return ''; } /** * Removes url ending from url * * @param string $url * @return string * @access protected */ protected function _removeUrlEnding($url) { $url_ending = $this->_findUrlEnding($url); if ( !$url_ending ) { return $url; } return mb_substr($url, 0, mb_strlen($url) - mb_strlen($url_ending)); } /** * Redirects user to page with default url ending, where needed * * @param string $url * @return void * @access protected */ protected function _redirectToDefaultUrlEnding($url) { $default_ending = $this->Application->ConfigValue('ModRewriteUrlEnding'); if ( $this->_findUrlEnding($url) == $default_ending || !$this->Application->ConfigValue('ForceModRewriteUrlEnding') ) { return; } // user manually typed url with different url ending -> redirect to same url with default url ending $target_url = $this->Application->BaseURL() . $this->_removeUrlEnding($url) . $default_ending; trigger_error('Mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" without "<strong>' . $default_ending . '</strong>" line ending used', E_USER_NOTICE); $this->Application->Redirect('external:' . $target_url, Array ('response_code' => 301)); } /** * Returns url parsing result from cache or false, when not yet parsed * * @param $url * @return Array|bool * @access protected */ protected function _getCachedUrl($url) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return false; } $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'CachedUrls WHERE Hash = ' . kUtil::crc32($url) . ' AND DomainId = ' . (int)$this->Application->siteDomainField('DomainId'); $data = $this->Conn->GetRow($sql); if ( $data ) { $lifetime = (int)$data['LifeTime']; // in seconds if ( ($lifetime > 0) && ($data['Cached'] + $lifetime < TIMENOW) ) { // delete expired $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls WHERE UrlId = ' . $data['UrlId']; $this->Conn->Query($sql); return false; } return unserialize($data['ParsedVars']); } return false; } /** * Caches url * * @param string $url * @param Array $data * @return void * @access protected */ protected function _setCachedUrl($url, $data) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return; } $vars = $data['vars']; $passed = $data['passed']; sort($passed); // get expiration if ( $vars['m_cat_id'] > 0 ) { $sql = 'SELECT PageExpiration FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . $vars['m_cat_id']; $expiration = $this->Conn->GetOne($sql); } // get prefixes $prefixes = Array (); $m_index = array_search('m', $passed); if ( $m_index !== false ) { unset($passed[$m_index]); if ( $vars['m_cat_id'] > 0 ) { $prefixes[] = 'c:' . $vars['m_cat_id']; } $prefixes[] = 'lang:' . $vars['m_lang']; $prefixes[] = 'theme:' . $vars['m_theme']; } foreach ($passed as $prefix) { if ( array_key_exists($prefix . '_id', $vars) && is_numeric($vars[$prefix . '_id']) ) { $prefixes[] = $prefix . ':' . $vars[$prefix . '_id']; } else { $prefixes[] = $prefix; } } $fields_hash = Array ( 'Url' => $url, 'Hash' => kUtil::crc32($url), 'DomainId' => (int)$this->Application->siteDomainField('DomainId'), 'Prefixes' => $prefixes ? '|' . implode('|', $prefixes) . '|' : '', 'ParsedVars' => serialize($data), 'Cached' => time(), 'LifeTime' => isset($expiration) && is_numeric($expiration) ? $expiration : -1 ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CachedUrls'); } /** - * Loads all registered rewrite listeners, so they could be quickly accessed later + * Loads all registered routers, so they could be quickly accessed later. * - * @access protected + * @return void */ - protected function _initRewriteListeners() + protected function initRouters() { static $init_done = false; - if ($init_done || count($this->Application->RewriteListeners) == 0) { - // not initialized OR mod-rewrite url with missing config cache - return ; + // Not initialized OR mod-rewrite url with missing config cache. + if ( $init_done || count($this->Application->routers) == 0 ) { + return; } - foreach ($this->Application->RewriteListeners as $prefix => $listener_data) { - foreach ($listener_data['listener'] as $index => $rewrite_listener) { - list ($listener_prefix, $listener_method) = explode(':', $rewrite_listener); - - // don't use temp variable, since it will swap objects in Factory in PHP5 - $this->rewriteListeners[$prefix][$index] = Array (); - $this->rewriteListeners[$prefix][$index][0] = $this->Application->recallObject($listener_prefix); - $this->rewriteListeners[$prefix][$index][1] = $listener_method; - } + foreach ( $this->Application->routers as $prefix => $router_data ) { + $this->routers[$prefix] = $this->Application->makeClass($router_data['class']); } define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding')); - $init_done = true; } /** * Parses given string into a set of variables (url in this case) * * @param string $string * @param string $pass_name * @return Array * @access public */ public function parse($string, $pass_name = 'pass') { // external url (could be back this website as well) if ( preg_match('/external:(.*)/', $string, $regs) ) { $string = $regs[1]; } $vars = Array (); $url_components = parse_url($string); if ( isset($url_components['query']) ) { parse_str(html_entity_decode($url_components['query']), $url_params); if ( isset($url_params[ENV_VAR_NAME]) ) { $url_params = array_merge($url_params, $this->manager->plain->parse($url_params[ENV_VAR_NAME], $pass_name)); unset($url_params[ENV_VAR_NAME]); } $vars = array_merge($vars, $url_params); } $this->_fixPass($vars, $pass_name); if ( isset($url_components['path']) ) { if ( BASE_PATH ) { $string = preg_replace('/^' . preg_quote(BASE_PATH, '/') . '/', '', $url_components['path'], 1); } else { $string = $url_components['path']; } $string = $this->_removeUrlEnding(trim($string, '/')); } else { $string = ''; } $url_parts = $string ? explode('/', mb_strtolower($string)) : Array (); $this->setModulePrefix(false); $this->_partsToParse = $url_parts; if ( ($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts ) { $this->_setDefaultValues($vars); } if ( !$url_parts ) { $this->_initAll(); $vars['t'] = $this->Application->UrlManager->getTemplateName(); return $vars; } $this->_parseLanguage($url_parts, $vars); $this->_parseTheme($url_parts, $vars); // http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_page> // http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_page> (category-based section template) // http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_item> // http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_item> (category-based detail template) // http://site-url/<language>/<theme>/<rl_injections>/<category>[_<category_page>]/<rl_part> (customized url) - if ( !$this->_processRewriteListeners($url_parts, $vars) ) { - // rewrite listener wasn't able to determine template + if ( !$this->processRouters($url_parts, $vars) ) { + // Routers weren't able to determine template. $this->_parsePhysicalTemplate($url_parts, $vars); if ( ($this->modulePrefix === false) && $vars['m_cat_id'] && !$this->_partsToParse ) { // no category item found, but category found and all url matched -> module index page return $vars; } } if ( $this->_partsToParse ) { $vars = array_merge($vars, $this->manager->prepare404($vars['m_theme'])); } return $vars; } /** * Ensures, that "m" is always in "pass" variable * * @param Array $vars * @param string $pass_name * @return void * @access protected */ protected function _fixPass(&$vars, $pass_name) { if ( isset($vars[$pass_name]) ) { $vars[$pass_name] = array_unique(explode(',', 'm,' . $vars[$pass_name])); } else { $vars[$pass_name] = Array ('m'); } } /** * Initializes theme & language based on parse results * * @return void * @access protected */ protected function _initAll() { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); // No need, since we don't have any cached phrase IDs + nobody will use kPhraseCache::LanguageId soon. // $this->Application->Phrases->Init('phrases'); } /** * Sets default parsed values before actual url parsing (only, for empty url) * * @param Array $vars * @access protected */ protected function _setDefaultValues(&$vars) { $defaults = Array ( 'm_cat_id' => 0, // no category 'm_cat_page' => 1, // first category page 'm_opener' => 's', // stay on same page 't' => 'index' // main site page ); if ($this->primaryLanguageId) { // domain-based primary language $defaults['m_lang'] = $this->primaryLanguageId; } if ($this->primaryThemeId) { // domain-based primary theme $defaults['m_theme'] = $this->primaryThemeId; } foreach ($defaults as $default_key => $default_value) { if ($this->HTTPQuery->Get($default_key) === false) { $vars[$default_key] = $default_value; } } } /** - * Processes url using rewrite listeners + * Processes url using routers. * * Pattern: Chain of Command * - * @param Array $url_parts - * @param Array $vars - * @return bool - * @access protected + * @param array $url_parts Url parts. + * @param array $vars Vars. + * + * @return boolean */ - protected function _processRewriteListeners(&$url_parts, &$vars) + protected function processRouters(array &$url_parts, array &$vars) { - $this->_initRewriteListeners(); + $this->initRouters(); $page_number = $this->_parsePage($url_parts, $vars); - foreach ($this->rewriteListeners as $prefix => $listeners) { - // set default page - // $vars[$prefix . '_Page'] = 1; // will override page in session in case, when none is given in url - - if ($page_number) { - // page given in url - use it + foreach ( $this->routers as $prefix => $router ) { + if ( $page_number ) { + // Page given in url - use it. $vars[$prefix . '_id'] = 0; $vars[$prefix . '_Page'] = $page_number; } - // $listeners[1] - listener, used for parsing - $listener_result = $listeners[1][0]->$listeners[1][1](REWRITE_MODE_PARSE, $prefix, $vars, $url_parts); - if ($listener_result === false) { - // will not proceed to other methods + if ( $router->parse($url_parts, $vars) === false ) { + // Will not proceed to other methods. return true; } } - // will proceed to other methods + // Will proceed to other methods. return false; } /** * Set's page (when found) to all modules * * @param Array $url_parts * @param Array $vars * @return string * @access protected * - * @todo Should find a way, how to determine what rewrite listener page is it + * @todo Should find a way, how to determine what router page is it. */ protected function _parsePage(&$url_parts, &$vars) { if (!$url_parts) { return false; } $page_number = end($url_parts); if (!is_numeric($page_number)) { return false; } array_pop($url_parts); $this->partParsed($page_number, 'rtl'); return $page_number; } /** * Gets language part from url * * @param Array $url_parts * @param Array $vars * @return bool * @access protected */ protected function _parseLanguage(&$url_parts, &$vars) { if (!$url_parts) { return false; } $url_part = reset($url_parts); $sql = 'SELECT LanguageId, IF(LOWER(PackName) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryLang) AS SortKey FROM ' . TABLE_PREFIX . 'Languages WHERE Enabled = 1 ORDER BY SortKey DESC'; $language_info = $this->Conn->GetRow($sql); if ($language_info && $language_info['LanguageId'] && $language_info['SortKey']) { // primary language will be selected in case, when $url_part doesn't match to other's language pack name // don't use next enabled language, when primary language is disabled $vars['m_lang'] = $language_info['LanguageId']; if ($language_info['SortKey'] == 2) { // language was found by pack name array_shift($url_parts); $this->partParsed($url_part); } elseif ($this->primaryLanguageId) { // use domain-based primary language instead of site-wide primary language $vars['m_lang'] = $this->primaryLanguageId; } return true; } return false; } /** * Gets theme part from url * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parseTheme(&$url_parts, &$vars) { if (!$url_parts) { return false; } $url_part = reset($url_parts); $sql = 'SELECT ThemeId, IF(LOWER(Name) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryTheme) AS SortKey, TemplateAliases FROM ' . TABLE_PREFIX . 'Themes WHERE Enabled = 1 ORDER BY SortKey DESC'; $theme_info = $this->Conn->GetRow($sql); if ($theme_info && $theme_info['ThemeId'] && $theme_info['SortKey']) { // primary theme will be selected in case, when $url_part doesn't match to other's theme name // don't use next enabled theme, when primary theme is disabled $vars['m_theme'] = $theme_info['ThemeId']; if ($theme_info['TemplateAliases']) { $this->_templateAliases = unserialize($theme_info['TemplateAliases']); } else { $this->_templateAliases = Array (); } if ($theme_info['SortKey'] == 2) { // theme was found by name array_shift($url_parts); $this->partParsed($url_part); } elseif ($this->primaryThemeId) { // use domain-based primary theme instead of site-wide primary theme $vars['m_theme'] = $this->primaryThemeId; } return true; } $vars['m_theme'] = 0; // required, because used later for category/template detection return false; } /** * Parses real template name from url * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parsePhysicalTemplate($url_parts, &$vars) { if ( !$url_parts ) { return false; } $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ do { $index_added = false; $template_path = implode('/', $url_parts); $template_found = $themes_helper->getTemplateId($template_path, $vars['m_theme']); if ( !$template_found ) { $index_added = true; $template_found = $themes_helper->getTemplateId($template_path . '/index', $vars['m_theme']); } if ( !$template_found ) { array_shift($url_parts); } } while ( !$template_found && $url_parts ); if ( $template_found ) { $template_parts = explode('/', $template_path); $vars['t'] = $template_path . ($index_added ? '/index' : ''); while ( $template_parts ) { $this->partParsed(array_pop($template_parts), 'rtl'); } // 1. will damage actual category during category item review add process // 2. will use "use_section" parameter of "m_Link" tag to gain same effect // $vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']); return true; } return false; } /** * Returns environment variable values for given prefix (uses directly given params, when available) * * @param string $prefix_special * @param Array $params * @param bool $keep_events * @return Array * @access public */ public function getProcessedParams($prefix_special, &$params, $keep_events) { list ($prefix) = explode('.', $prefix_special); $query_vars = $this->Application->getUnitConfig($prefix)->getQueryString(Array ()); if ( !$query_vars ) { // given prefix doesn't use "env" variable to pass it's data return false; } $event_key = array_search('event', $query_vars); if ( $event_key ) { // pass through event of this prefix unset($query_vars[$event_key]); } if ( array_key_exists($prefix_special . '_event', $params) && !$params[$prefix_special . '_event'] ) { // if empty event, then remove it from url unset($params[$prefix_special . '_event']); } // if pass events is off and event is not implicity passed if ( !$keep_events && !array_key_exists($prefix_special . '_event', $params) ) { unset($params[$prefix_special . '_event']); // remove event from url if requested //otherwise it will use value from get_var } $processed_params = Array (); foreach ($query_vars as $var_name) { // if value passed in params use it, otherwise use current from application $var_name = $prefix_special . '_' . $var_name; $processed_params[$var_name] = array_key_exists($var_name, $params) ? $params[$var_name] : $this->Application->GetVar($var_name); if ( array_key_exists($var_name, $params) ) { unset($params[$var_name]); } } return $processed_params; } /** * Returns module item details template specified in given category custom field for given module prefix * * @param int|Array $category * @param string $module_prefix * @param int $theme_id * @return string * @access public * @todo Move to kPlainUrlProcessor */ public function GetItemTemplate($category, $module_prefix, $theme_id = null) { if ( !isset($theme_id) ) { $theme_id = $this->Application->GetVar('m_theme'); } $category_id = is_array($category) ? $category['CategoryId'] : $category; $cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CIDSerial:' . $category_id . '%][%ThemeIDSerial:' . $theme_id . '%]' . $module_prefix; $cached_value = $this->Application->getCache($cache_key); if ( $cached_value !== false ) { return $cached_value; } if ( !is_array($category) ) { if ( $category == 0 ) { $category = $this->Application->findModule('Var', $module_prefix, 'RootCat'); } $sql = 'SELECT c.ParentPath, c.CategoryId FROM ' . TABLE_PREFIX . 'Categories AS c WHERE c.CategoryId = ' . $category; $category = $this->Conn->GetRow($sql); } $parent_path = implode(',', explode('|', substr($category['ParentPath'], 1, -1))); // item template is stored in module' system custom field - need to get that field Id $primary_lang = $this->Application->GetDefaultLanguageId(); $item_template_field_id = $this->getItemTemplateCustomField($module_prefix); // looking for item template through cats hierarchy sorted by parent path $query = ' SELECT ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ', FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') AS Ord1, c.CategoryId, c.Name, ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ' FROM ' . TABLE_PREFIX . 'Categories AS c LEFT JOIN ' . TABLE_PREFIX . 'CategoryCustomData AS ccd ON ccd.ResourceId = c.ResourceId WHERE c.CategoryId IN (' . $parent_path . ') AND ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ' != \'\' ORDER BY FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') DESC'; $item_template = $this->Conn->GetOne($query); if ( !isset($this->_templateAliases) ) { // when empty url OR mod-rewrite disabled $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $sql = 'SELECT TemplateAliases FROM ' . TABLE_PREFIX . 'Themes WHERE ThemeId = ' . (int)$themes_helper->getCurrentThemeId(); $template_aliases = $this->Conn->GetOne($sql); $this->_templateAliases = $template_aliases ? unserialize($template_aliases) : Array (); } if ( substr($item_template, 0, 1) == '#' ) { // it's template alias + "#" isn't allowed in filenames $item_template = (string)getArrayValue($this->_templateAliases, $item_template); } $this->Application->setCache($cache_key, $item_template); return $item_template; } /** * Returns category custom field id, where given module prefix item template name is stored * * @param string $module_prefix * @return int * @access public * @todo Move to kPlainUrlProcessor; decrease visibility, since used only during upgrade */ public function getItemTemplateCustomField($module_prefix) { $cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CfSerial%]:' . $module_prefix; $cached_value = $this->Application->getCache($cache_key); if ($cached_value !== false) { return $cached_value; } $sql = 'SELECT CustomFieldId FROM ' . TABLE_PREFIX . 'CustomFields WHERE FieldName = ' . $this->Conn->qstr($module_prefix . '_ItemTemplate'); $item_template_field_id = $this->Conn->GetOne($sql); $this->Application->setCache($cache_key, $item_template_field_id); return $item_template_field_id; } /** * Marks url part as parsed * * @param string $url_part * @param string $parse_direction * @access public */ public function partParsed($url_part, $parse_direction = 'ltr') { if ( !$this->_partsToParse ) { return ; } if ( $parse_direction == 'ltr' ) { $expected_url_part = reset($this->_partsToParse); if ( $url_part == $expected_url_part ) { array_shift($this->_partsToParse); } } else { $expected_url_part = end($this->_partsToParse); if ( $url_part == $expected_url_part ) { array_pop($this->_partsToParse); } } if ( $url_part != $expected_url_part ) { trigger_error('partParsed: expected URL part "<strong>' . $expected_url_part . '</strong>", received URL part "<strong>' . $url_part . '</strong>"', E_USER_NOTICE); } } /** * Determines if there is more to parse in url * * @return bool * @access public */ public function moreToParse() { return count($this->_partsToParse) > 0; } /** * 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 build($t, $params, $pass = 'all', $pass_events = false, $env_var = false) { if ( $this->Application->GetVar('admin') || (array_key_exists('admin', $params) && $params['admin']) ) { $params['admin'] = 1; if ( !array_key_exists('editing_mode', $params) ) { $params['editing_mode'] = EDITING_MODE; } } $ret = ''; $env = ''; if ( isset($params['__SSL__']) ) { unset($params['__SSL__']); } $catalog_item_found = false; $pass_info = $this->getPassInfo($pass); if ( $pass_info ) { if ( $pass_info[0] == 'm' ) { array_shift($pass_info); } - $inject_parts = Array (); // url parts for beginning of url - $params['t'] = $t; // make template available for rewrite listeners - $params['pass_template'] = true; // by default we keep given template in resulting url + $inject_parts = array(); // Url parts for beginning of url. + $params['t'] = $t; // Make template available for routers. + $params['pass_template'] = true; // By default we keep given template in resulting url. if ( !array_key_exists('pass_category', $params) ) { $params['pass_category'] = false; // by default we don't keep categories in url } foreach ($pass_info as $pass_index => $pass_element) { list ($prefix) = explode('.', $pass_element); $catalog_item = $this->Application->findModule('Var', $prefix) && $this->Application->getUnitConfig($prefix)->getCatalogItem(); - if ( array_key_exists($prefix, $this->rewriteListeners) ) { + if ( array_key_exists($prefix, $this->routers) ) { // if next prefix is same as current, but with special => exclude current prefix from url $next_prefix = array_key_exists($pass_index + 1, $pass_info) ? $pass_info[$pass_index + 1] : false; if ( $next_prefix ) { $next_prefix = substr($next_prefix, 0, strlen($prefix) + 1); if ( $prefix . '.' == $next_prefix ) { continue; } } // rewritten url part $url_part = $this->BuildModuleEnv($pass_element, $params, $pass_events); if ( is_string($url_part) && $url_part ) { $ret .= $url_part . '/'; if ( $catalog_item ) { // pass category later only for catalog items $catalog_item_found = true; } } elseif ( is_array($url_part) ) { - // rewrite listener want to insert something at the beginning of url too + // Router want to insert something at the beginning of url too. if ( $url_part[0] ) { $inject_parts[] = $url_part[0]; } if ( $url_part[1] ) { $ret .= $url_part[1] . '/'; } if ( $catalog_item ) { // pass category later only for catalog items $catalog_item_found = true; } } elseif ( $url_part === false ) { - // rewrite listener decided not to rewrite given $pass_element + // Router decided not to rewrite given $pass_element. $env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events); } } else { $env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events); } } if ( $catalog_item_found || preg_match('/c\.[-\d]*/', implode(',', $pass_info)) ) { // "c" prefix is present -> keep category $params['pass_category'] = true; } $params['inject_parts'] = $inject_parts; $ret = $this->BuildModuleEnv('m', $params, $pass_events) . '/' . $ret; $cat_processed = array_key_exists('category_processed', $params) && $params['category_processed']; - // remove temporary parameters used by listeners + // Remove temporary parameters used by routers. unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']); $ret = trim($ret, '/'); if ( isset($params['url_ending']) ) { if ( $ret ) { $ret .= $params['url_ending']; } unset($params['url_ending']); } elseif ( $ret ) { $ret .= MOD_REWRITE_URL_ENDING; } if ( $env ) { $params[ENV_VAR_NAME] = ltrim($env, ':'); } } unset($params['pass'], $params['opener'], $params['m_event']); // TODO: why? // $ret = str_replace('%2F', '/', urlencode($ret)); if ( $params ) { $params_str = http_build_query($params); $ret .= '?' . str_replace('%23', '#', $params_str); } return $ret; } /** * Builds env part that corresponds prefix passed * * @param string $prefix_special item's prefix & [special] * @param Array $params url params * @param bool $pass_events * @return string * @access protected */ protected function BuildModuleEnv($prefix_special, &$params, $pass_events = false) { list ($prefix) = explode('.', $prefix_special); - $url_parts = Array (); - $listener = $this->rewriteListeners[$prefix][0]; - - $ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $pass_events); - - return $ret; + return $this->routers[$prefix]->buildWrapper($prefix_special, $params, $pass_events); } } Index: branches/5.3.x/core/kernel/utility/Router/AbstractCategoryItemRouter.php =================================================================== --- branches/5.3.x/core/kernel/utility/Router/AbstractCategoryItemRouter.php (nonexistent) +++ branches/5.3.x/core/kernel/utility/Router/AbstractCategoryItemRouter.php (revision 16171) @@ -0,0 +1,266 @@ +<?php +/** +* @version $Id$ +* @package In-Portal +* @copyright Copyright (C) 1997 - 2015 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!'); + +abstract class AbstractCategoryItemRouter extends AbstractRouter +{ + + /** + * Builds url part. + * + * @return boolean Return true to continue to next router; return false not to rewrite given prefix. + */ + protected function build() + { + static $default_per_page = array(); + + $ret = ''; + $build_params = $this->extractBuildParams(); + + if ( $build_params === false ) { + return ''; + } + + $this->keepEvent(); + list ($prefix) = explode('.', $this->buildPrefix); + + if ( !array_key_exists($prefix, $default_per_page) ) { + $list_helper = $this->Application->recallObject('ListHelper'); + /* @var $list_helper ListHelper */ + + $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); + } + + if ( $build_params[$this->buildPrefix . '_id'] ) { + $category_id = $this->getBuildParam('m_cat_id'); + + // If template is also item template of category, then remove template. + $template = $this->getBuildParam('t', false); + $item_template = $this->getUrlProcessor()->GetItemTemplate($category_id, $prefix); + + if ( $template == $item_template || strtolower($template) == '__default__' ) { + // Given template is also default template for this category item or '__default__' given. + $this->setBuildParam('pass_template', false); + } + + // Get item's filename. + if ( $prefix == 'bb' ) { + $ret .= 'bb_' . $build_params[$this->buildPrefix . '_id'] . '/'; + } + else { + $filename = $this->getFilename($prefix, $build_params[$this->buildPrefix . '_id'], $category_id); + + if ( $filename !== false ) { + $ret .= $filename . '/'; + } + } + } + else { + if ( $build_params[$this->buildPrefix . '_Page'] == 1 ) { + // When printing category items and we are on the 1st page -> there is no information about + // category item prefix and $params['pass_category'] will not be added automatically. + $this->setBuildParam('pass_category', true); + } + elseif ( $build_params[$this->buildPrefix . '_Page'] > 1 ) { + $this->setBuildParam('page', $build_params[$this->buildPrefix . '_Page']); + } + + $per_page = $build_params[$this->buildPrefix . '_PerPage']; + + if ( $per_page && ($per_page != $default_per_page[$prefix]) ) { + $this->setBuildParam('per_page', $build_params[$this->buildPrefix . '_PerPage']); + } + } + + return mb_strtolower(rtrim($ret, '/')); + } + + /** + * Parses url part. + * + * @param array $url_parts Url parts to parse. + * @param array $params Parameters, that are used for url building or created during url parsing. + * + * @return boolean Return true to continue to next router; return false to stop processing at this router. + */ + public function parse(array &$url_parts, array &$params) + { + $module_prefix = $this->parseCategoryItemUrl($url_parts, $params); + + if ( $module_prefix !== false ) { + $params['pass'][] = $module_prefix; + $this->getUrlProcessor()->setModulePrefix($module_prefix); + } + + return true; + } + + /** + * Returns item's filename that corresponds id passed. If possible, then get it from cache. + * + * @param string $prefix Prefix. + * @param integer $id Id. + * @param integer $category_id Category Id. + * + * @return string + */ + protected function getFilename($prefix, $id, $category_id = null) + { + $category_id = isset($category_id) ? $category_id : $this->Application->GetVar('m_cat_id'); + + $serial_name = $this->Application->incrementCacheSerial($prefix, $id, false); + $cache_key = 'filenames[%' . $serial_name . '%]:' . (int)$category_id; + $filename = $this->Application->getCache($cache_key); + + if ( $filename === false ) { + $this->Conn->nextQueryCachable = true; + $config = $this->Application->getUnitConfig($prefix); + + $sql = 'SELECT ResourceId + FROM ' . $config->getTableName() . ' + WHERE ' . $config->getIDField() . ' = ' . $this->Conn->qstr($id); + $resource_id = $this->Conn->GetOne($sql); + + $this->Conn->nextQueryCachable = true; + $sql = 'SELECT Filename + FROM ' . TABLE_PREFIX . 'CategoryItems + WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')'; + $filename = $this->Conn->GetOne($sql); + + if ( $filename !== false ) { + $this->Application->setCache($cache_key, $filename); + } + } + + return $filename; + } + + /** + * Sets template and id, corresponding to category item given in url + * + * @param array $url_parts Url parts. + * @param array $params Params. + * + * @return boolean|string + */ + protected function parseCategoryItemUrl(array &$url_parts, array &$params) + { + if ( !$url_parts ) { + return false; + } + + $item_filename = end($url_parts); + + if ( is_numeric($item_filename) ) { + // This page, don't process here. + return false; + } + + if ( $this->buildPrefix == 'bb' && preg_match('/^bb_([\d]+)/', $item_filename, $regs) ) { + // Process topics separately, because they don't use item filenames. + array_pop($url_parts); + $this->partParsed($item_filename, 'rtl'); + + return $this->parseTopicUrl($regs[1], $params); + } + + $cat_item = $this->findCategoryItem((int)$params['m_cat_id'], $item_filename); + + if ( $cat_item !== false ) { + // Item found. + $module_prefix = $cat_item['ItemPrefix']; + $item_template = $this->getUrlProcessor()->GetItemTemplate($cat_item, $module_prefix, $params['m_theme']); + + // Converting ResourceId to corresponding Item id. + $module_config = $this->Application->getUnitConfig($module_prefix); + + $sql = 'SELECT ' . $module_config->getIDField() . ' + FROM ' . $module_config->getTableName() . ' + WHERE ResourceId = ' . $cat_item['ItemResourceId']; + $item_id = $this->Conn->GetOne($sql); + + if ( $item_id ) { + array_pop($url_parts); + $this->partParsed($item_filename, 'rtl'); + + if ( $item_template ) { + // When template is found in category -> set it. + $params['t'] = $item_template; + } + + // We have category item id. + $params[$module_prefix . '_id'] = $item_id; + + return $module_prefix; + } + } + + return false; + } + + /** + * Set's template and topic id corresponding to topic given in url + * + * @param integer $topic_id Topic ID. + * @param array $params Params. + * + * @return string + */ + protected function parseTopicUrl($topic_id, array &$params) + { + $sql = 'SELECT c.ParentPath, c.CategoryId + FROM ' . TABLE_PREFIX . 'Categories AS c + WHERE c.CategoryId = ' . (int)$params['m_cat_id']; + $cat_item = $this->Conn->GetRow($sql); + + $item_template = $this->getUrlProcessor()->GetItemTemplate($cat_item, 'bb', $params['m_theme']); + + if ( $item_template ) { + $params['t'] = $item_template; + } + + $params['bb_id'] = $topic_id; + + return 'bb'; + } + + /** + * Locating the item in CategoryItems by filename to detect its ItemPrefix and its category ParentPath. + * + * @param integer $category_id Category. + * @param string $filename Filename. + * + * @return string + */ + protected function findCategoryItem($category_id, $filename) + { + static $cache = array(); + + $cache_key = $category_id . ':' . $filename; + + if ( !isset($cache[$cache_key]) ) { + $sql = 'SELECT ci.ItemResourceId, ci.ItemPrefix, c.ParentPath, ci.CategoryId + FROM ' . TABLE_PREFIX . 'CategoryItems AS ci + LEFT JOIN ' . TABLE_PREFIX . 'Categories AS c ON c.CategoryId = ci.CategoryId + WHERE (ci.CategoryId = ' . $category_id . ') AND (ci.Filename = ' . $this->Conn->qstr($filename) . ')'; + $cache[$cache_key] = $this->Conn->GetRow($sql); + } + + $category_item = $cache[$cache_key]; + + return $category_item && $category_item['ItemPrefix'] == $this->buildPrefix ? $category_item : false; + } + +} Property changes on: branches/5.3.x/core/kernel/utility/Router/AbstractCategoryItemRouter.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Index: branches/5.3.x/core/kernel/utility/Router/AbstractRouter.php =================================================================== --- branches/5.3.x/core/kernel/utility/Router/AbstractRouter.php (nonexistent) +++ branches/5.3.x/core/kernel/utility/Router/AbstractRouter.php (revision 16171) @@ -0,0 +1,295 @@ +<?php +/** + * @version $Id$ + * @package In-Portal + * @copyright Copyright (C) 1997 - 2015 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!'); + +abstract class AbstractRouter extends kBase +{ + + /** + * Include debugging url into build urls. + * + * @var boolean + */ + protected $debug = false; + + /** + * Prefix, that router uses for system integration. Could contain special. + * + * @var string + */ + protected $buildPrefix = ''; + + /** + * Parameters, used during url building. + * + * @var array + */ + protected $buildParams = array(); + + /** + * Router's default template. + * + * @var string + */ + protected $defaultTemplate = ''; + + /** + * Include event from page url into links, generated on that page. + * + * @var boolean + */ + protected $keepEventFromUrl = false; + + /** + * Creates an instance of router + */ + public function __construct() + { + parent::__construct(); + + $this->buildPrefix = $this->getPrefix(); + $this->debug = $this->Application->isDebugMode() && (defined('DBG_ROUTING') && DBG_ROUTING); + } + + /** + * Add debug information to every returned url part. + * + * @param string|array $url_part Url part. + * + * @return array|string + */ + protected function debug($url_part) + { + if ( is_array($url_part) ) { + return array_map(array($this, 'debug'), $url_part); + } + + return '{' . $this->buildPrefix . ':' . $url_part . '}'; + } + + /** + * Returns unit config's prefix, that is associated with this router. + * + * @return string + */ + abstract public function getPrefix(); + + /** + * Returns weight of this router among other routers. + * + * @return float|boolean Number - when order is important; false, when order isn't important. + */ + public function getWeight() + { + return false; + } + + /** + * Wraps building part to hide internal implementation details. + * + * @param string $prefix_special Prefix. + * @param array $params Params. + * @param boolean $keep_events Keep events in url. + * + * @return mixed + */ + public final function buildWrapper($prefix_special, array &$params, $keep_events = false) + { + $this->buildPrefix = $prefix_special; + $this->buildParams = $params; + $this->keepEventFromUrl = $keep_events; + + $url_part = $this->build(); + $params = $this->buildParams; + + return $this->debug ? $this->debug($url_part) : $url_part; + } + + /** + * Builds url part. + * + * @return boolean Return true to continue to next router; return false not to rewrite given prefix. + */ + abstract protected function build(); + + /** + * Parses url part. + * + * @param array $url_parts Url parts to parse. + * @param array $params Parameters, that are used for url building or created during url parsing. + * + * @return boolean Return true to continue to next router; return false to stop processing at this router. + */ + abstract public function parse(array &$url_parts, array &$params); + + /** + * Returns value of build parameter (would use global value, when not given directly). + * + * @param string $name Name of parameter. Symbol "@" would be replaced to "prefix_special_". + * @param mixed $default Default value. + * + * @return string|boolean + */ + protected function getBuildParam($name, $default = null) + { + $param_name = str_replace('@', $this->buildPrefix . '_', $name); + + if ( isset($this->buildParams[$param_name]) ) { + return $this->buildParams[$param_name]; + } + + return isset($default) ? $default : $this->Application->GetVar($param_name); + } + + /** + * Returns build template. + * + * @return string + */ + protected function getBuildTemplate() + { + return $this->Application->getPhysicalTemplate($this->getBuildParam('t')); + } + + /** + * Sets new value of build parameter. + * + * @param string $name Name of build parameter. + * @param string $value New build parameter value or NULL to remove it. + * + * @return void + */ + protected function setBuildParam($name, $value = null) + { + $param_name = str_replace('@', $this->buildPrefix . '_', $name); + + if ( isset($value) ) { + $this->buildParams[$param_name] = $value; + } + else { + unset($this->buildParams[$param_name]); + } + } + + /** + * Returns environment variable values for given prefix + * Uses directly given params, when available and removes them from build params afterwards. + * + * @return array + */ + protected function extractBuildParams() + { + list ($prefix) = explode('.', $this->buildPrefix); + + $query_vars = $this->Application->getUnitConfig($prefix)->getQueryString(); + + if ( !$query_vars ) { + // Given prefix doesn't use "env" variable to pass it's data. + return false; + } + + $event_key = array_search('event', $query_vars); + + if ( $event_key ) { + // Pass through event of this prefix. + unset($query_vars[$event_key]); + } + + $event_name = $this->getBuildParam('@event', false); + + if ( ($event_name !== false && !$event_name) || ($event_name === false && !$this->keepEventFromUrl) ) { + // If empty event passed, then remove it from url. + // If keep event is off and event is not implicitly passed (to prevent global value). + $this->setBuildParam('@event'); + } + + $ret = array(); + + foreach ( $query_vars as $name ) { + $ret[$this->buildPrefix . '_' . $name] = $this->getBuildParam('@' . $name); + $this->setBuildParam('@' . $name); + } + + return $ret; + } + + /** + * Transforms event into form, that will survive in mod-rewritten url. + * + * @return void + */ + protected function keepEvent() + { + $event_name = $this->getBuildParam('@event'); + + if ( $event_name ) { + $this->setBuildParam('events[' . $this->buildPrefix . ']', $event_name); + $this->setBuildParam('@event'); + } + } + + /** + * Returns build parameters from given object (without changing it). + * + * @param kDBBase $object Object. + * @param array $params Parameter set to use as base. + * + * @return array + */ + public function getBuildParams(kDBBase $object, array $params = array()) + { + $params[$object->Prefix . '_id'] = $object->GetID(); + + if ( !isset($params['pass']) ) { + $params['pass'] = 'm,' . $object->Prefix; + } + + return $params; + } + + /** + * Marks url part as parsed. + * + * @param string $url_part Url part. + * @param string $parse_direction Parse direction. + * + * @return void + */ + protected function partParsed($url_part, $parse_direction = 'ltr') + { + $this->getUrlProcessor()->partParsed($url_part, $parse_direction); + } + + /** + * Determines if there is more to parse in url. + * + * @return boolean + */ + protected function moreToParse() + { + return $this->getUrlProcessor()->moreToParse(); + } + + /** + * Returns an instance of url processor in use. + * + * @return kRewriteUrlProcessor + */ + protected function getUrlProcessor() + { + return $this->Application->recallObject('kRewriteUrlProcessor'); + } + +} Property changes on: branches/5.3.x/core/kernel/utility/Router/AbstractRouter.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Index: branches/5.3.x/core/kernel/utility/Router/AbstractReviewRouter.php =================================================================== --- branches/5.3.x/core/kernel/utility/Router/AbstractReviewRouter.php (nonexistent) +++ branches/5.3.x/core/kernel/utility/Router/AbstractReviewRouter.php (revision 16171) @@ -0,0 +1,69 @@ +<?php + + +abstract class AbstractReviewRouter extends AbstractRouter +{ + + /** + * Builds url part. + * + * @return boolean Return true to continue to next router; return false not to rewrite given prefix. + */ + protected function build() + { + static $default_per_page = array(); + + $ret = ''; + $build_params = $this->extractBuildParams(); + + if ( $build_params === false ) { + return ''; + } + + list ($prefix) = explode('.', $this->buildPrefix); + + if ( !array_key_exists($prefix, $default_per_page) ) { + $list_helper = $this->Application->recallObject('ListHelper'); + /* @var $list_helper ListHelper */ + + $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); + } + + if ( $build_params[$this->buildPrefix . '_id'] ) { + return false; + } + else { + if ( $build_params[$this->buildPrefix . '_Page'] == 1 ) { + // When printing category items and we are on the 1st page -> there is no information about + // category item prefix and $params['pass_category'] will not be added automatically. + $this->setBuildParam('pass_category', true); + } + elseif ( $build_params[$this->buildPrefix . '_Page'] > 1 ) { + $this->setBuildParam('page', $build_params[$this->buildPrefix . '_Page']); + } + + $per_page = $build_params[$this->buildPrefix . '_PerPage']; + + if ( $per_page && ($per_page != $default_per_page[$this->buildPrefix]) ) { + $this->setBuildParam('per_page', $build_params[$this->buildPrefix . '_PerPage']); + } + } + + return mb_strtolower(rtrim($ret, '/')); + } + + /** + * Parses url part. + * + * @param array $url_parts Url parts to parse. + * @param array $params Parameters, that are used for url building or created during url parsing. + * + * @return boolean Return true to continue to next router; return false to stop processing at this router. + */ + public function parse(array &$url_parts, array &$params) + { + // Don't parse anything. + return true; + } + +} Property changes on: branches/5.3.x/core/kernel/utility/Router/AbstractReviewRouter.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Index: branches/5.3.x/core/kernel/utility/unit_config.php =================================================================== --- branches/5.3.x/core/kernel/utility/unit_config.php (revision 16170) +++ branches/5.3.x/core/kernel/utility/unit_config.php (revision 16171) @@ -1,1399 +1,1393 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2012 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!'); /** * TODO: Rename following settings * * ConfigMapping -> ConfigMappings * StatusField -> StatusFields * PermSection -> PermSections */ /** * @method Array getFilterMenu(mixed $default = false) * @method kUnitConfig setFilterMenu(Array $value) * * @method Array getConfigMapping(mixed $default = false) * @method kUnitConfig setConfigMapping(Array $value) * * * @method string getModuleFolder(mixed $default = false) * @method kUnitConfig setModuleFolder(string $value) * * @method string getBasePath(mixed $default = false) * @method kUnitConfig setBasePath(string $value) * * @method Array getEventHandlerClass(mixed $default = false) * @method kUnitConfig setEventHandlerClass(Array $value) * * @method Array getTagProcessorClass(mixed $default = false) * @method kUnitConfig setTagProcessorClass(Array $value) * * @method Array getItemClass(mixed $default = false) * @method kUnitConfig setItemClass(Array $value) * * @method Array getListClass(mixed $default = false) * @method kUnitConfig setListClass(Array $value) * * @method Array getValidatorClass(mixed $default = false) * @method kUnitConfig setValidatorClass(Array $value) * * @method Array getQueryString(mixed $default = false) * @method kUnitConfig setQueryString(Array $value) * * @method string getPermItemPrefix(mixed $default = false) * @method kUnitConfig setPermItemPrefix(string $value) * * @method string getPermTabText(mixed $default = false) * @method kUnitConfig setPermTabText(string $value) * * @method Array getPermSection(mixed $default = false) * @method kUnitConfig setPermSection(Array $value) * * @method bool getAutoLoad(mixed $default = false) * @method kUnitConfig setAutoLoad(bool $value) * * @method string getIDField(mixed $default = false) * @method kUnitConfig setIDField(string $value) * * @method string getTableName(mixed $default = false) * @method kUnitConfig setTableName(string $value) * * @method string getCustomDataTableName(mixed $default = false) * @method kUnitConfig setCustomDataTableName(string $value) * * @method kUnitConfig setStatusField(Array $value) * * @method string getTitleField(mixed $default = false) * @method kUnitConfig setTitleField(string $value) * * @method string getOrderField(mixed $default = false) * @method kUnitConfig setOrderField(string $value) * * @method string getOwnerField(mixed $default = false) * @method kUnitConfig setOwnerField(string $value) * * @method int getConfigPriority(mixed $default = false) * @method kUnitConfig setConfigPriority(int $value) * * @method bool getCatalogItem(mixed $default = false) * @method kUnitConfig setCatalogItem(bool $value) * * @method string getCatalogSelectorName(mixed $default = false) * @method kUnitConfig setCatalogSelectorName(string $value) * * @method string getAdminTemplatePath(mixed $default = false) * @method kUnitConfig setAdminTemplatePath(string $value) * * @method string getAdminTemplatePrefix(mixed $default = false) * @method kUnitConfig setAdminTemplatePrefix(string $value) * * @method string getSearchConfigPostfix(mixed $default = false) * @method kUnitConfig setSearchConfigPostfix(string $value) * * @method string getTitlePhrase(mixed $default = false) * @method kUnitConfig setTitlePhrase(string $value) * * @method int getItemType(mixed $default = false) * @method kUnitConfig setItemType(int $value) * * @method Array getStatisticsInfo(mixed $default = false) * @method kUnitConfig setStatisticsInfo(Array $value) * * @method string getViewMenuPhrase(mixed $default = false) * @method kUnitConfig setViewMenuPhrase(string $value) * * @method string getCatalogTabIcon(mixed $default = false) * @method kUnitConfig setCatalogTabIcon(string $value) * * @method bool getCacheModRewrite(mixed $default = false) * @method kUnitConfig setCacheModRewrite(bool $value) * * @method kUnitConfig setParentTableKey(mixed $value) * * @method kUnitConfig setForeignKey(mixed $value) * * @method string getConstrain(mixed $default = false) * @method kUnitConfig setConstrain(string $value) * * @method bool getAutoDelete(mixed $default = false) * @method kUnitConfig setAutoDelete(bool $value) * * @method bool getAutoClone(mixed $default = false) * @method kUnitConfig setAutoClone(bool $value) * * @method string getParentPrefix(mixed $default = false) * @method kUnitConfig setParentPrefix(string $value) * * @method bool getCheckSimulatniousEdit(mixed $default = false) * @method kUnitConfig setCheckSimulatniousEdit(bool $value) * * @method bool getPortalStyleEnv(mixed $default = false) * @method kUnitConfig setPortalStyleEnv(bool $value) * - * @method int getRewritePriority(mixed $default = false) - * @method kUnitConfig setRewritePriority(int $value) - * - * @method Array getRewriteListener(mixed $default = false) - * @method kUnitConfig setRewriteListener(Array $value) - * * @method bool getForceDontLogChanges(mixed $default = false) * @method kUnitConfig setForceDontLogChanges(bool $value) * * @method Array getUserProfileMapping(mixed $default = false) * @method kUnitConfig setUserProfileMapping(Array $value) * * @method bool getUsePendingEditing(mixed $default = false) * @method kUnitConfig setUsePendingEditing(bool $value) * * @method string getNavigationSelectClause(mixed $default = false) * @method kUnitConfig setNavigationSelectClause(string $value) * * @method bool getPopulateMlFields(bool $default = false) * @method kUnitConfig setPopulateMlFields(bool $value) * * @method bool getLogChanges(bool $default = false) * @method kUnitConfig setLogChanges(bool $value) * * @method int getFileCount(bool $default = false) * @method kUnitConfig setFileCount(int $value) * * @method int getImageCount(bool $default = false) * @method kUnitConfig setImageCount(int $value) * * @method string getDownloadHelperClass(bool $default = false) * @method kUnitConfig setDownloadHelperClass(string $value) * * @method string getSectionPrefix(bool $default = false) * @method kUnitConfig setSectionPrefix(string $value) * * @method bool getSiteConfigProcessed(bool $default = false) * @method kUnitConfig setSiteConfigProcessed(bool $value) * * * @method Array getRegisterClasses(mixed $default = false) * @method kUnitConfig setRegisterClasses(Array $value) * @method kUnitConfig addRegisterClasses(Array $value, string $name = null) * @method kUnitConfig removeRegisterClasses(string $name = null) * * @method Array getScheduledTasks(mixed $default = false) * @method Array getScheduledTaskByName(string $name, mixed $default = false) * @method kUnitConfig setScheduledTasks(Array $value) * @method kUnitConfig addScheduledTasks(Array $value, string $name = null) * @method kUnitConfig removeScheduledTasks(string $name = null) * * @method Array getTitlePresets(mixed $default = false) * @method Array getTitlePresetByName(string $name, mixed $default = false) * @method kUnitConfig setTitlePresets(Array $value) * @method kUnitConfig addTitlePresets(Array $value, string $name = null) * @method kUnitConfig removeTitlePresets(string $name = null) * * @method Array getSections(mixed $default = false) * @method Array getSectionByName(string $name, mixed $default = false) * @method kUnitConfig setSections(Array $value) * @method kUnitConfig addSections(Array $value, string $name = null) * @method kUnitConfig removeSections(string $name = null) * * @method Array getFields(mixed $default = false) * @method Array getFieldByName(string $name, mixed $default = false) * @method kUnitConfig setFields(Array $value) * @method kUnitConfig addFields(Array $value, string $name = null) * @method kUnitConfig removeFields(string $name = null) * * @method Array getVirtualFields(mixed $default = false) * @method Array getVirtualFieldByName(string $name, mixed $default = false) * @method kUnitConfig setVirtualFields(Array $value) * @method kUnitConfig addVirtualFields(Array $value, string $name = null) * @method kUnitConfig removeVirtualFields(string $name = null) * * @method Array getGrids(mixed $default = false) * @method Array getGridByName(string $name, mixed $default = false) * @method kUnitConfig setGrids(Array $value) * @method kUnitConfig addGrids(Array $value, string $name = null) * @method kUnitConfig removeGrids(string $name = null) * * @method Array getHooks(mixed $default = false) * @method kUnitConfig setHooks(Array $value) * @method kUnitConfig addHooks(Array $value, string $name = null) * @method kUnitConfig removeHooks(string $name = null) * * @method Array getAggregateTags(mixed $default = false) * @method kUnitConfig setAggregateTags(Array $value) * @method kUnitConfig addAggregateTags(Array $value, string $name = null) * @method kUnitConfig removeAggregateTags(string $name = null) * * @method Array getEditTabPresets(mixed $default = false) * @method Array getEditTabPresetByName(string $name, mixed $default = false) * @method kUnitConfig setEditTabPresets(Array $value) * @method kUnitConfig addEditTabPresets(Array $value, string $name = null) * @method kUnitConfig removeEditTabPresets(string $name = null) * * @method Array getSubItems(mixed $default = false) * @method kUnitConfig setSubItems(Array $value) * @method kUnitConfig addSubItems(string $value, string $name = null) * @method kUnitConfig removeSubItems(string $name = null) * * @method Array getCustomFields(mixed $default = false) * @method string getCustomFieldByName(string $name, mixed $default = false) * @method kUnitConfig setCustomFields(Array $value) * @method kUnitConfig addCustomFields(Array $value, string $name = null) * @method kUnitConfig removeCustomFields(string $name = null) * * @method Array getClones(mixed $default = false) * @method Array getCloneByName(string $name, mixed $default = false) * @method kUnitConfig setClones(Array $value) * @method kUnitConfig addClones(Array $value, string $name = null) * @method kUnitConfig removeClones(string $name = null) * * @method Array getProcessPrefixes(mixed $default = false) * @method kUnitConfig setProcessPrefixes(Array $value) * @method kUnitConfig addProcessPrefixes(string $value, string $name = null) * @method kUnitConfig removeProcessPrefixes(string $name = null) * * @method Array getForms(mixed $default = false) * @method Array getFormByName(string $name, mixed $default = false) * @method kUnitConfig setForms(Array $value) * @method kUnitConfig addForms(Array $value, string $name = null) * @method kUnitConfig removeForms(string $name = null) * * @method Array getReplacementTemplates(mixed $default = false) * @method string getReplacementTemplateByName(string $name, mixed $default = false) * @method kUnitConfig setReplacementTemplates(Array $value) * @method kUnitConfig addReplacementTemplates(string $value, string $name = null) * @method kUnitConfig removeReplacementTemplates(string $name = null) * * @method Array getItemPropertyMappings(mixed $default = false) * @method string getItemPropertyMappingByName(string $name, mixed $default = false) * @method kUnitConfig setItemPropertyMappings(Array $value) * @method kUnitConfig addItemPropertyMappings(string $value, string $name = null) * @method kUnitConfig removeItemPropertyMappings(string $name = null) * * @method Array getSectionAdjustments(mixed $default = false) * @method mixed getSectionAdjustmentByName(string $name, mixed $default = false) * @method kUnitConfig setSectionAdjustments(Array $value) * @method kUnitConfig addSectionAdjustments(mixed $value, string $name = null) * @method kUnitConfig removeSectionAdjustments(string $name = null) * * @method Array getImportKeys(mixed $default = false) * @method kUnitConfig setImportKeys(Array $value) * @method kUnitConfig addImportKeys(mixed $value, string $name = null) * @method kUnitConfig removeImportKeys(string $name = null) * * * @method Array getCalculatedFieldSpecials(mixed $default = Array ()) * @method Array getCalculatedFieldsBySpecial(mixed $special, mixed $default = Array ()) * @method kUnitConfig setCalculatedFieldsBySpecial(mixed $special, Array $value) * @method kUnitConfig addCalculatedFieldsBySpecial(mixed $special, mixed $value, string $name = null) * @method kUnitConfig removeCalculatedFieldsBySpecial(mixed $special, string $name = null) * * @method Array getAggregatedCalculatedFieldSpecials(mixed $default = Array ()) * @method Array getAggregatedCalculatedFieldsBySpecial(mixed $special, mixed $default = Array ()) * @method kUnitConfig setAggregatedCalculatedFieldsBySpecial(mixed $special, Array $value) * @method kUnitConfig addAggregatedCalculatedFieldsBySpecial(mixed $special, mixed $value, string $name = null) * @method kUnitConfig removeAggregatedCalculatedFieldsBySpecial(mixed $special, string $name = null) * * @method Array getListSQLSpecials(mixed $default = Array ()) * @method string getListSQLsBySpecial(mixed $special, mixed $default = Array ()) * @method kUnitConfig setListSQLsBySpecial(mixed $special, string $value) * @method kUnitConfig removeListSQLsBySpecial(mixed $special, string $name = null) * * @method Array getListSortingSpecials(mixed $default = Array ()) * @method Array getListSortingsBySpecial(mixed $special, mixed $default = Array ()) * @method kUnitConfig setListSortingsBySpecial(mixed $special, Array $value) * @method kUnitConfig addListSortingsBySpecial(mixed $special, mixed $value, string $name = null) * @method kUnitConfig removeListSortingsBySpecial(mixed $special, string $name = null) * * @method Array getItemSQLSpecials(mixed $default = Array ()) * @method string getItemSQLsBySpecial(mixed $special, mixed $default = Array ()) * @method kUnitConfig setItemSQLsBySpecial(mixed $special, string $value) * @method kUnitConfig removeItemSQLsBySpecial(mixed $special, string $name = null) */ class kUnitConfig { /** * Reference to global kApplication instance * * @var kApplication * @access protected */ protected $Application = null; /** * Connection to database * * @var IDBConnection * @access protected */ protected $Conn = null; /** * Unit config prefix * * @var string * @access protected */ protected $_prefix = ''; /** * Filename, where unit config is stored * * @var string * @access protected */ protected $_filename = ''; /** * Default unit config data * * @var Array * @access protected */ protected static $_defaults = Array ( 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'kDBEventHandler', 'file' => '', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'kDBTagProcessor', 'file' => '', 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s', ), 'Fields' => Array (), ); /** * Word endings, that are suffixed with "es" instead of just "s" during pluralisation * * @var string * @access protected */ protected static $_singularEndingsRegExp = '/(x|s|z|sh|ch)$/'; /** * Words, that ends with es/s, but are always singulars * * @var string * @access protected */ protected static $_alwaysSingularRegExp = '/(class|LogChanges|ForceDontLogChanges|PopulateMlFields)$/i'; /** * Unit config data * * @var Array * @access protected */ protected $_data = Array (); /** * Unit config settings that can have different values based on special * * @var Array * @access protected */ protected $_specialBased = Array ( 'CalculatedFields', 'AggregatedCalculatedFields', 'ListSQLs', 'ListSortings', 'ItemSQLs', ); /** * Unit config settings, that allow data to be added without a key * * @var Array * @access protected */ protected $_withoutKeys = Array ( 'RegisterClasses', 'Hooks', 'AggregateTags' ); /** * Dynamic method name, that was called * * @var string * @access protected */ protected $_currentMethodName = ''; /** * Arguments given to dynamic method name, that was called * * @var Array * @access protected */ protected $_currentArguments = Array (); /** * Creates new instance of kUnitConfig class * * @param string $prefix * @param Array $defaults * @param bool $use_global_defaults * @access public */ public function __construct($prefix, $defaults = null, $use_global_defaults = true) { $this->_prefix = $prefix; $merge_with = $use_global_defaults ? self::$_defaults : Array (); $this->_data = array_merge($merge_with, isset($defaults) ? $defaults : Array ()); $this->Application =& kApplication::Instance(); $this->Conn =& $this->Application->GetADODBConnection(); } /** * Sets filename, where unit config is stored * * @param string $filename * @return void * @access public */ public function setFilename($filename) { $this->_filename = $filename; } /** * Returns unit config prefix * * @return string * @access public */ public function getPrefix() { return $this->_prefix; } /** * Ensures, that only unit config data is saved when object is serialized * * @return Array * @access public */ public function __sleep() { return Array ('_prefix', '_data', 'Application', 'Conn'); } /** * Dumps unit config into a file * * @return void * @access public */ public function dump() { kUtil::print_r($this->_data, 'Unit Config', true); } /** * Returns unit config data in raw form * * @return Array * @access public */ public function getRaw() { return $this->_data; } /** * Processed dynamically created methods for changing unit config settings * * @param string $method_name * @param Array $arguments * @return mixed * @throws InvalidArgumentException * @throws RuntimeException * @access public */ public function __call($method_name, $arguments) { // === regular === // get{SettingName}() // set{SettingName}($value) // add{SettingName}s(string $value, string $name = null) // remove{SettingName}(string $name = null) // === by special === // get{SettingName}Specials() // get{SettingName}sBySpecial($special) // set{SettingName}BySpecial($special, $value) // add{SettingName}sBySpecial(string $special, Array $value, string $name = null) // remove{SettingName}sBySpecial(string $special, $name = null) if ( !preg_match('/^(get|set|add|remove)(.*?)(BySpecial|Specials|ByName)*$/', $method_name, $regs) ) { throw new RuntimeException('Unknown method <strong>' . __CLASS__ . '::' . $this->_currentMethodName . '</strong>'); } $this->_currentMethodName = $method_name; $this->_currentArguments = $arguments; $to_call = '_process' . ucfirst($regs[1]); return $this->$to_call($regs[2], isset($regs[3]) ? $regs[3] : ''); } /** * Processes calls to "get*" methods * * @param string $setting_name * @param string $suffix * @return mixed * @access protected */ protected function _processGet($setting_name, $suffix = '') { $is_plural = $this->_isPluralSetting($setting_name); if ( $suffix == 'BySpecial' && $is_plural ) { // get{SettingName}BySpecial($special, $default = false) $this->_verifyArguments(Array ('special')); return $this->getSetting($setting_name, $this->_getDefaultValue(1, Array ()), $this->_currentArguments[0]); } elseif ( $suffix == 'Specials' && !$is_plural ) { // get{SettingName}Specials($default = Array ()) $result = $this->getSetting($this->_getPlural($setting_name), $this->_getDefaultValue(0, Array ())); return array_keys($result); } elseif ( $suffix == 'ByName' && !$is_plural ) { $sub_key = $this->_currentArguments[0]; $result = $this->getSetting($this->_getPlural($setting_name), Array ()); return isset($result[$sub_key]) ? $result[$sub_key] : $this->_getDefaultValue(1, false); } // get{SettingName}($default = false) return $this->getSetting($setting_name, $this->_getDefaultValue(0, false)); } /** * Returns default value from given argument or false, when not passed * * @param int $arg_index * @param mixed $default * @return bool * @access protected */ protected function _getDefaultValue($arg_index, $default = false) { return isset($this->_currentArguments[$arg_index]) ? $this->_currentArguments[$arg_index] : $default; } /** * Processes calls to "set*" methods * * @param string $setting_name * @param string $suffix * @return kUnitConfig * @access protected */ protected function _processSet($setting_name, $suffix = '') { $is_plural = $this->_isPluralSetting($setting_name); if ( $suffix == 'BySpecial' && $is_plural ) { // set{SettingName}BySpecial($special, $value) $this->_verifyArguments(Array ('special', 'value')); return $this->setSetting($setting_name, $this->_currentArguments[1], $this->_currentArguments[0]); } // set{SettingName}($value) $this->_verifyArguments(Array ('value')); return $this->setSetting($setting_name, $this->_currentArguments[0]); } /** * Processes calls to "add*" method * * @param string $setting_name * @param string $suffix * @return kUnitConfig * @access protected * @throws InvalidArgumentException */ protected function _processAdd($setting_name, $suffix = '') { $arguments = $this->_currentArguments; if ( !$this->_isPluralSetting($setting_name) ) { throw new InvalidArgumentException('Setting "' . $setting_name . '" isn\'t plural'); } if ( $suffix == 'BySpecial' ) { // add{SettingName}BySpecial(string $special, string $value, string $name = null) $this->_verifyArguments(Array ('special', 'value')); if ( isset($arguments[2]) ) { // add a single value $this->_addToSetting($this->_getSingular($setting_name), $arguments[1], $arguments[2], $arguments[0]); } else { // add multiple values $this->_addToSetting($setting_name, $arguments[1], null, $arguments[0]); } } else { // add{SettingName}(string $value, string $name = null) $this->_verifyArguments(Array ('value')); if ( isset($arguments[1]) ) { // add a single value $this->_addToSetting($this->_getSingular($setting_name), $arguments[0], $arguments[1]); } else { // add multiple value $this->_addToSetting($setting_name, $arguments[0], null); } } return $this; } /** * Adds a value to a setting * * @param string $name * @param mixed $value * @param string $array_key * @param string $special * @return kUnitConfig * @throws InvalidArgumentException */ protected function _addToSetting($name, $value, $array_key, $special = null) { if ( $this->_isPluralSetting($name) ) { // multiple values given - merge with current values if ( !is_array($value) ) { throw new InvalidArgumentException('Argument $value must be an array'); } $result = $this->getSetting($name, Array (), $special); $this->setSetting($name, array_merge($result, $value), $special); } else { // single value given $result = $this->getSetting($this->_getPlural($name), Array (), $special); if ( $this->_isWithoutKeySetting($name) ) { $result[] = $value; } else { $result[$array_key] = $value; } $this->setSetting($this->_getPlural($name), $result, $special); } return $this; } /** * Processes calls to "remove*" method * * @param string $setting_name * @param string $suffix * @return kUnitConfig * @access protected * @throws InvalidArgumentException */ protected function _processRemove($setting_name, $suffix = '') { $arguments = $this->_currentArguments; if ( !$this->_isPluralSetting($setting_name) ) { throw new InvalidArgumentException('Setting "' . $setting_name . '" isn\'t plural'); } if ( $suffix == 'BySpecial' ) { // remove{SettingName}BySpecial(string $special, string $name = null) $this->_verifyArguments(Array ('special')); if ( isset($arguments[1]) ) { // remove single value $this->_removeFromSetting($this->_getSingular($setting_name), $arguments[1], $arguments[0]); } else { // remove multiple value $this->_removeFromSetting($setting_name, null, $arguments[0]); } } else { // remove{SettingName}(string $name = null) if ( isset($arguments[0]) ) { // remove single value $this->_removeFromSetting($this->_getSingular($setting_name), $arguments[0]); } else { // remove multiple values $this->_removeFromSetting($setting_name, null); } } return $this; } /** * Removes a value from a setting * * @param string $name * @param string $array_key * @param string $special * @return kUnitConfig * @access protected * @throws RuntimeException */ protected function _removeFromSetting($name, $array_key = null, $special = null) { if ( $this->_isPluralSetting($name) ) { // remove multiple values if ( $this->getSetting($name, false, $special) !== false ) { $this->setSetting($name, null, $special); } } else { // remove single value if ( $this->_isWithoutKeySetting($name) ) { throw new RuntimeException('Unable to change setting without key'); } $result = $this->getSetting($this->_getPlural($name), false, $special); if ( $result !== false ) { unset($result[$array_key]); $this->setSetting($this->_getPlural($name), $result, $special); } } return $this; } /** * Verifies argument count given * * @param Array $argument_names * @return void * @access protected * @throws InvalidArgumentException */ protected function _verifyArguments($argument_names) { if ( count($this->_currentArguments) < count($argument_names) ) { throw new InvalidArgumentException('Method <strong>' . __CLASS__ . '::' . $this->_currentMethodName . '</strong> expects following arguments: <strong>$' . implode('</strong>, <strong>$', $argument_names) . '</strong>'); } } /** * Returns setting value by name and filter by special (if passed) * * @param string $name * @param mixed $default * @param string|kBase $special * @return mixed * @access public */ public function getSetting($name, $default = false, $special = null) { if ( in_array($name, $this->_specialBased) && isset($special) ) { $use_special = $this->_guessSpecial($name, $special); return isset($this->_data[$name][$use_special]) ? $this->_data[$name][$use_special] : $default; } return isset($this->_data[$name]) ? $this->_data[$name] : $default; } /** * Changes value of a setting * * @param string $name * @param mixed $value * @param string|kBase $special * @return kUnitConfig * @access public */ public function setSetting($name, $value, $special = null) { if ( in_array($name, $this->_specialBased) && isset($special) ) { if ( !isset($this->_data[$name]) ) { $this->_data[$name] = Array (); } $use_special = $this->_guessSpecial($name, $special); if ( !isset($value) ) { unset($this->_data[$name][$use_special]); } else { $this->_data[$name][$use_special] = $value; } } else { if ( !isset($value) ) { unset($this->_data[$name]); } else { $this->_data[$name] = $value; } } return $this; } /** * Detects settings, that accept arrays * * @param string $name * @return bool * @access protected */ protected function _isPluralSetting($name) { if ( preg_match(self::$_alwaysSingularRegExp, $name) ) { // simplified exceptions return false; } return preg_match('/(es|s)$/', $name); } /** * Detects anonymous settings * * @param string $name * @return bool * @access protected */ protected function _isWithoutKeySetting($name) { return in_array($this->_getPlural($name), $this->_withoutKeys); } /** * Returns plural form given word * * @param string $name * @return string * @access protected */ protected function _getPlural($name) { return preg_match(self::$_singularEndingsRegExp, $name) ? $name . 'es' : $name . 's'; } /** * Returns singular form of given word * * @param $name * @return mixed * @throws InvalidArgumentException */ protected function _getSingular($name) { if ( !$this->_isPluralSetting($name) ) { throw new InvalidArgumentException('Setting "' . $name . '" isn\'t plural'); } return preg_replace('/(es|s)$/', '', $name); } /** * Guesses special to be used by event * * @param string $setting_name * @param string|kBase $special * @return string * @access protected */ protected function _guessSpecial($setting_name, $special) { $use_special = $special instanceof kBase ? $special->Special : $special; $value = $this->getSetting($setting_name, Array ()); return isset($value[$use_special]) ? $use_special : ''; } /** * Adds new tab to existing edit tab preset * * @param string $preset_name * @param Array $value * @param string $name * @return kUnitConfig * @throws InvalidArgumentException * @access public */ public function addEditTabPresetTabs($preset_name, $value, $name = null) { $preset = $this->getEditTabPresetByName($preset_name, false); if ( $preset === false ) { throw new InvalidArgumentException('Edit tab preset "' . $preset_name . '" not defined in "' . $this->_prefix . '" unit config'); } if ( isset($name) ) { $preset[$name] = $value; } else { $preset = array_merge($preset, $value); } $this->addEditTabPresets($preset, $preset_name); return $this; } public function addGridFields($grid_name, $value, $name = null) { $grid = $this->getGridByName($grid_name, false); if ( $grid === false ) { throw new InvalidArgumentException('Grid "' . $grid_name . '" not defined in "' . $this->_prefix . '" unit config'); } if ( isset($name) ) { $grid['Fields'][$name] = $value; } else { $grid['Fields'] = array_merge($grid['Fields'], $value); } $this->addGrids($grid, $grid_name); return $this; } /** * Returns individual permission section * * @param string $name * @param Array $default * @return Array * @access public * @todo Rename setting to plural form and them remove this method */ public function getPermSectionByName($name, $default = Array ()) { $perm_sections = $this->getPermSection(Array ()); return isset($perm_sections[$name]) ? $perm_sections[$name] : $default; } /** * Returns foreign key by given prefix * * @param string $parent_prefix * @param mixed $default * @return string|bool * @access public */ public function getForeignKey($parent_prefix = null, $default = false) { return $this->_getSettingByPrefix('ForeignKey', $parent_prefix, $default); } /** * Returns parent table key by given prefix * * @param string $parent_prefix * @param mixed $default * @return string|bool * @access public */ public function getParentTableKey($parent_prefix = null, $default = false) { return $this->_getSettingByPrefix('ParentTableKey', $parent_prefix, $default); } /** * Returns value of a setting by prefix (special workaround for non-special settings) * * @param string $name * @param string $prefix * @param mixed $default * @return mixed * @access protected */ protected function _getSettingByPrefix($name, $prefix = null, $default = false) { $value = $this->getSetting($name, $default); if ( !is_array($value) || !isset($prefix) ) { return $value; } return isset($value[$prefix]) ? $value[$prefix] : $default; } /** * Returns status field with option to get first field only * * @param bool $first_only * @param mixed $default * @return mixed * @access public */ public function getStatusField($first_only = false, $default = false) { $value = $this->getSetting('StatusField', $default); return $first_only ? $value[0] : $value; } /** * Register necessary classes * This method should only process the data which is cached! * * @return void * @access public */ public function parse() { $this->_parseClasses(); $this->_parseScheduledTasks(); $this->_parseHooks(); $this->_parseAggregatedTags(); } protected function _parseClasses() { $register_classes = $this->_getClasses(); foreach ($register_classes as $class_info) { // Thanks to static class map there is no need to specify file during class registration. $this->Application->registerClass($class_info['class'], '', $class_info['pseudo']); if ( isset($class_info['build_event']) && $class_info['build_event'] && $class_info['build_event'] != 'OnBuild' ) { $this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event'])); } } } protected function _getClasses() { $register_classes = $this->getRegisterClasses(); $class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass'); foreach ($class_params as $param_name) { $value = $this->getSetting($param_name); if ( !$value ) { continue; } $value['pseudo'] = $this->_getPseudoByOptionName($param_name); $this->setSetting($param_name, $value); $register_classes[] = $value; } return $register_classes; } protected function _getPseudoByOptionName($option_name) { $pseudo_class_map = Array ( 'ItemClass' => '%s', 'ListClass' => '%s_List', 'EventHandlerClass' => '%s_EventHandler', 'TagProcessorClass' => '%s_TagProcessor' ); return sprintf($pseudo_class_map[$option_name], $this->_prefix); } protected function _parseScheduledTasks() { if ( !$this->getScheduledTasks() ) { return ; } $scheduled_tasks = $this->getScheduledTasks(); foreach ($scheduled_tasks as $short_name => $scheduled_task_info) { $event_status = array_key_exists('Status', $scheduled_task_info) ? $scheduled_task_info['Status'] : STATUS_ACTIVE; $this->Application->delayUnitProcessing('registerScheduledTask', Array ($short_name, $this->_prefix . ':' . $scheduled_task_info['EventName'], $scheduled_task_info['RunSchedule'], $this->getModule(), $event_status)); } } /** * Detects module by unit location. * * @return string */ public function getModule() { $module_path = $this->getModuleFolder() . '/'; foreach ( $this->Application->ModuleInfo as $module_name => $module_data ) { if ( $module_name == 'In-Portal' ) { continue; } if ( $module_data['Path'] == $module_path ) { return $module_name; } } return ''; } protected function _parseHooks() { $hooks = $this->getHooks(); if ( !$hooks ) { return; } foreach ($hooks as $hook) { if ( $this->getParentPrefix() && ($hook['HookToPrefix'] == $this->getParentPrefix()) ) { trigger_error('Deprecated Hook Usage [prefix: <strong>' . $this->_prefix . '</strong>; do_prefix: <strong>' . $hook['DoPrefix'] . '</strong>] use <strong>#PARENT#</strong> as <strong>HookToPrefix</strong> value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE); } if ( $hook['HookToPrefix'] == '' ) { // new: set hooktoprefix to current prefix if not set $hook['HookToPrefix'] = $this->_prefix; } if ( $this->getParentPrefix() ) { // new: allow to set hook to parent prefix what ever it is if ( $hook['HookToPrefix'] == '#PARENT#' ) { $hook['HookToPrefix'] = $this->getParentPrefix(); } if ( $hook['DoPrefix'] == '#PARENT#' ) { $hook['DoPrefix'] = $this->getParentPrefix(); } } elseif ( $hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#' ) { // we need parent prefix but it's not set ! continue; } $hook_events = (array)$hook['HookToEvent']; $do_prefix = $hook['DoPrefix'] == '' ? $this->_prefix : $hook['DoPrefix']; foreach ($hook_events as $hook_event) { $hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event; $do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent']; $this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional'])); } } } protected function _parseAggregatedTags() { $aggregated_tags = $this->getAggregateTags(); if ( !$aggregated_tags ) { return; } foreach ($aggregated_tags as $aggregate_tag) { if ( $this->getParentPrefix() ) { if ( $aggregate_tag['AggregateTo'] == $this->getParentPrefix() ) { trigger_error('Deprecated Aggregate Tag Usage [prefix: <b>' . $this->_prefix . '</b>; AggregateTo: <b>' . $aggregate_tag['AggregateTo'] . '</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE); } if ( $aggregate_tag['AggregateTo'] == '#PARENT#' ) { $aggregate_tag['AggregateTo'] = $this->getParentPrefix(); } } $aggregate_tag['LocalPrefix'] = $this->_prefix; $this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag)); } } public function validate() { global $debugger; $table_name = $this->getTableName(); $float_types = Array ('float', 'double', 'numeric'); $table_found = $this->Conn->Query('SHOW TABLES LIKE "' . $table_name . '"'); if ( !$table_found ) { // config present, but table missing, strange kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1); $debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$table_name</strong> missing, but prefix <b>" . $this->_prefix . "</b> requires it!"); $debugger->WarningCount++; return; } $res = $this->Conn->Query('DESCRIBE ' . $table_name); $config_link = $debugger->getFileLink(FULL_PATH . $this->_filename, 1, $this->_prefix); $error_messages = Array ( 'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config', 'default_missing' => 'Default value for field <strong>%s</strong> not set in config', 'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required', 'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>', 'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>', 'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)', 'date_column_not_null_error' => 'Field <strong>%s</strong> must be NULL in config and database, since it contains date', 'user_column_default_error' => 'Field <strong>%s</strong> must be have NULL as default value, since it holds user id', 'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config', 'virtual_type_missing' => '<strong>Type definition</strong> for virtual field <strong>%s</strong> missing in config', 'virtual_default_missing' => 'Default value for virtual field <strong>%s</strong> not set in config', 'virtual_not_null_error' => 'Virtual field <strong>%s</strong> cannot be not null, since it doesn\'t exist in database', 'invalid_calculated_field' => 'Calculated field <strong>%s</strong> is missing corresponding virtual field', ); $config_errors = Array (); $fields = $this->getFields(); $table_name = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', '\\1', $table_name); // remove table prefix if ( $fields ) { // validate unit config field declaration in relation to database table structure foreach ($res as $field) { $f_name = $field['Field']; if ( preg_match('/l[\d]+_[\w]/', $f_name) ) { // skip multilingual fields continue; } if ( !array_key_exists($f_name, $fields) ) { $config_errors[] = sprintf($error_messages['field_not_found'], $f_name); } else { $db_default = $field['Default']; if ( is_numeric($db_default) ) { $db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default; } $default_missing = false; $options = $fields[$f_name]; $not_null = isset($options['not_null']) && $options['not_null']; $formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false; if ( !array_key_exists('default', $options) ) { $config_errors[] = sprintf($error_messages['default_missing'], $f_name); $default_missing = true; } if ( $field['Null'] != 'YES' ) { // field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "") if ( $f_name != $this->getIDField() && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) { $config_errors[] = sprintf($error_messages['not_null_error1'], $f_name); } if ( $not_null && !isset($options['default']) ) { $config_errors[] = sprintf($error_messages['not_null_error2'], $f_name); } } elseif ( $not_null ) { $config_errors[] = sprintf($error_messages['not_null_error3'], $f_name); } if ( ($formatter == 'kDateFormatter') && $not_null ) { $config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name); } // columns, holding userid should have NULL as default value if ( array_key_exists('type', $options) && !$default_missing ) { // both type and default value set if ( preg_match('/ById$/', $f_name) && $options['default'] !== null ) { $config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name); } } if ( !array_key_exists('type', $options) ) { $config_errors[] = sprintf($error_messages['type_missing'], $f_name); } if ( !$default_missing && ($field['Type'] != 'text') ) { if ( is_null($db_default) && $not_null ) { $db_default = $options['type'] == 'string' ? '' : 0; } if ( $f_name == $this->getIDField() && $options['type'] != 'string' && $options['default'] !== 0 ) { $config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->_varDump($options['default']), $this->_varDump($field['Default'])); } else if ( ((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types) ) { $config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->_varDump($options['default']), $this->_varDump($db_default)); } } } } } // validate virtual fields $virtual_fields = $this->getVirtualFields(); if ( $virtual_fields ) { foreach ($virtual_fields as $f_name => $options) { if ( !array_key_exists('type', $options) ) { $config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name); } if ( array_key_exists('not_null', $options) ) { $config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name); } if ( !array_key_exists('default', $options) ) { $config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name); } } } // validate calculated fields if ( $this->getCalculatedFieldSpecials() ) { foreach ($this->getCalculatedFieldSpecials() as $special) { foreach ($this->getCalculatedFieldsBySpecial($special) as $calculated_field => $calculated_field_expr) { if ( !isset($virtual_fields[$calculated_field]) ) { $config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field); } } } $config_errors = array_unique($config_errors); } if ( $config_errors ) { $error_prefix = '<strong class="debug_error">Config Error' . (count($config_errors) > 1 ? 's' : '') . ': </strong> for prefix <strong>' . $config_link . '</strong> (' . $table_name . ') in unit config:<br />'; $config_errors = $error_prefix . ' ' . implode('<br /> ', $config_errors); kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1); $debugger->appendHTML($config_errors); $debugger->WarningCount++; } } protected function _varDump($value) { return '<strong>' . var_export($value, true) . '</strong> of ' . gettype($value); } } Index: branches/5.3.x/core/kernel/utility/http_query.php =================================================================== --- branches/5.3.x/core/kernel/utility/http_query.php (revision 16170) +++ branches/5.3.x/core/kernel/utility/http_query.php (revision 16171) @@ -1,787 +1,785 @@ <?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 kHTTPQuery extends Params { /** * Cache of QueryString parameters * from config, that are represented * in environment variable * * @var Array */ protected $discoveredUnits = Array (); /** * $_POST vars * * @var Array * @access private */ var $Post; /** * $_GET vars * * @var Array * @access private */ var $Get; /** * $_COOKIE vars * * @var Array * @access private */ var $Cookie; /** * $_SERVER vars * * @var Array * @access private */ var $Server; /** * $_ENV vars * * @var Array * @access private */ var $Env; /** * Order in what write * all vars together in * the same array * * @var string */ var $Order; /** * Uploaded files info * * @var Array * @access private */ var $Files; var $specialsToRemove = Array(); /** * SessionID is given via "sid" variable in query string * * @var bool */ var $_sidInQueryString = false; /** * Trust information, provided by proxy * * @var bool */ protected $_trustProxy = false; /** * Loads info from $_POST, $_GET and * related arrays into common place * * @param string $order * @access public */ public function __construct($order = 'CGPF') { parent::__construct(); $this->Order = $order; if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { // when AJAX request is made from jQuery, then create ajax variable, // so any logic based in it (like redirects) will not break down $_GET['ajax'] = 'yes'; } $this->_trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } /** * Discovers unit form request and returns it's QueryString option on success * * @param string $prefix_special * * @return Array|bool * @access public */ public function discoverUnit($prefix_special) { list($prefix) = explode('.', $prefix_special); $query_string = $this->getQueryString($prefix); if ($query_string) { // only units with QueryString option can be discovered $this->discoveredUnits[$prefix_special] = $query_string; return $query_string; } unset( $this->discoveredUnits[$prefix] ); return false; } /** * Returns units, passed in request * * @param bool $prefix_special_only * @return Array * @access protected */ public function getDiscoveredUnits($prefix_special_only = true) { return $prefix_special_only ? array_keys( $this->discoveredUnits ) : $this->discoveredUnits; } /** * Returns QueryMap for requested unit config. * In case if unit config is a clone, then get parent item's (from prefix) config to create clone * * @param string $prefix * @return Array * @access protected */ protected function getQueryString($prefix) { return $this->Application->getUnitConfig($prefix)->getQueryString(Array ()); } /** * Removes specials from request * * @param Array $array * @return Array * @access protected */ protected function _removeSpecials($array) { $ret = Array (); $removed = false; foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag ) { $removed = true; list ($prefix, $special) = explode('.', $prefix_special, 2); foreach ($array as $key => $val) { $new_key = preg_match("/^" . $prefix . "[._]{1}" . $special . "(.*)/", $key, $regs) ? $prefix . $regs[1] : $key; $ret[$new_key] = is_array($val) ? $this->_removeSpecials($val) : $val; } } } return $removed ? $ret : $array; } public function process() { $this->AddAllVars(); $this->removeSpecials(); ini_set('magic_quotes_gpc', 0); $this->Application->UrlManager->LoadStructureTemplateMapping(); $this->AfterInit(); } /** * All all requested vars to * common storage place * * @return void * @access protected */ protected function AddAllVars() { for ($i = 0; $i < strlen($this->Order); $i++) { switch ($this->Order[$i]) { case 'G': $this->Get = $this->AddVars($_GET); if ( array_key_exists('sid', $_GET) ) { $this->_sidInQueryString = true; } $vars = $this->Application->processQueryString($this->Get(ENV_VAR_NAME)); if ( array_key_exists('sid', $vars) ) { // used by Session::GetPassedSIDValue $this->Get['sid'] = $vars['sid']; } $this->AddParams($vars); break; case 'P': $this->Post = $this->AddVars($_POST); $this->convertPostEvents(); $this->_processPostEnvVariables(); break; case 'C': $cookie_hasher = $this->Application->makeClass('kCookieHasher'); /* @var $cookie_hasher kCookieHasher */ $parsed_cookies = Array (); foreach ($_COOKIE as $cookie_name => $encrypted_value) { $parsed_cookies[$cookie_name] = $cookie_hasher->decrypt($cookie_name, $encrypted_value); } $this->Cookie = $this->AddVars($parsed_cookies); break; /*case 'E'; $this->Env = $this->AddVars($_ENV, false); //do not strip slashes! break; case 'S'; $this->Server = $this->AddVars($_SERVER, false); //do not strip slashes! break;*/ case 'F'; $this->convertFiles(); $this->Files = $this->MergeVars($_FILES); // , false); //do not strip slashes! break; } } } /** * Allow POST variables, that names were transformed by PHP ("." replaced with "_") to * override variables, that were virtually created through environment variable parsing * */ function _processPostEnvVariables() { $passed = $this->Get('passed'); if ( !$passed ) { return; } $passed = explode(',', $passed); foreach ($passed as $prefix_special) { if ( strpos($prefix_special, '.') === false ) { continue; } list ($prefix, $special) = explode('.', $prefix_special); $query_map = $this->getQueryString($prefix); $post_prefix_special = $prefix . '_' . $special; foreach ($query_map as $var_name) { if ( array_key_exists($post_prefix_special . '_' . $var_name, $this->Post) ) { $this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]); } } } } /** * Removes requested specials from all request variables * * @return void * @access protected */ protected function removeSpecials() { $this->specialsToRemove = $this->Get('remove_specials'); if ( $this->specialsToRemove ) { foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag && strpos($prefix_special, '.') === false ) { unset($this->specialsToRemove[$prefix_special]); trigger_error('Incorrect usage of "<strong>remove_specials[' . $prefix_special . ']</strong>" field (no special found)', E_USER_NOTICE); } } $this->_Params = $this->_removeSpecials($this->_Params); } } /** - * Finishes initialization of kHTTPQuery class + * Finishes initialization of kHTTPQuery class. * * @return void - * @access protected - * @todo: only uses build-in rewrite listeners, when cache is build for the first time */ protected function AfterInit() { $rewrite_url = $this->Get('_mod_rw_url_'); if ( $this->Application->RewriteURLs() || $rewrite_url ) { // maybe call onafterconfigread here $this->Application->UrlManager->initRewrite(); if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('url_parsing', 'Parsing <b>MOD_REWRITE</b> url'); $this->Application->UrlManager->rewrite->parseRewriteURL(); $description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>' . $this->Get('t') . '</b>)'; $this->Application->Debugger->profileFinish('url_parsing', $description); } else { $this->Application->UrlManager->rewrite->parseRewriteURL(); } if ( !$rewrite_url && $this->rewriteRedirectRequired() ) { // rewrite url is missing (e.g. not a script from tools folder) $url_params = $this->getRedirectParams(); // no idea about how to check, that given template require category to be passed with it, so pass anyway $url_params['pass_category'] = 1; $url_params['response_code'] = 301; // Moved Permanently trigger_error('Non mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" used', E_USER_NOTICE); $this->Application->Redirect('', $url_params); } } else { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); } if ( !$this->Application->isAdmin && $this->Application->ConfigValue('ForceCanonicalUrls') ) { $template = $this->Application->GetVar('t'); $seo_template = $this->Application->getSeoTemplate($template); if ( $seo_template && $seo_template != $template ) { $url_params = $this->getRedirectParams(); $url_params['response_code'] = 301; trigger_error('Request url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" points directly to physical template', E_USER_NOTICE); $this->Application->Redirect($seo_template, $url_params); } } } /** * Checks, that non-rewrite url was visited and it's automatic rewrite is required * * @return bool */ function rewriteRedirectRequired() { $redirect_conditions = Array ( !$this->IsHTTPSRedirect(), // not https <-> http redirect !$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains) !defined('GW_NOTIFY'), // not in payment gateway notification script preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited $this->Get('t') != 'index', // not on index page ); $perform_redirect = true; foreach ($redirect_conditions as $redirect_condition) { $perform_redirect = $perform_redirect && $redirect_condition; if (!$perform_redirect) { return false; } } return true; } /** * This is redirect from https to http or via versa * * @return bool */ function IsHTTPSRedirect() { $http_referer = array_key_exists('HTTP_REFERER', $_SERVER) ? $_SERVER['HTTP_REFERER'] : false; return ( ( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) ) || ( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) ) ); } /** * Checks, that referer is out site * * @return bool */ function refererIsOurSite() { if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) { // no referer -> don't care what happens return false; } $site_helper = $this->Application->recallObject('SiteHelper'); /* @var $site_helper SiteHelper */ $parsed_url = parse_url($_SERVER['HTTP_REFERER']); if ( $parsed_url['scheme'] == 'https' ) { $found = $site_helper->compare($parsed_url['host'], 'SSLDomainName', $this->Application->ConfigValue('SSLDomain')); } else { $found = $site_helper->compare($parsed_url['host'], 'DomainName', DOMAIN); } return $found; } function convertFiles() { if ( !$_FILES ) { return ; } $tmp = Array (); $file_keys = Array ('error', 'name', 'size', 'tmp_name', 'type'); foreach ($_FILES as $file_name => $file_info) { if ( is_array($file_info['error']) ) { $tmp[$file_name] = $this->getArrayLevel($file_info['error'], $file_name); } else { $normal_files[$file_name] = $file_info; } } if ( !$tmp ) { return ; } $files = $_FILES; $_FILES = Array (); foreach ($tmp as $prefix => $prefix_files) { $anchor =& $_FILES; foreach ($prefix_files['keys'] as $key) { $anchor =& $anchor[$key]; } foreach ($prefix_files['value'] as $field_name) { unset($inner_anchor, $copy); $work_copy = $prefix_files['keys']; foreach ($file_keys as $file_key) { $inner_anchor =& $files[$prefix][$file_key]; if ( isset($copy) ) { $work_copy = $copy; } else { $copy = $work_copy; } array_shift($work_copy); foreach ($work_copy as $prefix_file_key) { $inner_anchor =& $inner_anchor[$prefix_file_key]; } $anchor[$field_name][$file_key] = $inner_anchor[$field_name]; } } } // keys: img_temp, 0, values: LocalPath, ThumbPath } function getArrayLevel(&$level, $prefix='') { $ret['keys'] = $prefix ? Array($prefix) : Array(); $ret['value'] = Array(); foreach($level as $level_key => $level_value) { if( is_array($level_value) ) { $ret['keys'][] = $level_key; $tmp = $this->getArrayLevel($level_value); $ret['keys'] = array_merge($ret['keys'], $tmp['keys']); $ret['value'] = array_merge($ret['value'], $tmp['value']); } else { $ret['value'][] = $level_key; } } return $ret; } /** * Overwrites GET events with POST events in case if they are set and not empty * * @return void * @access protected */ protected function convertPostEvents() { $events = $this->Get('events', Array ()); /* @var $events Array */ if ( is_array($events) ) { $events = array_filter($events); foreach ($events as $prefix_special => $event_name) { $this->Set($prefix_special . '_event', $event_name); } } } function finalizeParsing($passed = Array()) { if (!$passed) { return; } foreach ($passed as $passed_prefix) { $this->discoverUnit($passed_prefix); // from mod-rewrite url parsing } $this->Set('passed', implode(',', $this->getDiscoveredUnits())); } /** * Saves variables from array specified * into common variable storage place * * @param Array $array * @param bool $strip_slashes * @return Array * @access private */ function AddVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value) { $this->Set($key, $value); } return $array; } function MergeVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value_array) { // $value_array is an array too $this->_Params = kUtil::array_merge_recursive($this->_Params, Array ($key => $value_array)); } return $array; } function StripSlashes($array) { static $magic_quotes = null; if (!isset($magic_quotes)) { $magic_quotes = get_magic_quotes_gpc(); } foreach ($array as $key => $value) { if (is_array($value)) { $array[$key] = $this->StripSlashes($value); } else { if ($magic_quotes) { $value = stripslashes($value); } if (!$this->Application->isAdmin) { // TODO: always escape output instead of input $value = kUtil::escape($value, kUtil::ESCAPE_HTML); } $array[$key] = $value; } } return $array; } /** * Removes forceful escaping done to the variable upon Front-End submission. * * @param string|array $value Value. * * @return string|array * @see StripSlashes */ public function unescapeRequestVariable($value) { if ( $this->Application->isAdmin ) { return $value; } // This allows to revert kUtil::escape() call for each field submitted on front-end. if ( is_array($value) ) { foreach ( $value as $param_name => $param_value ) { $value[$param_name] = $this->unescapeRequestVariable($param_value); } return $value; } return kUtil::unescape($value, kUtil::ESCAPE_HTML); } /** * Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls * * @param bool $access_error Method is called during no_permission, require login, session expiration link preparation * @return Array */ function getRedirectParams($access_error = false) { $vars = $this->Get; $unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action'); if (!$this->_sidInQueryString) { $unset_vars[] = 'sid'; } // remove system variables foreach ($unset_vars as $var_name) { if (array_key_exists($var_name, $vars)) { unset($vars[$var_name]); } } if ($access_error) { // place 1 of 2 (also in UsersEventHandler::OnSessionExpire) $vars = $this->_removePassThroughVariables($vars); } return $vars; } /** * Removes all pass_though variables from redirect params * * @param Array $url_params * @return Array */ function _removePassThroughVariables($url_params) { $pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : ''; if (!$pass_through) { return $url_params; } $pass_through = explode(',', $pass_through . ',pass_through'); foreach ($pass_through as $pass_through_var) { unset($url_params[$pass_through_var]); } $url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again return $url_params; } /** * Checks, that url is empty * * @return bool * @access public */ public function isEmptyUrl() { if ( $this->Application->RewriteURLs() ) { return !$this->Get('_mod_rw_url_'); } return !count($this->Get); } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public function getClientIp() { if ( $this->_trustProxy ) { if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) { return $_SERVER['HTTP_CLIENT_IP']; } if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) { $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($client_ip as $ip_address) { $clean_ip_address = trim($ip_address); if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) { return $clean_ip_address; } } return ''; } } return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } /** * Returns headers * * @return array * @access public */ public function getHeaders() { if ( function_exists('apache_request_headers') ) { // If apache_request_headers() exists... $headers = apache_request_headers(); if ( $headers ) { return $headers; // And works... Use it } } $headers = array(); foreach ( array_keys($_SERVER) as $server_key ) { if ( substr($server_key, 0, 5) == 'HTTP_' ) { $header_name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($server_key, 0, 5))))); $headers[$header_name] = $_SERVER[$server_key]; } } return $headers; } } Index: branches/5.3.x/core/kernel/utility/unit_config_reader.php =================================================================== --- branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16170) +++ branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16171) @@ -1,723 +1,729 @@ <?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 kUnitConfigReader extends kBase implements kiCacheable { /** * Unit config data storage. * * @var kUnitConfig[] */ protected $configData = array(); /** * List of discovered unit config files. * * @var array */ protected $configFiles = array(); /** * Mapping between unit config prefixes and files, where they data is stored. * * @var array */ protected $prefixFiles = array(); /** * Tells, that it's final stage of application initialization, where OnAfterConfigRead events can be called. * * @var boolean */ protected $finalStage = false; /** * Determines if cache should be stored. * * @var boolean */ protected $storeCache = false; /** * List of unit configs, that have called their OnAfterConfigRead event. * * @var array */ protected $afterConfigProcessed = array(); /** * Escaped directory separator for using in regular expressions * * @var string */ protected $directorySeparator = ''; /** * Regular expression for detecting module folder * * @var string */ protected $moduleFolderRegExp = ''; /** * Folders to skip during unit config search * * @var array */ protected $skipFolders = array('CVS', '.svn', 'admin_templates', 'libchart'); /** * Cloner. * * @var kUnitConfigCloner */ protected $cloner; /** * Creates instance of unit config reader. */ public function __construct() { parent::__construct(); $this->directorySeparator = preg_quote(DIRECTORY_SEPARATOR); $editor_path = explode('/', trim(EDITOR_PATH, '/')); $this->skipFolders[] = array_pop($editor_path); // last of cmseditor folders $this->moduleFolderRegExp = '#' . $this->directorySeparator . '(core|modules' . $this->directorySeparator . '.*?)' . $this->directorySeparator . '#'; $this->cloner = $this->Application->makeClass('kUnitConfigCloner', array($this)); } /** * Sets data from cache to object * * @param Array $data */ public function setFromCache(&$data) { $this->cloner->setFromCache($data); $this->prefixFiles = $data['ConfigReader.prefixFiles']; } /** * Gets object data for caching * * @return Array */ public function getToCache() { return array_merge( $this->cloner->getToCache(), array( 'ConfigReader.prefixFiles' => $this->prefixFiles, ) ); } public function scanModules($folder_path, $cache = true) { if ( defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE') ) { // disable config caching during installation $cache = false; } if ( $cache ) { $restored = $this->Application->cacheManager->LoadUnitCache(); if ( $restored ) { if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { $this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache'); } return; } } if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { $this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache'); } // === lines below would only executed on cold start (no unit config cache) === // no cache found -> include all unit configs to create it ! $this->includeConfigFiles($folder_path, $cache); $this->parseConfigs(); + $this->sortRouters(); // tell AfterConfigRead to store cache if needed // can't store it here because AfterConfigRead needs ability to change config data $this->storeCache = $cache; if ( !$this->Application->InitDone ) { // scanModules is called multiple times during installation process $this->Application->InitManagers(); - - // get build-in rewrite listeners ONLY to be able to parse mod-rewrite url when unit config cache is missing - $this->retrieveCollections(); - $this->sortRewriteListeners(); } $this->Application->cacheManager->applyDelayedUnitProcessing(); } /** * Locates (recursively) and reads all unit configs at given path. * * @param string $folder_path Folder path. * @param boolean $cache Store information to cache. * * @throws Exception When unit config file is missing a prefix defined inside it. */ protected function includeConfigFiles($folder_path, $cache = true) { $this->Application->refreshModuleInfo(); if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->getCache('master:config_files', false, $cache ? CacheSettings::$unitCacheRebuildTime : 0); } else { $data = $this->Application->getDBCache('config_files', $cache ? CacheSettings::$unitCacheRebuildTime : 0); } if ( $data ) { $this->configFiles = unserialize($data); if ( !(defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) ) { shuffle($this->configFiles); } } else { $this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from "core" directory $this->findConfigFiles($folder_path); // search from "modules" directory if ( $cache ) { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->setCache('master:config_files', serialize($this->configFiles)); } else { $this->Application->setDBCache('config_files', serialize($this->configFiles)); } } } foreach ( $this->configFiles as $filename ) { $prefix = $this->PreloadConfigFile($filename); if ( !$prefix ) { throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>'); } } // TODO: needed? if ( $cache ) { unset($this->configFiles); } } /** * Recursively searches for unit configs in given folder. * * @param string $folder_path Path to the folder. * @param int $level Deep level of the folder. * * @return void */ protected function findConfigFiles($folder_path, $level = 0) { // if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted $reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/'; $folder_path = preg_replace($reg_exp, '', $folder_path, 1); // this make sense, since $folderPath may NOT contain FULL_PATH $base_folder = FULL_PATH . $folder_path . DIRECTORY_SEPARATOR; $sub_folders = glob($base_folder . '*', GLOB_ONLYDIR); if ( !$sub_folders ) { return; } if ( $level == 0 ) { // don't scan Front-End themes because of extensive directory structure $sub_folders = array_diff($sub_folders, array($base_folder . 'themes', $base_folder . 'tools')); } foreach ( $sub_folders as $full_path ) { $sub_folder = substr($full_path, strlen($base_folder)); if ( in_array($sub_folder, $this->skipFolders) || preg_match('/^\./', $sub_folder) ) { // don't scan skipped or hidden folders continue; } $config_name = $this->getConfigName($folder_path . DIRECTORY_SEPARATOR . $sub_folder); if ( file_exists(FULL_PATH . $config_name) ) { $this->configFiles[] = $config_name; } $this->findConfigFiles($full_path, $level + 1); } } /** * Process all read config files - called ONLY when there is no cache! * * @return void */ protected function parseConfigs() { $this->parseUnitConfigs($this->getUnitConfigsWithoutPriority()); $this->parseUnitConfigs($this->getUnitConfigsWithPriority()); } /** * Parses unit config sub-set. * * @param array $prefixes Unit config prefixes. * * @return array */ protected function parseUnitConfigs(array $prefixes) { foreach ( $prefixes as $prefix ) { $this->configData[$prefix]->parse(); } $this->cloner->extrudeAndParse($prefixes); } /** * Returns unit configs prefixes without priority defined. * * @return array */ protected function getUnitConfigsWithoutPriority() { $ret = array(); foreach ( $this->configData as $prefix => $config ) { if ( $config->getConfigPriority() === false ) { $ret[] = $prefix; } } return $ret; } /** * Returns unit configs prefixes with priority defined. * * @return array */ protected function getUnitConfigsWithPriority() { $ret = array(); foreach ( $this->configData as $prefix => $config ) { $priority = $config->getConfigPriority(); if ( $priority !== false ) { $ret[$prefix] = $priority; } } asort($ret); return array_keys($ret); } public function AfterConfigRead($store_cache = null) { $this->finalStage = true; foreach ($this->configData as $prefix => $config) { $this->runAfterConfigRead($prefix); } if ( !isset($store_cache) ) { // $store_cache not overridden -> use global setting $store_cache = $this->storeCache; } if ( $store_cache || (defined('IS_INSTALL') && IS_INSTALL) ) { // cache is not stored during install, but dynamic clones should be processed in any case $this->cloner->processDynamicallyAdded(); $this->retrieveCollections(); } if ( $store_cache ) { - $this->sortRewriteListeners(); - $this->Application->HandleEvent(new kEvent('adm:OnAfterCacheRebuild')); $this->Application->cacheManager->UpdateUnitCache(); if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS ) { // validate configs here to have changes from OnAfterConfigRead hooks to prefixes foreach ( $this->configData as $config ) { if ( !$config->getTableName() ) { continue; } $config->validate(); } } } } /** - * Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first) + * Sort routers according to their weight (non-prioritized routers goes first). * * @return void */ - protected function sortRewriteListeners() + protected function sortRouters() { - $listeners = array(); - $prioritized_listeners = array(); - - // process non-prioritized listeners - foreach ( $this->Application->RewriteListeners as $prefix => $listener_data ) { - if ( $listener_data['priority'] === false ) { - $listeners[$prefix] = $listener_data; + $sorted_routers = array(); + $prioritized_routers = array(); + $routers = $this->collectRouters(); + + // Process non-prioritized routers. + foreach ( $routers as $prefix => $router_data ) { + if ( $router_data['priority'] === false ) { + $sorted_routers[$prefix] = $router_data; } else { - $prioritized_listeners[$prefix] = $listener_data['priority']; + $prioritized_routers[$prefix] = $router_data['priority']; } } - // process prioritized listeners - asort($prioritized_listeners, SORT_NUMERIC); + // Process prioritized routers. + asort($prioritized_routers, SORT_NUMERIC); - foreach ( $prioritized_listeners as $prefix => $priority ) { - $listeners[$prefix] = $this->Application->RewriteListeners[$prefix]; + foreach ( $prioritized_routers as $prefix => $priority ) { + $sorted_routers[$prefix] = $routers[$prefix]; } - $this->Application->RewriteListeners = $listeners; + $this->Application->routers = $sorted_routers; + } + + /** + * Collects routers. + * + * @return array + */ + protected function collectRouters() + { + $routers = array(); + $router_classes = $this->Application->getSubClasses('AbstractRouter'); + + foreach ( $router_classes as $router_class ) { + if ( !class_exists($router_class) ) { + // This can happen, when: + // - router class (coming from cache) was renamed; + // - new cache is built based on outdated class map. + continue; + } + + /** @var AbstractRouter $router */ + $router = new $router_class(); + $routers[$router->getPrefix()] = array( + 'class' => $router_class, + 'priority' => $router->getWeight(), + ); + } + + return $routers; } /** * Re-reads all configs. * * @return void */ public function ReReadConfigs() { // don't reset prefix file, since file scanning could slow down the process $prefix_files_backup = $this->prefixFiles; $this->Application->cacheManager->EmptyUnitCache(); $this->prefixFiles = $prefix_files_backup; // parse all configs $this->afterConfigProcessed = array(); $this->includeConfigFiles(MODULES_PATH, false); $this->parseConfigs(); + $this->sortRouters(); $this->AfterConfigRead(false); $this->cloner->processDynamicallyAdded(); $this->retrieveCollections(); } /** * Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events * */ protected function retrieveCollections() { foreach ( $this->configData as $prefix => $config ) { // collect replacement templates if ( $config->getReplacementTemplates() ) { $this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config->getReplacementTemplates()); } - - // collect rewrite listeners - if ( $config->getRewriteListener() ) { - $rewrite_listeners = $config->getRewriteListener(); - - if ( !is_array($rewrite_listeners) ) { - // when one method is used to build and parse url - $rewrite_listeners = array($rewrite_listeners, $rewrite_listeners); - } - - foreach ( $rewrite_listeners as $index => $rewrite_listener ) { - if ( strpos($rewrite_listener, ':') === false ) { - $rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener; - } - } - - $rewrite_priority = $config->getRewritePriority(); - - $this->Application->RewriteListeners[$prefix] = array('listener' => $rewrite_listeners, 'priority' => $rewrite_priority); - } } } public function loadConfig($prefix) { $preloaded_prefix = $this->PreloadConfigFile($this->getPrefixFile($prefix)); if ( $this->finalStage ) { // run prefix OnAfterConfigRead so all hooks to it can define their clones $this->runAfterConfigRead($preloaded_prefix); } $clones = $this->cloner->extrude($preloaded_prefix); if ( $this->finalStage ) { foreach ( $clones as $a_prefix ) { $this->runAfterConfigRead($a_prefix); } } } /** * Runs OnAfterConfigRead event for given prefix once. * * @param string $prefix Unit config prefix. * * @return void */ public function runAfterConfigRead($prefix) { if ( in_array($prefix, $this->afterConfigProcessed) ) { return; } $this->Application->HandleEvent(new kEvent($prefix . ':OnAfterConfigRead')); if ( !(defined('IS_INSTALL') && IS_INSTALL) ) { // allow to call OnAfterConfigRead multiple times during install array_push($this->afterConfigProcessed, $prefix); } } /** * Loads unit config file contents from disk. * * @param string $filename Unit config filename. * * @return string */ protected function PreloadConfigFile($filename) { $config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename); if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) { if ( in_array($filename, get_included_files()) ) { return ''; } global $debugger; if ( $config_found ) { $file = FULL_PATH . $filename; $file_crc = crc32($file); $debugger->ProfileStart('inc_' . $file_crc, $file); include_once($file); $debugger->ProfileFinish('inc_' . $file_crc); $debugger->profilerAddTotal('includes', 'inc_' . $file_crc); } } elseif ( $config_found ) { include_once(FULL_PATH . $filename); } if ( $config_found ) { /* @var $config kUnitConfig|Array */ if ( isset($config) && $config ) { // config file is included for 1st time -> save it's content for future processing if ( !is_object($config) ) { $prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : ''; $config = new kUnitConfig($prefix, $config); } else { $prefix = $config->getPrefix(); } preg_match($this->moduleFolderRegExp, $filename, $regs); $config->setModuleFolder(str_replace(DIRECTORY_SEPARATOR, '/', $regs[1])); $config->setBasePath(dirname(FULL_PATH . $filename)); if ( $config->getAdminTemplatePath() !== false ) { // append template base folder for admin templates path of this prefix $module_templates = $regs[1] == 'core' ? '' : substr($regs[1], 8) . '/'; $config->setAdminTemplatePath($module_templates . $config->getAdminTemplatePath()); } if ( array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename) ) { trigger_error( 'Single unit config prefix "<strong>' . $prefix . '</strong>" ' . 'is used in multiple unit config files: ' . '"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"', E_USER_WARNING ); } $this->add($config, $filename); return $prefix; } else { $prefix = array_search($filename, $this->prefixFiles); if ( $prefix ) { // attempt is made to include config file twice or more, but include_once prevents that, // but file exists on hdd, then it is already saved to all required arrays, just return it's prefix return $prefix; } } } return 'dummy'; } /** * Sets a file, for a given prefix. * * @param kUnitConfig $config Unit config. * @param string $filename File. * * @return void */ public function add(kUnitConfig $config, $filename) { $config->setFilename($filename); $prefix = $config->getPrefix(); $this->configData[$prefix] = $config; $this->prefixFiles[$prefix] = $filename; } /** * Removes unit config. * * @param string $prefix Unit config prefix. * * @return void */ public function remove($prefix) { unset($this->configData[$prefix], $this->prefixFiles[$prefix]); } /** * Returns unit config for given prefix * * @param string $prefix * @return kUnitConfig */ public function getUnitConfig($prefix = null) { if ( !isset($this->configData[$prefix]) ) { $this->loadConfig($prefix); } return $this->configData[$prefix]; } /** * Returns prefixes of unit configs, that were registered * * @return Array */ public function getPrefixes() { return array_keys($this->configData); } /** * Get's config file name based * on folder name supplied * * @param string $folder_path * @return string */ protected function getConfigName($folder_path) { return $folder_path . DIRECTORY_SEPARATOR . basename($folder_path) . '_config.php'; } /** * Checks if config file is allowed for inclusion (if module of config is installed). * * @param string $config_path Relative path from In-Portal directory. * * @return boolean */ protected function configAllowed($config_path) { static $module_paths = null; if ( defined('IS_INSTALL') && IS_INSTALL ) { // at installation start no modules in db and kernel configs could not be read return true; } if ( preg_match('#^' . $this->directorySeparator . 'core#', $config_path) ) { // always allow to include configs from "core" module's folder return true; } if ( !$this->Application->ModuleInfo ) { return false; } if ( !isset($module_paths) ) { $module_paths = array(); foreach ( $this->Application->ModuleInfo as $module_info ) { $module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/')); } $module_paths = array_unique($module_paths); } preg_match($this->moduleFolderRegExp, $config_path, $regs); // config file path starts with module folder path return in_array($regs[1], $module_paths); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix Unit config prefix. * * @return boolean */ public function prefixRegistered($prefix) { return isset($this->prefixFiles[$prefix]); } /** * Returns unit config file location by it's prefix. * * @param string $prefix Unit config prefix. * * @return string * @throws Exception When unit config is not found. */ public function getPrefixFile($prefix) { if ( !isset($this->prefixFiles[$prefix]) ) { throw new Exception('Configuration file for prefix "<strong>' . $prefix . '</strong>" is unknown'); } return $this->prefixFiles[$prefix]; } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/categories/categories_event_handler.php =================================================================== --- branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16170) +++ branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16171) @@ -1,3156 +1,2900 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class CategoriesEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnRebuildCache' => Array ('self' => 'add|edit'), 'OnCopy' => Array ('self' => true), 'OnCut' => Array ('self' => 'edit'), 'OnPasteClipboard' => Array ('self' => true), 'OnPaste' => Array ('self' => 'add|edit', 'subitem' => 'edit'), 'OnRecalculatePriorities' => Array ('self' => 'add|edit'), // category ordering 'OnItemBuild' => Array ('self' => true), // always allow to view individual categories (regardless of CATEGORY.VIEW right) 'OnUpdatePreviewBlock' => Array ('self' => true), // for FCKEditor integration ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Categories are sorted using special sorting event * */ function mapEvents() { parent::mapEvents(); $events_map = Array ( 'OnMassMoveUp' => 'OnChangePriority', 'OnMassMoveDown' => 'OnChangePriority', ); $this->eventMethods = array_merge($this->eventMethods, $events_map); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( $event->Name == 'OnResetCMSMenuCache' ) { // events from "Tools -> System Tools" section are controlled via that section "edit" permission $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $perm_value = $this->Application->CheckPermission('in-portal:service.edit'); return $perm_helper->finalizePermissionCheck($event, $perm_value); } if ( !$this->Application->isAdmin ) { if ( $event->Name == 'OnSetSortingDirect' ) { // allow sorting on front event without view permission return true; } if ( $event->Name == 'OnItemBuild' ) { $category_id = $this->getPassedID($event); if ( $category_id == 0 ) { return true; } } } if ( in_array($event->Name, $this->_getMassPermissionEvents()) ) { $items = $this->_getPermissionCheckInfo($event); $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ if ( ($event->Name == 'OnSave') && array_key_exists(0, $items) ) { // adding new item (ID = 0) $perm_value = $perm_helper->AddCheckPermission($items[0]['ParentId'], $event->Prefix) > 0; } else { // leave only items, that can be edited $ids = Array (); $check_method = in_array($event->Name, Array ('OnMassDelete', 'OnCut')) ? 'DeleteCheckPermission' : 'ModifyCheckPermission'; foreach ($items as $item_id => $item_data) { if ( $perm_helper->$check_method($item_data['CreatedById'], $item_data['ParentId'], $event->Prefix) > 0 ) { $ids[] = $item_id; } } if ( !$ids ) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } $perm_value = true; $event->setEventParam('ids', $ids); // will be used later by "kDBEventHandler::StoreSelectedIDs" method } return $perm_helper->finalizePermissionCheck($event, $perm_value); } if ( $event->Name == 'OnRecalculatePriorities' ) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $category_id = $this->Application->GetVar('m_cat_id'); return $perm_helper->AddCheckPermission($category_id, $event->Prefix) || $perm_helper->ModifyCheckPermission(0, $category_id, $event->Prefix); } if ( $event->Name == 'OnPasteClipboard' ) { // forces permission check to work by current category for "Paste In Category" operation $category_id = $this->Application->GetVar('m_cat_id'); $this->Application->SetVar('c_id', $category_id); } return parent::CheckPermission($event); } /** * Returns events, that require item-based (not just event-name based) permission check * * @return Array */ function _getMassPermissionEvents() { return Array ( 'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove', 'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown', 'OnCut', ); } /** * Returns category item IDs, that require permission checking * * @param kEvent $event * @return string */ function _getPermissionCheckIDs($event) { if ($event->Name == 'OnSave') { $selected_ids = implode(',', $this->getSelectedIDs($event, true)); if (!$selected_ids) { $selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave) } } else { // OnEdit, OnMassDelete events, when items are checked in grid $selected_ids = implode(',', $this->StoreSelectedIDs($event)); } return $selected_ids; } /** * Returns information used in permission checking * * @param kEvent $event * @return Array */ function _getPermissionCheckInfo($event) { // when saving data from temp table to live table check by data from temp table $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ($event->Name == 'OnSave') { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $sql = 'SELECT ' . $id_field . ', CreatedById, ParentId FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . $this->_getPermissionCheckIDs($event) . ')'; $items = $this->Conn->Query($sql, $id_field); if (!$items) { // when creating new category, then no IDs are stored in session $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); list ($id, $fields_hash) = each($items_info); if (array_key_exists('ParentId', $fields_hash)) { $item_category = $fields_hash['ParentId']; } else { $item_category = $this->Application->RecallVar('m_cat_id'); // saved in c:OnPreCreate event permission checking } $items[$id] = Array ( 'CreatedById' => $this->Application->RecallVar('user_id'), 'ParentId' => $item_category, ); } return $items; } /** * Set's mark, that root category is edited * * @param kEvent $event * @return void * @access protected */ protected function OnEdit(kEvent $event) { $category_id = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); $home_category = $this->Application->getBaseCategory(); $this->Application->StoreVar('IsRootCategory_' . $this->Application->GetVar('m_wid'), ($category_id === '0') || ($category_id == $home_category)); parent::OnEdit($event); if ( $event->status == kEvent::erSUCCESS ) { // keep "Section Properties" link (in browse modes) clean $this->Application->DeleteVar('admin'); } } /** * Adds selected link to listing * * @param kEvent $event */ function OnProcessSelected($event) { $object = $event->getObject(); /* @var $object kDBItem */ $selected_ids = $this->Application->GetVar('selected_ids'); $this->RemoveRequiredFields($object); $object->SetDBField($this->Application->RecallVar('dst_field'), $selected_ids['c']); $object->Update(); $event->SetRedirectParam('opener', 'u'); } /** * Apply system filter to categories list * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ // don't show "Content" category in advanced view $object->addFilter('system_categories', '%1$s.Status <> 4'); // show system templates from current theme only + all virtual templates $object->addFilter('theme_filter', '%1$s.ThemeId = ' . $this->_getCurrentThemeId() . ' OR %1$s.ThemeId = 0'); if ($event->Special == 'showall') { // if using recycle bin don't show categories from there $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ($recycle_bin) { $sql = 'SELECT TreeLeft, TreeRight FROM '.TABLE_PREFIX.'Categories WHERE CategoryId = '.$recycle_bin; $tree_indexes = $this->Conn->GetRow($sql); $object->addFilter('recyclebin_filter', '%1$s.TreeLeft < '.$tree_indexes['TreeLeft'].' OR %1$s.TreeLeft > '.$tree_indexes['TreeRight']); } } if ( (string)$event->getEventParam('parent_cat_id') !== '' ) { $parent_cat_id = $event->getEventParam('parent_cat_id'); if ("$parent_cat_id" == 'Root') { $module_name = $event->getEventParam('module') ? $event->getEventParam('module') : 'In-Commerce'; $parent_cat_id = $this->Application->findModule('Name', $module_name, 'RootCat'); } } else { $parent_cat_id = $this->Application->GetVar('c_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } if ("$parent_cat_id" == '0') { // replace "0" category with "Content" category id (this way template $parent_cat_id = $this->Application->getBaseCategory(); } if ("$parent_cat_id" != 'any') { if ($event->getEventParam('recursive')) { if ($parent_cat_id > 0) { // not "Home" category $tree_indexes = $this->Application->getTreeIndex($parent_cat_id); $object->addFilter('parent_filter', '%1$s.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight']); } } else { $object->addFilter('parent_filter', '%1$s.ParentId = '.$parent_cat_id); } } $this->applyViewPermissionFilter($object); if (!$this->Application->isAdminUser) { // apply status filter only on front $object->addFilter('status_filter', $object->TableName.'.Status = 1'); } // process "types" and "except" parameters $type_clauses = Array(); $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); $except_types = $event->getEventParam('except'); $except_types = $except_types ? explode(',', $except_types) : Array (); $config = $event->getUnitConfig(); 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->getUnitConfig('rel')->getTableName(); $item_type = (int)$config->getItemType(); if ($item_type == 0) { trigger_error('<strong>ItemType</strong> not defined for prefix <strong>' . $event->Prefix . '</strong>', E_USER_WARNING); } // process case, then this list is called inside another list $prefix_special = $event->getEventParam('PrefixSpecial'); if (!$prefix_special) { $prefix_special = $this->Application->Parser->GetParam('PrefixSpecial'); } $id = false; if ($prefix_special !== false) { $processed_prefix = $this->Application->processPrefix($prefix_special); if ($processed_prefix['prefix'] == $related_prefix) { // printing related categories within list of items (not on details page) $list = $this->Application->recallObject($prefix_special); /* @var $list kDBList */ $id = $list->GetID(); } } if ($id === false) { // printing related categories for single item (possibly on details page) if ($related_prefix == 'c') { $id = $this->Application->GetVar('m_cat_id'); } else { $id = $this->Application->GetVar($related_prefix . '_id'); } } $p_item = $this->Application->recallObject($related_prefix . '.current', null, Array('skip_autoload' => true)); /* @var $p_item kCatDBItem */ $p_item->Load( (int)$id ); $p_resource_id = $p_item->GetDBField('ResourceId'); $sql = 'SELECT SourceId, TargetId FROM '.$rel_table.' WHERE (Enabled = 1) AND ( (Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (Type = 1 AND ( (SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.') ) ) )'; $related_ids_array = $this->Conn->Query($sql); $related_ids = Array(); foreach ($related_ids_array as $key => $record) { $related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ]; } if (count($related_ids) > 0) { $type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).')'; $type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).')'; } else { $type_clauses['related']['include'] = '0'; $type_clauses['related']['except'] = '1'; } $type_clauses['related']['having_filter'] = false; } if (in_array('category_related', $type_clauses)) { $object->removeFilter('parent_filter'); $resource_id = $this->Conn->GetOne(' SELECT ResourceId FROM '.$config->getTableName().' 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'); $sql = 'SELECT ResourceId FROM ' . $this->Application->getUnitConfig('p')->getTableName() . ' WHERE ProductId = ' . $product_id; $resource_id = $this->Conn->GetOne($sql); $sql = 'SELECT DISTINCT(TargetId) FROM ' . TABLE_PREFIX . 'CatalogRelationships WHERE SourceId = '.$resource_id.' AND TargetType = 1'; $related_cats = $this->Conn->GetCol($sql); $related_cats = is_array($related_cats) ? $related_cats : Array(); $sql = 'SELECT DISTINCT(SourceId) FROM ' . TABLE_PREFIX . 'CatalogRelationships WHERE TargetId = '.$resource_id.' AND SourceType = 1 AND Type = 1'; $related_cats2 = $this->Conn->GetCol($sql); $related_cats2 = is_array($related_cats2) ? $related_cats2 : Array(); $related_cats = array_unique( array_merge( $related_cats2, $related_cats ) ); if ($related_cats) { $type_clauses['product_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')'; $type_clauses['product_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')'; } else { $type_clauses['product_related']['include'] = '0'; $type_clauses['product_related']['except'] = '1'; } $type_clauses['product_related']['having_filter'] = false; } $type_clauses['menu']['include'] = '%1$s.IsMenu = 1'; $type_clauses['menu']['except'] = '%1$s.IsMenu = 0'; $type_clauses['menu']['having_filter'] = false; if (in_array('search', $types) || in_array('search', $except_types)) { $event_mapping = Array ( 'simple' => 'OnSimpleSearch', 'subsearch' => 'OnSubSearch', 'advanced' => 'OnAdvancedSearch' ); $keywords = $event->getEventParam('keyword_string'); $type = $this->Application->GetVar('search_type', 'simple'); if ( $keywords ) { // processing keyword_string param of ListProducts tag $this->Application->SetVar('keywords', $keywords); $type = 'simple'; } $search_event = $event_mapping[$type]; $this->$search_event($event); $object = $event->getObject(); /* @var $object kDBList */ $search_sql = ' FROM ' . TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search search_result JOIN %1$s ON %1$s.ResourceId = search_result.ResourceId'; $sql = str_replace('FROM %1$s', $search_sql, $object->GetPlainSelectSQL()); $object->SetSelectSQL($sql); $object->addCalculatedField('Relevance', 'search_result.Relevance'); $type_clauses['search']['include'] = '1'; $type_clauses['search']['except'] = '0'; $type_clauses['search']['having_filter'] = false; } $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $search_helper->SetComplexFilter($event, $type_clauses, implode(',', $types), implode(',', $except_types)); } /** * Adds filter, that uses *.VIEW permissions to determine if an item should be shown to a user. * * @param kDBList $object Object. * * @return void * @access protected */ protected function applyViewPermissionFilter(kDBList $object) { if ( !$this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) { return; } if ( $this->Application->RecallVar('user_id') == USER_ROOT ) { // for "root" CATEGORY.VIEW permission is checked for items lists too $view_perm = 1; } else { $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ list ($view_perm, $view_filter) = $count_helper->GetPermissionClause($object->Prefix, 'perm'); $object->addFilter('perm_filter2', $view_filter); } $object->addFilter('perm_filter', 'perm.PermId = ' . $view_perm); // check for CATEGORY.VIEW permission } /** * Returns current theme id * * @return int */ function _getCurrentThemeId() { $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ return (int)$themes_helper->getCurrentThemeId(); } /** * Returns ID of current item to be edited * by checking ID passed in get/post as prefix_id * or by looking at first from selected ids, stored. * Returned id is also stored in Session in case * it was explicitly passed as get/post * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { if ( ($event->Special == 'page') || $this->_isVirtual($event) || ($event->Prefix == 'st') ) { return $this->_getPassedStructureID($event); } if ( $this->Application->isAdmin ) { return parent::getPassedID($event); } return $this->Application->GetVar('m_cat_id'); } /** * Enter description here... * * @param kEvent $event * @return int */ function _getPassedStructureID($event) { static $page_by_template = Array (); if ( $event->Special == 'current' ) { return $this->Application->GetVar('m_cat_id'); } $event->setEventParam('raise_warnings', 0); $page_id = parent::getPassedID($event); if ( $page_id === false ) { $template = $event->getEventParam('page'); if ( !$template ) { $template = $this->Application->GetVar('t'); } // bug: when template contains "-" symbols (or others, that stripDisallowed will replace) it's not found if ( !array_key_exists($template, $page_by_template) ) { $config = $event->getUnitConfig(); $template_crc = kUtil::crc32(mb_strtolower($template)); $sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName() . ' WHERE ( (NamedParentPathHash = ' . $template_crc . ') OR (`Type` = ' . PAGE_TYPE_TEMPLATE . ' AND CachedTemplateHash = ' . $template_crc . ') ) AND (ThemeId = ' . $this->_getCurrentThemeId() . ' OR ThemeId = 0)'; $page_id = $this->Conn->GetOne($sql); } else { $page_id = $page_by_template[$template]; } if ( $page_id ) { $page_by_template[$template] = $page_id; } } if ( !$page_id && !$this->Application->isAdmin ) { $page_id = $this->Application->GetVar('m_cat_id'); } return $page_id; } function ParentGetPassedID($event) { return parent::getPassedID($event); } /** * Adds calculates fields for item statuses * * @param kCatDBItem $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { if ( $this->_isVirtual($event) ) { return; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->addCalculatedField( 'IsNew', ' IF(%1$s.NewItem = 2, IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '. $this->Application->ConfigValue('Category_DaysNew'). '*3600*24), 1, 0), %1$s.NewItem )'); } /** * Checks, that this is virtual page * * @param kEvent $event * @return int * @access protected */ protected function _isVirtual(kEvent $event) { return strpos($event->Special, '-virtual') !== false; } /** * Gets right special for configuring virtual page * * @param kEvent $event * @return string * @access protected */ protected function _getCategorySpecial(kEvent $event) { return $this->_isVirtual($event) ? '-virtual' : $event->Special; } /** * Set correct parent path for newly created categories * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCopyToLive(kEvent $event) { parent::OnAfterCopyToLive($event); $object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true, 'live_table' => true)); /* @var $object CategoriesItem */ $parent_path = false; $object->Load($event->getEventParam('id')); if ( $event->getEventParam('temp_id') == 0 ) { if ( $object->isLoaded() ) { // update path only for real categories (not including "Home" root category) $fields_hash = $object->buildParentBasedFields(); $this->Conn->doUpdate($fields_hash, $object->TableName, 'CategoryId = ' . $object->GetID()); $parent_path = $fields_hash['ParentPath']; } } else { $parent_path = $object->GetDBField('ParentPath'); } if ( $parent_path ) { $cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $parent_path)); /* @var $cache_updater kPermCacheUpdater */ $cache_updater->OneStepRun(); } } /** * Set cache modification mark if needed * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { parent::OnBeforeDeleteFromLive($event); $id = $event->getEventParam('id'); // loading anyway, because this object is needed by "c-perm:OnBeforeDeleteFromLive" event $temp_object = $event->getObject(Array ('skip_autoload' => true)); /* @var $temp_object CategoriesItem */ $temp_object->Load($id); if ( $id == 0 ) { if ( $temp_object->isLoaded() ) { // new category -> update cache (not loaded when "Home" category) $this->Application->StoreVar('PermCache_UpdateRequired', 1); } return ; } // existing category was edited, check if in-cache fields are modified $live_object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('live_table' => true, 'skip_autoload' => true)); /* @var $live_object CategoriesItem */ $live_object->Load($id); $cached_fields = Array ('l' . $this->Application->GetDefaultLanguageId() . '_Name', 'Filename', 'Template', 'ParentId', 'Priority'); foreach ($cached_fields as $cached_field) { if ( $live_object->GetDBField($cached_field) != $temp_object->GetDBField($cached_field) ) { // use session instead of REQUEST because of permission editing in category can contain // multiple submits, that changes data before OnSave event occurs $this->Application->StoreVar('PermCache_UpdateRequired', 1); break; } } // remember category filename change between temp and live records if ( $temp_object->GetDBField('Filename') != $live_object->GetDBField('Filename') ) { $filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ()); $filename_changes[ $live_object->GetID() ] = Array ( 'from' => $live_object->GetDBField('Filename'), 'to' => $temp_object->GetDBField('Filename') ); $this->Application->SetVar($event->Prefix . '_filename_changes', $filename_changes); } } /** * Calls kDBEventHandler::OnSave original event * Used in proj-cms:StructureEventHandler->OnSave * * @param kEvent $event */ function parentOnSave($event) { parent::OnSave($event); } /** * Reset root-category flag when new category is created * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { // 1. for permission editing of Home category $this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); parent::OnPreCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ // 2. preset template $category_id = $this->Application->GetVar('m_cat_id'); $root_category = $this->Application->getBaseCategory(); if ( $category_id == $root_category ) { $object->SetDBField('Template', $this->_getDefaultDesign()); } // 3. set default owner $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } /** * Checks cache update mark and redirect to cache if needed * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { // get data from live table before it is overwritten by parent OnSave method call $ids = $this->getSelectedIDs($event, true); $is_editing = implode('', $ids); $old_statuses = $is_editing ? $this->_getCategoryStatus($ids) : Array (); $object = $event->getObject(); /* @var $object CategoriesItem */ parent::OnSave($event); if ( $event->status != kEvent::erSUCCESS ) { return; } if ( $this->Application->RecallVar('PermCache_UpdateRequired') ) { $this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); } $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); if ( $is_editing ) { // send email event to category owner, when it's status is changed (from admin) $object->SwitchToLive(); $new_statuses = $this->_getCategoryStatus($ids); $process_statuses = Array (STATUS_ACTIVE, STATUS_DISABLED); foreach ($new_statuses as $category_id => $new_status) { if ( $new_status != $old_statuses[$category_id] && in_array($new_status, $process_statuses) ) { $object->Load($category_id); $email_event = $new_status == STATUS_ACTIVE ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY'; $this->Application->emailUser($email_event, $object->GetDBField('CreatedById'), $object->getEmailParams()); } } } // change opener stack in case if edited category filename was changed $filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ()); if ( $filename_changes ) { $opener_stack = $this->Application->makeClass('kOpenerStack'); /* @var $opener_stack kOpenerStack */ list ($template, $params, $index_file) = $opener_stack->pop(); foreach ($filename_changes as $change_info) { $template = str_ireplace($change_info['from'], $change_info['to'], $template); } $opener_stack->push($template, $params, $index_file); $opener_stack->save(); } } /** * Returns statuses of given categories * * @param Array $category_ids * @return Array */ function _getCategoryStatus($category_ids) { $config = $this->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $sql = 'SELECT Status, ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $category_ids) . ')'; return $this->Conn->GetCol($sql, $id_field); } /** * Creates a new item in temp table and * stores item id in App vars and Session on success * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveCreated(kEvent $event) { $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object CategoriesItem */ if ( $object->IsRoot() ) { // don't create root category while saving permissions return; } parent::OnPreSaveCreated($event); } /** * Deletes sym link to other category * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemDelete(kEvent $event) { parent::OnAfterItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ $sql = 'UPDATE ' . $object->TableName . ' SET SymLinkCategoryId = NULL WHERE SymLinkCategoryId = ' . $object->GetID(); $this->Conn->Query($sql); // delete direct subscriptions to category, that was deleted $sql = 'SELECT SubscriptionId FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions WHERE CategoryId = ' . $object->GetID(); $ids = $this->Conn->GetCol($sql); if ( $ids ) { $temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event->MasterEvent)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('system-event-subscription', '', $ids); } } /** * Exclude root categories from deleting * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(kEvent $event, $type) { if ( $event->Name == 'OnMassDelete' && $type == 'before' ) { $ids = $event->getEventParam('ids'); if ( !$ids || $this->Application->ConfigValue('AllowDeleteRootCats') ) { return; } $root_categories = Array (); // get module root categories and exclude them foreach ($this->Application->ModuleInfo as $module_info) { $root_categories[] = $module_info['RootCat']; } $root_categories = array_unique($root_categories); if ( $root_categories && array_intersect($ids, $root_categories) ) { $event->setEventParam('ids', array_diff($ids, $root_categories)); $this->Application->StoreVar('root_delete_error', 1); } } } /** * Checks, that given template exists (physically) in given theme * * @param string $template * @param int $theme_id * @return bool */ function _templateFound($template, $theme_id = null) { static $init_made = false; if (!$init_made) { $this->Application->InitParser(true); $init_made = true; } if (!isset($theme_id)) { $theme_id = $this->_getCurrentThemeId(); } $theme_name = $this->_getThemeName($theme_id); return $this->Application->TemplatesCache->TemplateExists('theme:' . $theme_name . '/' . $template); } /** * Removes ".tpl" in template path * * @param string $template * @return string */ function _stripTemplateExtension($template) { // return preg_replace('/\.[^.\\\\\\/]*$/', '', $template); return preg_replace('/^[\\/]{0,1}(.*)\.tpl$/', "$1", $template); } /** * Deletes all selected items. * Automatically recourse into sub-items using temp handler, and deletes sub-items * by calling its Delete method if sub-item has AutoDelete set to true in its config file * * @param kEvent $event * @return void * @access protected */ protected function OnMassDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $to_delete = Array (); $ids = $this->StoreSelectedIDs($event); $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ( $recycle_bin ) { $rb = $this->Application->recallObject('c.recycle', null, Array ('skip_autoload' => true)); /* @var $rb CategoriesItem */ $rb->Load($recycle_bin); $cat = $event->getObject(Array ('skip_autoload' => true)); /* @var $cat CategoriesItem */ foreach ($ids as $id) { $cat->Load($id); if ( preg_match('/^' . preg_quote($rb->GetDBField('ParentPath'), '/') . '/', $cat->GetDBField('ParentPath')) ) { // already in "Recycle Bin" -> delete for real $to_delete[] = $id; continue; } // just move into "Recycle Bin" category $cat->SetDBField('ParentId', $recycle_bin); $cat->Update(); } $ids = $to_delete; } $event->setEventParam('ids', $ids); $this->customProcessing($event, 'before'); $ids = $event->getEventParam('ids'); if ( $ids ) { $recursive_helper = $this->Application->recallObject('RecursiveHelper'); /* @var $recursive_helper kRecursiveHelper */ foreach ($ids as $id) { $recursive_helper->DeleteCategory($id, $event->Prefix); } } $this->clearSelectedIDs($event); $this->_ensurePermCacheRebuild($event); } /** * Add selected items to clipboard with mode = COPY (CLONE) * * @param kEvent $event */ function OnCopy($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'copy', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Add selected items to clipboard with mode = CUT * * @param kEvent $event */ function OnCut($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Controls all item paste operations. Can occur only with filled clipboard. * * @param kEvent $event */ function OnPasteClipboard($event) { $clipboard = unserialize( $this->Application->RecallVar('clipboard') ); foreach ($clipboard as $prefix => $clipboard_data) { $paste_event = new kEvent($prefix.':OnPaste', Array('clipboard_data' => $clipboard_data)); $this->Application->HandleEvent($paste_event); $event->copyFrom($paste_event); } } /** * Checks permission for OnPaste event * * @param kEvent $event * @return bool */ function _checkPastePermission($event) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $category_id = $this->Application->GetVar('m_cat_id'); if ($perm_helper->AddCheckPermission($category_id, $event->Prefix) == 0) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } return true; } /** * Paste categories with sub-items from clipboard * * @param kEvent $event * @return void * @access protected */ protected function OnPaste($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) || !$this->_checkPastePermission($event) ) { $event->status = kEvent::erFAIL; return; } $clipboard_data = $event->getEventParam('clipboard_data'); if ( !$clipboard_data['cut'] && !$clipboard_data['copy'] ) { return; } // 1. get ParentId of moved category(-es) before it gets updated!!!) $source_category_id = 0; $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $clipboard_data['cut'] ) { $sql = 'SELECT ParentId FROM ' . $table_name . ' WHERE ' . $id_field . ' = ' . $clipboard_data['cut'][0]; $source_category_id = $this->Conn->GetOne($sql); } $recursive_helper = $this->Application->recallObject('RecursiveHelper'); /* @var $recursive_helper kRecursiveHelper */ if ( $clipboard_data['cut'] ) { $recursive_helper->MoveCategories($clipboard_data['cut'], $this->Application->GetVar('m_cat_id')); } if ( $clipboard_data['copy'] ) { // don't allow to copy/paste system OR theme-linked virtual pages $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $clipboard_data['copy']) . ') AND (`Type` = ' . PAGE_TYPE_VIRTUAL . ') AND (ThemeId = 0)'; $allowed_ids = $this->Conn->GetCol($sql); if ( !$allowed_ids ) { return; } foreach ($allowed_ids as $id) { $recursive_helper->PasteCategory($id, $event->Prefix); } } $priority_helper = $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ if ( $clipboard_data['cut'] ) { $ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $source_category_id); if ( $ids ) { $priority_helper->massUpdateChanged($event->Prefix, $ids); } } // recalculate priorities of newly pasted categories in destination category $parent_id = $this->Application->GetVar('m_cat_id'); $ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); if ( $ids ) { $priority_helper->massUpdateChanged($event->Prefix, $ids); } if ( $clipboard_data['cut'] || $clipboard_data['copy'] ) { $this->_ensurePermCacheRebuild($event); } } /** * Ensures, that category permission cache is rebuild when category is added/edited/deleted * * @param kEvent $event * @return void * @access protected */ protected function _ensurePermCacheRebuild(kEvent $event) { $this->Application->StoreVar('PermCache_UpdateRequired', 1); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Occurs when pasting category * * @param kEvent $event */ /*function OnCatPaste($event) { $inp_clipboard = $this->Application->RecallVar('ClipBoard'); $inp_clipboard = explode('-', $inp_clipboard, 2); if($inp_clipboard[0] == 'COPY') { $config = $event->getUnitConfig(); $cat_ids = $event->getEventParam('cat_ids'); $saved_cat_id = $this->Application->GetVar('m_cat_id'); $ids_sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName() . ' 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', Array ('parent_event' => $event)); if($item_ids) $temp->CloneItems($event->Prefix, $event->Special, $item_ids); } $this->Application->SetVar('m_cat_id', $saved_cat_id); } }*/ /** * Clears clipboard content * * @param kEvent $event */ function OnClearClipboard($event) { $this->Application->RemoveVar('clipboard'); } /** * Sets correct status for new categories created on front-end * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $object = $event->getObject(); /* @var $object CategoriesItem */ if ( $object->GetDBField('ParentId') <= 0 ) { // no parent category - use current (happens during import) $object->SetDBField('ParentId', $this->Application->GetVar('m_cat_id')); } $this->_beforeItemChange($event); if ( $this->Application->isAdmin || $event->Prefix == 'st' ) { // don't check category permissions when auto-creating structure pages return ; } $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $new_status = false; $category_id = $this->Application->GetVar('m_cat_id'); if ( $perm_helper->CheckPermission('CATEGORY.ADD', 0, $category_id) ) { $new_status = STATUS_ACTIVE; } else { if ( $perm_helper->CheckPermission('CATEGORY.ADD.PENDING', 0, $category_id) ) { $new_status = STATUS_PENDING; } } if ( $new_status ) { $object->SetDBField('Status', $new_status); // don't forget to set Priority for suggested from Front-End categories $min_priority = $this->_getNextPriority($object->GetDBField('ParentId'), $object->TableName); $object->SetDBField('Priority', $min_priority); } else { $event->status = kEvent::erPERM_FAIL; return ; } } /** * Returns next available priority for given category from given table * * @param int $category_id * @param string $table_name * @return int */ function _getNextPriority($category_id, $table_name) { $sql = 'SELECT MIN(Priority) FROM ' . $table_name . ' WHERE ParentId = ' . $category_id; return (int)$this->Conn->GetOne($sql) - 1; } /** * Sets correct status for new categories created on front-end * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_beforeItemChange($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetChangedFields() ) { $object->SetDBField('ModifiedById', $this->Application->RecallVar('user_id')); } } /** * Creates needed sql query to load item, * if no query is defined in config for * special requested, then use list query * * @param kEvent $event * @return string * @access protected */ protected function ItemPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $sqls = $object->getFormOption('ItemSQLs', Array ()); $category_special = $this->_getCategorySpecial($event); $special = isset($sqls[$category_special]) ? $category_special : ''; // preferred special not found in ItemSQLs -> use analog from ListSQLs return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event); } /** * Creates needed sql query to load list, * if no query is defined in config for * special requested, then use default * query * * @param kEvent $event * @return string * @access protected */ protected function ListPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $special = $this->_getCategorySpecial($event); $sqls = $object->getFormOption('ListSQLs', Array ()); return $sqls[array_key_exists($special, $sqls) ? $special : '']; } /** * Performs redirect to correct suggest confirmation template * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { parent::OnCreate($event); if ( $this->Application->isAdmin || $event->status != kEvent::erSUCCESS ) { // don't sent email or rebuild cache directly after category is created by admin return; } $object = $event->getObject(); /* @var $object kDBItem */ $cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $object->GetDBField('ParentPath'))); /* @var $cache_updater kPermCacheUpdater */ $cache_updater->OneStepRun(); $is_active = ($object->GetDBField('Status') == STATUS_ACTIVE); $next_template = $is_active ? 'suggest_confirm_template' : 'suggest_pending_confirm_template'; $event->redirect = $this->Application->GetVar($next_template); $event->SetRedirectParam('opener', 's'); // send email events $send_params = $object->getEmailParams(); $event_suffix = $is_active ? 'ADD' : 'ADD.PENDING'; $perm_prefix = $event->getUnitConfig()->getPermItemPrefix(); $this->Application->emailUser($perm_prefix . '.' . $event_suffix, $object->GetDBField('CreatedById'), $send_params); $this->Application->emailAdmin($perm_prefix . '.' . $event_suffix, null, $send_params); } /** * Returns current per-page setting for list * * @param kEvent $event * @return int * @access protected */ protected function getPerPage(kEvent $event) { if ( !$this->Application->isAdmin ) { $same_special = $event->getEventParam('same_special'); $event->setEventParam('same_special', true); $per_page = parent::getPerPage($event); $event->setEventParam('same_special', $same_special); } return parent::getPerPage($event); } /** * Set's correct page for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetPagination(kEvent $event) { parent::SetPagination($event); if ( !$this->Application->isAdmin ) { $page_var = $event->getEventParam('page_var'); if ( $page_var !== false ) { $page = $this->Application->GetVar($page_var); if ( is_numeric($page) ) { $object = $event->getObject(); /* @var $object kDBList */ $object->SetPage($page); } } } } /** * Apply same processing to each item being selected in grid * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline' ) { parent::iterateItems($event); } if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object CategoriesItem */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $propagate_category_status = $this->Application->GetVar('propagate_category_status'); $status_field = $event->getUnitConfig()->getStatusField(true); foreach ($ids as $id) { $object->Load($id); $object->SetDBField($status_field, $event->Name == 'OnMassApprove' ? 1 : 0); if ( $object->Update() ) { if ( $propagate_category_status ) { $sql = 'UPDATE ' . $object->TableName . ' SET ' . $status_field . ' = ' . $object->GetDBField($status_field) . ' WHERE TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight'); $this->Conn->Query($sql); } $event->status = kEvent::erSUCCESS; $email_event = $event->Name == 'OnMassApprove' ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY'; $this->Application->emailUser($email_event, $object->GetDBField('CreatedById'), $object->getEmailParams()); } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } $this->clearSelectedIDs($event); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool * @access protected */ protected function checkItemStatus(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->isLoaded() ) { return true; } if ( $object->GetDBField('Status') != STATUS_ACTIVE && $object->GetDBField('Status') != 4 ) { if ( !$object->GetDBField('DirectLinkEnabled') || !$object->GetDBField('DirectLinkAuthKey') ) { return false; } return $this->Application->GetVar('authkey') == $object->GetDBField('DirectLinkAuthKey'); } return true; } /** * Set's correct sorting for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetSorting(kEvent $event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); if ( in_array('search', $types) ) { $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ // 1. no user sorting - sort by relevance $default_sortings = parent::_getDefaultSorting($event); $default_sorting = key($default_sortings['Sorting']) . ',' . current($default_sortings['Sorting']); if ( $object->isMainList() ) { $sort_by = $this->Application->GetVar('sort_by', ''); if ( !$sort_by ) { $this->Application->SetVar('sort_by', 'Relevance,desc|' . $default_sorting); } elseif ( strpos($sort_by, 'Relevance,') !== false ) { $this->Application->SetVar('sort_by', $sort_by . '|' . $default_sorting); } } else { $sorting_settings = $this->getListSetting($event, 'Sortings'); $sort_by = trim(getArrayValue($sorting_settings, 'Sort1') . ',' . getArrayValue($sorting_settings, 'Sort1_Dir'), ','); if ( !$sort_by ) { $event->setEventParam('sort_by', 'Relevance,desc|' . $default_sorting); } elseif ( strpos($sort_by, 'Relevance,') !== false ) { $event->setEventParam('sort_by', $sort_by . '|' . $default_sorting); } } $this->_removeForcedSortings($event); } parent::SetSorting($event); } /** * Removes forced sortings * * @param kEvent $event */ protected function _removeForcedSortings(kEvent $event) { $config = $event->getUnitConfig(); foreach ($config->getListSortingSpecials() as $special) { $list_sortings = $config->getListSortingsBySpecial($special); unset($list_sortings['ForcedSorting']); $config->setListSortingsBySpecial('', $list_sortings); } } /** * Default sorting in search results only comes from relevance field * * @param kEvent $event * @return Array * @access protected */ protected function _getDefaultSorting(kEvent $event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); return in_array('search', $types) ? Array () : parent::_getDefaultSorting($event); } // ============= for cms page processing ======================= /** * Returns default design template * * @return string */ function _getDefaultDesign() { $default_design = trim($this->Application->ConfigValue('cms_DefaultDesign'), '/'); if (!$default_design) { // theme-based alias for default design return '#default_design#'; } if (strpos($default_design, '#') === false) { // real template, not alias, so prefix with "/" return '/' . $default_design; } // alias return $default_design; } /** * Returns default design based on given virtual template (used from kApplication::Run) * * @param string $t * @return string * @access public */ public function GetDesignTemplate($t = null) { if ( !isset($t) ) { $t = $this->Application->GetVar('t'); } $page = $this->Application->recallObject($this->Prefix . '.-virtual', null, Array ('page' => $t)); /* @var $page CategoriesItem */ if ( $page->isLoaded() ) { $real_t = $page->GetDBField('CachedTemplate'); $this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId')); if ( $page->GetDBField('FormId') ) { $this->Application->SetVar('form_id', $page->GetDBField('FormId')); } } else { $not_found = $this->Application->ConfigValue('ErrorTemplate'); $real_t = $not_found ? $not_found : 'error_notfound'; $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $theme_id = $this->Application->GetVar('m_theme'); $category_id = $themes_helper->getPageByTemplate($real_t, $theme_id); $this->Application->SetVar('m_cat_id', $category_id); header('HTTP/1.0 404 Not Found'); } // replace alias in form #alias_name# to actual template used in this theme if ( $this->Application->isAdmin ) { $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ // only, used when in "Design Mode" $this->Application->SetVar('theme.current_id', $themes_helper->getCurrentThemeId()); } $theme = $this->Application->recallObject('theme.current'); /* @var $theme kDBItem */ $template = $theme->GetField('TemplateAliases', $real_t); if ( $template ) { return $template; } return $real_t; } /** * Sets category id based on found template (used from kApplication::Run) * * @deprecated */ /*function SetCatByTemplate() { $t = $this->Application->GetVar('t'); $page = $this->Application->recallObject($this->Prefix . '.-virtual'); if ( $page->isLoaded() ) { $this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId')); } }*/ /** * Prepares template paths * * @param kEvent $event */ function _beforeItemChange($event) { $object = $event->getObject(); /* @var $object CategoriesItem */ $object->checkFilename(); $object->generateFilename(); $now = time(); if ( !$this->Application->isDebugMode() && strpos($event->Special, 'rebuild') === false ) { $object->SetDBField('Type', $object->GetOriginalField('Type')); $object->SetDBField('Protected', $object->GetOriginalField('Protected')); if ( $object->GetDBField('Protected') ) { // some fields are read-only for protected pages, when debug mode is off $object->SetDBField('AutomaticFilename', $object->GetOriginalField('AutomaticFilename')); $object->SetDBField('Filename', $object->GetOriginalField('Filename')); $object->SetDBField('Status', $object->GetOriginalField('Status')); } } $is_admin = $this->Application->isAdminUser; if ( (!$object->IsTempTable() && !$is_admin) || ($is_admin && !$object->GetDBField('CreatedById')) ) { $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } if ($object->GetChangedFields()) { $object->SetDBField('Modified_date', $now); $object->SetDBField('Modified_time', $now); } $object->setRequired('PageCacheKey', $object->GetDBField('OverridePageCacheKey')); $object->SetDBField('Template', $this->_stripTemplateExtension( $object->GetDBField('Template') )); if ($object->GetDBField('Type') == PAGE_TYPE_TEMPLATE) { if (!$this->_templateFound($object->GetDBField('Template'), $object->GetDBField('ThemeId'))) { $object->SetError('Template', 'template_file_missing', 'la_error_TemplateFileMissing'); } } $this->_saveTitleField($object, 'Title'); $this->_saveTitleField($object, 'MenuTitle'); $root_category = $this->Application->getBaseCategory(); if ( file_exists(FULL_PATH . '/themes') && ($object->GetDBField('ParentId') == $root_category) && ($object->GetDBField('Template') == CATEGORY_TEMPLATE_INHERIT) ) { // there are themes + creating top level category $object->SetError('Template', 'no_inherit'); } if ( !$this->Application->isAdminUser && $object->isVirtualField('cust_RssSource') ) { // only administrator can set/change "cust_RssSource" field if ($object->GetDBField('cust_RssSource') != $object->GetOriginalField('cust_RssSource')) { $object->SetError('cust_RssSource', 'not_allowed', 'la_error_OperationNotAllowed'); } } if ( !$object->GetDBField('DirectLinkAuthKey') ) { $key_parts = Array ( $object->GetID(), $object->GetDBField('ParentId'), $object->GetField('Name'), 'b38' ); $object->SetDBField('DirectLinkAuthKey', substr( md5( implode(':', $key_parts) ), 0, 20 )); } } /** * Sets page name to requested field in case when: * 1. page was auto created (through theme file rebuild) * 2. requested field is empty * * @param kDBItem $object * @param string $field * @author Alex */ function _saveTitleField(&$object, $field) { $value = $object->GetField($field, 'no_default'); // current value of target field $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $src_field = $ml_formatter->LangFieldName('Name'); $dst_field = $ml_formatter->LangFieldName($field); $dst_field_not_changed = $object->GetOriginalField($dst_field) == $value; if ($value == '' || preg_match('/^_Auto: (.*)/', $value) || (($object->GetOriginalField($src_field) == $value) && $dst_field_not_changed)) { // target field is empty OR target field value starts with "_Auto: " OR (source field value // before change was equals to current target field value AND target field value wasn't changed) $object->SetField($dst_field, $object->GetField($src_field)); } } /** * Don't allow to delete system pages, when not in debug mode * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemDelete(kEvent $event) { parent::OnBeforeItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField('Protected') && !$this->Application->isDebugMode(false) ) { $event->status = kEvent::erFAIL; } } /** * Creates category based on given TPL file * * @param CategoriesItem $object * @param string $template * @param int $theme_id * @param int $system_mode * @param array $template_info * @return bool */ function _prepareAutoPage(&$object, $template, $theme_id = null, $system_mode = SMS_MODE_AUTO, $template_info = Array ()) { $template = $this->_stripTemplateExtension($template); if ($system_mode == SMS_MODE_AUTO) { $page_type = $this->_templateFound($template, $theme_id) ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL; } else { $page_type = $system_mode == SMS_MODE_FORCE ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL; } if (($page_type == PAGE_TYPE_TEMPLATE) && ($template_info === false)) { // do not auto-create system pages, when browsing through site return false; } if (!isset($theme_id)) { $theme_id = $this->_getCurrentThemeId(); } $root_category = $this->Application->getBaseCategory(); $page_category = $this->Application->GetVar('m_cat_id'); if (!$page_category) { $page_category = $root_category; $this->Application->SetVar('m_cat_id', $page_category); } if (($page_type == PAGE_TYPE_VIRTUAL) && (strpos($template, '/') !== false)) { // virtual page, but have "/" in template path -> create it's path $category_path = explode('/', $template); $template = array_pop($category_path); $page_category = $this->_getParentCategoryFromPath($category_path, $root_category, $theme_id); } $page_name = ($page_type == PAGE_TYPE_TEMPLATE) ? '_Auto: ' . $template : $template; $page_description = ''; if ($page_type == PAGE_TYPE_TEMPLATE) { $design_template = strtolower($template); // leading "/" not added ! if ($template_info) { if (array_key_exists('name', $template_info) && $template_info['name']) { $page_name = $template_info['name']; } if (array_key_exists('desc', $template_info) && $template_info['desc']) { $page_description = $template_info['desc']; } if (array_key_exists('section', $template_info) && $template_info['section']) { // this will override any global "m_cat_id" $page_category = $this->_getParentCategoryFromPath(explode('||', $template_info['section']), $root_category, $theme_id); } } } else { $design_template = $this->_getDefaultDesign(); // leading "/" added ! } $object->Clear(); $object->SetDBField('ParentId', $page_category); $object->SetDBField('Type', $page_type); $object->SetDBField('Protected', 1); // $page_type == PAGE_TYPE_TEMPLATE $object->SetDBField('IsMenu', 0); $object->SetDBField('ThemeId', $theme_id); // put all templates to then end of list (in their category) $min_priority = $this->_getNextPriority($page_category, $object->TableName); $object->SetDBField('Priority', $min_priority); $object->SetDBField('Template', $design_template); $object->SetDBField('CachedTemplate', $design_template); $primary_language = $this->Application->GetDefaultLanguageId(); $current_language = $this->Application->GetVar('m_lang'); $object->SetDBField('l' . $primary_language . '_Name', $page_name); $object->SetDBField('l' . $current_language . '_Name', $page_name); $object->SetDBField('l' . $primary_language . '_Description', $page_description); $object->SetDBField('l' . $current_language . '_Description', $page_description); return $object->Create(); } function _getParentCategoryFromPath($category_path, $base_category, $theme_id = null) { static $category_ids = Array (); if (!$category_path) { return $base_category; } if (array_key_exists(implode('||', $category_path), $category_ids)) { return $category_ids[ implode('||', $category_path) ]; } $backup_category_id = $this->Application->GetVar('m_cat_id'); $object = $this->Application->recallObject($this->Prefix . '.rebuild-path', null, Array ('skip_autoload' => true)); /* @var $object CategoriesItem */ $parent_id = $base_category; $filenames_helper = $this->Application->recallObject('FilenamesHelper'); /* @var $filenames_helper kFilenamesHelper */ $safe_category_path = array_map(Array (&$filenames_helper, 'replaceSequences'), $category_path); foreach ($category_path as $category_order => $category_name) { $this->Application->SetVar('m_cat_id', $parent_id); // get virtual category first, when possible $sql = 'SELECT ' . $object->IDField . ' FROM ' . $object->TableName . ' WHERE ( Filename = ' . $this->Conn->qstr($safe_category_path[$category_order]) . ' OR Filename = ' . $this->Conn->qstr( $filenames_helper->replaceSequences('_Auto: ' . $category_name) ) . ' ) AND (ParentId = ' . $parent_id . ') AND (ThemeId = 0 OR ThemeId = ' . $theme_id . ') ORDER BY ThemeId ASC'; $parent_id = $this->Conn->GetOne($sql); if ($parent_id === false) { // page not found $template = implode('/', array_slice($safe_category_path, 0, $category_order + 1)); // don't process system templates in sub-categories $system = $this->_templateFound($template, $theme_id) && (strpos($template, '/') === false); if (!$this->_prepareAutoPage($object, $category_name, $theme_id, $system ? SMS_MODE_FORCE : false)) { // page was not created break; } $parent_id = $object->GetID(); } } $this->Application->SetVar('m_cat_id', $backup_category_id); $category_ids[ implode('||', $category_path) ] = $parent_id; return $parent_id; } /** * Returns theme name by it's id. Used in structure page creation. * * @param int $theme_id * @return string */ function _getThemeName($theme_id) { static $themes = null; if (!isset($themes)) { $theme_config = $this->Application->getUnitConfig('theme'); $id_field = $theme_config->getIDField(); $table_name = $theme_config->getTableName(); $sql = 'SELECT Name, ' . $id_field . ' FROM ' . $table_name . ' WHERE Enabled = 1'; $themes = $this->Conn->GetCol($sql, $id_field); } return array_key_exists($theme_id, $themes) ? $themes[$theme_id] : false; } /** * Resets SMS-menu cache * * @param kEvent $event */ function OnResetCMSMenuCache($event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } $this->_resetMenuCache(); $event->SetRedirectParam('action_completed', 1); } /** * Performs reset of category-related caches (menu, structure dropdown, template mapping) * * @return void * @access protected */ protected function _resetMenuCache() { // reset cms menu cache (all variables are automatically rebuild, when missing) if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->rebuildCache('master:cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime); $this->Application->rebuildCache('master:StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime); $this->Application->rebuildCache('master:template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime); } else { $this->Application->rebuildDBCache('cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime); $this->Application->rebuildDBCache('StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime); $this->Application->rebuildDBCache('template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime); } } /** * Updates structure config * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); if (defined('IS_INSTALL') && IS_INSTALL) { // skip any processing, because Categories table doesn't exists until install is finished $this->addViewPermissionJoin($event); return ; } $site_config_helper = $this->Application->recallObject('SiteConfigHelper'); /* @var $site_config_helper SiteConfigHelper */ $settings = $site_config_helper->getSettings(); $root_category = $this->Application->getBaseCategory(); $config = $event->getUnitConfig(); // set root category $config->addSectionAdjustments(Array ( 'in-portal:browse' => Array ( 'url' => Array ('m_cat_id' => $root_category), 'late_load' => Array ('m_cat_id' => $root_category), 'onclick' => 'checkCatalog(' . $root_category . ')', ), 'in-portal:browse_site' => Array ( 'url' => Array ('editing_mode' => $settings['default_editing_mode']), ) )); // prepare structure dropdown $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $fields = $config->getFields(); $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')); $config->setFields($fields); if ($this->Application->isAdmin) { // don't sort by Front-End sorting fields $config_mapping = $config->getConfigMapping(); $remove_keys = Array ('DefaultSorting1Field', 'DefaultSorting2Field', 'DefaultSorting1Dir', 'DefaultSorting2Dir'); foreach ($remove_keys as $remove_key) { unset($config_mapping[$remove_key]); } $config->setConfigMapping($config_mapping); } else { // sort by parent path on Front-End only $config->setListSortingsBySpecial('', Array ( 'ForcedSorting' => Array ('CurrentSort' => 'asc'), )); } $this->addViewPermissionJoin($event); // add grids for advanced view (with primary category column) foreach (Array ('Default', 'Radio') as $process_grid) { $grid_data = $config->getGridByName($process_grid); $grid_data['Fields']['CachedNavbar'] = Array ('title' => 'la_col_Path', 'data_block' => 'grid_parent_category_td', 'filter_block' => 'grid_like_filter'); $config->addGrids($grid_data, $process_grid . 'ShowAll'); } } /** * 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 = ''; } $config = $event->getUnitConfig(); foreach ( $config->getListSQLSpecials() as $special ) { $list_sql = str_replace('{PERM_JOIN}', $join_clause, $config->getListSQLsBySpecial($special)); $config->setListSQLsBySpecial($special, $list_sql); } return $this; } /** * Returns folders, that can contain design templates * * @return array * @access protected */ protected function getDesignFolders() { $ret = Array ('tf.FilePath = "/designs"', 'tf.FilePath = "/platform/designs"'); foreach ($this->Application->ModuleInfo as $module_info) { $ret[] = 'tf.FilePath = "/' . $module_info['TemplatePath'] . 'designs"'; } return array_unique($ret); } /** * Removes this item and it's children (recursive) from structure dropdown * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); if ( !$this->Application->isAdmin ) { // calculate priorities dropdown only for admin return; } $object = $event->getObject(); /* @var $object kDBItem */ // remove this category & it's children from dropdown $sql = 'SELECT ' . $object->IDField . ' FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%"'; $remove_categories = $this->Conn->GetCol($sql); $options = $object->GetFieldOption('ParentId', 'options'); foreach ($remove_categories as $remove_category) { unset($options[$remove_category]); } $object->SetFieldOption('ParentId', 'options', $options); } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $object = $event->getObject(); /* @var $object CategoriesItem */ // need to update path after category is created, so category is included in that path $fields_hash = $object->buildParentBasedFields(); $this->Conn->doUpdate($fields_hash, $object->TableName, $object->IDField . ' = ' . $object->GetID()); $object->SetDBFieldsFromHash($fields_hash); } /** * Enter description here... * * @param kEvent $event */ function OnAfterRebuildThemes($event) { $sql = 'SELECT t.ThemeId, CONCAT( tf.FilePath, \'/\', tf.FileName ) AS Path, tf.FileMetaInfo FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId WHERE t.Enabled = 1 AND tf.FileType = 1 AND ( SELECT COUNT(CategoryId) FROM ' . TABLE_PREFIX . 'Categories c WHERE CONCAT(\'/\', c.Template, \'.tpl\') = CONCAT( tf.FilePath, \'/\', tf.FileName ) AND (c.ThemeId = t.ThemeId) ) = 0 '; $files = $this->Conn->Query($sql, 'Path'); if ( !$files ) { // all possible pages are already created return; } kUtil::setResourceLimit(); $dummy = $this->Application->recallObject($event->Prefix . '.rebuild', NULL, Array ('skip_autoload' => true)); /* @var $dummy CategoriesItem */ $error_count = 0; foreach ($files as $a_file => $file_info) { $status = $this->_prepareAutoPage($dummy, $a_file, $file_info['ThemeId'], SMS_MODE_FORCE, unserialize($file_info['FileMetaInfo'])); // create system page if ( !$status ) { $error_count++; } } if ( $this->Application->ConfigValue('CategoryPermissionRebuildMode') == CategoryPermissionRebuild::SILENT ) { $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); } $this->_resetMenuCache(); if ( $error_count ) { // allow user to review error after structure page creation $event->MasterEvent->redirect = false; } } /** * Processes OnMassMoveUp, OnMassMoveDown events * * @param kEvent $event */ function OnChangePriority($event) { $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); $event->CallSubEvent('priority:' . $event->Name); $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); } /** * Completely recalculates priorities in current category * * @param kEvent $event */ function OnRecalculatePriorities($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); $event->CallSubEvent('priority:' . $event->Name); $this->_resetMenuCache(); } /** * Update Preview Block for FCKEditor * * @param kEvent $event */ function OnUpdatePreviewBlock($event) { $event->status = kEvent::erSTOP; $string = $this->Application->unescapeRequestVariable($this->Application->GetVar('preview_content')); $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $string = $category_helper->replacePageIds($string); $this->Application->StoreVar('_editor_preview_content_', $string); } /** * Makes simple search for categories * based on keywords string * * @param kEvent $event */ function OnSimpleSearch($event) { $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('kHTTPQuery'); /* @var $query_object kHTTPQuery */ $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if ( !isset($query_object->Get['keywords']) && !isset($query_object->Post['keywords']) && $this->Conn->Query($sql) ) { // used when navigating by pages or changing sorting in search results return; } if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $this->Application->SetVar('keywords_too_short', 1); return; // if no or too short keyword entered, doing nothing } $this->Application->StoreVar('keywords', $keywords); $this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search $keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_')); $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ $config = $event->getUnitConfig(); $this->Application->SetVar($event->getPrefixSpecial().'_Page', 1); $lang = $this->Application->GetVar('m_lang'); $items_table = $config->getTableName(); $module_name = 'In-Portal'; $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('confs')->getTableName() . ' 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 = $config->getCustomFields(); if ($custom_fields) { $custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; } // what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table)) $search_config_map = Array(); foreach ($field_list as $key => $field) { $local_table = TABLE_PREFIX.$search_config[$field]['TableName']; $weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if ( !$search_config[$field]['CustomFieldId'] && $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) { $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $field_list[$key] = 'l'.$lang.'_'.$field; if (!isset($search_config[$field]['ForeignField'])) { $field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary']; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } } // processing fields from other tables $foreign_field = $search_config[$field]['ForeignField']; if ( $foreign_field ) { $exploded = explode(':', $foreign_field, 2); if ($exploded[0] == 'CALC') { // ignoring having type clauses in simple search unset($field_list[$key]); continue; } else { $multi_lingual = false; if ($exploded[0] == 'MULTI') { $multi_lingual = true; $foreign_field = $exploded[1]; } $exploded = explode('.', $foreign_field); // format: table.field_name $foreign_table = TABLE_PREFIX.$exploded[0]; $alias_counter++; $alias = 't'.$alias_counter; if ($multi_lingual) { $field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1]; $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $search_config_map[ $field_list[$key] ] = $field; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } else { $field_list[$key] = $alias.'.'.$exploded[1]; $search_config_map[ $field_list[$key] ] = $field; } $join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']); $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); $join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($search_config[$field]['CustomFieldId']) { $local_table = 'custom_data'; // search by custom field value on current language $custom_field_id = array_search($field_list[$key], $custom_fields); $field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id; // search by custom field value on primary language $field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } $field_list[$key] = $local_table.'.'.$field_list[$key]; $search_config_map[ $field_list[$key] ] = $field; } } // keyword string processing $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $where_clause = Array (); foreach ($field_list as $field) { if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) { // local real field $filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false); if ($filter_data) { $where_clause[] = $filter_data['value']; } } elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) { $custom_field_name = 'cust_' . $search_config_map[$field]; $filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false); if ($filter_data) { $where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']); } } else { $where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field)); } } $where_clause = '((' . implode(') OR (', $where_clause) . '))'; // 2 braces for next clauses, see below! $where_clause = $where_clause . ' AND (' . $items_table . '.Status = ' . STATUS_ACTIVE . ')'; if ($event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild') { $sub_search_ids = $event->MasterEvent->getEventParam('ResultIds'); if ( $sub_search_ids !== false ) { if ( $sub_search_ids ) { $where_clause .= 'AND (' . $items_table . '.ResourceId IN (' . implode(',', $sub_search_ids) . '))'; } else { $where_clause .= 'AND FALSE'; } } } // exclude template based sections from search results (ie. registration) if ( $this->Application->ConfigValue('ExcludeTemplateSectionsFromSearch') ) { $where_clause .= ' AND ' . $items_table . '.ThemeId = 0'; } // making relevance clause $positive_words = $search_helper->getPositiveKeywords($keywords); $this->Application->StoreVar('highlight_keywords', serialize($positive_words)); $revelance_parts = Array(); reset($search_config); foreach ($positive_words as $keyword_index => $positive_word) { $positive_word = $search_helper->transformWildcards($positive_word); $positive_words[$keyword_index] = $this->Conn->escape($positive_word); } foreach ($field_list as $field) { if (!array_key_exists($field, $search_config_map)) { $map_key = $search_config_map[$items_table . '.' . $field]; } else { $map_key = $search_config_map[$field]; } $config_elem = $search_config[ $map_key ]; $weight = $config_elem['Priority']; // search by whole words only ([[:<:]] - word boundary) /*$revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.implode(' ', $positive_words).')[[:>:]]", '.$weight.', 0)'; foreach ($positive_words as $keyword) { $revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.$keyword.')[[:>:]]", '.$weight.', 0)'; }*/ // search by partial word matches too $revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; foreach ($positive_words as $keyword) { $revelance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)'; } } $revelance_parts = array_unique($revelance_parts); $conf_postfix = $config->getSearchConfigPostfix(); $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 = $config->getFieldByName('EditorsPick') ? $items_table.'.EditorsPick' : '0'; $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance, '.$items_table.'.'.$config->getIDField().' AS ItemId, '.$items_table.'.ResourceId, '.$config->getItemType().' AS ItemType, '.$edpick_clause.' AS EdPick FROM '.$object->TableName.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$items_table.'.'.$config->getIDField().' ORDER BY Relevance DESC'; $this->Conn->Query($sql); if ( !$search_table_exists ) { $sql = 'ALTER TABLE ' . $search_table . ' ADD INDEX (ResourceId), ADD INDEX (Relevance)'; $this->Conn->Query($sql); } } /** * Enter description here... * * @param kEvent $event */ function OnSubSearch($event) { // keep search results from other items after doing a sub-search on current item type $this->Application->SetVar('do_not_drop_search_table', true); $ids = Array (); $search_table = TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search'; $sql = 'SHOW TABLES LIKE "' . $search_table . '"'; if ( $this->Conn->Query($sql) ) { $item_type = $event->getUnitConfig()->getItemType(); // 1. get ids to be used as search bounds $sql = 'SELECT DISTINCT ResourceId FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $ids = $this->Conn->GetCol($sql); // 2. delete previously found ids $sql = 'DELETE FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $this->Conn->Query($sql); } $event->setEventParam('ResultIds', $ids); $event->CallSubEvent('OnSimpleSearch'); } /** * Make record to search log * * @param string $keywords * @param int $search_type 0 - simple search, 1 - advanced search */ function saveToSearchLog($keywords, $search_type = 0) { // don't save keywords for each module separately, just one time // static variable can't help here, because each module uses it's own class instance ! if (!$this->Application->GetVar('search_logged')) { $sql = 'UPDATE '.TABLE_PREFIX.'SearchLogs SET Indices = Indices + 1 WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search $this->Conn->Query($sql); if ($this->Conn->getAffectedRows() == 0) { $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLogs'); } $this->Application->SetVar('search_logged', 1); } } /** * Load item if id is available * * @param kEvent $event * @return void * @access protected */ protected function LoadItem(kEvent $event) { if ( !$this->_isVirtual($event) ) { parent::LoadItem($event); return; } $object = $event->getObject(); /* @var $object kDBItem */ $id = $this->getPassedID($event); if ( $object->isLoaded() && !is_array($id) && ($object->GetID() == $id) ) { // object is already loaded by same id return; } if ( $object->Load($id, null, true) ) { $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $object->setID($id); } } /** * Returns constrain for priority calculations * * @param kEvent $event * @return void * @see PriorityEventHandler * @access protected */ protected function OnGetConstrainInfo(kEvent $event) { $constrain = ''; // for OnSave $event_name = $event->getEventParam('original_event'); $actual_event_name = $event->getEventParam('actual_event'); if ( $actual_event_name == 'OnSavePriorityChanges' || $event_name == 'OnAfterItemLoad' || $event_name == 'OnAfterItemDelete' ) { $object = $event->getObject(); /* @var $object kDBItem */ $constrain = 'ParentId = ' . $object->GetDBField('ParentId'); } elseif ( $actual_event_name == 'OnPreparePriorities' ) { $constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); } elseif ( $event_name == 'OnSave' ) { $constrain = ''; } else { $constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); } $event->setEventParam('constrain_info', Array ($constrain, '')); } /** - * Parses category part of url, build main part of url - * - * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE. - * @param string $prefix Prefix, that listener uses for system integration - * @param Array $params Params, that are used for url building or created during url parsing. - * @param Array $url_parts Url parts to parse (only for parsing). - * @param bool $keep_events Keep event names in resulting url (only for building). - * @return bool|string|Array Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener. - */ - public function CategoryRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false) - { - if ($rewrite_mode == REWRITE_MODE_BUILD) { - return $this->_buildMainUrl($prefix, $params, $keep_events); - } - - if ( $this->_parseFriendlyUrl($url_parts, $params) ) { - // friendly urls work like exact match only! - return false; - } - - $this->_parseCategory($url_parts, $params); - - return true; - } - - /** - * Build main part of every url - * - * @param string $prefix_special - * @param Array $params - * @param bool $keep_events - * @return string - */ - protected function _buildMainUrl($prefix_special, &$params, $keep_events) - { - $ret = ''; - list ($prefix) = explode('.', $prefix_special); - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); - if ($processed_params === false) { - return ''; - } - - // add language - if ($processed_params['m_lang'] && ($processed_params['m_lang'] != $rewrite_processor->primaryLanguageId)) { - $language_name = $this->Application->getCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]'); - if ($language_name === false) { - $sql = 'SELECT PackName - FROM ' . TABLE_PREFIX . 'Languages - WHERE LanguageId = ' . $processed_params['m_lang']; - $language_name = $this->Conn->GetOne($sql); - - $this->Application->setCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]', $language_name); - } - - $ret .= $language_name . '/'; - } - - // add theme - if ($processed_params['m_theme'] && ($processed_params['m_theme'] != $rewrite_processor->primaryThemeId)) { - $theme_name = $this->Application->getCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]'); - if ($theme_name === false) { - $sql = 'SELECT Name - FROM ' . TABLE_PREFIX . 'Themes - WHERE ThemeId = ' . $processed_params['m_theme']; - $theme_name = $this->Conn->GetOne($sql); - - $this->Application->setCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]', $theme_name); - - } - - $ret .= $theme_name . '/'; - } - - // inject custom url parts made by other rewrite listeners just after language/theme url parts - if ($params['inject_parts']) { - $ret .= implode('/', $params['inject_parts']) . '/'; - } - - // add category - if ($processed_params['m_cat_id'] > 0 && $params['pass_category']) { - $category_filename = $this->Application->getCategoryCache($processed_params['m_cat_id'], 'filenames'); - - preg_match('/^Content\/(.*)/i', $category_filename, $regs); - - if ($regs) { - $template = array_key_exists('t', $params) ? $params['t'] : false; - - if (strtolower($regs[1]) == strtolower($template)) { - // we could have category path like "Content/<template_path>" in this case remove template - $params['pass_template'] = false; - } - - $ret .= $regs[1] . '/'; - } - - $params['category_processed'] = true; - } - - // reset category page - $force_page_adding = false; - if (array_key_exists('reset', $params) && $params['reset']) { - unset($params['reset']); - - if ($processed_params['m_cat_id']) { - $processed_params['m_cat_page'] = 1; - $force_page_adding = true; - } - } - - if ((array_key_exists('category_processed', $params) && $params['category_processed'] && ($processed_params['m_cat_page'] > 1)) || $force_page_adding) { - // category name was added before AND category page number found - $ret = rtrim($ret, '/') . '_' . $processed_params['m_cat_page'] . '/'; - } - - $template = array_key_exists('t', $params) ? $params['t'] : false; - $category_template = ($processed_params['m_cat_id'] > 0) && $params['pass_category'] ? $this->Application->getCategoryCache($processed_params['m_cat_id'], 'category_designs') : ''; - - if ((strtolower($template) == '__default__') && ($processed_params['m_cat_id'] == 0)) { - // for "Home" category set template to index when not set - $template = 'index'; - } - - // remove template from url if it is category index cached template - if ( ($template == $category_template) || (mb_strtolower($template) == '__default__') ) { - // given template is also default template for this category OR '__default__' given - $params['pass_template'] = false; - } - - // remove template from url if it is site homepage on primary language & theme - if ( ($template == 'index') && $processed_params['m_lang'] == $rewrite_processor->primaryLanguageId && $processed_params['m_theme'] == $rewrite_processor->primaryThemeId ) { - // given template is site homepage on primary language & theme - $params['pass_template'] = false; - } - - if ($template && $params['pass_template']) { - $ret .= $template . '/'; - } - - return mb_strtolower( rtrim($ret, '/') ); - } - - /** - * Checks if whole url_parts matches a whole In-CMS page - * - * @param Array $url_parts - * @param Array $vars - * @return bool - */ - protected function _parseFriendlyUrl($url_parts, &$vars) - { - if (!$url_parts) { - return false; - } - - $sql = 'SELECT CategoryId, NamedParentPath - FROM ' . TABLE_PREFIX . 'Categories - WHERE FriendlyURL = ' . $this->Conn->qstr(implode('/', $url_parts)); - $friendly = $this->Conn->GetRow($sql); - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - if ($friendly) { - $vars['m_cat_id'] = $friendly['CategoryId']; - $vars['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']); - - while ($url_parts) { - $rewrite_processor->partParsed( array_shift($url_parts) ); - } - - return true; - } - - return false; - } - - /** - * Extracts category part from url - * - * @param Array $url_parts - * @param Array $vars - * @return bool - */ - protected function _parseCategory($url_parts, &$vars) - { - if (!$url_parts) { - return false; - } - - $res = false; - $url_part = array_shift($url_parts); - - $category_id = 0; - $last_category_info = false; - $category_path = $url_part == 'content' ? '' : 'content'; - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - do { - $category_path = trim($category_path . '/' . $url_part, '/'); - // bb_<topic_id> -> forums/bb_2 - if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $rets) ) { - $category_path = $rets[1]; - $vars['m_cat_page'] = $rets[2]; - } - - $sql = 'SELECT CategoryId, SymLinkCategoryId, NamedParentPath - FROM ' . TABLE_PREFIX . 'Categories - WHERE (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)'; - $category_info = $this->Conn->GetRow($sql); - - if ($category_info !== false) { - $last_category_info = $category_info; - $rewrite_processor->partParsed($url_part); - - $url_part = array_shift($url_parts); - $res = true; - } - } while ($category_info !== false && $url_part); - - if ($last_category_info) { - // this category is symlink to other category, so use it's url instead - // (used in case if url prior to symlink adding was indexed by spider or was bookmarked) - if ($last_category_info['SymLinkCategoryId']) { - $sql = 'SELECT CategoryId, NamedParentPath - FROM ' . TABLE_PREFIX . 'Categories - WHERE (CategoryId = ' . $last_category_info['SymLinkCategoryId'] . ')'; - $category_info = $this->Conn->GetRow($sql); - - if ($category_info) { - // web symlinked category was found use it - // TODO: maybe 302 redirect should be made to symlinked category url (all other url parts should stay) - $last_category_info = $category_info; - } - } - - // 1. Set virtual page as template, this will be replaced to physical template later in kApplication::Run. - // 2. Don't set CachedTemplate field as template here, because we will loose original page associated with it's cms blocks! - $vars['t'] = mb_strtolower( preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath'])); - - $vars['m_cat_id'] = $last_category_info['CategoryId']; - $vars['is_virtual'] = true; // for template from POST, strange code there! - } - /*else { - $vars['m_cat_id'] = 0; - }*/ - - return $res; - } - - /** * Set's new unique resource id to user * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemValidate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $resource_id = $object->GetDBField('ResourceId'); if ( !$resource_id ) { $object->SetDBField('ResourceId', $this->Application->NextResourceId()); } } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { parent::OnBeforeClone($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('ResourceId', 0); // this will reset it } } Index: branches/5.3.x/core/units/helpers/mod_rewrite_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/mod_rewrite_helper.php (revision 16170) +++ branches/5.3.x/core/units/helpers/mod_rewrite_helper.php (nonexistent) @@ -1,356 +0,0 @@ -<?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 CategoryItemRewrite extends kHelper { - - /** - * Builds/parses category item 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 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. - * @access public - */ - public function RewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false) - { - if ($rewrite_mode == REWRITE_MODE_BUILD) { - return $this->_buildCategoryItemUrl($prefix, $params, $keep_events); - } - - $module_prefix = $this->_parseCategoryItemUrl($url_parts, $params, $prefix); - - if ($module_prefix !== false) { - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - $params['pass'][] = $module_prefix; - $rewrite_processor->setModulePrefix($module_prefix); - } - - return true; - } - - /** - * Build category item part of url - * - * @param string $prefix_special - * @param Array $params - * @param bool $keep_events - * @return string - * @access protected - */ - protected function _buildCategoryItemUrl($prefix_special, &$params, $keep_events) - { - static $default_per_page = Array (); - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - $ret = ''; - list ($prefix) = explode('.', $prefix_special); - $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); - - if ($processed_params === false) { - return ''; - } - - if ( isset($params[$prefix_special . '_event']) && $params[$prefix_special . '_event'] ) { - $params['events[' . $prefix_special . ']'] = $params[$prefix_special . '_event']; - unset($params[$prefix_special . '_event']); - } - - if (!array_key_exists($prefix, $default_per_page)) { - $list_helper = $this->Application->recallObject('ListHelper'); - /* @var $list_helper ListHelper */ - - $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); - } - - if ($processed_params[$prefix_special . '_id']) { - $category_id = array_key_exists('m_cat_id', $params) ? $params['m_cat_id'] : $this->Application->GetVar('m_cat_id'); - - // if template is also item template of category, then remove template - $template = array_key_exists('t', $params) ? $params['t'] : false; - $item_template = $rewrite_processor->GetItemTemplate($category_id, $prefix); - - if ($template == $item_template || strtolower($template) == '__default__') { - // given template is also default template for this category item or '__default__' given - $params['pass_template'] = false; - } - - // get item's filename - if ($prefix == 'bb') { - $ret .= 'bb_' . $processed_params[$prefix_special . '_id'] . '/'; - } - else { - $filename = $this->getFilename($prefix, $processed_params[$prefix_special . '_id'], $category_id); - if ($filename !== false) { - $ret .= $filename . '/'; - } - } - } else { - if ($processed_params[$prefix_special . '_Page'] == 1) { - // when printing category items and we are on the 1st page -> there is no information about - // category item prefix and $params['pass_category'] will not be added automatically - $params['pass_category'] = true; - } - elseif ($processed_params[$prefix_special . '_Page'] > 1) { - // $ret .= $processed_params[$prefix_special . '_Page'] . '/'; - $params['page'] = $processed_params[$prefix_special . '_Page']; - } - - $per_page = $processed_params[$prefix_special . '_PerPage']; - - if ($per_page && ($per_page != $default_per_page[$prefix])) { - $params['per_page'] = $processed_params[$prefix_special . '_PerPage']; - } - } - - return mb_strtolower( rtrim($ret, '/') ); - } - - /** - * Builds/parses review 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_special 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 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. - * @access public - */ - public function ReviewRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix_special, &$params, &$url_parts, $keep_events = false) - { - static $default_per_page = Array (); - - if ( $rewrite_mode != REWRITE_MODE_BUILD ) { - // don't parse anything - return true; - } - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - $ret = ''; - list ($prefix) = explode('.', $prefix_special); - $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); - - if ($processed_params === false) { - return ''; - } - - if (!array_key_exists($prefix, $default_per_page)) { - $list_helper = $this->Application->recallObject('ListHelper'); - /* @var $list_helper ListHelper */ - - $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); - } - - if ($processed_params[$prefix_special . '_id']) { - return false; - } - else { - if ($processed_params[$prefix_special . '_Page'] == 1) { - // when printing category items and we are on the 1st page -> there is no information about - // category item prefix and $params['pass_category'] will not be added automatically - $params['pass_category'] = true; - } - elseif ($processed_params[$prefix_special . '_Page'] > 1) { - // $ret .= $processed_params[$prefix_special . '_Page'] . '/'; - $params['page'] = $processed_params[$prefix_special . '_Page']; - } - - $per_page = $processed_params[$prefix_special . '_PerPage']; - - if ($per_page && ($per_page != $default_per_page[$prefix])) { - $params['per_page'] = $processed_params[$prefix_special . '_PerPage']; - } - } - - return mb_strtolower( rtrim($ret, '/') ); - } - - /** - * Returns item's filename that corresponds id passed. If possible, then get it from cache - * - * @param string $prefix - * @param int $id - * @param int $category_id - * @return string - * @access protected - */ - protected function getFilename($prefix, $id, $category_id = null) - { - if ($prefix == 'c') { - throw new Exception('Method "<strong>' . __FUNCTION__ . '</strong>" no longer work with "<strong>c</strong>" prefix. Please use "<strong>getCategoryCache</strong>" method instead'); - } - - $category_id = isset($category_id) ? $category_id : $this->Application->GetVar('m_cat_id'); - - $cache_key = 'filenames[%' . $this->Application->incrementCacheSerial($prefix, $id, false) . '%]:' . (int)$category_id; - $filename = $this->Application->getCache($cache_key); - - if ($filename === false) { - $this->Conn->nextQueryCachable = true; - $config = $this->Application->getUnitConfig($prefix); - - $sql = 'SELECT ResourceId - FROM ' . $config->getTableName() . ' - WHERE ' . $config->getIDField() . ' = ' . $this->Conn->qstr($id); - $resource_id = $this->Conn->GetOne($sql); - - $this->Conn->nextQueryCachable = true; - $sql = 'SELECT Filename - FROM ' . TABLE_PREFIX . 'CategoryItems - WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')'; - $filename = $this->Conn->GetOne($sql); - - if ($filename !== false) { - $this->Application->setCache($cache_key, $filename); - } - } - - return $filename; - } - - /** - * Sets template and id, corresponding to category item given in url - * - * @param Array $url_parts - * @param Array $vars - * @param string $prefix Prefix, that listener uses for system integration - * - * @return boolean|string - * @access protected - */ - protected function _parseCategoryItemUrl(&$url_parts, &$vars, $prefix) - { - if ( !$url_parts ) { - return false; - } - - $item_filename = end($url_parts); - if ( is_numeric($item_filename) ) { - // this page, don't process here - return false; - } - - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - if ( $prefix == 'bb' && preg_match('/^bb_([\d]+)/', $item_filename, $regs) ) { - // process topics separately, because they don't use item filenames - array_pop($url_parts); - $rewrite_processor->partParsed($item_filename, 'rtl'); - - return $this->_parseTopicUrl($regs[1], $vars); - } - - $cat_item = $this->findCategoryItem((int)$vars['m_cat_id'], $item_filename, $prefix); - - if ( $cat_item !== false ) { - // item found - $module_prefix = $cat_item['ItemPrefix']; - $item_template = $rewrite_processor->GetItemTemplate($cat_item, $module_prefix, $vars['m_theme']); - - // converting ResourceId to corresponding Item id - $module_config = $this->Application->getUnitConfig($module_prefix); - - $sql = 'SELECT ' . $module_config->getIDField() . ' - FROM ' . $module_config->getTableName() . ' - WHERE ResourceId = ' . $cat_item['ItemResourceId']; - $item_id = $this->Conn->GetOne($sql); - - if ( $item_id ) { - array_pop($url_parts); - $rewrite_processor->partParsed($item_filename, 'rtl'); - - if ( $item_template ) { - // when template is found in category -> set it - $vars['t'] = $item_template; - } - - // we have category item id - $vars[$module_prefix . '_id'] = $item_id; - - return $module_prefix; - } - } - - return false; - } - - /** - * Locating the item in CategoryItems by filename to detect its ItemPrefix and its category ParentPath. - * - * @param integer $category_id Category. - * @param string $filename Filename. - * @param string $prefix Prefix, that listener uses for system integration - * - * @return string - */ - protected function findCategoryItem($category_id, $filename, $prefix) - { - static $cache = array(); - - $cache_key = $category_id . ':' . $filename; - - if ( !isset($cache[$cache_key]) ) { - $sql = 'SELECT ci.ItemResourceId, ci.ItemPrefix, c.ParentPath, ci.CategoryId - FROM ' . TABLE_PREFIX . 'CategoryItems AS ci - LEFT JOIN ' . TABLE_PREFIX . 'Categories AS c ON c.CategoryId = ci.CategoryId - WHERE (ci.CategoryId = ' . $category_id . ') AND (ci.Filename = ' . $this->Conn->qstr($filename) . ')'; - $cache[$cache_key] = $this->Conn->GetRow($sql); - } - - $category_item = $cache[$cache_key]; - - return $category_item && $category_item['ItemPrefix'] == $prefix ? $category_item : false; - } - - /** - * Set's template and topic id corresponding to topic given in url - * - * @param int $topic_id - * @param Array $vars - * @return string - * @access protected - */ - protected function _parseTopicUrl($topic_id, &$vars) - { - $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); - /* @var $rewrite_processor kRewriteUrlProcessor */ - - $sql = 'SELECT c.ParentPath, c.CategoryId - FROM ' . TABLE_PREFIX . 'Categories AS c - WHERE c.CategoryId = ' . (int)$vars['m_cat_id']; - $cat_item = $this->Conn->GetRow($sql); - - $item_template = $rewrite_processor->GetItemTemplate($cat_item, 'bb', $vars['m_theme']); - - if ($item_template) { - $vars['t'] = $item_template; - } - - $vars['bb_id'] = $topic_id; - - return 'bb'; - } - } Property changes on: branches/5.3.x/core/units/helpers/mod_rewrite_helper.php ___________________________________________________________________ Deleted: cvs2svn:cvs-rev ## -1 +0,0 ## -1.11.2.9 \ No newline at end of property Deleted: svn:eol-style ## -1 +0,0 ## -LF \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -Id \ No newline at end of property Index: branches/5.3.x/core/units/helpers/helpers_config.php =================================================================== --- branches/5.3.x/core/units/helpers/helpers_config.php (revision 16170) +++ branches/5.3.x/core/units/helpers/helpers_config.php (revision 16171) @@ -1,78 +1,77 @@ <?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' => 'helpers', 'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'), 'RegisterClasses' => Array ( Array ('pseudo' => 'kMultiLanguageHelper', 'class' => 'kMultiLanguageHelper', 'file' => 'multilanguage_helper.php', 'build_event' => ''), Array ('pseudo' => 'SearchHelper', 'class' => 'kSearchHelper', 'file' => 'search_helper.php', 'build_event' => ''), Array ('pseudo' => 'SectionsHelper', 'class' => 'kSectionsHelper', 'file' => 'sections_helper.php', 'build_event' => ''), Array ('pseudo' => 'PermissionsHelper', 'class' => 'kPermissionsHelper', 'file' => 'permissions_helper.php', 'build_event' => ''), Array ('pseudo' => 'kModulesHelper', 'class' => 'kModulesHelper', 'file' => 'modules_helper.php', 'build_event' => ''), - Array ('pseudo' => 'CategoryItemRewrite', 'class' => 'CategoryItemRewrite', 'file' => 'mod_rewrite_helper.php', 'build_event' => ''), Array ('pseudo' => 'RecursiveHelper', 'class' => 'kRecursiveHelper', 'file' => 'recursive_helper.php', 'build_event' => ''), Array ('pseudo' => 'FilenamesHelper', 'class' => 'kFilenamesHelper', 'file' => 'filenames_helper.php', 'build_event' => ''), Array ('pseudo' => 'ClipboardHelper', 'class' => 'kClipboardHelper', 'file' => 'clipboard_helper.php', 'build_event' => ''), Array ('pseudo' => 'ColumnPickerHelper', 'class' => 'kColumnPickerHelper', 'file' => 'col_picker_helper.php', 'build_event' => ''), Array ('pseudo' => 'ThemesHelper', 'class' => 'kThemesHelper', 'file' => 'themes_helper.php', 'build_event' => ''), Array ('pseudo' => 'CaptchaHelper', 'class' => 'kCaptchaHelper', 'file' => 'captcha_helper.php', 'build_event' => ''), Array ('pseudo' => 'PriorityHelper', 'class' => 'kPriorityHelper', 'file' => 'priority_helper.php', 'build_event' => ''), Array ('pseudo' => 'CurlHelper', 'class' => 'kCurlHelper', 'file' => 'curl_helper.php', 'build_event' => ''), Array ('pseudo' => 'CountHelper', 'class' => 'kCountHelper', 'file' => 'count_helper.php', 'build_event' => ''), Array ('pseudo' => 'ImageHelper', 'class' => 'ImageHelper', 'file' => 'image_helper.php', 'build_event' => ''), Array ('pseudo' => 'FileHelper', 'class' => 'FileHelper', 'file' => 'file_helper.php', 'build_event' => ''), Array ('pseudo' => 'CategoryHelper', 'class' => 'CategoryHelper', 'file' => 'category_helper.php', 'build_event' => ''), Array ('pseudo' => 'kNavigationBar', 'class' => 'kNavigationBar', 'file' => 'navigation_bar.php', 'build_event' => ''), Array ('pseudo' => 'CSVHelper', 'class' => 'kCSVHelper', 'file' => 'csv_helper.php', 'build_event' => ''), Array ('pseudo' => 'ChartHelper', 'class' => 'kChartHelper', 'file' => 'chart_helper.php', 'build_event' => ''), Array ('pseudo' => 'RatingHelper', 'class' => 'RatingHelper', 'file' => 'rating_helper.php', 'build_event' => ''), Array ('pseudo' => 'FCKHelper', 'class' => 'fckFCKHelper', 'file' => 'fck_helper.php', 'build_event' => ''), Array ('pseudo' => 'SpamHelper', 'class' => 'SpamHelper', 'file' => 'spam_helper.php', 'build_event' => ''), Array ('pseudo' => 'TemplateHelper', 'class' => 'TemplateHelper', 'file' => 'template_helper.php', 'build_event' => ''), Array ('pseudo' => 'MailingListHelper', 'class' => 'MailingListHelper', 'file' => 'mailing_list_helper.php', 'build_event' => ''), Array ('pseudo' => 'JSONHelper', 'class' => 'JSONHelper', 'file' => 'json_helper.php', 'build_event' => ''), Array ('pseudo' => 'LanguageImportHelper', 'class' => 'LanguageImportHelper', 'file' => 'language_import_helper.php', 'build_event' => ''), Array ('pseudo' => 'SkinHelper', 'class' => 'SkinHelper', 'file' => 'skin_helper.php', 'build_event' => ''), Array ('pseudo' => 'SiteConfigHelper', 'class' => 'SiteConfigHelper', 'file' => 'site_config_helper.php', 'build_event' => ''), Array ('pseudo' => 'MenuHelper', 'class' => 'MenuHelper', 'file' => 'menu_helper.php', 'build_event' => ''), Array ('pseudo' => 'InpCustomFieldsHelper', 'class' => 'InpCustomFieldsHelper', 'file' => 'custom_fields_helper.php', 'build_event' => ''), Array ('pseudo' => 'CountryStatesHelper', 'class' => 'kCountryStatesHelper', 'file' => 'country_states_helper.php', 'build_event' => ''), Array ('pseudo' => 'BracketsHelper', 'class' => 'kBracketsHelper', 'file' => 'brackets_helper.php', 'build_event' => ''), Array ('pseudo' => 'kXMLHelper', 'class' => 'kXMLHelper', 'file' => 'xml_helper.php', 'build_event' => ''), Array ('pseudo' => 'CatItemExportHelper', 'class' => 'kCatDBItemExportHelper', 'file' => 'cat_dbitem_export_helper.php', 'build_event' => ''), Array ('pseudo' => 'kEmailTemplateHelper', 'class' => 'kEmailTemplateHelper', 'file' => 'email_template_helper.php', 'build_event' => ''), Array ('pseudo' => 'ListHelper', 'class' => 'ListHelper', 'file' => 'list_helper.php', 'build_event' => ''), Array ('pseudo' => 'FormSubmissionHelper', 'class' => 'FormSubmissionHelper', 'file' => 'form_submission_helper.php', 'build_event' => ''), Array ('pseudo' => 'MailboxHelper', 'class' => 'MailboxHelper', 'file' => 'mailbox_helper.php', 'build_event' => ''), Array ('pseudo' => 'POP3Helper', 'class' => 'POP3Helper', 'file' => 'pop3_helper.php', 'build_event' => ''), Array ('pseudo' => 'MimeDecodeHelper', 'class' => 'MimeDecodeHelper', 'file' => 'mime_decode_helper.php', 'build_event' => ''), Array ('pseudo' => 'UserHelper', 'class' => 'UserHelper', 'file' => 'user_helper.php', 'build_event' => ''), Array ('pseudo' => 'SiteHelper', 'class' => 'SiteHelper', 'file' => 'site_helper.php', 'build_event' => ''), Array ('pseudo' => 'DeploymentHelper', 'class' => 'DeploymentHelper', 'file' => 'deployment_helper.php', 'build_event' => ''), Array ('pseudo' => 'PageHelper', 'class' => 'PageHelper', 'file' => 'page_helper.php', 'build_event' => ''), Array ('pseudo' => 'BackupHelper', 'class' => 'BackupHelper', 'file' => 'backup_helper.php', 'build_event' => ''), Array ('pseudo' => 'AjaxFormHelper', 'class' => 'AjaxFormHelper', 'file' => 'ajax_form_helper.php', 'build_event' => ''), Array ('pseudo' => 'kCronHelper', 'class' => 'kCronHelper', 'file' => 'cron_helper.php', 'build_event' => ''), Array ('pseudo' => 'kUploadHelper', 'class' => 'kUploadHelper', 'file' => 'upload_helper.php', 'build_event' => ''), ), ); Index: branches/5.3.x/core/units/reviews/reviews_config.php =================================================================== --- branches/5.3.x/core/units/reviews/reviews_config.php (revision 16170) +++ branches/5.3.x/core/units/reviews/reviews_config.php (revision 16171) @@ -1,216 +1,214 @@ <?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' => 'rev', 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'ReviewsEventHandler', 'file' => 'reviews_event_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'ReviewsTagProcessor', 'file' => 'reviews_tag_processor.php', 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), - 'RewriteListener' => 'CategoryItemRewrite:ReviewRewriteListener', - 'ParentPrefix' => 'p', // replace all usage of rev to "p-rev" and then remove this param from here and Prefix too 'ConfigMapping' => Array ( 'PerPage' => 'Comm_Perpage_Reviews', 'ReviewDelayInterval' => 'product_ReviewDelay_Value', 'ReviewDelayValue' => 'product_ReviewDelay_Interval', ), 'IDField' => 'ReviewId', 'StatusField' => Array ('Status'), // field, that is affected by Approve/Decline events 'TableName' => TABLE_PREFIX.'CatalogReviews', 'ParentTableKey' => 'ResourceId', // linked field in master table 'ForeignKey' => 'ItemId', // linked field in sub-table 'AutoDelete' => true, 'AutoClone' => true, 'TitlePresets' => Array ( 'reviews_edit' => Array ('format' => "!la_title_Editing_Review!"), 'reviews' => Array ( 'toolbar_buttons' => Array ('edit', 'delete', 'approve', 'decline', 'view', 'dbl-click'), ), ), 'CalculatedFields' => Array ( '' => Array ( 'ReviewedBy' => 'CASE %1$s.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(CONCAT(pu.FirstName, pu.LastName) <> "", CONCAT(pu.FirstName, " ", pu.LastName), pu.Username) END', ), 'products' => Array ( 'ReviewedBy' => 'CASE %1$s.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(CONCAT(pu.FirstName, pu.LastName) <> "", CONCAT(pu.FirstName, " ", pu.LastName), pu.Username) END', 'ItemName' => 'pr.l1_Name', 'ProductId' => 'pr.ProductId', ), 'product' => Array ( 'ReviewedBy' => 'CASE %1$s.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(CONCAT(pu.FirstName, pu.LastName) <> "", CONCAT(pu.FirstName, " ", pu.LastName), pu.Username) END', 'ItemName' => 'pr.l1_Name', 'ProductId' => 'pr.ProductId', ), ), // key - special, value - list select sql 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN ' . TABLE_PREFIX . 'Users pu ON pu.PortalUserId = %1$s.CreatedById', 'products' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN ' . TABLE_PREFIX . 'Products pr ON pr.ResourceId = %1$s.ItemId LEFT JOIN ' . TABLE_PREFIX . 'Users pu ON pu.PortalUserId = %1$s.CreatedById', 'product' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN ' . TABLE_PREFIX . 'Products pr ON pr.ResourceId = %1$s.ItemId LEFT JOIN ' . TABLE_PREFIX . 'Users pu ON pu.PortalUserId = %1$s.CreatedById', ), 'ListSortings' => Array ( '' => Array ( 'ForcedSorting' => Array ('Priority' => 'desc'), 'Sorting' => Array ('ReviewId' => 'desc'), ) ), 'Fields' => Array ( 'ReviewId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'CreatedOn' => Array ( 'type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#', ), 'ReviewText' => Array ( 'type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null, 'required' => 1, ), 'Rating' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 0 => 'lu_None', 1 => 'lu_Rating_1', 2 => 'lu_Rating_2', 3 => 'lu_Rating_3', 4 => 'lu_Rating_4', 5 => 'lu_Rating_5'), 'use_phrases' => 1, 'min_value_inc' => 0, 'max_value_inc' => 5, 'not_null' => 1, 'default' => 0, ), 'IPAddress' => Array ( 'type' => 'string', 'max_value_inc' => 15, 'not_null' =>1, 'default' => '', ), 'ItemId' => Array ( 'type' => 'int', 'not_null' => 1, 'default' => 0 ), 'CreatedById' => Array ( 'type' => 'int', 'formatter' => 'kLEFTFormatter', '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, 'required' => 1, 'default' => NULL, 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), ), 'ItemType' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Priority' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Status' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' => Array ( 0 => 'la_Disabled', 1 => 'la_Active', 2 => 'la_Pending', ), 'not_null' =>1, 'default' => 2, ), 'TextFormat' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_text', 1 => 'la_html'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'Module' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'HelpfulCount' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'NotHelpfulCount' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0) ), 'VirtualFields' => Array ( 'ReviewedBy' => Array ('type' => 'string', 'default' => ''), 'CatalogItemName' => Array ('type' => 'string', 'default' => ''), 'CatalogItemId' => Array ('type' => 'int', 'default' => 0), 'CatalogItemCategory' => Array ('type' => 'int', 'default' => 0), 'ItemName' => Array ('type' => 'string', 'default' => ''), 'ProductId' => Array ('type' => 'int', 'default' => 0), ), 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array ( 'default' => 'icon16_item.png', 0 => 'icon16_disabled.png', 1 => 'icon16_item.png', 2 => 'icon16_pending.png', ), 'Fields' => Array ( 'ReviewId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ), 'ReviewText' => Array ('filter_block' => 'grid_like_filter', 'width' => 210, 'first_chars' => 200, ), 'ReviewedBy' => Array ( 'title' => 'la_col_ReviewedBy', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 145, ), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 80, ), 'Rating' => Array ('filter_block' => 'grid_options_filter', 'width' => 80, ), 'HelpfulCount' => Array ('filter_block' => 'grid_range_filter'), 'NotHelpfulCount' => Array ('filter_block' => 'grid_range_filter'), ), ), 'ReviewsSection' => Array ( 'Icons' => Array ( 'default' => 'icon16_item.png', 0 => 'icon16_disabled.png', 1 => 'icon16_item.png', 2 => 'icon16_pending.png', ), 'Fields' => Array ( 'ReviewId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ), 'ReviewText' => Array ('data_block' => 'grid_reviewtext_td', 'filter_block' => 'grid_like_filter', 'width' => 210, 'first_chars' => 200, ), 'ReviewedBy' => Array ( 'title' => 'la_col_ReviewedBy', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 145, ), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 80, ), 'Rating' => Array ('filter_block' => 'grid_options_filter', 'width' => 80, ), 'HelpfulCount' => Array ('filter_block' => 'grid_range_filter'), 'NotHelpfulCount' => Array ('filter_block' => 'grid_range_filter'), ), ), ), -); \ No newline at end of file +); Index: branches/5.3.x/core/units/general/general_config.php =================================================================== --- branches/5.3.x/core/units/general/general_config.php (revision 16170) +++ branches/5.3.x/core/units/general/general_config.php (revision 16171) @@ -1,43 +1,40 @@ <?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 = new kUnitConfig('m', null, false); $config->setEventHandlerClass(Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild')); //$config->setTagProcessorClass(Array ('class' => 'kMainTagProcessor', 'file' => '', 'build_event' => 'OnBuild')); $config->setQueryString(Array ( 1 => 'cat_id', 2 => 'cat_page', 3 => 'lang', 4 => 'theme', 5 => 'opener', 6 => 'wid', )); $config->setTitleField('CachedNavbar'); $config->setTitlePhrase('la_Text_Category'); $config->setCatalogTabIcon('icon16_section.png'); $config->setItemType(1); $config->setTableName(TABLE_PREFIX . 'Categories'); $config->setCatalogItem(true); $config->setPortalStyleEnv(true); -$config->setRewritePriority(100); -$config->setRewriteListener('c_EventHandler:CategoryRewriteListener'); - $config->setPermTabText('In-Portal'); $config->setPermSection(Array ('search' => 'in-portal:configuration_search', 'custom' => 'in-portal:configuration_custom')); Index: branches/5.3.x/core/units/general/MainRouter.php =================================================================== --- branches/5.3.x/core/units/general/MainRouter.php (nonexistent) +++ branches/5.3.x/core/units/general/MainRouter.php (revision 16171) @@ -0,0 +1,305 @@ +<?php +/** +* @version $Id$ +* @package In-Portal +* @copyright Copyright (C) 1997 - 2015 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 MainRouter extends AbstractRouter +{ + + /** + * Returns unit config's prefix, that is associated with this router. + * + * @return string + */ + public function getPrefix() + { + return 'm'; + } + + /** + * Returns weight of this router among other routers. + * + * @return float + */ + public function getWeight() + { + return 100; + } + + /** + * Builds url part. + * + * @return boolean Return true to continue to next router; return false not to rewrite given prefix. + */ + protected function build() + { + $ret = ''; + + $rewrite_processor = $this->getUrlProcessor(); + + $build_params = $this->extractBuildParams(); + + if ( $build_params === false ) { + return ''; + } + + // Add language. + if ( $build_params['m_lang'] && ($build_params['m_lang'] != $rewrite_processor->primaryLanguageId) ) { + $cache_key = 'language_names[%LangIDSerial:' . $build_params['m_lang'] . '%]'; + $language_name = $this->Application->getCache($cache_key); + + if ( $language_name === false ) { + $sql = 'SELECT PackName + FROM ' . TABLE_PREFIX . 'Languages + WHERE LanguageId = ' . $build_params['m_lang']; + $language_name = $this->Conn->GetOne($sql); + + $this->Application->setCache($cache_key, $language_name); + } + + $ret .= $language_name . '/'; + } + + // Add theme. + if ( $build_params['m_theme'] && ($build_params['m_theme'] != $rewrite_processor->primaryThemeId) ) { + $cache_key = 'theme_names[%ThemeIDSerial:' . $build_params['m_theme'] . '%]'; + $theme_name = $this->Application->getCache($cache_key); + + if ( $theme_name === false ) { + $sql = 'SELECT Name + FROM ' . TABLE_PREFIX . 'Themes + WHERE ThemeId = ' . $build_params['m_theme']; + $theme_name = $this->Conn->GetOne($sql); + + $this->Application->setCache($cache_key, $theme_name); + } + + $ret .= $theme_name . '/'; + } + + // Inject custom url parts made by other routers just after language/theme url parts. + $inject_parts = $this->getBuildParam('inject_parts', false); + + if ( $inject_parts ) { + $ret .= implode('/', $inject_parts) . '/'; + } + + // Add category. + if ( $build_params['m_cat_id'] > 0 && $this->getBuildParam('pass_category', false) ) { + $category_filename = $this->Application->getCategoryCache($build_params['m_cat_id'], 'filenames'); + + preg_match('/^Content\/(.*)/i', $category_filename, $regs); + + if ( $regs ) { + $template = $this->getBuildParam('t', false); + + if ( strtolower($regs[1]) == strtolower($template) ) { + // We could have category path like "Content/<template_path>" in this case remove template. + $this->setBuildParam('pass_template', false); + } + + $ret .= $regs[1] . '/'; + } + + $this->setBuildParam('category_processed', true); + } + + // Reset category page. + $force_page_adding = false; + $reset_category_page = $this->getBuildParam('reset', false); + + if ( $reset_category_page ) { + $this->setBuildParam('reset'); + + if ( $build_params['m_cat_id'] ) { + $build_params['m_cat_page'] = 1; + $force_page_adding = true; + } + } + + $category_processed = $this->getBuildParam('category_processed', false); + + if ( ($category_processed && ($build_params['m_cat_page'] > 1)) || $force_page_adding ) { + // Category name was added before AND category page number found. + $ret = rtrim($ret, '/') . '_' . $build_params['m_cat_page'] . '/'; + } + + $template = $this->getBuildParam('t', false); + + if ( ($build_params['m_cat_id'] > 0) && $this->getBuildParam('pass_category', false) ) { + $category_template = $this->Application->getCategoryCache($build_params['m_cat_id'], 'category_designs'); + } + else { + $category_template = ''; + } + + if ( (strtolower($template) == '__default__') && ($build_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. + $this->setBuildParam('pass_template', false); + } + + // Remove template from url if it is site homepage on primary language & theme. + if ( $template == 'index' + && $build_params['m_lang'] == $rewrite_processor->primaryLanguageId + && $build_params['m_theme'] == $rewrite_processor->primaryThemeId + ) { + // Given template is site homepage on primary language & theme. + $this->setBuildParam('pass_template', false); + } + + if ( $template && $this->getBuildParam('pass_template', false) ) { + $ret .= $template . '/'; + } + + return mb_strtolower(rtrim($ret, '/')); + } + + /** + * Parses url part. + * + * @param array $url_parts Url parts to parse. + * @param array $params Parameters, that are used for url building or created during url parsing. + * + * @return boolean Return true to continue to next router; return false to stop processing at this router. + */ + public function parse(array &$url_parts, array &$params) + { + if ( $this->parseFriendlyUrl($url_parts, $params) ) { + // Friendly urls work like exact match only! + return false; + } + + $this->parseCategory($url_parts, $params); + + return true; + } + + /** + * Checks if whole url_parts matches a whole In-CMS page + * + * @param array $url_parts Url parts. + * @param array $params Params. + * + * @return boolean + */ + protected function parseFriendlyUrl(array $url_parts, array &$params) + { + 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); + + if ( $friendly ) { + $params['m_cat_id'] = $friendly['CategoryId']; + $params['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']); + + while ( $url_parts ) { + $this->partParsed(array_shift($url_parts)); + } + + return true; + } + + return false; + } + + /** + * Extracts category part from url + * + * @param array $url_parts Url parts. + * @param array $params Params. + * + * @return boolean + */ + protected function parseCategory(array $url_parts, array &$params) + { + if ( !$url_parts ) { + return false; + } + + $res = false; + $url_part = array_shift($url_parts); + + $category_id = 0; + $last_category_info = false; + $category_path = $url_part == 'content' ? '' : 'content'; + + $rewrite_processor = $this->getUrlProcessor(); + + do { + $category_path = trim($category_path . '/' . $url_part, '/'); + + // Part: "bb_<topic_id>" matches "forums/bb_2". + if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $regs) ) { + $category_path = $regs[1]; + $params['m_cat_page'] = $regs[2]; + } + + $sql = 'SELECT CategoryId, SymLinkCategoryId, NamedParentPath + FROM ' . TABLE_PREFIX . 'Categories + WHERE + (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') + AND + (ThemeId = ' . $params['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! + $params['t'] = mb_strtolower(preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath'])); + + $params['m_cat_id'] = $last_category_info['CategoryId']; + $params['is_virtual'] = true; // For template from POST, strange code there! + } + + return $res; + } + +} Property changes on: branches/5.3.x/core/units/general/MainRouter.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Index: branches/5.3.x/core/install/cache/class_structure.php =================================================================== --- branches/5.3.x/core/install/cache/class_structure.php (revision 16170) +++ branches/5.3.x/core/install/cache/class_structure.php (revision 16171) @@ -1,2537 +1,2561 @@ <?php // @codingStandardsIgnoreFile /** * This file is automatically @generated. Use 'php tools/build_class_map.php' to rebuild it. */ return array( 'cache_format' => 2, 'classes' => array( + 'AbstractCategoryItemRouter' => '/core/kernel/utility/Router/AbstractCategoryItemRouter.php', + 'AbstractReviewRouter' => '/core/kernel/utility/Router/AbstractReviewRouter.php', + 'AbstractRouter' => '/core/kernel/utility/Router/AbstractRouter.php', 'AdminEventsHandler' => '/core/units/admin/admin_events_handler.php', 'AdminTagProcessor' => '/core/units/admin/admin_tag_processor.php', 'AjaxFormHelper' => '/core/units/helpers/ajax_form_helper.php', 'ApcCacheHandler' => '/core/kernel/utility/cache.php', 'BackupHelper' => '/core/units/helpers/backup_helper.php', 'BaseSession' => '/core/kernel/session/session.php', 'BaseSessionStorage' => '/core/kernel/session/session_storage.php', 'CacheSettings' => '/core/kernel/startup.php', 'CaptchaEventHandler' => '/core/units/captcha/captcha_eh.php', 'CategoriesEventHandler' => '/core/units/categories/categories_event_handler.php', 'CategoriesItem' => '/core/units/categories/categories_item.php', 'CategoriesTagProcessor' => '/core/units/categories/categories_tag_processor.php', 'CategoryHelper' => '/core/units/helpers/category_helper.php', - 'CategoryItemRewrite' => '/core/units/helpers/mod_rewrite_helper.php', 'CategoryItemsEventHandler' => '/core/units/category_items/category_items_event_handler.php', 'CategoryItemsTagProcessor' => '/core/units/category_items/category_items_tag_processor.php', 'CategoryPermissionRebuild' => '/core/kernel/constants.php', 'ChangeLog' => '/core/kernel/constants.php', 'ChangeLogEventHandler' => '/core/units/logs/change_logs/change_log_eh.php', 'ChangeLogTagProcessor' => '/core/units/logs/change_logs/change_log_tp.php', 'ColumnSet' => '/core/units/helpers/col_picker_helper.php', 'ConfigSearchEventHandler' => '/core/units/config_search/config_search_event_handler.php', 'ConfigSearchTagProcessor' => '/core/units/config_search/config_search_tag_processor.php', 'ConfigurationEventHandler' => '/core/units/configuration/configuration_event_handler.php', 'ConfigurationItem' => '/core/units/configuration/configuration.php', 'ConfigurationTagProcessor' => '/core/units/configuration/configuration_tag_processor.php', 'ConfigurationValidator' => '/core/units/configuration/configuration_validator.php', 'ContentEventHandler' => '/core/units/content/content_eh.php', 'ContentTagProcessor' => '/core/units/content/content_tp.php', 'CoreUpgrades' => '/core/install/upgrades.php', 'CountryStateEventHandler' => '/core/units/country_states/country_state_eh.php', 'CssMinifyHelper' => '/core/units/helpers/minifiers/css_minify_helper.php', 'CustomDataEventHandler' => '/core/units/custom_data/custom_data_event_handler.php', 'CustomFieldsEventHandler' => '/core/units/custom_fields/custom_fields_event_handler.php', 'CustomFieldsTagProcessor' => '/core/units/custom_fields/custom_fields_tag_processor.php', 'Debugger' => '/core/kernel/utility/debugger.php', 'DebuggerUtil' => '/core/kernel/utility/debugger.php', 'DeploymentHelper' => '/core/units/helpers/deployment_helper.php', 'DraftEventHandler' => '/core/units/forms/drafts/draft_eh.php', 'EditPickerHelper' => '/core/units/helpers/controls/edit_picker_helper.php', 'EmailDelivery' => '/core/kernel/constants.php', 'EmailLogEventHandler' => '/core/units/logs/email_logs/email_log_eh.php', 'EmailLogStatus' => '/core/kernel/constants.php', 'EmailLogTagProcessor' => '/core/units/logs/email_logs/email_log_tp.php', 'EmailQueueEventHandler' => '/core/units/email_queue/email_queue_eh.php', 'EmailQueueTagProcessor' => '/core/units/email_queue/email_queue_tp.php', 'EmailTemplate' => '/core/kernel/constants.php', 'EmailTemplateEventHandler' => '/core/units/email_templates/email_template_eh.php', 'EmailTemplateTagProcessor' => '/core/units/email_templates/email_template_tp.php', 'FakeCacheHandler' => '/core/kernel/utility/cache.php', 'FavoritesEventHandler' => '/core/units/favorites/favorites_eh.php', 'FckEventHandler' => '/core/units/fck/fck_eh.php', 'FckTagProcessor' => '/core/units/fck/fck_tp.php', 'FileEventHandler' => '/core/units/files/file_eh.php', 'FileHelper' => '/core/units/helpers/file_helper.php', 'FileTagProcessor' => '/core/units/files/file_tp.php', 'FormFieldEventHandler' => '/core/units/forms/form_fields/form_field_eh.php', 'FormFieldsTagProcessor' => '/core/units/forms/form_fields/form_fields_tp.php', 'FormSubmissionHelper' => '/core/units/helpers/form_submission_helper.php', 'FormSubmissionTagProcessor' => '/core/units/forms/form_submissions/form_submission_tp.php', 'FormSubmissionsEventHandler' => '/core/units/forms/form_submissions/form_submissions_eh.php', 'FormsEventHandler' => '/core/units/forms/forms/forms_eh.php', 'FormsTagProcessor' => '/core/units/forms/forms/forms_tp.php', 'GeoCodeHelper' => '/core/units/helpers/geocode_helper.php', 'GroupTagProcessor' => '/core/units/groups/group_tp.php', 'GroupsEventHandler' => '/core/units/groups/groups_event_handler.php', 'IDBConnection' => '/core/kernel/db/i_db_connection.php', 'ImageEventHandler' => '/core/units/images/image_event_handler.php', 'ImageHelper' => '/core/units/helpers/image_helper.php', 'ImageTagProcessor' => '/core/units/images/image_tag_processor.php', 'ImagesItem' => '/core/units/images/images.php', 'InPortalPrerequisites' => '/core/install/prerequisites.php', 'InpCustomFieldsHelper' => '/core/units/helpers/custom_fields_helper.php', 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\ClassDetector' => '/core/kernel/utility/ClassDiscovery/ClassDetector.php', 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\ClassMapBuilder' => '/core/kernel/utility/ClassDiscovery/ClassMapBuilder.php', 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\CodeFolderFilterIterator' => '/core/kernel/utility/ClassDiscovery/CodeFolderFilterIterator.php', 'ItemFilterEventHandler' => '/core/units/filters/item_filter_eh.php', 'ItemFilterTagProcessor' => '/core/units/filters/item_filter_tp.php', 'JSONHelper' => '/core/units/helpers/json_helper.php', 'JsMinifyHelper' => '/core/units/helpers/minifiers/js_minify_helper.php', 'Language' => '/core/kernel/constants.php', 'LanguageImportHelper' => '/core/units/helpers/language_import_helper.php', 'LanguagesEventHandler' => '/core/units/languages/languages_event_handler.php', 'LanguagesItem' => '/core/units/languages/languages_item.php', 'LanguagesTagProcessor' => '/core/units/languages/languages_tag_processor.php', 'LeftJoinOptimizer' => '/core/kernel/db/dblist.php', 'ListHelper' => '/core/units/helpers/list_helper.php', 'LoginResult' => '/core/kernel/constants.php', 'MInputHelper' => '/core/units/helpers/controls/minput_helper.php', 'MailboxHelper' => '/core/units/helpers/mailbox_helper.php', 'MailingList' => '/core/kernel/constants.php', 'MailingListEventHandler' => '/core/units/mailing_lists/mailing_list_eh.php', 'MailingListHelper' => '/core/units/helpers/mailing_list_helper.php', 'MailingListTagProcessor' => '/core/units/mailing_lists/mailing_list_tp.php', + 'MainRouter' => '/core/units/general/MainRouter.php', 'MaintenanceMode' => '/core/kernel/startup.php', 'MassImageResizer' => '/core/units/admin/admin_events_handler.php', 'MemcacheCacheHandler' => '/core/kernel/utility/cache.php', 'MenuHelper' => '/core/units/helpers/menu_helper.php', 'MimeDecodeHelper' => '/core/units/helpers/mime_decode_helper.php', 'MinifyHelper' => '/core/units/helpers/minifiers/minify_helper.php', 'ModuleDeploymentLog' => '/core/kernel/constants.php', 'ModuleDeploymentLogEventHandler' => '/core/units/logs/module_deployment_logs/module_deployment_log_eh.php', 'ModulesEventHandler' => '/core/units/modules/modules_event_handler.php', 'ModulesTagProcessor' => '/core/units/modules/modules_tag_processor.php', 'NParser' => '/core/kernel/nparser/nparser.php', 'NParserCompiler' => '/core/kernel/nparser/compiler.php', 'POP3Helper' => '/core/units/helpers/pop3_helper.php', 'PageHelper' => '/core/units/helpers/page_helper.php', 'PageRevisionEventHandler' => '/core/units/page_revisions/page_revision_eh.php', 'PageRevisionTagProcessor' => '/core/units/page_revisions/page_revision_tp.php', 'Params' => '/core/kernel/utility/params.php', 'ParserException' => '/core/kernel/nparser/nparser.php', 'PasswordHash' => '/core/kernel/utility/php_pass.php', 'PasswordHashingMethod' => '/core/kernel/constants.php', 'PermissionTypeEventHandler' => '/core/units/permission_types/permission_type_eh.php', 'PermissionsEventHandler' => '/core/units/permissions/permissions_event_handler.php', 'PermissionsTagProcessor' => '/core/units/permissions/permissions_tag_processor.php', 'PhraseTagProcessor' => '/core/units/phrases/phrase_tp.php', 'PhrasesEventHandler' => '/core/units/phrases/phrases_event_handler.php', 'PriorityEventHandler' => '/core/units/priorites/priority_eh.php', 'PromoBlockEventHandler' => '/core/units/promo_blocks/promo_block_eh.php', 'PromoBlockGroupEventHandler' => '/core/units/promo_block_groups/promo_block_group_eh.php', 'PromoBlockGroupTagProcessor' => '/core/units/promo_block_groups/promo_block_group_tp.php', 'PromoBlockTagProcessor' => '/core/units/promo_blocks/promo_block_tp.php', 'PromoBlockType' => '/core/kernel/constants.php', 'RatingHelper' => '/core/units/helpers/rating_helper.php', 'RelatedSearchEventHandler' => '/core/units/related_searches/related_searches_event_handler.php', 'RelatedSearchTagProcessor' => '/core/units/related_searches/related_searches_tag_processor.php', 'RelationshipEventHandler' => '/core/units/relationship/relationship_event_handler.php', 'RelationshipTagProcessor' => '/core/units/relationship/relationship_tp.php', 'ReviewsEventHandler' => '/core/units/reviews/reviews_event_handler.php', 'ReviewsTagProcessor' => '/core/units/reviews/reviews_tag_processor.php', 'ScheduledTask' => '/core/kernel/constants.php', 'ScheduledTaskEventHandler' => '/core/units/scheduled_tasks/scheduled_task_eh.php', 'SelectorsEventHandler' => '/core/units/selectors/selectors_event_handler.php', 'SelectorsItem' => '/core/units/selectors/selectors_item.php', 'SelectorsTagProcessor' => '/core/units/selectors/selectors_tag_processor.php', 'Session' => '/core/kernel/session/inp_session.php', 'SessionLogEventHandler' => '/core/units/logs/session_logs/session_log_eh.php', 'SessionStorage' => '/core/kernel/session/inp_session_storage.php', 'SiteConfigEventHandler' => '/core/units/sections/site_config_eh.php', 'SiteConfigHelper' => '/core/units/helpers/site_config_helper.php', 'SiteConfigTagProcessor' => '/core/units/sections/site_config_tp.php', 'SiteDomainEventHandler' => '/core/units/site_domains/site_domain_eh.php', 'SiteHelper' => '/core/units/helpers/site_helper.php', 'SkinEventHandler' => '/core/units/skins/skin_eh.php', 'SkinHelper' => '/core/units/helpers/skin_helper.php', 'SpamHelper' => '/core/units/helpers/spam_helper.php', 'SpamReportEventHandler' => '/core/units/spam_reports/spam_report_eh.php', 'SpamReportTagProcessor' => '/core/units/spam_reports/spam_report_tp.php', 'StatisticsEventHandler' => '/core/units/statistics/statistics_event_handler.php', 'StatisticsTagProcessor' => '/core/units/statistics/statistics_tag_processor.php', 'StorageEngine' => '/core/kernel/constants.php', 'StylesheetsEventHandler' => '/core/units/stylesheets/stylesheets_event_handler.php', 'StylesheetsItem' => '/core/units/stylesheets/stylesheets_item.php', 'SubmissionFormField' => '/core/kernel/constants.php', 'SubmissionLogEventHandler' => '/core/units/forms/submission_log/submission_log_eh.php', 'SubmissionLogTagProcessor' => '/core/units/forms/submission_log/submission_log_tp.php', 'SystemEventSubscriptionEventHandler' => '/core/units/system_event_subscriptions/system_event_subscription_eh.php', 'SystemEventSubscriptionTagProcessor' => '/core/units/system_event_subscriptions/system_event_subscription_tp.php', 'SystemLogEventHandler' => '/core/units/logs/system_logs/system_log_eh.php', 'SystemLogTagProcessor' => '/core/units/logs/system_logs/system_log_tp.php', 'TemplateHelper' => '/core/units/helpers/template_helper.php', 'TemplatesCache' => '/core/kernel/nparser/template_cache.php', 'ThemeFileEventHandler' => '/core/units/theme_files/theme_file_eh.php', 'ThemeItem' => '/core/units/themes/theme_item.php', 'ThemesEventHandler' => '/core/units/themes/themes_eh.php', 'ThemesTagProcessor' => '/core/units/themes/themes_tag_processor.php', 'ThesaurusEventHandler' => '/core/units/thesaurus/thesaurus_eh.php', 'ThesaurusTagProcessor' => '/core/units/thesaurus/thesaurus_tp.php', 'TranslationSaveMode' => '/core/kernel/constants.php', 'TranslatorEventHandler' => '/core/units/translator/translator_event_handler.php', 'TranslatorTagProcessor' => '/core/units/translator/translator_tp.php', 'UnitConfigDecorator' => '/core/units/admin/admin_events_handler.php', 'UserGroupsEventHandler' => '/core/units/user_groups/user_groups_eh.php', 'UserHelper' => '/core/units/helpers/user_helper.php', 'UserProfileEventHandler' => '/core/units/user_profile/user_profile_eh.php', 'UserProfileTagProcessor' => '/core/units/user_profile/user_profile_tp.php', 'UserType' => '/core/kernel/constants.php', 'UsersEventHandler' => '/core/units/users/users_event_handler.php', 'UsersItem' => '/core/units/users/users_item.php', 'UsersSyncronize' => '/core/units/users/users_syncronize.php', 'UsersSyncronizeManager' => '/core/units/users/users_syncronize.php', 'UsersTagProcessor' => '/core/units/users/users_tag_processor.php', 'VisitsEventHandler' => '/core/units/visits/visits_event_handler.php', 'VisitsList' => '/core/units/visits/visits_list.php', 'VisitsTagProcessor' => '/core/units/visits/visits_tag_processor.php', 'XCacheCacheHandler' => '/core/kernel/utility/cache.php', 'XMLIterator' => '/core/units/helpers/xml_helper5.php', '_BlockTag' => '/core/kernel/nparser/ntags.php', '_Tag_Cache' => '/core/kernel/nparser/ntags.php', '_Tag_Capture' => '/core/kernel/nparser/ntags.php', '_Tag_Comment' => '/core/kernel/nparser/ntags.php', '_Tag_Compress' => '/core/kernel/nparser/ntags.php', '_Tag_DefaultParam' => '/core/kernel/nparser/ntags.php', '_Tag_DefineElement' => '/core/kernel/nparser/ntags.php', '_Tag_If' => '/core/kernel/nparser/ntags.php', '_Tag_IfDataExists' => '/core/kernel/nparser/ntags.php', '_Tag_IfNot' => '/core/kernel/nparser/ntags.php', '_Tag_Include' => '/core/kernel/nparser/ntags.php', '_Tag_Param' => '/core/kernel/nparser/ntags.php', '_Tag_RenderElement' => '/core/kernel/nparser/ntags.php', '_Tag_RenderElements' => '/core/kernel/nparser/ntags.php', '_Tag_SetParam' => '/core/kernel/nparser/ntags.php', 'clsCachedPermissions' => '/core/units/categories/cache_updater.php', 'clsRecursionStack' => '/core/units/categories/cache_updater.php', 'fckFCKHelper' => '/core/units/helpers/fck_helper.php', 'kApplication' => '/core/kernel/application.php', 'kArray' => '/core/kernel/utility/params.php', 'kBase' => '/core/kernel/kbase.php', 'kBracketsHelper' => '/core/units/helpers/brackets_helper.php', 'kCCDateFormatter' => '/core/kernel/utility/formatters/ccdate_formatter.php', 'kCSSDefaults' => '/core/units/pdf/css_defaults.php', 'kCSVHelper' => '/core/units/helpers/csv_helper.php', 'kCache' => '/core/kernel/utility/cache.php', 'kCacheHandler' => '/core/kernel/utility/cache.php', 'kCacheManager' => '/core/kernel/managers/cache_manager.php', 'kCaptchaHelper' => '/core/units/helpers/captcha_helper.php', 'kCatDBEventHandler' => '/core/kernel/db/cat_event_handler.php', 'kCatDBItem' => '/core/kernel/db/cat_dbitem.php', 'kCatDBItemExportHelper' => '/core/units/helpers/cat_dbitem_export_helper.php', 'kCatDBList' => '/core/kernel/db/cat_dblist.php', 'kCatDBTagProcessor' => '/core/kernel/db/cat_tag_processor.php', 'kChangesFormatter' => '/core/units/logs/change_logs/changes_formatter.php', 'kChartHelper' => '/core/units/helpers/chart_helper.php', 'kClipboardHelper' => '/core/units/helpers/clipboard_helper.php', 'kColumnPickerHelper' => '/core/units/helpers/col_picker_helper.php', 'kCookieHasher' => '/core/kernel/utility/cookie_hasher.php', 'kCountHelper' => '/core/units/helpers/count_helper.php', 'kCountryStatesHelper' => '/core/units/helpers/country_states_helper.php', 'kCronField' => '/core/units/helpers/cron_helper.php', 'kCronHelper' => '/core/units/helpers/cron_helper.php', 'kCurlHelper' => '/core/units/helpers/curl_helper.php', 'kCustomFieldFormatter' => '/core/kernel/utility/formatters/customfield_formatter.php', 'kDBBase' => '/core/kernel/kbase.php', 'kDBConnection' => '/core/kernel/db/db_connection.php', 'kDBConnectionDebug' => '/core/kernel/db/db_connection.php', 'kDBEventHandler' => '/core/kernel/db/db_event_handler.php', 'kDBItem' => '/core/kernel/db/dbitem.php', 'kDBList' => '/core/kernel/db/dblist.php', 'kDBLoadBalancer' => '/core/kernel/db/db_load_balancer.php', 'kDBTagProcessor' => '/core/kernel/db/db_tag_processor.php', 'kDateFormatter' => '/core/kernel/utility/formatters/date_formatter.php', 'kEmail' => '/core/kernel/utility/email.php', 'kEmailSendingHelper' => '/core/kernel/utility/email_send.php', 'kEmailTemplateHelper' => '/core/units/helpers/email_template_helper.php', 'kErrorHandlerStack' => '/core/kernel/utility/logger.php', 'kEvent' => '/core/kernel/utility/event.php', 'kEventHandler' => '/core/kernel/event_handler.php', 'kEventManager' => '/core/kernel/event_manager.php', 'kExceptionHandlerStack' => '/core/kernel/utility/logger.php', 'kFactory' => '/core/kernel/utility/factory.php', 'kFactoryException' => '/core/kernel/utility/factory.php', 'kFilenamesHelper' => '/core/units/helpers/filenames_helper.php', 'kFilesizeFormatter' => '/core/kernel/utility/formatters/filesize_formatter.php', 'kFormatter' => '/core/kernel/utility/formatters/formatter.php', 'kHTTPQuery' => '/core/kernel/utility/http_query.php', 'kHandlerStack' => '/core/kernel/utility/logger.php', 'kHelper' => '/core/kernel/kbase.php', 'kHookManager' => '/core/kernel/managers/hook_manager.php', 'kInstallToolkit' => '/core/install/install_toolkit.php', 'kInstallator' => '/core/install.php', 'kLEFTFormatter' => '/core/kernel/utility/formatters/left_formatter.php', 'kLogger' => '/core/kernel/utility/logger.php', 'kMainTagProcessor' => '/core/kernel/processors/main_processor.php', 'kModulesHelper' => '/core/units/helpers/modules_helper.php', 'kMultiLanguage' => '/core/kernel/utility/formatters/multilang_formatter.php', 'kMultiLanguageHelper' => '/core/units/helpers/multilanguage_helper.php', 'kMultipleFilter' => '/core/kernel/utility/filters.php', 'kMySQLQuery' => '/core/kernel/db/db_connection.php', 'kMySQLQueryCol' => '/core/kernel/db/db_connection.php', 'kNavigationBar' => '/core/units/helpers/navigation_bar.php', 'kNoPermissionException' => '/core/kernel/kbase.php', 'kOpenerStack' => '/core/kernel/utility/opener_stack.php', 'kOptionsFormatter' => '/core/kernel/utility/formatters/options_formatter.php', 'kPDFElemFactory' => '/core/units/pdf/pdf_helper.php', 'kPDFElement' => '/core/units/pdf/pdf_helper.php', 'kPDFHelper' => '/core/units/pdf/pdf_helper.php', 'kPDFImage' => '/core/units/pdf/pdf_image.php', 'kPDFLine' => '/core/units/pdf/pdf_helper.php', 'kPDFRenderer' => '/core/units/pdf/pdf_renderer.php', 'kPDFStylesheet' => '/core/units/pdf/pdf_styles.php', 'kPDFTable' => '/core/units/pdf/pdf_table.php', 'kPDFTableRow' => '/core/units/pdf/pdf_table.php', 'kPDFTextElement' => '/core/units/pdf/pdf_text.php', 'kPasswordFormatter' => '/core/kernel/utility/formatters/password_formatter.php', 'kPermCacheUpdater' => '/core/units/categories/cache_updater.php', 'kPermissionsHelper' => '/core/units/helpers/permissions_helper.php', 'kPhraseCache' => '/core/kernel/languages/phrases_cache.php', 'kPictureFormatter' => '/core/kernel/utility/formatters/upload_formatter.php', 'kPlainUrlProcessor' => '/core/kernel/managers/plain_url_processor.php', 'kPriorityHelper' => '/core/units/helpers/priority_helper.php', 'kRecursiveHelper' => '/core/units/helpers/recursive_helper.php', 'kRedirectException' => '/core/kernel/kbase.php', 'kRequestManager' => '/core/kernel/managers/request_manager.php', 'kRewriteUrlProcessor' => '/core/kernel/managers/rewrite_url_processor.php', 'kScheduledTaskManager' => '/core/kernel/managers/scheduled_task_manager.php', 'kSearchHelper' => '/core/units/helpers/search_helper.php', 'kSectionsHelper' => '/core/units/helpers/sections_helper.php', 'kSerializedFormatter' => '/core/kernel/utility/formatters/serialized_formatter.php', 'kSocket' => '/core/kernel/utility/socket.php', 'kSubscriptionAnalyzer' => '/core/units/system_event_subscriptions/system_event_subscription_tp.php', 'kSubscriptionItem' => '/core/kernel/managers/subscription_manager.php', 'kSubscriptionManager' => '/core/kernel/managers/subscription_manager.php', 'kSystemConfig' => '/core/kernel/utility/system_config.php', 'kSystemConfigException' => '/core/kernel/utility/system_config.php', 'kTCPDFRenderer' => '/core/units/pdf/pdf_renderer_tcpdf.php', 'kTagProcessor' => '/core/kernel/processors/tag_processor.php', 'kTempHandlerSubTable' => '/core/kernel/utility/temp_handler.php', 'kTempHandlerTable' => '/core/kernel/utility/temp_handler.php', 'kTempHandlerTopTable' => '/core/kernel/utility/temp_handler.php', 'kTempTablesHandler' => '/core/kernel/utility/temp_handler.php', 'kThemesHelper' => '/core/units/helpers/themes_helper.php', 'kUnitConfig' => '/core/kernel/utility/unit_config.php', 'kUnitConfigCloner' => '/core/kernel/utility/unit_config_cloner.php', 'kUnitConfigReader' => '/core/kernel/utility/unit_config_reader.php', 'kUnitFormatter' => '/core/kernel/utility/formatters/unit_formatter.php', 'kUpgradeHelper' => '/core/install/upgrade_helper.php', 'kUploadFormatter' => '/core/kernel/utility/formatters/upload_formatter.php', 'kUploadHelper' => '/core/units/helpers/upload_helper.php', 'kUploaderException' => '/core/units/helpers/upload_helper.php', 'kUrlManager' => '/core/kernel/managers/url_manager.php', 'kUrlProcessor' => '/core/kernel/managers/url_processor.php', 'kUtil' => '/core/kernel/globals.php', 'kValidator' => '/core/kernel/utility/validator.php', 'kXMLHelper' => '/core/units/helpers/xml_helper.php', 'kXMLNode' => '/core/units/helpers/xml_helper.php', 'kXMLNode5' => '/core/units/helpers/xml_helper5.php', 'kZendPDFRenderer' => '/core/units/pdf/pdf_renderer_zend.php', 'kiCacheable' => '/core/kernel/interfaces/cacheable.php', ), 'class_info' => array( + 'AbstractCategoryItemRouter' => array( + 'type' => 1, + 'modifiers' => 1, + 'extends' => array( + 0 => 'AbstractRouter', + ), + ), + 'AbstractReviewRouter' => array( + 'type' => 1, + 'modifiers' => 1, + 'extends' => array( + 0 => 'AbstractRouter', + ), + ), + 'AbstractRouter' => array( + 'type' => 1, + 'modifiers' => 1, + 'extends' => array( + 0 => 'kBase', + ), + ), 'AdminEventsHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'AdminTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'AjaxFormHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'ApcCacheHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kCacheHandler', ), ), 'BackupHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'BaseSession' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'BaseSessionStorage' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBBase', ), ), 'CacheSettings' => array( 'type' => 1, 'modifiers' => 0, ), 'CaptchaEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kEventHandler', ), ), 'CategoriesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'CategoriesItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'CategoriesTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'CategoryHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), - 'CategoryItemRewrite' => array( - 'type' => 1, - 'modifiers' => 0, - 'extends' => array( - 0 => 'kHelper', - ), - ), 'CategoryItemsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'CategoryItemsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'CategoryPermissionRebuild' => array( 'type' => 1, 'modifiers' => 0, ), 'ChangeLog' => array( 'type' => 1, 'modifiers' => 0, ), 'ChangeLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ChangeLogTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ColumnSet' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'ConfigSearchEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ConfigSearchTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ConfigurationEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ConfigurationItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'ConfigurationTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ConfigurationValidator' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kValidator', ), ), 'ContentEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ContentTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'CoreUpgrades' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kUpgradeHelper', ), ), 'CountryStateEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'CssMinifyHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'CustomDataEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'CustomFieldsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'CustomFieldsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'Debugger' => array( 'type' => 1, 'modifiers' => 0, ), 'DebuggerUtil' => array( 'type' => 1, 'modifiers' => 0, ), 'DeploymentHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'DraftEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'EditPickerHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'EmailDelivery' => array( 'type' => 1, 'modifiers' => 0, ), 'EmailLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'EmailLogStatus' => array( 'type' => 1, 'modifiers' => 0, ), 'EmailLogTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'EmailQueueEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'EmailQueueTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'EmailTemplate' => array( 'type' => 1, 'modifiers' => 0, ), 'EmailTemplateEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'EmailTemplateTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'FakeCacheHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kCacheHandler', ), ), 'FavoritesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FckEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FckTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'FileEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FileHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'FileTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'FormFieldEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FormFieldsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'FormSubmissionHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'FormSubmissionTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'FormSubmissionsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FormsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'FormsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'GeoCodeHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'GroupTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'GroupsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'IDBConnection' => array( 'type' => 2, ), 'ImageEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ImageHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'ImageTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ImagesItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'InPortalPrerequisites' => array( 'type' => 1, 'modifiers' => 0, ), 'InpCustomFieldsHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\ClassDetector' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'PhpParser\\NodeVisitorAbstract', ), ), 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\ClassMapBuilder' => array( 'type' => 1, 'modifiers' => 0, ), 'Intechnic\\InPortal\\Core\\kernel\\utility\\ClassDiscovery\\CodeFolderFilterIterator' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'RecursiveFilterIterator', ), ), 'ItemFilterEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ItemFilterTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'JSONHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'JsMinifyHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'Language' => array( 'type' => 1, 'modifiers' => 0, ), 'LanguageImportHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'LanguagesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'LanguagesItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'LanguagesTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'LeftJoinOptimizer' => array( 'type' => 1, 'modifiers' => 0, ), 'ListHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'LoginResult' => array( 'type' => 1, 'modifiers' => 0, ), 'MInputHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'MailboxHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'MailingList' => array( 'type' => 1, 'modifiers' => 0, ), 'MailingListEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'MailingListHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'MailingListTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), + 'MainRouter' => array( + 'type' => 1, + 'modifiers' => 0, + 'extends' => array( + 0 => 'AbstractRouter', + ), + ), 'MaintenanceMode' => array( 'type' => 1, 'modifiers' => 0, ), 'MassImageResizer' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'MemcacheCacheHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kCacheHandler', ), ), 'MenuHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'MimeDecodeHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'MinifyHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'ModuleDeploymentLog' => array( 'type' => 1, 'modifiers' => 0, ), 'ModuleDeploymentLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ModulesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ModulesTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'NParser' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'NParserCompiler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'POP3Helper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'PageHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'PageRevisionEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PageRevisionTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'Params' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'ParserException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Exception', ), ), 'PasswordHash' => array( 'type' => 1, 'modifiers' => 0, ), 'PasswordHashingMethod' => array( 'type' => 1, 'modifiers' => 0, ), 'PermissionTypeEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PermissionsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PermissionsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'PhraseTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'PhrasesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PriorityEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PromoBlockEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PromoBlockGroupEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'PromoBlockGroupTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'PromoBlockTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'PromoBlockType' => array( 'type' => 1, 'modifiers' => 0, ), 'RatingHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'RelatedSearchEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'RelatedSearchTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'RelationshipEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'RelationshipTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ReviewsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ReviewsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ScheduledTask' => array( 'type' => 1, 'modifiers' => 0, ), 'ScheduledTaskEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SelectorsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SelectorsItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'SelectorsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'Session' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'BaseSession', ), ), 'SessionLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SessionStorage' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'BaseSessionStorage', ), ), 'SiteConfigEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kEventHandler', ), ), 'SiteConfigHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'SiteConfigTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kTagProcessor', ), ), 'SiteDomainEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SiteHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'SkinEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SkinHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'SpamHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'SpamReportEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SpamReportTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'StatisticsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'StatisticsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'StorageEngine' => array( 'type' => 1, 'modifiers' => 0, ), 'StylesheetsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'StylesheetsItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'SubmissionFormField' => array( 'type' => 1, 'modifiers' => 0, ), 'SubmissionLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SubmissionLogTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'SystemEventSubscriptionEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SystemEventSubscriptionTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'SystemLogEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'SystemLogTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'TemplateHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'TemplatesCache' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'ThemeFileEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ThemeItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'ThemesEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ThemesTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'ThesaurusEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'ThesaurusTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'TranslationSaveMode' => array( 'type' => 1, 'modifiers' => 0, ), 'TranslatorEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'TranslatorTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'UnitConfigDecorator' => array( 'type' => 1, 'modifiers' => 0, ), 'UserGroupsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'UserHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'UserProfileEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'UserProfileTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'UserType' => array( 'type' => 1, 'modifiers' => 0, ), 'UsersEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'UsersItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'UsersSyncronize' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'UsersSyncronizeManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'UsersTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'VisitsEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'VisitsList' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBList', ), ), 'VisitsTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'XCacheCacheHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kCacheHandler', ), ), 'XMLIterator' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Iterator', ), ), '_BlockTag' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), '_Tag_Cache' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_Capture' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_Tag_DefineElement', ), ), '_Tag_Comment' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_Compress' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_DefaultParam' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_DefineElement' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_If' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_IfDataExists' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_IfNot' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_Tag_If', ), ), '_Tag_Include' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_Param' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_RenderElement' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_Tag_DefineElement', ), ), '_Tag_RenderElements' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), '_Tag_SetParam' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => '_BlockTag', ), ), 'clsCachedPermissions' => array( 'type' => 1, 'modifiers' => 0, ), 'clsRecursionStack' => array( 'type' => 1, 'modifiers' => 0, ), 'fckFCKHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kApplication' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kiCacheable', ), ), 'kArray' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kBase' => array( 'type' => 1, 'modifiers' => 0, ), 'kBracketsHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCCDateFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kCSSDefaults' => array( 'type' => 1, 'modifiers' => 0, ), 'kCSVHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCache' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kCacheHandler' => array( 'type' => 1, 'modifiers' => 1, ), 'kCacheManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kCaptchaHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCatDBEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBEventHandler', ), ), 'kCatDBItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBItem', ), ), 'kCatDBItemExportHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCatDBList' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBList', ), ), 'kCatDBTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBTagProcessor', ), ), 'kChangesFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kChartHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kClipboardHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kColumnPickerHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCookieHasher' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kCountHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCountryStatesHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCronField' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kCronHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCurlHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kCustomFieldFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kDBBase' => array( 'type' => 1, 'modifiers' => 1, 'extends' => array( 0 => 'kBase', ), ), 'kDBConnection' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'IDBConnection', ), ), 'kDBConnectionDebug' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBConnection', ), ), 'kDBEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kEventHandler', ), ), 'kDBItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBBase', ), ), 'kDBList' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kDBBase', 1 => 'Iterator', 2 => 'Countable', ), ), 'kDBLoadBalancer' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'IDBConnection', ), ), 'kDBTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kTagProcessor', ), ), 'kDateFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kEmail' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kEmailSendingHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kEmailTemplateHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kErrorHandlerStack' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHandlerStack', ), ), 'kEvent' => array( 'type' => 1, 'modifiers' => 2, 'extends' => array( 0 => 'kBase', ), ), 'kEventHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kEventManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kExceptionHandlerStack' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHandlerStack', ), ), 'kFactory' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kFactoryException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Exception', ), ), 'kFilenamesHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kFilesizeFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kHTTPQuery' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Params', ), ), 'kHandlerStack' => array( 'type' => 1, 'modifiers' => 1, 'extends' => array( 0 => 'kBase', ), ), 'kHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kHookManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kInstallToolkit' => array( 'type' => 1, 'modifiers' => 0, ), 'kInstallator' => array( 'type' => 1, 'modifiers' => 0, ), 'kLEFTFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kLogger' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kMainTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kTagProcessor', ), ), 'kModulesHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kMultiLanguage' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kMultiLanguageHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kMultipleFilter' => array( 'type' => 1, 'modifiers' => 0, ), 'kMySQLQuery' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Iterator', 1 => 'Countable', 2 => 'SeekableIterator', ), ), 'kMySQLQueryCol' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kMySQLQuery', ), ), 'kNavigationBar' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kNoPermissionException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kRedirectException', ), ), 'kOpenerStack' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kOptionsFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kPDFElemFactory' => array( 'type' => 1, 'modifiers' => 0, ), 'kPDFElement' => array( 'type' => 1, 'modifiers' => 0, ), 'kPDFHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kPDFImage' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFElement', ), ), 'kPDFLine' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFElement', ), ), 'kPDFRenderer' => array( 'type' => 1, 'modifiers' => 0, ), 'kPDFStylesheet' => array( 'type' => 1, 'modifiers' => 0, ), 'kPDFTable' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFElement', ), ), 'kPDFTableRow' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFElement', ), ), 'kPDFTextElement' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFElement', ), ), 'kPasswordFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kPermCacheUpdater' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kPermissionsHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kPhraseCache' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kPictureFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kUploadFormatter', ), ), 'kPlainUrlProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kUrlProcessor', ), ), 'kPriorityHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kRecursiveHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kRedirectException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Exception', ), ), 'kRequestManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kRewriteUrlProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kUrlProcessor', ), ), 'kScheduledTaskManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kSearchHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kSectionsHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kSerializedFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kSocket' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kSubscriptionAnalyzer' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kSubscriptionItem' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kSubscriptionManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kSystemConfig' => array( 'type' => 1, 'modifiers' => 0, ), 'kSystemConfigException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Exception', ), ), 'kTCPDFRenderer' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFRenderer', ), ), 'kTagProcessor' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kTempHandlerSubTable' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kTempHandlerTable', ), ), 'kTempHandlerTable' => array( 'type' => 1, 'modifiers' => 1, 'extends' => array( 0 => 'kBase', ), ), 'kTempHandlerTopTable' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kTempHandlerTable', ), ), 'kTempTablesHandler' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kThemesHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kUnitConfig' => array( 'type' => 1, 'modifiers' => 0, ), 'kUnitConfigCloner' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kUnitConfigReader' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', 1 => 'kiCacheable', ), ), 'kUnitFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kUpgradeHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kUploadFormatter' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kFormatter', ), ), 'kUploadHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kUploaderException' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'Exception', ), ), 'kUrlManager' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kUrlProcessor' => array( 'type' => 1, 'modifiers' => 1, 'extends' => array( 0 => 'kBase', ), ), 'kUtil' => array( 'type' => 1, 'modifiers' => 0, ), 'kValidator' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kBase', ), ), 'kXMLHelper' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kHelper', ), ), 'kXMLNode' => array( 'type' => 1, 'modifiers' => 0, ), 'kXMLNode5' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kXMLNode', 1 => 'IteratorAggregate', ), ), 'kZendPDFRenderer' => array( 'type' => 1, 'modifiers' => 0, 'extends' => array( 0 => 'kPDFRenderer', ), ), 'kiCacheable' => array( 'type' => 2, ), ), ); Index: branches/5.3.x/tools/debug_sample.php =================================================================== --- branches/5.3.x/tools/debug_sample.php (revision 16170) +++ branches/5.3.x/tools/debug_sample.php (revision 16171) @@ -1,60 +1,60 @@ <?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. */ // define('MAINTENANCE_MODE_FRONT', MaintenanceMode::NONE); // Set to MaintenanceMode::SOFT for SOFT Maintenance mode, set to MaintenanceMode::HARD for HARD Maintenance mode (no DB load) // define('MAINTENANCE_MODE_ADMIN', MaintenanceMode::NONE); // Set to MaintenanceMode::SOFT for SOFT Maintenance mode, set to MaintenanceMode::HARD for HARD Maintenance mode (no DB load) // define('MAINTENANCE_MODE_IPS', ''); // Define IP addresses/hosts, which will be able to continue accessing website // define('DBG_REQUEST_LOG', 1);// Log all user requests to site into "System Log" // define('DBG_ZEND_PRESENT', 0); // Set to 0 to debug debugger (because debugger automatically got disabled during zend debug sessions) // define('SA_IP', '173.9.192.210'); // Define IP addresses, from which super admin are allowed to login // define('DBG_CAPTURE_STATISTICS', 1); // Capture performance statistics // define('DBG_MAX_SQL_TIME', 2); // Maximal allowed sql execution time in seconds, all sqls above this become slow sqls // define('DBG_CURL', 1); // Log all curl requests to CurlLog database table // define('OVERRIDE_EMAIL_RECIPIENTS', ''); // Overwrites email recipients with some-email@domain-name.com or @domain-name.com $dbg_options = Array ( // !!! DEBUG MODE will be off if IP does not match !!! 'DBG_IP' => '192.168.1.1/24;192.168.2.1/24;173.9.192.210', // !!!REQUIRED!!! Define IP addreses, which are allowed to use debugger (semicolon separated) 'DEBUG_MODE' => 1, // Debug mode is allowed/disabled (note: set DBG_IP to use this one) // 'DBG_LOCAL_BASE_PATH' => 'w:', // Folder name on mapped drive, where site resides // 'DBG_TOOLBAR_BUTTONS' => 1, // Show "Show Debugger" & "Refresh Frame" buttons (on front) // 'DBG_USE_HIGHLIGHT' => 0, // Use "highlight_string" php function for debugger output formatting // 'DBG_RAISE_ON_WARNINGS' => 1, // Show debugger output in case of any non-fatal error 'DBG_SQL_PROFILE' => defined('IS_INSTALL') && IS_INSTALL ? 0 : 1, // Profile SQL queries // 'DBG_SQL_EXPLAIN' => 1, // Explain every SQL query, that is retrieving data 'DBG_SQL_FAILURE' => isset($GLOBALS['pathtoroot']) && defined('IS_INSTALL') && IS_INSTALL ? 0 : 1, // treat sql errors as fatal errors except for installation process // 'DBG_SQL_SERVERINFO' => 1, // Display database server info next each sql query in debugger 'DBG_SHOW_HTTPQUERY' => 1, // Show http query content (parsed user submit, GPC) 'DBG_SHOW_SESSIONDATA' => 1, // Show session data (at script finish) // 'DBG_SHOW_PERSISTENTDATA' => 1, // Show persistent session data (at script finish) // 'DBG_FORCE_THEME' => 1, // Use this theme_id instead of one in url 'DBG_PHRASES' => 1, // Add ability to translate phrases on the fly // 'DBG_WINDOW_WIDTH' => 700, // Set custom debugger layer width (in pixels) // 'DBG_REDIRECT' => 1, // Show links with redirect url instead of performing it (useful in events debugging) // 'DBG_VALIDATE_CONFIGS' => 1, // Check that config fields match ones from database // 'DBG_SHOW_TAGS' => 1, // Show tags beeing processed // 'DBG_SHOW_TREE_PRIORITY'=> 1, // Show tree node priority // 'DBG_SKIP_AJAX' => 1, // Don't debug AJAX requests // 'DBG_PAYMENT_GW' => 1, // All requests to payment gateways goes in TEST MODE // 'DBG_IMAGE_RECOVERY' => 1, // Don't replace missing images with noimage.gif // 'DBG_SQL_MODE' => 'TRADITIONAL', // Extra control over sql syntax & data from MySQL server side // 'DBG_RESET_ROOT' => 1, // Shows "root" user password reset link on Admin Console login screen -// 'DBG_CACHE_URLS' => 0, // Cache urls, that are build by rewrite listeners +// 'DBG_CACHE_URLS' => 0, // Cache urls, that are build by routers // 'DBG_SHORTCUT' => 'F12' // Defines debugger activation shortcut (any symbols or Ctrl/Alt/Shift are allowed, e.g. Ctrl+Alt+F12) - ); \ No newline at end of file + );