Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F784818
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Feb 10, 6:14 PM
Size
89 KB
Mime Type
text/x-diff
Expires
Wed, Feb 12, 6:14 PM (10 h, 1 m)
Engine
blob
Format
Raw Data
Handle
563151
Attached To
rINP In-Portal
in-portal
View Options
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
Log In to Comment