Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Mon, Feb 10, 6:14 PM

in-portal

Index: branches/5.0.x/core/kernel/event_manager.php
===================================================================
--- branches/5.0.x/core/kernel/event_manager.php (revision 12524)
+++ branches/5.0.x/core/kernel/event_manager.php (revision 12525)
@@ -1,794 +1,811 @@
<?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.net/license/ for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
define('hBEFORE', 1);
define('hAFTER', 2);
define('reBEFORE', 1);
define('reAFTER', 2);
class kEventManager extends kBase {
/**
* Connection to database
*
* @var kDBConnection
* @access public
*/
var $Conn;
/**
* Cache of QueryString parameters
* from config, that are represented
* in enviroment variable
*
* @var Array
*/
var $queryMaps = Array();
/**
* Build events registred for
* pseudo classes. key - pseudo class
* value - event name
*
* @var Array
* @access private
*/
var $buildEvents=Array();
/**
* Events, that should be run before parser initialization
*
* @var Array
*/
var $beforeRegularEvents = Array();
/**
* Events, that should be run after parser initialization
*
* @var Array
*/
var $afterRegularEvents = Array();
/**
* Holds before hooks
* key - prefix.event (to link to)
* value - hooked event info
*
* @var Array
* @access private
*/
var $beforeHooks=Array();
/**
* Holds after hooks
* key - prefix.event (to link to)
* value - hooked event info
*
* @var Array
* @access private
*/
var $afterHooks = Array();
var $recursionStack = Array();
function kEventManager()
{
parent::kBase();
$this->Conn =& $this->Application->GetADODBConnection();
}
/**
* Returns information about registered regular events
*
* @param bool $from_cache
*
* @return Array
*/
function getRegularEvents($from_cache = false)
{
static $agents = null;
if ($from_cache) {
return Array (
reBEFORE => $this->beforeRegularEvents,
reAFTER => $this->afterRegularEvents,
);
}
else {
if (!isset($agents)) {
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('agent', 'TableName') . '
WHERE Status = ' . STATUS_ACTIVE . ' AND LastRunStatus <> ' . AGENT_LAST_RUN_RUNNING;
$all_agents = $this->Conn->Query($sql);
$agents = Array (
reBEFORE => Array (),
reAFTER => Array (),
);
foreach ($all_agents as $agent_data) {
$agents[ $agent_data['RunMode'] ][ $agent_data['AgentName'] ] = Array (
'EventName' => $agent_data['Event'],
'RunInterval' => (int)$agent_data['RunInterval'],
'LastRunOn' => (int)$agent_data['LastRunOn'],
'NextRunOn' => (int)$agent_data['NextRunOn'],
'Status' => $agent_data['Status'],
);
}
}
return $agents;
}
}
/**
* Set's new enviroment parameter mappings
* between their names as application vars
*
* @param Array $new_query_maps
* @access public
*/
/*function setQueryMaps($new_query_maps)
{
$this->queryMaps = $new_query_maps;
}*/
/**
* Adds new query map to already parsed query maps
*
* @param string $prefix
*/
function setQueryMap($prefix_special)
{
list($prefix) = explode('.', $prefix_special);
$query_map = $this->getQueryMap($prefix);
if ($query_map) {
$this->queryMaps[$prefix_special] = $query_map;
}
else {
unset($this->queryMaps[$prefix]);
}
// 'passed' is set later in ProcessRequest - do we really need it here? (it breakes HTTPQuery initialization...)
// $this->Application->SetVar('passed', implode(',', array_keys($this->queryMaps)) );
return $query_map;
}
/**
* 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
*/
function getQueryMap($prefix)
{
$query_map = $this->Application->getUnitOption($prefix, 'QueryString');
if (!$query_map) {
if (preg_match('/(.*?)-(.*)/', $prefix, $regs)) {
// #prefix - format for configs, that are only cloned & optionally used for hooking (without # is old format)
$check_prefixes = Array ('#'.$regs[2], $regs[2]);
foreach ($check_prefixes as $check_prefix) {
if ($this->Application->UnitConfigReader->prefixRegistred($check_prefix)) {
return $this->Application->getUnitOption($check_prefix, 'QueryString');
}
}
}
}
return $query_map;
}
/**
* Registers new regular event
*
* @param string $short_name name to be used to store last maintenace run info
* @param string $event_name
* @param int $run_interval run interval in seconds
* @param int $type before or after regular event
*/
function registerRegularEvent($short_name, $event_name, $run_interval, $type = reBEFORE, $status = STATUS_ACTIVE)
{
if($type == reBEFORE)
{
$this->beforeRegularEvents[$short_name] = Array('EventName' => $event_name, 'RunInterval' => $run_interval, 'Status' => $status);
}
else
{
$this->afterRegularEvents[$short_name] = Array('EventName' => $event_name, 'RunInterval' => $run_interval, 'Status' => $status);
}
}
function registerBuildEvent($pseudo_class,$build_event_name)
{
$this->buildEvents[$pseudo_class]=$build_event_name;
}
/**
* Returns build event by pseudo class
* name if any defined in config
*
* @param string $pseudo_class
* @return kEvent
* @access public
*/
function &getBuildEvent($pseudo_class)
{
$false = false;
if( !isset($this->buildEvents[$pseudo_class]) ) return $false;
$event = new kEvent();
$event->Name=$this->buildEvents[$pseudo_class];
$event->MasterEvent=null;
return $event;
}
/**
* Check if event is called twice, that causes recursion
*
* @param kEvent $event
*/
function isRecursion(&$event)
{
$event_key = $event->getPrefixSpecial().':'.$event->Name;
return in_array($event_key, $this->recursionStack) ? true : false;
}
function pushEvent(&$event)
{
$event_key = $event->getPrefixSpecial().':'.$event->Name;
array_push($this->recursionStack, $event_key);
}
function popEvent()
{
array_pop($this->recursionStack);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @access public
*/
function HandleEvent(&$event)
{
if ($this->isRecursion($event)) {
return true;
}
$this->pushEvent($event);
if( !$this->Application->prefixRegistred($event->Prefix) )
{
$unit_config_reader =& $this->Application->recallObject('kUnitConfigReader');
$unit_config_reader->loadConfig($event->Prefix);
if( !$this->Application->prefixRegistred($event->Prefix) )
{
trigger_error('Prefix <b>'.$event->Prefix.'</b> not registred (requested event <b>'.$event->Name.'</b>)', E_USER_NOTICE);
return false;
}
}
if (!$event->SkipBeforeHooks) {
$this->processHooks($event, hBEFORE);
if ($event->status == erFATAL) return true;
}
$event_handler =& $this->Application->recallObject($event->Prefix.'_EventHandler');
/* @var $event_handler kEventHandler */
$event_handler->processEvent($event);
if ($event->status == erFATAL) return true;
if (!$event->SkipAfterHooks) {
$this->processHooks($event, hAFTER);
}
$this->popEvent();
return true;
}
/**
* Returns event names given in POST
*
* @return Array
*/
function _getEventsFromPost()
{
$events = $this->Application->GetVar('events');
if ($events === false) {
return Array ();
}
$ret = Array ();
foreach ($events as $prefix_special => $event_name) {
if (!$event_name) {
continue;
}
if (is_array($event_name)) {
// HTML-input names like "events[prefix.special][event_name]", input value don't matter
$event_name = key($event_name);
$this->Application->SetVar($prefix_special . '_event', $event_name);
}
// HTML-input names like "events[prefix.special]", input value is event name
$ret[$prefix_special] = $event_name;
}
return $ret;
}
function ProcessRequest()
{
$this->processOpener();
// 1. get events from $_POST
$events = $this->_getEventsFromPost();
// 2. if nothing there, then try to find them in $_GET
if ($this->queryMaps && !$events) {
// if we got $_GET type submit (links, not javascript)
foreach ($this->queryMaps as $prefix_special => $query_map) {
$query_map = array_flip($query_map);
if (isset($query_map['event'])) {
$event_name = $this->Application->GetVar($prefix_special . '_event');
if ($event_name) {
$events[$prefix_special] = $event_name;
}
}
}
// used ?
$actions = $this->Application->GetVar('do');
if ($actions) {
list ($prefix, $event_name) = explode('_', $actions);
if ($event_name) {
$events[$prefix] = $event_name;
}
}
}
// 3. store all prefixes passed before event processing, because they are used by GetTopmostPrefix
$all_passed = explode(',', $this->Application->GetVar('passed'));
foreach ($events as $prefix_special => $event_name) {
if (!$event_name) {
continue;
}
if ($this->Application->IsAdmin()) {
array_push($all_passed, $prefix_special);
}
else {
// don't add special on front-end because of category item list special is autogenerated
$prefix_special = explode('.', $prefix_special);
array_push($all_passed, $prefix_special[0]);
}
}
$this->Application->SetVar('all_passed', implode(',', $all_passed));
foreach ($events as $prefix_special => $event_name) {
$event = new kEvent();
$event->Name = $event_name;
$event->Prefix_Special = $prefix_special;
$prefix_special = explode('.',$prefix_special);
$event->Prefix = $prefix_special[0];
// array_push($passed, $prefix_special[0]);
$event->Special = isset($prefix_special[1]) ? $prefix_special[1] : '';
$event->redirect_params = Array('opener' => 's', 'pass' => 'all');
$event->redirect = true;
$event_handler =& $this->Application->recallObject($event->Prefix.'_EventHandler');
/* @var $event_handler kEventHandler */
if (preg_match('/(.*?)-(.*)/', $event->Prefix, $regs) && $this->Application->UnitConfigReader->prefixRegistred($regs[1])) {
// this is event from cloned config -> load parent config to create valid clone
$this->Application->UnitConfigReader->loadConfig($regs[1]);
$this->Application->HandleEvent( new kEvent($regs[1].':OnAfterConfigRead') );
}
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
if (($this->Application->RecallVar('user_id') == -1) || $event_handler->CheckPermission($event)) {
$this->HandleEvent($event);
}
if ($event->status == erSTOP) {
// event requested to stop processing at this point
safeDefine('DBG_SKIP_REPORTING', 1);
$this->Application->Session->SaveData();
exit;
}
if ($event->status == erPERM_FAIL) {
// should do redirect but to no_permissions template
$event->redirect = $this->Application->IsAdmin() ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
$event->redirect_params['pass'] = 'm';
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$event->redirect_params['m_cat_id'] = $themes_helper->getPageByTemplate($event->redirect);
// restore stuff, that processOpener() changed
$wid = $this->Application->GetVar('m_wid');
$this->Application->RestoreVar(rtrim('opener_stack_'.$wid, '_'));
// don't save last_template, because no_permission template does js history.back and could cause invalid opener_stack content
$this->Application->SetVar('skip_last_template', 1);
}
if ( ($event->status == erSUCCESS || $event->status == erPERM_FAIL) && ($event->redirect === true || strlen($event->redirect) > 0)) {
// we need to pass category if the action was submitted to self-template, with the category passed
// and it has not explicly set redirect template or pass_cateogry param
if ($event->redirect === true && !array_key_exists('pass_category', $event->redirect_params) && $this->Application->GetVar('m_cat_id')) {
$event->redirect_params['pass_category'] = 1;
}
$wid = $this->Application->GetVar('m_wid');
if ($wid && $event->redirect_params['opener'] == 'u') {
$event->redirect_params['opener'] = 's'; // because Application->HREF will react differently when 'opener' = 'u'
$event->redirect = defined('CLOSE_POPUP_TPL') ? CLOSE_POPUP_TPL : 'incs/close_popup';
}
$this->Application->Redirect($event->redirect, $event->redirect_params, null, $event->redirect_script);
}
}
$this->Application->SetVar('events', $events);
$this->Application->SetVar('passed', implode(',', $all_passed));
}
function processOpener()
{
$wid = $this->Application->GetVar('m_wid');
$opener_stack = $this->Application->RecallVar(rtrim('opener_stack_'.$wid, '_'));
$opener_stack = $opener_stack ? unserialize($opener_stack) : Array();
$opener_action = $this->Application->GetVar('m_opener');
switch ($opener_action) {
case 'r': // "reset" opener stack
$opener_stack = Array();
break;
case 'd': // "down/push" new template to opener stack, deeplevel++
if ($this->Application->GetVar('front')) {
$front_session =& $this->Application->recallObject('Session.front');
array_push($opener_stack, '../'.$front_session->RecallVar('last_template') );
}
else {
array_push($opener_stack, $this->Application->RecallVar('last_template') );
}
break;
case 'u': // "up/pop" last template from opener stack, deeplevel--
array_pop($opener_stack);
break;
case 'p': // pop-up - generate new wid
$parent_wid = $this->Application->GetVar('m_wid'); // window_id of popup's parent window
$popup_wid = (int)$this->Application->RecallVar('last_wid') + 1;
$this->Application->StoreVar('last_wid', $popup_wid);
$this->Application->SetVar('m_wid', $popup_wid);
if ($this->Application->GetVar('front')) {
$front_session =& $this->Application->recallObject('Session.front');
$last_template = $front_session->RecallVar(rtrim('last_template_popup_'.$parent_wid, '_'));
$last_template = '../'.$last_template;
}
else {
if ($this->Application->GetVar('merge_opener_stack')) {
// get last template from parent (that was closed) window opener stack
$parent_opener_stack_name = rtrim('opener_stack_' . $parent_wid, '_');
$parent_opener_stack = unserialize( $this->Application->RecallVar($parent_opener_stack_name) );
$last_template = array_pop($parent_opener_stack);
if ($parent_opener_stack) {
$this->Application->StoreVar($parent_opener_stack_name, serialize($parent_opener_stack));
}
else {
$this->Application->RemoveVar($parent_opener_stack_name);
}
}
else {
$last_template = $this->Application->RecallVar(rtrim('last_template_popup_'.$parent_wid, '_'));
}
}
$opener_stack = Array ( $last_template );
$this->Application->SetVar('m_opener', 's');
$wid = $popup_wid;
/*// store window relations
$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
$window_relations[$popup_wid] = $parent_wid;
$this->Application->StoreVar('window_relations', serialize($window_relations));*/
break;
default: // "s/0," stay on same deep level
break;
}
$this->Application->SetVar('m_opener', 's');
$this->Application->StoreVar(rtrim('opener_stack_'.$wid, '_'), serialize($opener_stack), !$opener_stack); // empty stack is optional
}
/**
* Allows to add new element to opener stack
*
* @param string $template
* @param Array $params
* @param string $pass
*/
function openerStackPush($template, $params, $pass = 'all', $wid = null)
{
if (!isset($wid)) {
$wid = $this->Application->GetVar('m_wid');
}
/*// get parent window wid, when this was popup
$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
$wid = array_key_exists($wid, $window_relations) ? $window_relations[$wid] : false;*/
// get opener stack
$stack_name = rtrim('opener_stack_' . $wid, '_');
$opener_stack = $this->Application->RecallVar($stack_name);
$opener_stack = $opener_stack ? unserialize($opener_stack) : Array ();
// change opener stack
$default_params = Array ('m_opener' => 'u', '__URLENCODE__' => 1);
+
+ if (!$this->Application->ConfigValue('UsePopups') && $wid) {
+ // remove wid to show combined header block in editing window
+ $default_params['m_wid'] = '';
+
+ // move last popup's opener stack elemenent to main window's opener stack
+ if ($opener_stack) {
+ list ($index_file, $env) = explode('|', $opener_stack[ count($opener_stack) - 1 ], 2);
+ $main_params = $this->Application->HttpQuery->processQueryString($env, 'pass');
+ $main_template = $main_params['t'];
+ unset($main_params['t']);
+
+ $main_params = array_merge($main_params, $default_params);
+ $this->openerStackPush($main_template, $main_params, $main_params['pass'], '');
+ }
+ }
+
$redirect_params = array_merge_recursive2($default_params, $params);
$new_level = $this->Application->BuildEnv($template, $redirect_params, $pass, true);
array_push($opener_stack, 'index.php|' . ltrim($new_level, ENV_VAR_NAME . '=') );
$this->Application->StoreVar($stack_name, serialize($opener_stack));
}
/**
* Allows to change last element in opener stack
*
* @param string $template
* @param Array $params
* @param string $pass
*/
function openerStackChange($params = Array(), $pass_events = true, $wid = null)
{
if (!isset($wid)) {
$wid = $this->Application->GetVar('m_wid');
}
// get opener stack
$stack_name = rtrim('opener_stack_' . $wid, '_');
$opener_stack = $this->Application->RecallVar($stack_name);
$opener_stack = $opener_stack ? unserialize($opener_stack) : Array ();
// change opener stack
list ($index_file, $env) = explode('|', $opener_stack[ count($opener_stack) - 1 ], 2);
$vars = $this->Application->HttpQuery->processQueryString($env, 'pass');
$vars = array_merge_recursive2($vars, $params);
// save opener stack
$new_level = $this->Application->BuildEnv($vars['t'], $vars, $vars['pass'], $pass_events, false);
$opener_stack[ count($opener_stack) - 1 ] = $index_file . '|' . $new_level;
$this->Application->StoreVar($stack_name, serialize($opener_stack));
}
function registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional)
{
if ( !$this->Application->prefixRegistred($hookto_prefix) && $hookto_prefix != '*' ) {
if ($this->Application->isDebugMode()) {
trigger_error('Prefix <b>'.$hookto_prefix.'</b> doesn\'t exist when trying to hook from <b>'.$do_prefix.':'.$do_event.'</b>', E_USER_WARNING);
}
return;
}
$hookto_prefix_special = rtrim($hookto_prefix.'.'.$hookto_special, '.');
if ($mode == hBEFORE) {
$this->beforeHooks[strtolower($hookto_prefix_special.'.'.$hookto_event)][] = Array(
'DoPrefix' => $do_prefix,
'DoSpecial' => $do_special,
'DoEvent' => $do_event,
'Conditional' => $conditional,
);
}
elseif ($mode == hAFTER) {
$this->afterHooks[strtolower($hookto_prefix_special.'.'.$hookto_event)][] = Array(
'DoPrefix' => $do_prefix,
'DoSpecial' => $do_special,
'DoEvent' => $do_event,
'Conditional' => $conditional,
);
}
}
/**
* Enter description here...
*
* @param kEvent $event
* @param int $mode hBEFORE or hAFTER
* @param string $event_key
* @return Array
*/
function &_getHooks(&$event, $mode, $event_key = null)
{
$event_key = isset($event_key) ? $event_key : $event->Prefix_Special;
if ($mode == hBEFORE) {
$mode_hooks =& $this->beforeHooks;
}
else {
$mode_hooks =& $this->afterHooks;
}
if (!isset($mode_hooks[strtolower($event_key.'.'.$event->Name)])) {
$hooks = array();
return $hooks;
}
return $mode_hooks[strtolower($event_key.'.'.$event->Name)];
}
/**
* Enter description here...
*
* @param kEvent $event
* @param int $mode hBEFORE or hAFTER
*/
function processHooks(&$event, $mode)
{
// * - get hooks that are valid with any special of given prefix
$hooks = array_merge(
// given prefix, any special
$this->_getHooks($event, $mode, $event->Prefix.'.*'),
// given special, given special
$this->_getHooks($event, $mode),
// any prefix, any special
$this->_getHooks($event, $mode, '*.*'),
// any prefix, given special
$this->_getHooks($event, $mode, rtrim('*.' . $event->Special, '.'))
);
if ($hooks) {
foreach ($hooks as $hook) {
if ($hook['DoSpecial'] == '*') {
// use same special as master event
$hook['DoSpecial'] = $event->Special;
}
$prefix_special = rtrim($hook['DoPrefix'].'_'.$hook['DoSpecial'], '_');
if ( $hook['Conditional'] && !$this->Application->GetVar($prefix_special) ) {
continue;
}
if ($this->Application->prefixRegistred($hook['DoPrefix'])) {
// run hook only, when it's config is found
$hook_event = new kEvent( Array('name'=>$hook['DoEvent'],'prefix'=>$hook['DoPrefix'],'special'=>$hook['DoSpecial']) );
$hook_event->MasterEvent =& $event;
$this->HandleEvent($hook_event);
}
}
}
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @access public
*/
function setEvent($prefix_special,$event_name)
{
$actions =& $this->Application->recallObject('kActions');
$actions->Set('events['.$prefix_special.']',$event_name);
}
/**
* Run registred regular events with specified event type
*
* @param int $event_type
*/
function RunRegularEvents($event_type = reBEFORE, $from_cron=false)
{
if (defined('IS_INSTALL')) return ;
// if RegularEvents are set to run from cron
if (!$from_cron && $this->Application->ConfigValue('UseCronForRegularEvent')) return ;
$agents = $this->getRegularEvents();
$events_source = $agents[$event_type];
$user_id = $this->Application->RecallVar('user_id');
$this->Application->StoreVar('user_id', -1, true); // to prevent permission checking inside events, true for optional storage
foreach ($events_source as $short_name => $event_data) {
$next_run = $event_data['NextRunOn'];
$last_run = $event_data['LastRunOn'];
if ($next_run && ($next_run > adodb_mktime())) {
continue;
}
else {
$event = new kEvent($event_data['EventName']);
if (!$this->Application->prefixRegistred($event->Prefix)) {
// don't process agents, left from disabled modules
continue;
}
$start_time = adodb_mktime();
$fields_hash = Array (
'LastRunOn' => $start_time,
'LastRunStatus' => AGENT_LAST_RUN_RUNNING,
'NextRunOn' => $start_time + $event_data['RunInterval'],
);
$this->Conn->doUpdate(
$fields_hash,
$this->Application->getUnitOption('agent', 'TableName'),
'AgentName = ' . $this->Conn->qstr($short_name)
);
$event->redirect = false;
$this->Application->HandleEvent($event);
$now = adodb_mktime();
$next_run = $event_data['RunInterval'] ? $start_time + $event_data['RunInterval'] : $now;
while ($next_run < $now) {
// in case event execution took longer, then RunInterval (don't use <=, because RunInterval can be 0)
$next_run += $event_data['RunInterval'];
}
$fields_hash = Array (
'NextRunOn' => $next_run,
'RunTime' => round(($now - $start_time) / 60),
'LastRunStatus' => $event->status == erSUCCESS ? AGENT_LAST_RUN_SUCCEDED : AGENT_LAST_RUN_FAILED,
);
$this->Conn->doUpdate(
$fields_hash,
$this->Application->getUnitOption('agent', 'TableName'),
'AgentName = ' . $this->Conn->qstr($short_name)
);
}
}
$this->Application->StoreVar('user_id', $user_id, true); // true for optional
}
/**
* Allows to determine, that required event is beeing processed right now
*
* @param string $event_key Event name in format prefix[.special]:event_name
* @return bool
*/
function eventRunning($event_key)
{
return array_search($event_key, $this->recursionStack) !== false;
}
}
\ No newline at end of file
Index: branches/5.0.x/core/units/categories/categories_event_handler.php
===================================================================
--- branches/5.0.x/core/units/categories/categories_event_handler.php (revision 12524)
+++ branches/5.0.x/core/units/categories/categories_event_handler.php (revision 12525)
@@ -1,1979 +1,1982 @@
<?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.net/license/ for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class CategoriesEventHandler extends kDBEventHandler {
/**
* Allows to override standart permission mapping
*
*/
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 permissions of user
*
* @param kEvent $event
*/
function CheckPermission(&$event)
{
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 == 'OnPasteClipboard') {
// forces permission check to work by current category for "Paste In Category" operation
$category_id = $this->Application->GetVar('m_cat_id');
$this->Application->SetVar('c_id', $category_id);
}
return parent::CheckPermission($event);
}
/**
* Returns events, that require item-based (not just event-name based) permission check
*
* @return Array
*/
function _getMassPermissionEvents()
{
return Array (
'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove',
'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown',
'OnCut',
);
}
/**
* Returns category item IDs, that require permission checking
*
* @param kEvent $event
* @return string
*/
function _getPermissionCheckIDs(&$event)
{
if ($event->Name == 'OnSave') {
$selected_ids = implode(',', $this->getSelectedIDs($event, true));
if (!$selected_ids) {
$selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave)
}
}
else {
// OnEdit, OnMassDelete events, when items are checked in grid
$selected_ids = implode(',', $this->StoreSelectedIDs($event));
}
return $selected_ids;
}
/**
* Returns information used in permission checking
*
* @param kEvent $event
* @return Array
*/
function _getPermissionCheckInfo(&$event)
{
// when saving data from temp table to live table check by data from temp table
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
if ($event->Name == 'OnSave') {
$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix);
}
$sql = 'SELECT ' . $id_field . ', CreatedById, ParentId
FROM ' . $table_name . '
WHERE ' . $id_field . ' IN (' . $this->_getPermissionCheckIDs($event) . ')';
$items = $this->Conn->Query($sql, $id_field);
if (!$items) {
// when creating new category, then no IDs are stored in session
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list ($id, $fields_hash) = each($items_info);
if (array_key_exists('ParentId', $fields_hash)) {
$item_category = $fields_hash['ParentId'];
}
else {
$item_category = $this->Application->RecallVar('m_cat_id'); // saved in c:OnPreCreate event permission checking
}
$items[$id] = Array (
'CreatedById' => $this->Application->RecallVar('user_id'),
'ParentId' => $item_category,
);
}
return $items;
}
/**
* Set's mark, that root category is edited
*
* @param kEvent $event
*/
function OnEdit(&$event)
{
$category_id = $this->Application->GetVar($event->getPrefixSpecial().'_id');
$this->Application->StoreVar('IsRootCategory_'.$this->Application->GetVar('m_wid'), $category_id === '0');
parent::OnEdit($event);
}
/**
* 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();
$this->finalizePopup($event);
}
/**
* Apply system filter to categories list
*
* @param kEvent $event
*/
function SetCustomQuery(&$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.'Category
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 ($event->getEventParam('parent_cat_id') !== false) {
$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->findModule('Name', 'Core', 'RootCat');
}
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', TABLE_PREFIX.'Category.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight']);
}
}
else {
$object->addFilter('parent_filter', 'ParentId = '.$parent_cat_id);
}
}
$object->addFilter('perm_filter', 'PermId = 1'); // check for CATEGORY.VIEW permission
if ($this->Application->RecallVar('user_id') != -1) {
// apply permission filters to all users except "root"
$groups = explode(',',$this->Application->RecallVar('UserGroups'));
foreach ($groups as $group) {
$view_filters[] = 'FIND_IN_SET('.$group.', acl)';
}
$view_filter = implode(' OR ', $view_filters);
$object->addFilter('perm_filter2', $view_filter);
}
if (!$this->Application->IsAdmin()) {
// apply status filter only on front
$object->addFilter('status_filter', $object->TableName.'.Status = 1');
}
// process "types" and "except" parameters
$type_clauses = Array();
$types = $event->getEventParam('types');
$types = $types ? explode(',', $types) : Array ();
$except_types = $event->getEventParam('except');
$except_types = $except_types ? explode(',', $except_types) : Array ();
if (in_array('related', $types) || in_array('related', $except_types)) {
$related_to = $event->getEventParam('related_to');
if (!$related_to) {
$related_prefix = $event->Prefix;
}
else {
$sql = 'SELECT Prefix
FROM '.TABLE_PREFIX.'ItemTypes
WHERE ItemName = '.$this->Conn->qstr($related_to);
$related_prefix = $this->Conn->GetOne($sql);
}
$rel_table = $this->Application->getUnitOption('rel', 'TableName');
$item_type = (int)$this->Application->getUnitOption($event->Prefix, 'ItemType');
if ($item_type == 0) {
trigger_error('<strong>ItemType</strong> not defined for prefix <strong>' . $event->Prefix . '</strong>', E_USER_WARNING);
}
// process case, then this list is called inside another list
$prefix_special = $event->getEventParam('PrefixSpecial');
if (!$prefix_special) {
$prefix_special = $this->Application->Parser->GetParam('PrefixSpecial');
}
$id = false;
if ($prefix_special !== false) {
$processed_prefix = $this->Application->processPrefix($prefix_special);
if ($processed_prefix['prefix'] == $related_prefix) {
// printing related categories within list of items (not on details page)
$list =& $this->Application->recallObject($prefix_special);
/* @var $list kDBList */
$id = $list->GetID();
}
}
if ($id === false) {
// printing related categories for single item (possibly on details page)
if ($related_prefix == 'c') {
$id = $this->Application->GetVar('m_cat_id');
}
else {
$id = $this->Application->GetVar($related_prefix . '_id');
}
}
$p_item =& $this->Application->recallObject($related_prefix . '.current', null, Array('skip_autoload' => true));
$p_item->Load( (int)$id );
$p_resource_id = $p_item->GetDBField('ResourceId');
$sql = 'SELECT SourceId, TargetId FROM '.$rel_table.'
WHERE
(Enabled = 1)
AND (
(Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.')
OR
(Type = 1
AND (
(SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.')
OR
(TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.')
)
)
)';
$related_ids_array = $this->Conn->Query($sql);
$related_ids = Array();
foreach ($related_ids_array as $key => $record) {
$related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ];
}
if (count($related_ids) > 0) {
$type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).')';
$type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).')';
}
else {
$type_clauses['related']['include'] = '0';
$type_clauses['related']['except'] = '1';
}
$type_clauses['related']['having_filter'] = false;
}
if (in_array('category_related', $type_clauses)) {
$object->removeFilter('parent_filter');
$resource_id = $this->Conn->GetOne('
SELECT ResourceId FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
WHERE CategoryId = '.$parent_cat_id
);
$sql = 'SELECT DISTINCT(TargetId) FROM '.TABLE_PREFIX.'Relationship
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.'Relationship
WHERE TargetId = '.$resource_id.' AND TargetType = 1 AND Type = 1';
$related_cats2 = $this->Conn->GetCol($sql);
$related_cats2 = is_array($related_cats2) ? $related_cats2 : Array();
$related_cats = array_unique( array_merge( $related_cats2, $related_cats ) );
if ($related_cats) {
$type_clauses['category_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')';
$type_clauses['category_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')';
}
else
{
$type_clauses['category_related']['include'] = '0';
$type_clauses['category_related']['except'] = '1';
}
$type_clauses['category_related']['having_filter'] = false;
}
if (in_array('product_related', $types)) {
$object->removeFilter('parent_filter');
$product_id = $event->getEventParam('product_id') ? $event->getEventParam('product_id') : $this->Application->GetVar('p_id');
$resource_id = $this->Conn->GetOne('
SELECT ResourceId FROM '.$this->Application->getUnitOption('p', 'TableName').'
WHERE ProductId = '.$product_id
);
$sql = 'SELECT DISTINCT(TargetId) FROM '.TABLE_PREFIX.'Relationship
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.'Relationship
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';
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->SetComplexFilter($event, $type_clauses, implode(',', $types), implode(',', $except_types));
}
/**
* Returns current theme id
*
* @return int
*/
function _getCurrentThemeId()
{
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
return (int)$themes_helper->getCurrentThemeId();
}
/**
* Enter description here...
*
* @param kEvent $event
* @return int
*/
function getPassedID(&$event)
{
if (($event->Special == 'page') || ($event->Special == '-virtual') || ($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)) {
$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
WHERE
(
(NamedParentPath = ' . $this->Conn->qstr($template) . ') OR
(NamedParentPath = ' . $this->Conn->qstr('Content/' . $template) . ') OR
(IsSystem = 1 AND CachedTemplate = ' . $this->Conn->qstr($template) . ')
) AND (ThemeId = ' . $this->_getCurrentThemeId() . ' OR ThemeId = 0)';
$page_id = $this->Conn->GetOne($sql);
}
else {
$page_id = $page_by_template[$template];
}
if ($page_id === false && EDITING_MODE) {
// create missing pages, when in editing mode
$object =& $this->Application->recallObject($this->Prefix . '.-new', null, Array('skip_autoload' => true));
/* @var $object kDBItem */
$created = $this->_prepareAutoPage($object, $template, null, SMS_MODE_AUTO, false); // create virtual (not system!) page
if ($created) {
if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild') || !$this->Application->IsAdmin()) {
$updater =& $this->Application->recallObject('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
}
$event->CallSubEvent('OnResetMenuCache');
$this->Application->RemoveVar('PermCache_UpdateRequired');
$page_id = $object->GetID();
$this->Application->SetVar('m_cat_id', $page_id);
}
}
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
*/
function prepareObject(&$object, &$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$object->addCalculatedField(
'IsNew',
' IF(%1$s.NewItem = 2,
IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '.
$this->Application->ConfigValue('Category_DaysNew').
'*3600*24), 1, 0),
%1$s.NewItem
)');
}
/**
* Set correct parent path for newly created categories
*
* @param kEvent $event
*/
function OnAfterCopyToLive(&$event)
{
$parent_path = false;
$object =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true, 'live_table' => true));
$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 = Array('ParentPath' => $object->buildParentPath());
$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->recallObject('kPermCacheUpdater', null, array('strict_path' => $parent_path));
$cache_updater->OneStepRun();
$cache_updater->StrictPath = false;
}
}
/**
* Set cache modification mark if needed
*
* @param kEvent $event
*/
function OnBeforeDeleteFromLive(&$event)
{
$id = $event->getEventParam('id');
// loding anyway, because this object is needed by "c-perm:OnBeforeDeleteFromLive" event
$temp_object =& $event->getObject( Array('skip_autoload' => true) );
$temp_object->Load($id);
if ($id == 0) {
if ($temp_object->isLoaded()) {
// new category -> update cache (not loaded when "Home" category)
$this->Application->StoreVar('PermCache_UpdateRequired', 1);
}
return ;
}
// existing category was edited, check if in-cache fields are modified
$live_object =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('live_table' => true, 'skip_autoload' => true));
$live_object->Load($id);
$cached_fields = Array('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;
}
}
}
/**
* 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
*/
function OnPreCreate(&$event)
{
// 1. for permission editing of Home category
$this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid'));
parent::OnPreCreate($event);
$object =& $event->getObject();
// 2. preset template
$category_id = $this->Application->GetVar('m_cat_id');
$root_category = $this->Application->findModule('Name', 'Core', 'RootCat');
if ($category_id == $root_category) {
$object->SetDBField('Template', $this->_getDefaultDesign());
}
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
// 3. prepare priorities dropdown
$priority_helper->preparePriorities($event, true, 'ParentId = ' . $category_id);
}
/**
* Checks cache update mark and redirect to cache if needed
*
* @param kEvent $event
*/
function OnSave(&$event)
{
$ids = $this->getSelectedIDs($event, true);
$is_editing = implode('', $ids);
if ($is_editing) {
$old_statuses = $this->_getCategoryStatus($ids);
}
$object =& $event->getObject();
/* @var $object kDBItem */
if ($object->IsRoot()) {
$event->setEventParam('master_ids', Array(0));
$this->RemoveRequiredFields($object);
}
parent::OnSave($event);
if ($event->status != erSUCCESS) {
return ;
}
// 1. update priorities
$tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid'));
$changes = $tmp ? unserialize($tmp) : Array ();
$changed_ids = array_keys($changes);
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$priority_helper->updatePriorities($event, $changes, Array (0 => $event->getEventParam('ids')));
if ($this->Application->RecallVar('PermCache_UpdateRequired')) {
$this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid'));
}
$this->Application->StoreVar('RefreshStructureTree', 1);
$event->CallSubEvent('OnResetMenuCache');
if ($is_editing) {
// 2. 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->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
}
}
}
}
/**
* Returns statuses of given categories
*
* @param Array $category_ids
* @return Array
*/
function _getCategoryStatus($category_ids)
{
$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
$sql = 'SELECT Status, ' . $id_field . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' IN (' . implode(',', $category_ids) . ')';
return $this->Conn->GetCol($sql, $id_field);
}
/**
* Creates a new item in temp table and
* stores item id in App vars and Session on succsess
*
* @param kEvent $event
*/
function OnPreSaveCreated(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object CategoriesItem */
if ($object->IsRoot()) {
// don't create root category while saving permissions
return ;
}
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$category_id = $this->Application->GetVar('m_cat_id');
$priority_helper->preparePriorities($event, true, 'ParentId = ' . $category_id);
parent::OnPreSaveCreated($event);
}
/**
* Deletes sym link to other category
*
* @param kEvent $event
*/
function OnAfterItemDelete(&$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);
}
/**
* Exclude root categories from deleting
*
* @param kEvent $event
*/
function customProcessing(&$event, $type)
{
if ($event->Name == 'OnMassDelete' && $type == 'before') {
$ids = $event->getEventParam('ids');
if (!$ids || $this->Application->ConfigValue('AllowDeleteRootCats')) {
return ;
}
// 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);
}
}
$change_events = Array ('OnPreSave', 'OnPreSaveCreated', 'OnUpdate', 'OnSave');
if ($type == 'after' && in_array($event->Name, $change_events)) {
$object =& $event->getObject();
$tmp = $this->Application->RecallVar('priority_changes'.$this->Application->GetVar('m_wid'));
$changes = $tmp ? unserialize($tmp) : array();
if (!isset($changes[$object->GetID()])) {
$changes[$object->GetId()]['old'] = $object->GetID() == 0 ? 'new' : $object->GetDBField('OldPriority');
}
if ($changes[$object->GetId()]['old'] == $object->GetDBField('Priority')) return ;
$changes[$object->GetId()]['new'] = $object->GetDBField('Priority');
$changes[$object->GetId()]['parent'] = $object->GetDBField('ParentId');
$this->Application->StoreVar('priority_changes'.$this->Application->GetVar('m_wid'), serialize($changes));
}
}
/**
* 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 recurse into sub-items using temp handler, and deletes sub-items
* by calling its Delete method if sub-item has AutoDelete set to true in its config file
*
* @param kEvent $event
*/
function OnMassDelete(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$ids = $this->StoreSelectedIDs($event);
$to_delete = array();
if ($recycle_bin = $this->Application->ConfigValue('RecycleBinFolder')) {
$rb =& $this->Application->recallObject('c.recycle', null, Array ('skip_autoload' => true));
$rb->Load($recycle_bin);
$cat =& $event->getObject(Array('skip_autoload' => true));
foreach ($ids as $id) {
$cat->Load($id);
if (preg_match('/^'.preg_quote($rb->GetDBField('ParentPath'),'/').'/', $cat->GetDBField('ParentPath'))) {
$to_delete[] = $id;
continue;
}
$cat->SetDBField('ParentId', $recycle_bin);
$cat->Update();
}
$ids = $to_delete;
$event->redirect = 'categories/cache_updater';
}
$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);
// update priorities
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
// after deleting categories, all priorities should be recalculated
$parent_id = $this->Application->GetVar('m_cat_id');
$priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id);
$this->Application->StoreVar('RefreshStructureTree', 1);
$event->CallSubEvent('OnResetMenuCache');
}
/**
* 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');
$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');
$clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event));
$this->clearSelectedIDs($event);
}
/**
* Controls all item paste operations. Can occur only with filled clipbord.
*
* @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->redirect = $paste_event->redirect;
$event->redirect_params = $paste_event->redirect_params;
$event->status = $paste_event->status;
}
}
/**
* 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 subitems from clipboard
*
* @param kEvent $event
*/
function OnPaste(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) || !$this->_checkPastePermission($event)) {
return ;
}
$clipboard_data = $event->getEventParam('clipboard_data');
if (!$clipboard_data['cut'] && !$clipboard_data['copy']) {
return false;
}
// 1. get ParentId of moved category(-es) before it gets updated!!!)
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
if ($clipboard_data['cut']) {
$sql = 'SELECT ParentId
FROM ' . $table_name . '
WHERE ' . $id_field . ' = ' . $clipboard_data['cut'][0];
$source_category_id = $this->Conn->GetOne($sql);
}
$recursive_helper =& $this->Application->recallObject('RecursiveHelper');
/* @var $recursive_helper kRecursiveHelper */
if ($clipboard_data['cut']) {
$recursive_helper->MoveCategories($clipboard_data['cut'], $this->Application->GetVar('m_cat_id'));
}
if ($clipboard_data['copy']) {
// don't allow to copy/paste system OR theme-linked virtual pages
$sql = 'SELECT ' . $id_field . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' IN (' . implode(',', $clipboard_data['copy']) . ') AND (IsSystem = 0) 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']) {
$priority_helper->recalculatePriorities($event, 'ParentId = '.$source_category_id);
}
// recalculate priorities of newly pasted categories in destination category
$parent_id = $this->Application->GetVar('m_cat_id');
$priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id);
if ($clipboard_data['cut'] || $clipboard_data['copy']) {
// rebuild with progress bar
if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) {
$updater =& $this->Application->recallObject('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
}
else {
$event->redirect = 'categories/cache_updater';
}
$event->CallSubEvent('OnResetMenuCache');
$this->Application->StoreVar('RefreshStructureTree', 1);
}
}
/**
* Occurs when pasting category
*
* @param kEvent $event
*/
/*function OnCatPaste(&$event)
{
$inp_clipboard = $this->Application->RecallVar('ClipBoard');
$inp_clipboard = explode('-', $inp_clipboard, 2);
if($inp_clipboard[0] == 'COPY')
{
$saved_cat_id = $this->Application->GetVar('m_cat_id');
$cat_ids = $event->getEventParam('cat_ids');
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table = $this->Application->getUnitOption($event->Prefix, 'TableName');
$ids_sql = 'SELECT '.$id_field.' FROM '.$table.' WHERE ResourceId IN (%s)';
$resource_ids_sql = 'SELECT ItemResourceId FROM '.TABLE_PREFIX.'CategoryItems WHERE CategoryId = %s AND PrimaryCat = 1';
$object =& $this->Application->recallObject($event->Prefix.'.item', $event->Prefix, Array('skip_autoload' => true));
foreach($cat_ids as $source_cat => $dest_cat)
{
$item_resource_ids = $this->Conn->GetCol( sprintf($resource_ids_sql, $source_cat) );
if(!$item_resource_ids) continue;
$this->Application->SetVar('m_cat_id', $dest_cat);
$item_ids = $this->Conn->GetCol( sprintf($ids_sql, implode(',', $item_resource_ids) ) );
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
if($item_ids) $temp->CloneItems($event->Prefix, $event->Special, $item_ids);
}
$this->Application->SetVar('m_cat_id', $saved_cat_id);
}
}*/
/**
* Cleares 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
*/
function OnBeforeItemCreate(&$event)
{
$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 =& $event->getObject();
/* @var $object kDBItem */
$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);
/*if (!$this->Application->IsAdmin()) {
$object->SetDBField('IsMenu', 0); // add all suggested categories as non-menu
}*/
}
else {
$event->status = 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
*/
function OnBeforeItemUpdate(&$event)
{
parent::OnBeforeItemUpdate($event);
$this->_beforeItemChange($event);
}
/**
* Performs redirect to correct suggest confirmation template
*
* @param kEvent $event
*/
function OnCreate(&$event)
{
parent::OnCreate($event);
if ($this->Application->IsAdmin() || $event->status != erSUCCESS) {
return ;
}
$object =& $event->getObject();
$cache_updater =& $this->Application->recallObject('kPermCacheUpdater', null, array('strict_path' => $object->GetDBField('ParentPath')));
$cache_updater->OneStepRun();
$cache_updater->StrictPath = false;
$is_active = ($object->GetDBField('Status') == STATUS_ACTIVE);
$next_template = $is_active ? 'suggest_confirm_template' : 'suggest_pending_confirm_template';
$event->redirect = $this->Application->GetVar($next_template);
$event->SetRedirectParam('opener', 's');
// send email events
$perm_prefix = $this->Application->getUnitOption($event->Prefix, 'PermItemPrefix');
$event_suffix = $is_active ? 'ADD' : 'ADD.PENDING';
$this->Application->EmailEventAdmin($perm_prefix.'.'.$event_suffix);
$this->Application->EmailEventUser($perm_prefix.'.'.$event_suffix, $object->GetDBField('CreatedById'));
}
/**
* Returns current per-page setting for list
*
* @param kEvent $event
* @return int
*/
function getPerPage(&$event)
{
if (!$this->Application->IsAdmin()) {
$event->setEventParam('same_special', true);
}
return parent::getPerPage($event);
}
/**
* Set's correct page for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetPagination(&$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();
$object->SetPage($page);
}
}
}
}
/**
* Apply same processing to each item beeing selected in grid
*
* @param kEvent $event
* @access private
*/
function iterateItems(&$event)
{
if ($event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline') {
return parent::iterateItems($event);
}
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$object =& $event->getObject( Array('skip_autoload' => true) );
$ids = $this->StoreSelectedIDs($event);
if ($ids) {
$status_field = array_shift( $this->Application->getUnitOption($event->Prefix,'StatusField') );
foreach ($ids as $id) {
$object->Load($id);
switch ($event->Name) {
case 'OnMassApprove':
$object->SetDBField($status_field, 1);
break;
case 'OnMassDecline':
$object->SetDBField($status_field, 0);
break;
}
if ($this->Application->GetVar('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);
}
if ($object->Update()) {
$event->status = erSUCCESS;
+
+ $email_event = $event->Name == 'OnMassApprove' ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY';
+ $this->Application->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
}
else {
$event->status = 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
*/
function checkItemStatus(&$event)
{
$status_fields = $this->Application->getUnitOption($event->Prefix,'StatusField');
if (!$status_fields) {
return true;
}
$status_field = array_shift($status_fields);
if ($status_field == 'Status' || $status_field == 'Enabled') {
$object =& $event->getObject();
if (!$object->isLoaded()) {
return true;
}
return $object->GetDBField($status_field) == STATUS_ACTIVE || $object->GetDBField($status_field) == 4;
}
return true;
}
// ============= for cms page processing =======================
/**
* Returns default design template
*
* @return string
*/
function _getDefaultDesign()
{
$default_design = $this->Application->ConfigValue('cms_DefaultDesign');
return '/' . trim($default_design ? $default_design : 'designs/default_design', '/');
}
/**
* Returns default design based on given virtual template (used from kApplication::Run)
*
* @return string
*/
function GetDesignTemplate($t = null)
{
if (!isset($t)) {
$t = $this->Application->GetVar('t');
}
$page =& $this->Application->recallObject($this->Prefix . '.-virtual', null, Array ('page' => $t));
if ($page->isLoaded()) {
$real_t = $page->GetDBField('CachedTemplate');
$this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId') );
if ($page->GetDBField('FormId')) {
$this->Application->SetVar('form_id', $page->GetDBField('FormId'));
}
}
else {
$real_t = $this->_getDefaultDesign();
}
// $this->Application->SetVar('t', $t);
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 kDBItem */
$object->SetDBField('Template', $this->_stripTemplateExtension( $object->GetDBField('Template') ));
if ($object->GetDBField('IsSystem') == 1) {
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->findModule('Name', 'Core', 'RootCat');
if (($object->GetDBField('ParentId') == $root_category) && ($object->GetDBField('Template') == CATEGORY_TEMPLATE_INHERIT)) {
$object->SetError('Template', 'no_inherit');
}
if (!$this->Application->IsAdmin() || !$this->Application->LoggedIn()) {
// 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_NotAllowed');
}
}
}
/**
* Sets page name to requested field in case when:
* 1. page was auto created (through theme file rebuld)
* 2. requested field is emtpy
*
* @param kDBItem $object
* @param string $field
* @author Alex
*/
function _saveTitleField(&$object, $field)
{
$value = $object->GetField($field, 'no_default');
if ($value == '' || preg_match('/^_Auto: (.*)/', $value)) {
$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
$object->SetField(
$ml_formatter->LangFieldName($field),
$object->GetField( $ml_formatter->LangFieldName('Name') )
);
}
}
/**
* Don't allow to delete system pages, when not in debug mode
*
* @param kEvent $event
*/
function OnBeforeItemDelete(&$event)
{
$object =& $event->getObject();
if ($object->GetDBField('IsSystem') && !$this->Application->isDebugMode()) {
$event->status = erFAIL;
}
}
/**
* Enter description here...
*
* @param StructureItem $object
* @param string $template
*/
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) {
$system = $this->_templateFound($template, $theme_id) ? 1 : 0;
}
else {
$system = $system_mode == SMS_MODE_FORCE ? 1 : 0;
}
if ($system && $template_info === false) {
// do not autocreate system pages, when browsing through site
return false;
}
if (!isset($theme_id)) {
$theme_id = $this->_getCurrentThemeId();
}
$root_category = $this->Application->findModule('Name', 'Core', 'RootCat');
$page_category = $this->Application->GetVar('m_cat_id');
if (!$page_category) {
$page_category = $root_category;
$this->Application->SetVar('m_cat_id', $page_category);
}
if (!$system && 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 = $system ? '_Auto: ' . $template : $template;
$page_description = '';
if ($system) {
$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('IsSystem', $system);
$object->SetDBField('IsMenu', 0);
$object->SetDBField('ThemeId', $theme_id); // $system ? $theme_id : 0
// 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 . '.-item', null, Array ('skip_autoload' => true));
/* @var $object kDBItem */
$parent_id = $base_category;
$filenames_helper =& $this->Application->recallObject('FilenamesHelper');
/* @var $filenames_helper kFilenamesHelper */
$safe_category_path = array_map(Array (&$filenames_helper, 'replaceSequences'), $category_path);
foreach ($category_path as $category_order => $category_name) {
$this->Application->SetVar('m_cat_id', $parent_id);
// get virtual category first, when possible
$sql = 'SELECT ' . $object->IDField . '
FROM ' . $object->TableName . '
WHERE
(
Filename = ' . $this->Conn->qstr($safe_category_path[$category_order]) . ' OR
Filename = ' . $this->Conn->qstr( $filenames_helper->replaceSequences('_Auto: ' . $category_name) ) . '
) AND
(ParentId = ' . $parent_id . ') AND
(ThemeId = 0 OR ThemeId = ' . $theme_id . ')
ORDER BY ThemeId ASC';
$parent_id = $this->Conn->GetOne($sql);
if ($parent_id === false) {
// page not found
$template = implode('/', array_slice($safe_category_path, 0, $category_order + 1));
// don't process system templates in sub-categories
$system = $this->_templateFound($template, $theme_id) && (strpos($template, '/') === false);
if (!$this->_prepareAutoPage($object, $category_name, $theme_id, $system ? SMS_MODE_FORCE : false)) {
// page was not created
break;
}
$parent_id = $object->GetID();
}
}
$this->Application->SetVar('m_cat_id', $backup_category_id);
$category_ids[ implode('||', $category_path) ] = $parent_id;
return $parent_id;
}
/**
* Returns theme name by it's id. Used in structure page creation.
*
* @param int $theme_id
* @return string
*/
function _getThemeName($theme_id)
{
static $themes = null;
if (!isset($themes)) {
$id_field = $this->Application->getUnitOption('theme', 'IDField');
$table_name = $this->Application->getUnitOption('theme', 'TableName');
$sql = 'SELECT Name, ' . $id_field . '
FROM ' . $table_name . '
WHERE Enabled = 1';
$themes = $this->Conn->GetCol($sql, $id_field);
}
return array_key_exists($theme_id, $themes) ? $themes[$theme_id] : false;
}
/**
* Resets SMS-menu cache
*
* @param kEvent $event
*/
function OnResetMenuCache(&$event)
{
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName IN ("cms_menu", "StructureTree")');
}
/**
* Updates structure config
*
* @param kEvent $event
*/
function OnAfterConfigRead(&$event)
{
parent::OnAfterConfigRead($event);
if (defined('IS_INSTALL') && IS_INSTALL) {
// skip any processing, because Category table doesn't exists until install is finished
return ;
}
$site_config_helper =& $this->Application->recallObject('SiteConfigHelper');
/* @var $site_config_helper SiteConfigHelper */
$settings = $site_config_helper->getSettings();
$root_category = $this->Application->findModule('Name', 'Core', 'RootCat');
// set root category
$section_ajustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments');
$section_ajustments['in-portal:browse'] = Array (
'url' => Array ('m_cat_id' => $root_category),
'late_load' => Array ('m_cat_id' => $root_category),
'onclick' => 'checkCatalog(' . $root_category . ')',
);
$section_ajustments['in-portal:browse_site'] = Array (
'url' => Array ('editing_mode' => $settings['default_editing_mode']),
);
$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_ajustments);
// prepare structure dropdown
$category_helper =& $this->Application->recallObject('CategoryHelper');
/* @var $category_helper CategoryHelper */
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$fields['ParentId']['default'] = (int)$this->Application->GetVar('m_cat_id');
$fields['ParentId']['options'] = $category_helper->getStructureTreeAsOptions();
// limit design list by theme
$design_folders = Array ('tf.FilePath = "/designs"', 'tf.FilePath = "/platform/designs"');
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$design_folders[] = 'tf.FilePath = "/' . $module_info['TemplatePath'] . 'designs"';
}
$design_folders = array_unique($design_folders);
$theme_id = $this->_getCurrentThemeId();
$design_sql = $fields['Template']['options_sql'];
$design_sql = str_replace('(tf.FilePath = "/designs")', '(' . implode(' OR ', $design_folders) . ')' . ' AND (t.ThemeId = ' . $theme_id . ')', $design_sql);
$fields['Template']['options_sql'] = $design_sql;
// adds "Inherit From Parent" option to "Template" field
$fields['Template']['options'] = Array (CATEGORY_TEMPLATE_INHERIT => $this->Application->Phrase('la_opt_InheritFromParent'));
$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
if ($this->Application->IsAdmin()) {
// don't sort by Front-End sorting fields
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
$remove_keys = Array ('DefaultSorting1Field', 'DefaultSorting2Field', 'DefaultSorting1Dir', 'DefaultSorting2Dir');
foreach ($remove_keys as $remove_key) {
unset($config_mapping[$remove_key]);
}
$this->Application->setUnitOption($event->Prefix, 'ConfigMapping', $config_mapping);
}
else {
// sort by parent path on Front-End only
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings');
$list_sortings['']['ForcedSorting'] = Array ("CurrentSort" => 'asc');
$this->Application->setUnitOption($event->Prefix, 'ListSortings', $list_sortings);
}
// add grids for advanced view (with primary category column)
$grids = $this->Application->getUnitOption($this->Prefix, 'Grids');
$process_grids = Array ('Default', 'Radio');
foreach ($process_grids as $process_grid) {
$grid_data = $grids[$process_grid];
$grid_data['Fields']['CachedNavbar'] = Array ('title' => 'la_col_Path', 'data_block' => 'grid_parent_category_td', 'filter_block' => 'grid_like_filter');
$grids[$process_grid . 'ShowAll'] = $grid_data;
}
$this->Application->setUnitOption($this->Prefix, 'Grids', $grids);
}
/**
* Removes this item and it's children (recursive) from structure dropdown
*
* @param kEvent $event
*/
function OnAfterItemLoad(&$event)
{
parent::OnAfterItemLoad($event);
if (!$this->Application->IsAdmin()) {
return ;
}
$object =& $event->getObject();
// remove this category & it's children from dropdown
$sql = 'SELECT '.$object->IDField.'
FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
WHERE ParentPath LIKE "'.$object->GetDBField('ParentPath').'%"';
$remove_categories = $this->Conn->GetCol($sql);
$field_options = $object->GetFieldOptions('ParentId');
foreach ($remove_categories as $remove_category) {
unset($field_options['options'][$remove_category]);
}
$object->SetFieldOptions('ParentId', $field_options);
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$priority_helper->preparePriorities($event, false, 'ParentId = '.$object->GetDBField('ParentId'));
// storing prioriry right after load for comparing when updating
$object->SetDBField('OldPriority', $object->GetDBField('Priority'));
}
/**
* Builds list
*
* @param kEvent $event
* @access protected
*/
function OnListBuild(&$event)
{
parent::OnListBuild($event);
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$priority_helper->preparePriorities($event, false, 'ParentId = '.$this->Application->GetVar('m_cat_id'));
}
/**
* 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.'Theme AS t ON t.ThemeId = tf.ThemeId
WHERE t.Enabled = 1 AND tf.FileType = 1
AND (
SELECT COUNT(CategoryId)
FROM ' . TABLE_PREFIX . 'Category 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 ;
}
set_time_limit(0);
ini_set('memory_limit', -1);
$dummy =& $this->Application->recallObject($event->Prefix . '.-dummy', null, Array ('skip_autoload' => true));
/* @var $dummy kDBItem */
$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('QuickCategoryPermissionRebuild')) {
$updater =& $this->Application->recallObject('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
}
$event->CallSubEvent('OnResetMenuCache');
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)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$ids = $this->StoreSelectedIDs($event);
if ($ids) {
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
$parent_id = $this->Application->GetVar('m_cat_id');
$sql = 'SELECT Priority, '.$id_field.'
FROM '.$table_name.'
WHERE '.$id_field.' IN ('.implode(',', $ids).')';
$priorities = $this->Conn->GetCol($sql, $id_field);
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
foreach ($ids as $id) {
$new_priority = $priorities[$id] + ($event->Name == 'OnMassMoveUp' ? +1 : -1);
$changes = Array (
$id => Array ('old' => $priorities[$id], 'new' => $new_priority, 'parent' => $parent_id),
);
$sql = 'UPDATE '.$table_name.'
SET Priority = '.$new_priority.'
WHERE '.$id_field.' = '.$id;
$this->Conn->Query($sql);
$priority_helper->updatePriorities($event, $changes, Array ($id => $id));
}
}
$this->clearSelectedIDs($event);
$this->Application->StoreVar('RefreshStructureTree', 1);
}
/**
* Completely recalculates priorities in current category
*
* @param kEvent $event
*/
function OnRecalculatePriorities(&$event)
{
$priority_helper =& $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$parent_id = $this->Application->GetVar('m_cat_id');
$priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id);
}
/**
* Update Preview Block for FCKEditor
*
* @param kEvent $event
*/
function OnUpdatePreviewBlock(&$event)
{
$event->status = erSTOP;
$string = unhtmlentities($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);
}
}
\ No newline at end of file

Event Timeline