Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sun, Apr 20, 2:46 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.0.x/themes/default2009/platform/inc/captcha_image.elm.tpl
===================================================================
--- branches/5.0.x/themes/default2009/platform/inc/captcha_image.elm.tpl (revision 12367)
+++ branches/5.0.x/themes/default2009/platform/inc/captcha_image.elm.tpl (revision 12368)
@@ -1 +1 @@
-<inp2:m_NoDebug/><inp2:m_CaptchaImage />
\ No newline at end of file
+<inp2:m_CaptchaImage/>
\ No newline at end of file
Index: branches/5.0.x/core/kernel/event_manager.php
===================================================================
--- branches/5.0.x/core/kernel/event_manager.php (revision 12367)
+++ branches/5.0.x/core/kernel/event_manager.php (revision 12368)
@@ -1,794 +1,794 @@
<?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));
+ $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);
$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/kernel/processors/main_processor.php
===================================================================
--- branches/5.0.x/core/kernel/processors/main_processor.php (revision 12367)
+++ branches/5.0.x/core/kernel/processors/main_processor.php (revision 12368)
@@ -1,1006 +1,1005 @@
<?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 kMainTagProcessor extends kTagProcessor {
function Init($prefix, $special, $event_params = null)
{
parent::Init($prefix, $special, $event_params);
$actions =& $this->Application->recallObject('kActions');
$actions->Set('t', $this->Application->GetVar('t'));
$actions->Set('sid', $this->Application->GetSID());
$actions->Set('m_opener', $this->Application->GetVar('m_opener') );
}
/**
* Base folder for all template includes
*
* @param Array $params
* @return string
*/
function TemplatesBase($params)
{
$force_admin = array_key_exists('force_admin', $params) && $params['force_admin'];
$module = array_key_exists('module', $params) ? $params['module'] : 'core';
if ($this->Application->IsAdmin() || $force_admin) {
if ($module == 'in-portal') {
$module = 'kernel';
}
$path = $force_admin ? '/core/admin_templates' : THEMES_PATH;
$path = preg_replace('/\/(.*?)\/(.*)/', $module.'/\\2', $path); // remove leading slash + substitute module
}
else {
$path = mb_substr(THEMES_PATH, 1);
if (mb_strtolower($module) == 'in-portal') {
$module_folder = 'platform';
}
else {
$module_folder = $this->Application->findModule('Name', $module, 'TemplatePath');
}
$path .= rtrim('/' . trim($module_folder, '/'), '/') . '/';
}
return $this->Application->BaseURL() . $path;
}
/**
* Creates <base href ..> HTML tag for all templates
* affects future css, js files and href params of links
*
* @return string
* @access public
*/
function Base_Ref($params)
{
return '<base href="'.$this->TemplatesBase($params).'/" />';
}
/**
* Returns base url for web-site
*
* @return string
* @access public
*/
function BaseURL()
{
return $this->Application->BaseURL();
}
//for compatability with K3 tags
function Base($params)
{
return $this->TemplatesBase($params).'/';
}
function ProjectBase($params)
{
return $this->Application->BaseURL();
}
/*function Base($params)
{
return $this->Application->BaseURL().$params['add'];
}*/
/**
* Used to create link to any template.
* use "pass" paramter if "t" tag to specify
* prefix & special of object to be represented
* in resulting url
*
* @param Array $params
* @return string
* @access public
*/
function T($params)
{
//by default link to current template
$t = $this->SelectParam($params, 't,template');
unset($params['t']);
unset($params['template']);
$prefix=isset($params['prefix']) ? $params['prefix'] : ''; unset($params['prefix']);
$index_file = isset($params['index_file']) ? $params['index_file'] : null; unset($params['index_file']);
return $this->Application->HREF($t, $prefix, $params, $index_file);
}
function Link($params)
{
if (isset($params['template'])) {
$params['t'] = $params['template'];
unset($params['template']);
}
if (!isset($params['pass']) && !isset($params['no_pass'])) $params['pass'] = 'm';
if (isset($params['no_pass'])) unset($params['no_pass']);
if ( $this->Application->GetVar('admin') ) {
$params['admin'] = 1;
if (!array_key_exists('editing_mode', $params)) {
$params['editing_mode'] = EDITING_MODE;
}
}
return $this->T($params);
}
function Env($params)
{
$t = $params['template'];
unset($params['template']);
return $this->Application->BuildEnv($t, $params, 'm', null, false);
}
function FormAction($params)
{
$params['pass'] = 'all,m';
$params['pass_category'] = 1;
return $this->Application->HREF('', '', $params);
}
/*// NEEDS TEST
function Config($params)
{
return $this->Application->ConfigOption($params['var']);
}
function Object($params)
{
$name = $params['name'];
$method = $params['method'];
$tmp =& $this->Application->recallObject($name);
if ($tmp != null) {
if (method_exists($tmp, $method))
return $tmp->$method($params);
else
echo "Method $method does not exist in object ".get_class($tmp)." named $name<br>";
}
else
echo "Object $name does not exist in the appliaction<br>";
}*/
/**
* Tag, that always returns true.
* For parser testing purposes
*
* @param Array $params
* @return bool
* @access public
*/
function True($params)
{
return true;
}
/**
* Tag, that always returns false.
* For parser testing purposes
*
* @param Array $params
* @return bool
* @access public
*/
function False($params)
{
return false;
}
/**
* Returns block parameter by name (used only as "check" parameter value for "m_if" tag !)
*
* @param Array $params
* @return stirng
* @access public
*/
function Param($params)
{
$name = $params['name'];
/*if (isset($this->Application->LateParsed[$name])) {
$f = $this->Application->PreParsedBlocks['capture_'.$name.$this->Application->LateParsed[$name]];
$this->Application->Parser->SetParam($name, $f(array()));
}*/
$res = $this->Application->Parser->GetParam($params['name']);
if ($res === false) {
$res = '';
}
if (isset($params['plus'])) {
$res += $params['plus'];
}
return $res;
}
/**
* Compares block parameter with value specified
*
* @param Array $params
* @return bool
* @access public
*/
function ParamEquals($params)
{
$name = $this->SelectParam($params, 'name,var,param');
$value = $params['value'];
return ($this->Application->Parser->GetParam($name) == $value);
}
/*function PHP_Self($params)
{
return $HTTP_SERVER_VARS['PHP_SELF'];
}
*/
/**
* Returns session variable value by name
*
* @param Array $params
* @return string
* @access public
*/
function Recall($params)
{
$var_name = $this->SelectParam($params,'name,var,param');
if (isset($params['persistent']) && $params['persistent']) {
$ret = $this->Application->RecallPersistentVar($var_name);
}
else {
$ret = $this->Application->RecallVar($var_name);
}
$ret = ($ret === false && isset($params['no_null'])) ? '' : $ret;
if (getArrayValue($params, 'special') || getArrayValue($params, 'htmlchars')) {
$ret = htmlspecialchars($ret);
}
if (getArrayValue($params, 'urlencode')) {
$ret = urlencode($ret);
}
return $ret;
}
function RemoveVar($params)
{
$this->Application->RemoveVar( $this->SelectParam($params,'name,var,param') );
}
// bad style to store something from template to session !!! (by Alex)
// Used here only to test how session works, nothing more
function Store($params)
{
//echo"Store $params[name]<br>";
$name = $params['name'];
$value = $params['value'];
$this->Application->StoreVar($name,$value);
}
/**
* Sets application variable value(-s)
*
* @param Array $params
* @access public
*/
function Set($params)
{
foreach ($params as $param => $value) {
$this->Application->SetVar($param, $value);
}
}
/**
* Increment application variable
* specified by number specified
*
* @param Array $params
* @access public
*/
function Inc($params)
{
$this->Application->SetVar($params['param'], $this->Application->GetVar($params['param']) + $params['by']);
}
/**
* Retrieves application variable
* value by name
*
* @param Array $params
* @return string
* @access public
*/
function Get($params)
{
$ret = $this->Application->GetVar($this->SelectParam($params, 'name,var,param'), '');
return getArrayValue($params, 'htmlchars') ? htmlspecialchars($ret) : $ret;
}
/**
* Retrieves application constant
* value by name
*
* @param Array $params
* @return string
* @access public
*/
function GetConst($params)
{
$constant_name = $this->SelectParam($params, 'name,const');
return defined($constant_name) ? constant($constant_name) : '';
}
/**
* Retrieves configuration variable value by name
*
* @param Array $params
* @return string
* @access public
*/
function GetConfig($params)
{
$config_name = $this->SelectParam($params, 'name,var');
$ret = $this->Application->ConfigValue($config_name);
if( getArrayValue($params, 'escape') ) $ret = addslashes($ret);
return $ret;
}
function ConfigEquals($params)
{
$option = $this->SelectParam($params, 'name,option,var');
return $this->Application->ConfigValue($option) == getArrayValue($params, 'value');
}
/**
* Creates all hidden fields
* needed for kernel_form
*
* @param Array $params
* @return string
* @access public
*/
function DumpSystemInfo($params)
{
$actions =& $this->Application->recallObject('kActions');
$actions->Set('t', $this->Application->GetVar('t') );
$params = $actions->GetParams();
$o='';
foreach ($params AS $name => $val)
{
$o .= "<input type='hidden' name='$name' id='$name' value='$val'>\n";
}
return $o;
}
/**
* Used for search sidebox on front-end only
*
* @param Array $params
* @return string
* @author Alex
*/
function GetFormHiddens($params)
{
$t = $this->SelectParam($params, 'template,t');
unset($params['template']);
$form_fields = Array ();
if ($this->Application->RewriteURLs()) {
$session =& $this->Application->recallObject('Session');
if ($session->NeedQueryString()) {
$form_fields['sid'] = $this->Application->GetSID();
}
}
else {
$form_fields['env'] = $this->Application->BuildEnv($t, $params, 'm', null, false);
}
if ($this->Application->GetVar('admin') == 1) {
$form_fields['admin'] = 1;
}
$ret = '';
$field_tpl = '<input type="hidden" name="%1$s" id="%1$s" value="%2$s"/>'."\n";
foreach ($form_fields as $form_field => $field_value) {
$ret .= sprintf($field_tpl, $form_field, $field_value);
}
return $ret;
}
function Odd_Even($params)
{
$odd = $params['odd'];
$even = $params['even'];
if (!isset($params['var'])) {
$var = 'odd_even';
}
else {
$var = $params['var'];
}
if ($this->Application->GetVar($var) == 'even') {
if (!isset($params['readonly']) || !$params['readonly']) {
$this->Application->SetVar($var, 'odd');
}
return $even;
}
else {
if (!isset($params['readonly']) || !$params['readonly']) {
$this->Application->SetVar($var, 'even');
}
return $odd;
}
}
/**
* Returns phrase translation by name
*
* @param Array $params
* @return string
* @access public
*/
function Phrase($params)
{
// m:phrase name="phrase_name" default="Tr-alala" updated="2004-01-29 12:49"
$phrase_name = $this->SelectParam($params, 'label,name,title');
if (isset($params['source']) && $params['source']) {
$phrase_name = $this->Application->GetVar($phrase_name);
if (array_key_exists('default', $params) && !$phrase_name) {
$phrase_name = $params['default'];
}
}
$no_editing = array_key_exists('no_editing', $params) && $params['no_editing'];
$translation = $this->Application->Phrase($phrase_name, !$no_editing);
if (isset($params['escape']) && $params['escape']) {
$translation = htmlspecialchars($translation, ENT_QUOTES);
$translation = addslashes($translation);
}
return $translation;
}
// for tabs
function is_active($params)
{
$test_templ = $this->SelectParam($params, 'templ,template,t');
if ( !getArrayValue($params,'allow_empty') )
{
$if_true=getArrayValue($params,'true') ? $params['true'] : 1;
$if_false=getArrayValue($params,'false') ? $params['false'] : 0;
}
else
{
$if_true=$params['true'];
$if_false=$params['false'];
}
if ( preg_match("/^".str_replace('/', '\/', $test_templ)."/i", $this->Application->GetVar('t'))) {
return $if_true;
}
else {
return $if_false;
}
}
function IsNotActive($params)
{
return !$this->is_active($params);
}
function IsActive($params)
{
return $this->is_active($params);
}
function is_t_active($params)
{
return $this->is_active($params);
}
function CurrentTemplate($params)
{
return $this->is_active($params);
}
/**
* Checks if session variable
* specified by name value match
* value passed as parameter
*
* @param Array $params
* @return string
* @access public
*/
function RecallEquals($params)
{
$name = $this->SelectParam($params, 'name,var');
$value = $params['value'];
if (isset($params['persistent']) && $params['persistent']) {
return $this->Application->RecallPersistentVar($name) == $value;
}
return ($this->Application->RecallVar($name) == $value);
}
/**
* Checks if application variable
* specified by name value match
* value passed as parameter
*
* @param Array $params
* @return bool
* @access public
*/
function GetEquals($params)
{
$name = $this->SelectParam($params, 'var,name,param');
$value = $params['value'];
if ($this->Application->GetVar($name) == $value) {
return 1;
}
}
function ModuleInclude($params)
{
$ret = '';
$block_params = array_merge($params, Array('is_silent' => 2)); // don't make fatal errors in case if template is missing
$current_template = $this->Application->GetVar('t');
$replace_main = isset($params['replace_m']) && $params['replace_m'];
$skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array();
$cms_mode = $this->Application->GetVar('admin');
foreach ($this->Application->ModuleInfo as $module_name => $module_data) {
$module_key = mb_strtolower($module_name);
if ($module_name == 'In-Portal') {
if (!$cms_mode && $this->Application->IsAdmin()) {
// don't process In-Portal templates in admin
continue;
}
// Front-End still relies on In-Portal module
$module_prefix = $module_data['TemplatePath'];
}
else {
$module_prefix = $this->Application->IsAdmin() ? $module_key.'/' : rtrim($module_data['TemplatePath'], '/').'/';
}
$block_params['t'] = $module_prefix.$this->SelectParam($params, $module_key.'_template,'.$module_key.'_t,template,t');
$check_prefix = $module_data['Var'];
if ($check_prefix == 'adm' && $replace_main) {
$check_prefix = 'c';
}
if ($block_params['t'] == $current_template || in_array($check_prefix, $skip_prefixes)) {
continue;
}
$no_data = $this->SelectParam($params, $module_key.'_block_no_data,block_no_data');
if ($no_data) {
$block_params['block_no_data'] = $module_prefix.'/'.$no_data;
}
$ret .= $this->Application->Parser->IncludeTemplate($block_params, isset($block_params['is_silent']) ? 1 : 0);
}
return $ret;
}
function ModuleEnabled($params)
{
return $this->Application->isModuleEnabled( $params['module'] );
}
/**
* Checks if debug mode is on
*
* @param Array $params
* @return bool
* @access public
*/
function IsDebugMode($params)
{
return defined('DEBUG_MODE') && $this->Application->isDebugMode();
}
/*function MassParse($params)
{
$qty = $params['qty'];
$block = $params['block'];
$mode = $params['mode'];
$o = '';
if ($mode == 'func') {
$func = create_function('$params', '
$o = \'<tr>\';
$o.= \'<td>a\'.$params[\'param1\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param2\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param3\'].\'</td>\';
$o.= \'<td>a\'.$params[\'param4\'].\'</td>\';
$o.= \'</tr>\';
return $o;
');
for ($i=1; $i<$qty; $i++) {
$block_params['param1'] = rand(1, 10000);
$block_params['param2'] = rand(1, 10000);
$block_params['param3'] = rand(1, 10000);
$block_params['param4'] = rand(1, 10000);
$o .= $func($block_params);
}
return $o;
}
$block_params['name'] = $block;
for ($i=0; $i<$qty; $i++) {
$block_params['param1'] = rand(1, 10000);
$block_params['param2'] = rand(1, 10000);
$block_params['param3'] = rand(1, 10000);
$block_params['param4'] = rand(1, 10000);
$block_params['passed'] = $params['passed'];
$block_params['prefix'] = 'm';
$o.= $this->Application->ParseBlock($block_params, 1);
}
return $o;
}*/
function LoggedIn($params)
{
return $this->Application->LoggedIn();
}
/**
* Allows to check if permission exists directly in template and perform additional actions if required
*
* @param Array $params
* @return bool
*/
function CheckPermission($params)
{
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
return $perm_helper->TagPermissionCheck($params);
}
/**
* Checks if user is logged in and if not redirects it to template passed
*
* @param Array $params
*/
function RequireLogin($params)
{
$t = $this->Application->GetVar('t');
if ($next_t = getArrayValue($params, 'next_template')) {
$t = $next_t;
}
// check by permissions: begin
if ((isset($params['perm_event']) && $params['perm_event']) ||
(isset($params['perm_prefix']) && $params['perm_prefix']) ||
(isset($params['permissions']) && $params['permissions'])) {
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$perm_status = $perm_helper->TagPermissionCheck($params);
if (!$perm_status) {
list($redirect_template, $redirect_params) = $perm_helper->getPermissionTemplate($params);
$this->Application->Redirect($redirect_template, $redirect_params);
}
else {
return ;
}
}
// check by permissions: end
// check by configuration value: begin
$condition = getArrayValue($params, 'condition');
if (!$condition) {
$condition = true;
}
else {
if (substr($condition, 0, 1) == '!') {
$condition = !$this->Application->ConfigValue(substr($condition, 1));
}
else {
$condition = $this->Application->ConfigValue($condition);
}
}
// check by configuration value: end
// check by belonging to group: begin
$group = $this->SelectParam($params, 'group');
$group_access = true;
if ($group) {
$conn =& $this->Application->GetADODBConnection();
$group_id = $conn->GetOne('SELECT GroupId FROM '.TABLE_PREFIX.'PortalGroup WHERE Name = '.$conn->qstr($group));
if ($group_id) {
$groups = explode(',', $this->Application->RecallVar('UserGroups'));
$group_access = in_array($group_id, $groups);
}
}
// check by belonging to group: end
if ((!$this->Application->LoggedIn() || !$group_access) && $condition) {
$redirect_params = $this->Application->HttpQuery->getRedirectParams(true);
$redirect_params['next_template'] = $t;
if (array_key_exists('pass_category', $params)) {
$redirect_params['pass_category'] = $params['pass_category'];
}
if ( $this->Application->LoggedIn() && !$group_access) {
$this->Application->Redirect( $params['no_group_perm_template'], $redirect_params);
}
$this->Application->Redirect($params['login_template'], $redirect_params);
}
}
function IsMember($params)
{
$group = getArrayValue($params, 'group');
$conn =& $this->Application->DB;
$group_id = $conn->GetOne('SELECT GroupId FROM '.TABLE_PREFIX.'PortalGroup WHERE Name = '.$conn->qstr($group));
if ($group_id) {
$groups = explode(',', $this->Application->RecallVar('UserGroups'));
$group_access = in_array($group_id, $groups);
}
return $group_access;
}
/**
* Checks if SSL is on and redirects to SSL URL if needed
* If SSL_URL is not defined in config - the tag does not do anything
* If for_logged_in_only="1" exits if user is not logged in.
* If called without params forces https right away. If called with by_config="1" checks the
* Require SSL setting from General Config and if it is ON forces https
*
* @param unknown_type $params
*/
function CheckSSL($params)
{
$ssl = $this->Application->IsAdmin() ? $this->Application->ConfigValue('AdminSSL_URL') : false;
if (!$ssl) {
// not in admin or admin ssl url is empty
$ssl = $this->Application->ConfigValue('SSL_URL');
}
if (!$ssl) return; //SSL URL is not set - no way to require SSL
$require = false;
if (isset($params['mode']) && $params['mode'] == 'required') {
$require = true;
if (isset($params['for_logged_in_only']) && $params['for_logged_in_only'] && !$this->Application->LoggedIn()) {
$require = false;
}
if (isset($params['condition'])) {
if (!$this->Application->ConfigValue($params['condition'])) {
$require = false;
}
}
}
$http_query =& $this->Application->recallObject('HTTPQuery');
$pass = $http_query->getRedirectParams();
if ($require) {
if (PROTOCOL == 'https://') {
$this->Application->SetVar('__KEEP_SSL__', 1);
return;
}
$this->Application->Redirect('', array_merge_recursive2($pass, Array('__SSL__' => 1)));
}
else {
if (PROTOCOL == 'https://' && $this->Application->ConfigValue('Force_HTTP_When_SSL_Not_Required')) {
if ($this->Application->GetVar('__KEEP_SSL__')) return;
// $pass_more = Array ('pass' => 'm', 'm_cat_id' => 0, '__SSL__' => 0);
$this->Application->Redirect('', array_merge_recursive2($pass, Array('__SSL__' => 0))); // $pass_more
}
}
}
function ConstOn($params)
{
$name = $this->SelectParam($params,'name,const');
return constOn($name);
}
function SetDefaultCategory($params)
{
$category_id = $this->Application->findModule('Name', $params['module'], 'RootCat');
$this->Application->SetVar('m_cat_id', $category_id);
}
function XMLTemplate($params)
{
safeDefine('DBG_SKIP_REPORTING', 1);
if (isset($params['cache']) && $params['cache']) {
$nextyear = intval(date('Y') + 1);
$format = "D, d M Y H:i:s";
$expiration = gmdate($format, mktime() + $params['cache']).' GMT';
$last_modified = mktime();
header ('Cache-Control: public, cache, max-age='.$params['cache']);
header ("Expires: $expiration");
header ('Pragma: public');
// Getting headers sent by the client.
$headers = request_headers();
// Checking if the client is validating his cache and if it is current.
if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) > $last_modified-$params['cache'])) {
// Client's cache IS current, so we just respond '304 Not Modified'.
header('Last-Modified: '.date($format, strtotime($headers['If-Modified-Since'])).' GMT', true, 304);
exit();
} else {
// Image not cached or cache outdated, we respond '200 OK' and output the image.
header('Last-Modified: '.gmdate($format, $last_modified).' GMT', true, 200);
}
}
// xml documents are usually long
set_time_limit(0);
ini_set('memory_limit', -1);
return $this->Application->XMLHeader(getArrayValue($params, 'xml_version'));
}
function Header($params)
{
header($params['data']);
}
function NoDebug($params)
{
if (!$this->Application->GetVar('debug')) {
define('DBG_SKIP_REPORTING', 1);
}
}
function RootCategoryName($params)
{
$root_phrase = $this->Application->ConfigValue('Root_Name');
return $this->Application->Phrase($root_phrase);
}
/**
* Allows to attach file directly from email event template
*
* @param Array $params
*/
function AttachFile($params)
{
$esender =& $application->recallObject('EmailSender'.(isset($params['special']) ? '.'.$params['special'] : ''));
/* @var $esender kEmailSendingHelper */
$path = FULL_PATH.'/'.$params['path'];
if (file_exists($path)) {
$esender->AddAttachment($path);
}
}
function CaptchaImage($params)
{
+ $this->NoDebug($params);
$this->Application->SetVar('skip_last_template', 1);
$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
- $captcha_helper->GenerateCaptchaImage(
- $this->Application->RecallVar($this->Application->GetVar('var')),
- $this->Application->GetVar('w'),
- $this->Application->GetVar('h'),
- true
- );
+ // generate captcha code
+ $code = $captcha_helper->prepareCode( $this->Application->GetVar('var') );
+
+ $captcha_helper->GenerateCaptchaImage($code, $this->Application->GetVar('w'), $this->Application->GetVar('h'), true);
}
function SID($params)
{
return $this->Application->GetSID();
}
function ModuleInfo($params)
{
return $this->Application->findModule($params['key'], $params['value'], $params['return']);
}
function Random($params)
{
return rand(1, 100000000);
}
/**
* Prints parser params, available at current deep level
*
* @param Array $params
* @return string
*/
function PrintCurrentParams($params)
{
$current_params = $this->Application->Parser->Params;
foreach ($current_params as $param_name => $param_value) {
$current_params[$param_name] = $param_name . ' = "' . $param_value . '"';
}
return '<pre>' . implode("\n", $current_params) . '</pre>';
}
/**
* Gets previously defined counter result
*
* @param Array $params
* @return int
*/
function GetCounter($params)
{
return $this->Application->getCounter($params['name'], $params);
}
/**
* Increments PageHit counter
*
* @param Array $params
* @return int
*/
function RegisterPageHit($params)
{
if ($this->Application->ConfigValue('UsePageHitCounter')) {
$db =& $this->Application->GetADODBConnection();
// get current counte
$sql = 'SELECT VariableValue
FROM '.TABLE_PREFIX.'ConfigurationValues
WHERE VariableName = "PageHitCounter"';
$page_counter = (int)$db->GetOne($sql);
$sql = 'UPDATE LOW_PRIORITY '.TABLE_PREFIX.'ConfigurationValues
SET VariableValue = '.($page_counter + 1).'
WHERE VariableName = "PageHitCounter"';
$db->Query($sql);
}
}
function Timestamp($params)
{
$format = isset($params['format']) ? $params['format'] : 'd.m.Y H:i:s';
return adodb_date($format);
}
}
Index: branches/5.0.x/core/kernel/session/inp_session.php
===================================================================
--- branches/5.0.x/core/kernel/session/inp_session.php (revision 12367)
+++ branches/5.0.x/core/kernel/session/inp_session.php (revision 12368)
@@ -1,148 +1,158 @@
<?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 InpSession extends Session
{
- function Init($prefix,$special)
+ function Init($prefix, $special)
{
$this->SessionTimeout = $this->Application->ConfigValue('SessionTimeout');
$path = (BASE_PATH == '') ? '/' : BASE_PATH;
-// if ( $this->Application->IsAdmin() ) $path = rtrim($path, '/').'/admin';
$this->SetCookiePath($path);
$cookie_name = $this->Application->ConfigValue('SessionCookieName');
- if (!$cookie_name) $cookie_name = 'sid';
- if (($this->Application->IsAdmin() && $special !== 'front') || $special == 'admin' ) { // || $this->Application->GetVar('admin') == 1
- $cookie_name = 'adm_'.$cookie_name;
+ if (!$cookie_name) {
+ $cookie_name = 'sid';
}
+
+ $admin_session = ($this->Application->IsAdmin() && $special !== 'front') || ($special == 'admin');
+
+ if ($admin_session) {
+ $cookie_name = 'adm_' . $cookie_name;
+ }
+
$this->SetCookieName($cookie_name);
$this->SetCookieDomain(SERVER_NAME);
- if( $this->Application->IsAdmin()) { // && $this->Application->GetVar('admin') != 1
+ if ($admin_session) {
$mode = smAUTO;
}
- elseif (constOn('IS_INSTALL')) {
+ elseif (defined('IS_INSTALL') && IS_INSTALL) {
$mode = smCOOKIES_ONLY;
}
else {
$ses_mode = $this->Application->ConfigValue('CookieSessions');
+
if ($ses_mode == 2) $mode = smAUTO;
if ($ses_mode == 1) $mode = smCOOKIES_ONLY;
if ($ses_mode == 0) $mode = smGET_ONLY;
}
+
$this->SetMode($mode);
- parent::Init($prefix,$special);
+ parent::Init($prefix, $special);
- if( !$this->Application->IsAdmin() && $this->GetField('PortalUserId') <= 0 )
- {
+ if (!$this->Application->IsAdmin() && $this->GetField('PortalUserId') <= 0) {
$group_list = $this->Application->ConfigValue('User_GuestGroup').','.$this->Application->ConfigValue('User_LoggedInGroup');
$this->SetField('GroupId', $this->Application->ConfigValue('User_GuestGroup'));
$this->SetField('GroupList', $group_list);
}
}
function Destroy()
{
$this->Storage->DeleteSession($this);
$this->Storage->DeleteEditTables();
$this->Data = new Params();
$this->SID = $this->CachedSID = '';
- if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty
+
+// if ($this->CookiesEnabled) {
+ // remove cookie, because we will have fake session and it should be getting sid left in cookies
+ $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty
+// }
$this->SetSession(); //will create a new session
}
}
class InpSessionStorage extends SessionStorage {
function Init($prefix,$special)
{
parent::Init($prefix,$special);
$this->setTableName(TABLE_PREFIX.'UserSession');
$this->SessionDataTable = TABLE_PREFIX.'SessionData';
$this->setIDField('SessionKey');
$this->TimestampField = 'LastAccessed';
$this->DataValueField = 'VariableValue';
$this->DataVarField = 'VariableName';
}
function LocateSession($sid)
{
$res = parent::LocateSession($sid);
if ($res) {
$this->Expiration += $this->SessionTimeout;
}
return $res;
}
function UpdateSession(&$session)
{
$time = adodb_mktime();
// Update LastAccessed only if it's newer than 1/10 of session timeout - perfomance optimization to eliminate needless updates on every click
// if ($time - $this->DirectVars['LastAccessed'] > $this->SessionTimeout/10) {
$this->SetField($session, $this->TimestampField, $time + $this->SessionTimeout);
// }
}
function StoreSession(&$session, $additional_fields = Array())
{
$fields_hash = Array (
'PortalUserId' => $this->Application->IsAdmin() ? 0 : -2, // Guest
'Language' => $this->Application->GetDefaultLanguageId(),
'Theme' => $this->Application->GetDefaultThemeId(),
'IpAddress' => $_SERVER['REMOTE_ADDR'],
'GroupId' => $this->Application->ConfigValue('User_GuestGroup'),
'GroupList' => $this->Application->ConfigValue('User_GuestGroup'),
'CurrentTempKey'=> $session->SID,
);
parent::StoreSession($session, $fields_hash);
}
function GetExpiredSIDs()
{
$query = ' SELECT '.$this->IDField.' FROM '.$this->TableName.' WHERE '.$this->TimestampField.' < '.(adodb_mktime());
$ret = $this->Conn->GetCol($query);
if($ret) {
$this->DeleteEditTables();
}
return $ret;
}
function DeleteEditTables()
{
$tables = $this->Conn->GetCol('SHOW TABLES');
$mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_(.*)/';
$mask_search_table = '/'.TABLE_PREFIX.'ses_(.*?)_(.*)/';
$sql='SELECT COUNT(*) FROM '.$this->TableName.' WHERE '.$this->IDField.' = \'%s\'';
foreach($tables as $table)
{
if( preg_match($mask_edit_table,$table,$rets) || preg_match($mask_search_table,$table,$rets) )
{
$sid = preg_replace('/(.*)_(.*)/', '\\1', $rets[1]); // remove popup's wid from sid
$is_alive = $this->Conn->GetOne( sprintf($sql,$sid) );
if(!$is_alive) $this->Conn->Query('DROP TABLE IF EXISTS '.$table);
}
}
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/session/session.php
===================================================================
--- branches/5.0.x/core/kernel/session/session.php (revision 12367)
+++ branches/5.0.x/core/kernel/session/session.php (revision 12368)
@@ -1,1173 +1,1235 @@
<?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!');
/*
The session works the following way:
1. When a visitor loads a page from the site the script checks if cookies_on varibale has been passed to it as a cookie.
2. If it has been passed, the script tries to get Session ID (SID) from the request:
3. Depending on session mode the script is getting SID differently.
The following modes are available:
- AUTO
Automatic mode: if cookies are on at the client side, the script relays only on cookies and
ignore all other methods of passing SID. If cookies are off at the client side, the script relays on SID
passed through query string and referal passed by the client. THIS METHOD IS NOT 100% SECURE, as long as
attacker may get SID and substitude referal to gain access to user' session. One of the faults of this method
is that the session is only created when the visitor clicks the first link on the site, so there
is NO session at the first load of the page. (Actually there is a session, but it gets lost after
the first click because we do not use SID in query string while we are not sure if we need it)
- smCOOKIES_ONLY
Cookies only: in this mode the script relays solely on cookies passed from the browser and ignores
all other methods. In this mode there is no way to use sessions for clients without cookies support
or cookies support disabled. The cookies are stored with the full domain name and path to base-directory
of script installation.
- smGET_ONLY
GET only: the script will not set any cookies and will use only SID passed in query string using GET,
it will also check referal. The script will set SID at the first load of the page
- smCOOKIES_AND_GET
Combined mode: the script will use both cookies and GET right from the start. If client has cookies enabled,
the script will check SID stored in cookie and passed in query string, and will use this SID only if both
cookie and query string matches. However if cookies are disabled on the client side, the script will work
the same way as in GET_ONLY mode.
4. After the script has the SID it tries to load it from the Storage (default is database)
5. If such SID is found in the database, the script checks its expiration time. If session is not expired,
it updates its expiration, and resend the cookie (if applicable to session mode)
6. Then the script loads all the data (session variables) pertaining to the SID.
Usage:
$session = new Session(smAUTO); //smAUTO is default, you could just leave the brackets empty, or provide another mode
$session->SetCookieDomain('my.domain.com');
$session->SetCookiePath('/myscript');
$session->SetCookieName('my_sid_cookie');
$session->SetGETName('sid');
$session->InitSession();
...
//link output:
echo "<a href='index.php?'". ( $session->NeedQueryString() ? 'sid='.$session->SID : '' ) .">My Link</a>";
*/
/**
* Implements Session Store in the Database
*
*/
class SessionStorage extends kDBBase {
var $Expiration;
var $SessionTimeout=0;
var $DirectVars = Array();
var $ChangedDirectVars = Array();
var $PersistentVars = Array ();
var $OriginalData=Array();
var $TimestampField;
var $SessionDataTable;
var $DataValueField;
var $DataVarField;
function Init($prefix,$special)
{
parent::Init($prefix,$special);
$this->setTableName('sessions');
$this->setIDField('sid');
$this->TimestampField = 'expire';
$this->SessionDataTable = 'SessionData';
$this->DataValueField = 'value';
$this->DataVarField = 'var';
}
function setSessionTimeout($new_timeout)
{
$this->SessionTimeout = $new_timeout;
}
function StoreSession(&$session, $additional_fields = Array())
{
if (defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound($this->TableName)) {
return false;
}
$fields_hash = Array (
$this->IDField => $session->SID,
$this->TimestampField => $session->Expiration
);
+ // default values + additional values + values set during this script run
+ $additional_fields = array_merge($additional_fields, $this->DirectVars); // used 2 times later
$fields_hash = array_merge($fields_hash, $additional_fields);
$this->Conn->doInsert($fields_hash, $this->TableName);
foreach ($additional_fields as $field_name => $field_value) {
$this->SetField($session, $field_name, $field_value);
}
}
function DeleteSession(&$session)
{
$query = ' DELETE FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID);
$this->Conn->Query($query);
$query = ' DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID);
$this->Conn->Query($query);
$this->OriginalData = Array();
}
function UpdateSession(&$session, $timeout=0)
{
$this->SetField($session, $this->TimestampField, $session->Expiration);
$query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID);
$this->Conn->Query($query);
}
function LocateSession($sid)
{
$query = ' SELECT * FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($sid);
$result = $this->Conn->GetRow($query);
if ($result === false) {
return false;
}
$this->DirectVars = $result;
$this->Expiration = $result[$this->TimestampField];
return true;
}
function GetExpiration()
{
return $this->Expiration;
}
function LoadData(&$session)
{
$query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID);
$this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField);
return $this->OriginalData;
}
/**
* Enter description here...
*
* @param Session $session
* @param string $var_name
* @param mixed $default
*/
function GetField(&$session, $var_name, $default = false)
{
return isset($this->DirectVars[$var_name]) ? $this->DirectVars[$var_name] : $default;
//return $this->Conn->GetOne('SELECT '.$var_name.' FROM '.$this->TableName.' WHERE `'.$this->IDField.'` = '.$this->Conn->qstr($session->GetID()) );
}
function SetField(&$session, $var_name, $value)
{
$value_changed = !isset($this->DirectVars[$var_name]) || ($this->DirectVars[$var_name] != $value);
if ($value_changed) {
$this->DirectVars[$var_name] = $value;
$this->ChangedDirectVars[] = $var_name;
$this->ChangedDirectVars = array_unique($this->ChangedDirectVars);
}
//return $this->Conn->Query('UPDATE '.$this->TableName.' SET '.$var_name.' = '.$this->Conn->qstr($value).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID()) );
}
function SaveData(&$session)
{
if(!$session->SID) return false; // can't save without sid
$ses_data = $session->Data->GetParams();
$replace = '';
foreach ($ses_data as $key => $value)
{
if ( isset($this->OriginalData[$key]) && $this->OriginalData[$key] == $value)
{
continue; //skip unchanged session data
}
else
{
$replace .= sprintf("(%s, %s, %s),",
$this->Conn->qstr($session->SID),
$this->Conn->qstr($key),
$this->Conn->qstr($value));
}
}
$replace = rtrim($replace, ',');
if ($replace != '') {
$query = ' REPLACE INTO '.$this->SessionDataTable. ' ('.$this->IDField.', '.$this->DataVarField.', '.$this->DataValueField.') VALUES '.$replace;
$this->Conn->Query($query);
}
if ($this->ChangedDirectVars) {
$changes = array();
foreach ($this->ChangedDirectVars as $var) {
$changes[] = $var.' = '.$this->Conn->qstr($this->DirectVars[$var]);
}
$query = 'UPDATE '.$this->TableName.' SET '.implode(',', $changes).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID());
$this->Conn->Query($query);
}
}
function RemoveFromData(&$session, $var)
{
$query = 'DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID).
' AND '.$this->DataVarField.' = '.$this->Conn->qstr($var);
$this->Conn->Query($query);
unset($this->OriginalData[$var]);
}
- function GetFromData(&$session, $var)
+ function GetFromData(&$session, $var, $default = false)
{
- return getArrayValue($this->OriginalData, $var);
+ return array_key_exists($var, $this->OriginalData) ? $this->OriginalData[$var] : $default;
}
function GetExpiredSIDs()
{
$query = ' SELECT '.$this->IDField.' FROM '.$this->TableName.' WHERE '.$this->TimestampField.' > '.adodb_mktime();
return $this->Conn->GetCol($query);
}
function DeleteExpired()
{
$expired_sids = $this->GetExpiredSIDs();
if ($expired_sids) {
$sessionlog_table = $this->Application->getUnitOption('session-log', 'TableName');
$session_log_sql =
' UPDATE '.$sessionlog_table.'
SET Status = 2, SessionEnd =
( SELECT '.$this->TimestampField.' - '.$this->SessionTimeout.'
FROM '.$this->TableName.'
WHERE '.$this->IDField.' = '.$sessionlog_table.'.SessionId
)
WHERE Status = 0 AND SessionId IN ('.join(',', $expired_sids).')';
if ($sessionlog_table) {
$this->Conn->Query($session_log_sql);
}
$where_clause = ' WHERE '.$this->IDField.' IN ("'.implode('","',$expired_sids).'")';
$sql = 'DELETE FROM '.$this->SessionDataTable.$where_clause;
$this->Conn->Query($sql);
$sql = 'DELETE FROM '.$this->TableName.$where_clause;
$this->Conn->Query($sql);
// delete debugger ouputs left of expired sessions
foreach ($expired_sids as $expired_sid) {
$debug_file = WRITEABLE . '/cache/debug_@' . $expired_sid . '@.txt';
if (file_exists($debug_file)) {
@unlink($debug_file);
}
}
}
return $expired_sids;
}
function LoadPersistentVars(&$session)
{
$user_id = $session->RecallVar('user_id');
if ($user_id != -2) {
// root & normal users
$sql = 'SELECT VariableValue, VariableName
FROM '.TABLE_PREFIX.'PersistantSessionData
WHERE PortalUserId = '.$user_id;
$this->PersistentVars = $this->Conn->GetCol($sql, 'VariableName');
}
else {
$this->PersistentVars = Array ();
}
}
/**
* Stores variable to persistent session
*
* @param Session $session
* @param string $var_name
* @param mixed $var_value
*/
function StorePersistentVar(&$session, $var_name, $var_value)
{
$user_id = $session->RecallVar('user_id');
if ($user_id == -2 || $user_id === false) {
// -2 (when not logged in), false (when after u:OnLogout event)
$session->StoreVar($var_name, $var_value);
return ;
}
$this->PersistentVars[$var_name] = $var_value;
$key_clause = 'PortalUserId = '.$user_id.' AND VariableName = '.$this->Conn->qstr($var_name);
$sql = 'SELECT VariableName
FROM '.TABLE_PREFIX.'PersistantSessionData
WHERE '.$key_clause;
$record_found = $this->Conn->GetOne($sql);
$fields_hash = Array (
'PortalUserId' => $user_id,
'VariableName' => $var_name,
'VariableValue' => $var_value,
);
if ($record_found) {
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX.'PersistantSessionData', $key_clause);
}
else {
$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'PersistantSessionData');
}
}
/**
* Gets persistent variable
*
* @param Session $session
* @param string $var_name
* @param mixed $default
* @return mixed
*/
function RecallPersistentVar(&$session, $var_name, $default = false)
{
if ($session->RecallVar('user_id') == -2) {
if ($default == ALLOW_DEFAULT_SETTINGS) {
$default = null;
}
return $session->RecallVar($var_name, $default);
}
if (array_key_exists($var_name, $this->PersistentVars)) {
return $this->PersistentVars[$var_name];
}
elseif ($default == ALLOW_DEFAULT_SETTINGS) {
$default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId');
if (!$default_user_id) {
$default_user_id = -1;
}
$sql = 'SELECT VariableValue, VariableName
FROM '.TABLE_PREFIX.'PersistantSessionData
WHERE VariableName = '.$this->Conn->qstr($var_name).' AND PortalUserId = '.$default_user_id;
$value = $this->Conn->GetOne($sql);
$this->PersistentVars[$var_name] = $value;
if ($value !== false) {
$this->StorePersistentVar($session, $var_name, $value); //storing it, so next time we don't load default user setting
}
return $value;
}
else {
return $default;
}
}
function RemovePersistentVar(&$session, $var_name)
{
unset($this->PersistentVars[$var_name]);
$user_id = $session->RecallVar('user_id');
if ($user_id != -2) {
$sql = 'DELETE FROM '.TABLE_PREFIX.'PersistantSessionData
WHERE PortalUserId = '.$user_id.' AND VariableName = '.$this->Conn->qstr($var_name);
$this->Conn->Query($sql);
}
}
}
define('smAUTO', 1);
define('smCOOKIES_ONLY', 2);
define('smGET_ONLY', 3);
define('smCOOKIES_AND_GET', 4);
class Session extends kBase {
var $Checkers;
var $Mode;
var $OriginalMode = null;
var $GETName = 'sid';
var $CookiesEnabled = true;
var $CookieName = 'sid';
var $CookieDomain;
var $CookiePath;
var $CookieSecure = 0;
var $SessionTimeout = 3600;
var $Expiration;
var $SID;
var $CachedSID;
var $SessionSet = false;
/**
+ * Session ID is used from GET
+ *
+ * @var bool
+ */
+ var $_fromGet = false;
+
+ /**
* Enter description here...
*
* @var SessionStorage
*/
var $Storage;
var $CachedNeedQueryString = null;
/**
* Session Data array
*
* @var Params
*/
var $Data;
/**
- * Names of optional session keys (which does not need to be always stored
+ * Names of optional session keys with their optional values (which does not need to be always stored)
*
- * @var array
+ * @var Array
*/
- var $OptionalData = array();
+ var $OptionalData = Array ();
- function Session($mode=smAUTO)
+ function Session($mode = smAUTO)
{
parent::kBase();
+
$this->SetMode($mode);
}
function SetMode($mode)
{
$this->Mode = $mode;
$this->CachedNeedQueryString = null;
$this->CachedSID = null;
}
function SetCookiePath($path)
{
$this->CookiePath = str_replace(' ', '%20', $path);
}
/**
* Setting cookie domain. Set false for local domains, because they don't contain dots in their names.
*
* @param string $domain
*/
function SetCookieDomain($domain)
{
$this->CookieDomain = substr_count($domain, '.') ? '.'.ltrim($domain, '.') : false;
}
function SetGETName($get_name)
{
$this->GETName = $get_name;
}
function SetCookieName($cookie_name)
{
$this->CookieName = $cookie_name;
}
function InitStorage($special)
{
$this->Storage =& $this->Application->recallObject('SessionStorage.'.$special);
$this->Storage->setSessionTimeout($this->SessionTimeout);
}
function Init($prefix,$special)
{
parent::Init($prefix,$special);
$this->CheckIfCookiesAreOn();
if ($this->CookiesEnabled) $_COOKIE['cookies_on'] = 1;
$this->Checkers = Array();
$this->InitStorage($special);
$this->Data = new Params();
$tmp_sid = $this->GetPassedSIDValue();
$check = $this->Check();
if ($this->Application->IsAdmin()) {
// 1. Front-End session may not be created (SID is present, but no data in database).
// Check expiration LATER from kApplication::Init, because template, used in session
// expiration redirect should be retrieved from mod-rewrite url first.
// 2. Admin sessions are always created, so case when SID is present,
// but session in database isn't is 100% session expired. Check expiration
// HERE because Session::SetSession will create missing session in database
// and when Session::ValidateExpired will be called later from kApplication::Init
// it won't consider such session as expired !!!
$this->ValidateExpired();
}
if ($check) {
$this->SID = $this->GetPassedSIDValue();
$this->Refresh();
$this->LoadData();
}
else {
$this->SetSession();
}
if (!is_null($this->OriginalMode)) $this->SetMode($this->OriginalMode);
}
function ValidateExpired()
{
if (defined('IS_INSTALL') && IS_INSTALL) {
return ;
}
$expired_sids = $this->DeleteExpired();
$my_sid_expired = in_array($this->CachedSID, $expired_sids);
- if ( ($expired_sids && $my_sid_expired) || ($this->CachedSID && !$this->SessionSet) ) {
+ if ( ($expired_sids && $my_sid_expired) || ($this->CachedSID && !$this->_fromGet && !$this->SessionSet) ) {
$this->RemoveSessionCookie();
// true was here to force new session creation, but I (kostja) used
// RemoveCookie a line above, to avoid redirect loop with expired sid
// not being removed setSession with true was used before, to set NEW
// session cookie
$this->SetSession();
// case #1: I've OR other site visitor expired my session
// case #2: I have no session in database, but SID is present
$expire_event = new kEvent('u:OnSessionExpire');
$this->Application->HandleEvent($expire_event);
}
}
function IsHTTPSRedirect()
{
$http_referer = getArrayValue($_SERVER, 'HTTP_REFERER');
return (
( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) )
||
( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) )
);
}
function CheckReferer($for_cookies=0)
{
if (!$for_cookies) {
if ( !$this->Application->ConfigValue('SessionReferrerCheck') || $_SERVER['REQUEST_METHOD'] != 'POST') {
return true;
}
}
$path = preg_replace('/admin[\/]{0,1}$/', '', $this->CookiePath); // removing /admin for compatability with in-portal (in-link/admin/add_link.php)
$reg = '#^'.preg_quote(PROTOCOL.ltrim($this->CookieDomain, '.').$path).'#';
return preg_match($reg, getArrayValue($_SERVER, 'HTTP_REFERER') ) || (defined('IS_POPUP') && IS_POPUP);
}
- /*function CheckDuplicateCookies()
- {
- if (isset($_SERVER['HTTP_COOKIE'])) {
- $cookie_str = $_SERVER['HTTP_COOKIE'];
- $cookies = explode('; ', $cookie_str);
- $all_cookies = array();
- foreach ($cookies as $cookie) {
- list($name, $value) = explode('=', $cookie);
- if (isset($all_cookies[$name])) {
- //double cookie name!!!
- $this->RemoveCookie($name);
- }
- else $all_cookies[$name] = $value;
- }
- }
- }
-
- function RemoveCookie($name)
- {
- $path = $_SERVER['PHP_SELF'];
- $path_parts = explode('/', $path);
- $cur_path = '';
- setcookie($name, false, null, $cur_path);
- foreach ($path_parts as $part) {
- $cur_path .= $part;
- setcookie($name, false, null, $cur_path);
- $cur_path .= '/';
- setcookie($name, false, null, $cur_path);
- }
- }*/
-
function CheckIfCookiesAreOn()
{
-// $this->CheckDuplicateCookies();
- if ($this->Mode == smGET_ONLY)
- {
+ if ($this->Mode == smGET_ONLY) {
//we don't need to bother checking if we would not use it
$this->CookiesEnabled = false;
return;
}
+
$http_query =& $this->Application->recallObject('HTTPQuery');
- $cookies_on = isset($http_query->Cookie['cookies_on']); // not good here
+ $cookies_on = array_key_exists('cookies_on', $http_query->Cookie); // not good here
$get_sid = getArrayValue($http_query->Get, $this->GETName);
- if ($this->IsHTTPSRedirect() && $get_sid) { //Redirect from http to https on different domain
+
+ if ($this->IsHTTPSRedirect() && $get_sid) { // Redirect from http to https on different domain
$this->OriginalMode = $this->Mode;
$this->SetMode(smGET_ONLY);
}
if (!$cookies_on || $this->IsHTTPSRedirect()) {
//If referer is our server, but we don't have our cookies_on, it's definetly off
$is_install = defined('IS_INSTALL') && IS_INSTALL;
if (!$is_install && $this->CheckReferer(1) && !$this->Application->GetVar('admin') && !$this->IsHTTPSRedirect()) {
$this->CookiesEnabled = false;
}
else {
//Otherwise we still suppose cookies are on, because may be it's the first time user visits the site
//So we send cookies on to get it next time (when referal will tell us if they are realy off
$this->SetCookie('cookies_on', 1, adodb_mktime() + 31104000); //one year should be enough
}
}
- else
+ else {
$this->CookiesEnabled = true;
+ }
+
return $this->CookiesEnabled;
}
/**
* Sets cookie for current site using path and domain
*
* @param string $name
* @param mixed $value
* @param int $expires
*/
function SetCookie($name, $value, $expires = null)
{
setcookie($name, $value, $expires, $this->CookiePath, $this->CookieDomain, $this->CookieSecure);
}
function Check()
{
// we should check referer if cookies are disabled, and in combined mode
// auto mode would detect cookies, get only mode would turn it off - so we would get here
// and we don't care about referal in cookies only mode
if ( $this->Mode != smCOOKIES_ONLY && (!$this->CookiesEnabled || $this->Mode == smCOOKIES_AND_GET) ) {
if (!$this->CheckReferer())
return false;
}
$sid = $this->GetPassedSIDValue();
if (empty($sid)) return false;
//try to load session by sid, if everything is fine
$result = $this->LoadSession($sid);
- $this->SessionSet = $result;
+ $this->SessionSet = $result; // fake front-end session will given "false" here
return $result;
}
function LoadSession($sid)
{
if( $this->Storage->LocateSession($sid) ) {
//if we have session with such SID - get its expiration
$this->Expiration = $this->Storage->GetExpiration();
//If session has expired
if ($this->Expiration < adodb_mktime()) {
$this->Destroy();
// when Destory methods calls SetSession inside and new session get created
return $this->SessionSet;
}
//Otherwise it's ok
return true;
}
else //fake or deleted due to expiration SID
return false;
}
function GetPassedSIDValue($use_cache = 1)
{
- if (!empty($this->CachedSID) && $use_cache) return $this->CachedSID;
+ if (!empty($this->CachedSID) && $use_cache) {
+ return $this->CachedSID;
+ }
+
$http_query =& $this->Application->recallObject('HTTPQuery');
$get_sid = getArrayValue($http_query->Get, $this->GETName);
+ $sid_from_get = $get_sid ? true : false;
if ($this->Application->GetVar('admin') == 1 && $get_sid) {
$sid = $get_sid;
}
else {
switch ($this->Mode) {
case smAUTO:
//Cookies has the priority - we ignore everything else
$sid = $this->CookiesEnabled ? $this->GetSessionCookie() : $get_sid;
+
+ if ($this->CookiesEnabled) {
+ $sid_from_get = false;
+ }
break;
+
case smCOOKIES_ONLY:
$sid = $this->GetSessionCookie();
break;
+
case smGET_ONLY:
$sid = $get_sid;
break;
+
case smCOOKIES_AND_GET:
$cookie_sid = $this->GetSessionCookie();
//both sids should match if cookies are enabled
- if (!$this->CookiesEnabled || ($cookie_sid == $get_sid))
- {
+ if (!$this->CookiesEnabled || ($cookie_sid == $get_sid)) {
$sid = $get_sid; //we use get here just in case cookies are disabled
}
- else
- {
+ else {
$sid = '';
+ $sid_from_get = false;
}
break;
}
}
-
$this->CachedSID = $sid;
+ $this->_fromGet = $sid_from_get;
+
return $this->CachedSID;
}
/**
* Returns session id
*
* @return int
* @access public
*/
function GetID()
{
return $this->SID;
}
/**
* Generates new session id
*
* @return int
* @access private
*/
function GenerateSID()
{
list($usec, $sec) = explode(" ",microtime());
$sid_part_1 = substr($usec, 4, 4);
$sid_part_2 = mt_rand(1,9);
$sid_part_3 = substr($sec, 6, 4);
$digit_one = substr($sid_part_1, 0, 1);
if ($digit_one == 0) {
$digit_one = mt_rand(1,9);
$sid_part_1 = ereg_replace("^0","",$sid_part_1);
$sid_part_1=$digit_one.$sid_part_1;
}
$this->setSID($sid_part_1.$sid_part_2.$sid_part_3);
return $this->SID;
}
/**
* Set's new session id
*
* @param int $new_sid
* @access private
*/
function setSID($new_sid)
{
- $this->SID = $this->CachedSID = $new_sid;
+ $this->SID /*= $this->CachedSID*/ = $new_sid; // don't set cached sid here
$this->Application->SetVar($this->GETName,$new_sid);
}
function NeedSession()
{
$data = $this->Data->GetParams();
+
$data_keys = array_keys($data);
- $optional_keys = array_unique($this->OptionalData);
+ $optional_keys = array_keys($this->OptionalData);
$real_keys = array_diff($data_keys, $optional_keys);
+
return $real_keys ? true : false;
}
function SetSession($force = false)
{
- if ($this->SessionSet && !$force) return true;
+ if ($this->SessionSet && !$force) {
+ return true;
+ }
+
if (!$force && !($this->Application->IsAdmin() || $this->Application->GetVar('admin')) && !$this->NeedSession()) {
// don't create session (in db) on Front-End, when sid is present (GPC), but data in db isn't
- $this->GenerateSID();
+ if ($this->_fromGet) {
+ // set sid, that was given in GET
+ $this->setSID( $this->GetPassedSIDValue() );
+ } else {
+ // re-generate sid only, when cookies are used
+ $this->GenerateSID();
+ }
return false;
}
- if (!$this->SID || $force) $this->GenerateSID();
+ if (!$this->SID || $force) {
+ $this->GenerateSID();
+ }
+
$this->Expiration = adodb_mktime() + $this->SessionTimeout;
+
switch ($this->Mode) {
case smAUTO:
if ($this->CookiesEnabled) {
$this->SetSessionCookie();
}
break;
+
case smGET_ONLY:
break;
+
case smCOOKIES_ONLY:
case smCOOKIES_AND_GET:
$this->SetSessionCookie();
break;
}
+
$this->Storage->StoreSession($this);
if ($this->Application->IsAdmin() || $this->Special == 'admin') {
$this->StoreVar('admin', 1);
}
$this->SessionSet = true; // should be called before SaveData, because SaveData will try to SetSession again
if ($this->Special != '') {
// front-session called from admin or otherwise, then save it's data
$this->SaveData();
}
$this->Application->resetCounters('UserSession');
return true;
}
/**
* Returns SID from cookie.
*
* Use 2 cookies to have 2 expiration:
* - 1. for normal expiration when browser is not closed (30 minutes by default), configurable
* - 2. for advanced expiration when browser is closed
*
* @return int
*/
function GetSessionCookie()
{
$keep_session_on_browser_close = $this->Application->ConfigValue('KeepSessionOnBrowserClose');
if (isset($this->Application->HttpQuery->Cookie[$this->CookieName]) &&
( $keep_session_on_browser_close ||
(
!$keep_session_on_browser_close &&
isset($this->Application->HttpQuery->Cookie[$this->CookieName.'_live'])
&&
$this->Application->HttpQuery->Cookie[$this->CookieName] == $this->Application->HttpQuery->Cookie[$this->CookieName.'_live']
)
)
) {
return $this->Application->HttpQuery->Cookie[$this->CookieName];
}
return false;
}
/**
* Updates SID in cookie with new value
*
*/
function SetSessionCookie()
{
$this->SetCookie($this->CookieName, $this->SID, $this->Expiration);
$this->SetCookie($this->CookieName.'_live', $this->SID);
$_COOKIE[$this->CookieName] = $this->SID; // for compatibility with in-portal
}
function RemoveSessionCookie()
{
$this->SetCookie($this->CookieName, '');
$this->SetCookie($this->CookieName.'_live', '');
$_COOKIE[$this->CookieName] = null; // for compatibility with in-portal
}
/**
* Refreshes session expiration time
*
* @access private
*/
function Refresh()
{
if ($this->Application->GetVar('skip_session_refresh')) {
return ;
}
if ($this->CookiesEnabled) {
// we need to refresh the cookie
$this->SetSessionCookie();
}
$this->Storage->UpdateSession($this);
}
function Destroy()
{
$this->Storage->DeleteSession($this);
$this->Data = new Params();
$this->SID = $this->CachedSID = '';
$this->SessionSet = false;
- if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty
+// if ($this->CookiesEnabled) {
+ // remove cookie, because we will have fake session and it should be getting sid left in cookies
+ $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty
+// }
$this->SetSession(true); //will create a new session, true to force
}
function NeedQueryString($use_cache = 1)
{
- if ($this->CachedNeedQueryString != null && $use_cache) return $this->CachedNeedQueryString;
+ if ($this->CachedNeedQueryString != null && $use_cache) {
+ return $this->CachedNeedQueryString;
+ }
$result = false;
- switch ($this->Mode)
- {
+ switch ($this->Mode) {
case smAUTO:
- if (!$this->CookiesEnabled) $result = true;
+ if (!$this->CookiesEnabled) {
+ $result = true;
+ }
break;
+
/*case smCOOKIES_ONLY:
break;*/
+
case smGET_ONLY:
case smCOOKIES_AND_GET:
$result = true;
break;
}
+
$this->CachedNeedQueryString = $result;
+
return $result;
}
function LoadData()
{
$this->Data->AddParams($this->Storage->LoadData($this));
}
- function PrintSession($comment='')
+ function PrintSession($comment = '')
{
if (defined('DEBUG_MODE') && $this->Application->isDebugMode() && constOn('DBG_SHOW_SESSIONDATA')) {
// dump session data
$this->Application->Debugger->appendHTML('SessionStorage [' . ($this->RecallVar('admin') == 1 ? 'Admin' : 'Front-End') . '] ('.$comment.'):');
$session_data = $this->Data->GetParams();
ksort($session_data);
foreach ($session_data as $session_key => $session_value) {
if (IsSerialized($session_value)) {
$session_data[$session_key] = unserialize($session_value);
}
}
$this->Application->Debugger->dumpVars($session_data);
+
+ // dump real keys
+ $data_keys = array_keys($session_data);
+ $optional_keys = array_keys($this->OptionalData);
+ $real_keys = array_diff($data_keys, $optional_keys);
+
+ if ($real_keys) {
+ $ret = '';
+ foreach ($real_keys as $real_key) {
+ $ret .= '[' . $real_key . '] = [' . $session_data[$real_key] . ']<br/>';
+ }
+
+ $this->Application->Debugger->appendHTML('Real Keys:<br/> ' . $ret);
+ }
}
+
if (defined('DEBUG_MODE') && $this->Application->isDebugMode() && constOn('DBG_SHOW_PERSISTENTDATA')) {
// dump persistent session data
if ($this->Storage->PersistentVars) {
$this->Application->Debugger->appendHTML('Persistant Session:');
$session_data = $this->Storage->PersistentVars;
ksort($session_data);
foreach ($session_data as $session_key => $session_value) {
if (IsSerialized($session_value)) {
$session_data[$session_key] = unserialize($session_value);
}
}
$this->Application->Debugger->dumpVars($session_data);
}
}
}
function SaveData($params = Array ())
{
if (!$this->SetSession()) { // call it here - it may be not set before, because there was no need; if there is a need, it will be set here
return;
}
if (!$this->Application->GetVar('skip_last_template') && $this->Application->GetVar('ajax') != 'yes') {
$this->SaveLastTemplate( $this->Application->GetVar('t'), $params );
}
$this->PrintSession('after save');
$this->Storage->SaveData($this);
}
/**
* Save last template
*
* @param string $t
* @param Array $params
*/
function SaveLastTemplate($t, $params = Array ())
{
$wid = $this->Application->GetVar('m_wid');
$last_env = $this->getLastTemplateENV($t, Array('m_opener' => 'u'));
$last_template = basename($_SERVER['PHP_SELF']).'|'.mb_substr($last_env, mb_strlen(ENV_VAR_NAME) + 1);
$this->StoreVar(rtrim('last_template_'.$wid, '_'), $last_template);
// prepare last_template for opener stack, module & session could be added later
$last_env = $this->getLastTemplateENV($t, null, false);
$last_template = basename($_SERVER['PHP_SELF']).'|'.mb_substr($last_env, mb_strlen(ENV_VAR_NAME) + 1);
// save last_template in persistant session
if (!$wid) {
if ($this->Application->IsAdmin()) {
// only for main window, not popups, not login template, not temp mode (used in adm:MainFrameLink tag)
$temp_mode = false;
$passed = explode(',', $this->Application->GetVar('passed'));
foreach ($passed as $passed_prefix) {
if ($this->Application->GetVar($passed_prefix.'_mode')) {
$temp_mode = true;
break;
}
}
if (!$temp_mode) {
if (isset($this->Application->HttpQuery->Get['section'])) {
// check directly in GET, bacause LinkVar (session -> request) used on these vars
$last_template .= '&section='.$this->Application->GetVar('section').'&module='.$this->Application->GetVar('module');
}
$this->StorePersistentVar('last_template_popup', $last_template);
}
}
elseif ($this->Application->GetVar('admin')) {
// admin checking by session data to prevent recursive session save
if (!$this->RecallVar('admin')) {
+ // bug: we get recursion in this place, when cookies are disabled in browser and we are browsing
+ // front-end in admin's frame (front-end session is initialized using admin's sid and they are
+ // mixed together)
+
$admin_session =& $this->Application->recallObject('Session.admin');
/* @var $admin_session Session */
// save to admin last_template too, because when F5 is pressed in frameset Front-End frame should reload as well
$admin_session->StoreVar('last_template_popup', '../' . $last_template);
$admin_session->StorePersistentVar('last_template_popup', '../' . $last_template);
$admin_session->SaveData( Array ('save_last_template' => false) );
}
else {
// don't allow admin=1 & editing_mode=* to get in admin last_template
$last_template = preg_replace('/&(admin|editing_mode)=[\d]/', '', $last_template);
}
}
}
// save other last... variables for mistical purposes (customizations may be)
$this->StoreVar('last_url', $_SERVER['REQUEST_URI']); // needed by ord:StoreContinueShoppingLink
$this->StoreVar('last_env', mb_substr($last_env, mb_strlen(ENV_VAR_NAME)+1));
$save_last_template = array_key_exists('save_last_template', $params) ? $params['save_last_template'] : true;
if ($save_last_template) {
// save last template here, becase section & module could be added before
$this->StoreVar(rtrim('last_template_popup_'.$wid, '_'), $last_template);
}
}
function getLastTemplateENV($t, $params = null, $encode = true)
{
if (!isset($params)) {
$params = Array ();
}
$params['__URLENCODE__'] = 1; // uses "&" instead of "&amp;" for url part concatenation + replaces "\" to "%5C" (works in HTML)
+
+ if ($this->Application->GetVar('admin') && !array_key_exists('admin', $params) && !defined('EDITING_MODE')) {
+ $params['editing_mode'] = ''; // used in kApplication::Run
+ }
+
$params = array_merge($this->Application->getPassThroughVariables($params), $params);
$ret = $this->Application->BuildEnv($t, $params, 'all');
if (!$encode) {
// cancels 2nd part of replacements, that URLENCODE does
$ret = str_replace('%5C', '\\', $ret);
}
return $ret;
}
function StoreVar($name, $value, $optional = false)
{
$this->Data->Set($name, $value);
+
if ($optional) {
- $this->OptionalData[] = $name;
+ // make variable optional, also remember optional value
+ $this->OptionalData[$name] = $value;
+ }
+ elseif (!$optional && array_key_exists($name, $this->OptionalData)) {
+ if ($this->OptionalData[$name] == $value) {
+ // same value as optional -> don't remove optional mark
+ return ;
+ }
+
+ // make variable non-optional
+ unset($this->OptionalData[$name]);
}
}
function StorePersistentVar($name, $value)
{
$this->Storage->StorePersistentVar($this, $name, $value);
}
function LoadPersistentVars()
{
$this->Storage->LoadPersistentVars($this);
}
function StoreVarDefault($name, $value, $optional=false)
{
$tmp = $this->RecallVar($name);
if($tmp === false || $tmp == '')
{
$this->StoreVar($name, $value, $optional);
}
}
function RecallVar($name, $default = false)
{
$ret = $this->Data->Get($name);
return ($ret === false) ? $default : $ret;
}
function RecallPersistentVar($name, $default = false)
{
return $this->Storage->RecallPersistentVar($this, $name, $default);
}
function RemoveVar($name)
{
$this->Storage->RemoveFromData($this, $name);
$this->Data->Remove($name);
}
function RemovePersistentVar($name)
{
return $this->Storage->RemovePersistentVar($this, $name);
}
/**
* Ignores session varible value set before
*
* @param string $name
*/
function RestoreVar($name)
{
- return $this->StoreVar($name, $this->Storage->GetFromData($this, $name));
+ $value = $this->Storage->GetFromData($this, $name, '__missing__');
+
+ if ($value === '__missing__') {
+ // there is nothing to restore (maybe session was not saved), look in optional variable values
+ $value = array_key_exists($name, $this->OptionalData) ? $this->OptionalData[$name] : false;
+ }
+
+ return $this->StoreVar($name, $value);
}
function GetField($var_name, $default = false)
{
return $this->Storage->GetField($this, $var_name, $default);
}
function SetField($var_name, $value)
{
$this->Storage->SetField($this, $var_name, $value);
}
/**
* Deletes expired sessions
*
* @return Array expired sids if any
* @access private
*/
function DeleteExpired()
{
return $this->Storage->DeleteExpired();
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
*/
function LoggedIn()
{
$user_id = $this->RecallVar('user_id');
$ret = $user_id > 0;
if (($this->RecallVar('admin') == 1 || defined('ADMIN')) && ($user_id == -1)) {
$ret = true;
}
return $ret;
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/utility/http_query.php
===================================================================
--- branches/5.0.x/core/kernel/utility/http_query.php (revision 12367)
+++ branches/5.0.x/core/kernel/utility/http_query.php (revision 12368)
@@ -1,649 +1,670 @@
<?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 kHTTPQuery extends Params {
/**
* $_POST vars
*
* @var Array
* @access private
*/
var $Post;
/**
* $_GET vars
*
* @var Array
* @access private
*/
var $Get;
/**
* $_COOKIE vars
*
* @var Array
* @access private
*/
var $Cookie;
/**
* $_SERVER vars
*
* @var Array
* @access private
*/
var $Server;
/**
* $_ENV vars
*
* @var Array
* @access private
*/
var $Env;
/**
* Order in what write
* all vars together in
* the same array
*
* @var string
*/
var $Order;
/**
* Uploaded files info
*
* @var Array
* @access private
*/
var $Files;
var $specialsToRemove = Array();
var $Admin = false;
/**
* Description
*
* @var kDBConnection
* @access public
*/
var $Conn;
/**
+ * SessionID is given via "sid" variable in query string
+ *
+ * @var bool
+ */
+ var $_sidInQueryString = false;
+
+ /**
* Loads info from $_POST, $_GET and
* related arrays into common place
*
* @param string $order
* @return HTTPQuery
* @access public
*/
function kHTTPQuery($order = 'CGPF')
{
parent::Params();
$this->Conn =& $this->Application->GetADODBConnection();
$this->Order = $order;
$this->Admin = $this->Application->IsAdmin(); // better cache this value, not to calculate it each time in foreach
if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
// when AJAX request is made from jQuery, then create ajax variable,
// so any logic based in it (like redirects) will not break down
$_GET['ajax'] = 'yes';
}
}
function Init($prefix, $special)
{
$this->AddAllVars();
$this->specialsToRemove = $this->Get('remove_specials');
if ($this->specialsToRemove) {
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ($flag && strpos($prefix_special, '.') === false) {
unset($this->specialsToRemove[$prefix_special]);
trigger_error('Incorrect usage of "<strong>remove_specials['.$prefix_special.']</strong>" field (no special found)', E_USER_WARNING);
}
}
$this->_Params = $this->removeSpecials($this->_Params);
}
ini_set('magic_quotes_gpc', 0);
}
function removeSpecials($array)
{
$ret = Array();
$removed = false;
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ($flag) {
$removed = true;
list ($prefix,$special) = explode('.', $prefix_special, 2);
foreach ($array as $key => $val) {
$new_key = preg_match("/^".$prefix."[._]{1}".$special."(.*)/", $key, $regs) ? $prefix.$regs[1] : $key;
$ret[$new_key] = is_array($val) ? $this->removeSpecials($val) : $val;
}
}
}
return $removed ? $ret : $array;
}
/**
* All all requested vars to
* common storage place
*
* @access private
*/
function AddAllVars()
{
for ($i=0; $i < strlen($this->Order); $i++)
{
$current = $this->Order[$i];
switch ($current) {
case 'G':
$this->Get = $this->AddVars($_GET);
+ if (array_key_exists('sid', $_GET)) {
+ $this->_sidInQueryString = true;
+ }
+
$vars = $this->processQueryString( $this->Get(ENV_VAR_NAME) );
+
+ if (array_key_exists('sid', $vars)) {
+ // used by Session::GetPassedSIDValue
+ $this->Get['sid'] = $vars['sid'];
+ }
+
$this->AddParams($vars);
break;
case 'P':
$this->Post = $this->AddVars($_POST);
$this->convertPostEvents();
$this->_processPostEnvVariables();
break;
case 'C':
$this->Cookie = $this->AddVars($_COOKIE);
break;
case 'E';
$this->Env = $this->AddVars($_ENV);
break;
case 'S';
$this->Server = $this->AddVars($_SERVER);
break;
case 'F';
$this->convertFiles();
$this->Files = $this->MergeVars($_FILES, false); //do not strip slashes!
break;
}
}
// $this->AfterInit();
}
/**
* Allow POST variables, that names were transformed by PHP ("." replaced with "_") to
* override variables, that were virtually created through enviroment variable parsing
*
*/
function _processPostEnvVariables()
{
$passed = $this->Get('passed');
if (!$passed) {
return ;
}
$passed = explode(',', $passed);
foreach ($passed as $prefix_special) {
if (strpos($prefix_special, '.') === false) {
continue;
}
list ($prefix, $special) = explode('.', $prefix_special);
$query_map = $this->Application->EventManager->getQueryMap($prefix);
$post_prefix_special = $prefix . '_' . $special;
foreach ($query_map as $index => $var_name) {
if (array_key_exists($post_prefix_special . '_' . $var_name, $this->Post)) {
$this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]);
}
}
}
}
function AfterInit()
{
// $vars = $this->processQueryString($this->Get(ENV_VAR_NAME));
// $this->AddParams($vars);
// $this->convertPostEvents();
// if ($this->Application->RewriteURLs()) {
if ($this->Application->RewriteURLs() || $this->Get('_mod_rw_url_')) {
if (defined('DEBUG_MODE') && $this->Application->isDebugMode()) {
$this->Application->Debugger->profileStart('url_parsing');
$this->processRewriteURL();
$description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>'.$this->Get('t').'</b>)';
$this->Application->Debugger->profileFinish('url_parsing', $description);
}
else {
$this->processRewriteURL();
}
}
else {
$this->Application->VerifyLanguageId();
$this->Application->VerifyThemeId();
}
}
function convertFiles()
{
if (!$_FILES)
{
return false;
}
$file_keys = Array('error','name','size','tmp_name','type');
$tmp = Array();
foreach($_FILES as $file_name => $file_info)
{
if( is_array($file_info['error']) )
{
$tmp[$file_name] = $this->getArrayLevel( $file_info['error'], $file_name );
}
else
{
$normal_files[$file_name] = $file_info;
}
}
if(!$tmp) return false;
$files = $_FILES;
$_FILES = Array();
foreach($tmp as $prefix => $prefix_files)
{
$anchor =& $_FILES;
foreach($prefix_files['keys'] as $key)
{
$anchor =& $anchor[$key];
}
foreach($prefix_files['value'] as $field_name)
{
unset($inner_anchor);
unset($copy);
$work_copy = $prefix_files['keys'];
foreach($file_keys as $file_key)
{
$inner_anchor =& $files[$prefix][$file_key];
if (isset($copy))
{
$work_copy = $copy;
}
else
{
$copy = $work_copy;
}
array_shift($work_copy);
foreach($work_copy as $prefix_file_key)
{
$inner_anchor =& $inner_anchor[$prefix_file_key];
}
$anchor[$field_name][$file_key] = $inner_anchor[$field_name];
}
}
}
// keys: img_temp, 0, values: LocalPath, ThumbPath
}
function getArrayLevel(&$level, $prefix='')
{
$ret['keys'] = $prefix ? Array($prefix) : Array();
$ret['value'] = Array();
foreach($level as $level_key => $level_value)
{
if( is_array($level_value) )
{
$ret['keys'][] = $level_key;
$tmp = $this->getArrayLevel($level_value);
$ret['keys'] = array_merge($ret['keys'], $tmp['keys']);
$ret['value'] = array_merge($ret['value'], $tmp['value']);
}
else
{
$ret['value'][] = $level_key;
}
}
return $ret;
}
/**
* Owerwrites GET events with POST events in case if they are set and not empty
*
*/
function convertPostEvents()
{
$events = $this->Get('events');
if (is_array($events)) {
foreach ($events as $prefix_special => $event_name) {
if ($event_name) {
$this->Set($prefix_special.'_event', $event_name);
}
}
}
}
function finalizeParsing($passed = Array(), $module_params = Array() )
{
if ($passed) {
$event_manger =& $this->Application->recallObject('EventManager');
foreach ($passed as $passed_prefix) {
$event_manger->setQueryMap($passed_prefix);
}
$this->Set('passed', implode(',', array_keys($event_manger->queryMaps)));
}
// get joined version (env var + mod rewrite parsed)
$passed = $this->Application->GetVar('passed');
$env = $this->Application->BuildEnv( $this->Get('t'), $module_params, $passed, false, false );
$this->Set(ENV_VAR_NAME, $env);
$_REQUEST['env'] = $_GET['env'] = $env; // for capability with old in-portal code
}
function processRewriteURL()
{
$mod_rw_helper = $this->Application->recallObject('ModRewriteHelper');
$mod_rw_helper->processRewriteURL();
}
function getDefaultTemplate($t)
{
$t = $this->getTemplateName( trim($t, '/') );
if (!$t) $t = 'index';
return trim($t, '/');
}
function extractSIDAndTemplate(&$parts)
{
$vars = Array ();
$sub_parts = array_shift($parts);
list ($sid, $t) = explode('-', $sub_parts, 2);
if ($sid) {
// Save Session ID
$this->Set('sid', $sid);
$vars['sid'] = $sid;
}
// Save Template Name
$vars['t'] = $this->getDefaultTemplate($t);
return $vars;
}
/**
* Process QueryString only, create
* events, ids, based on config
* set template name and sid in
* desired application variables.
*
* @param string $env_var enviroment string value
*
* @access public
*/
function processQueryString($env_var, $pass_name = 'passed')
{
// env=SID:TEMPLATE:m-1-1-1-1:l0-0-0:n-0-0-0:bb-0-0-1-1-1-0
$vars = Array ();
if ($env_var) {
$more_vars = strpos($env_var, '&');
if ($more_vars !== false) {
parse_str(substr($env_var, $more_vars + 1), $vars);
$env_var = substr($env_var, 0, $more_vars);
}
// replace escaped ":" symbol not to explode by it
$env_var = str_replace('\:','_&+$$+&_', $env_var); // replace escaped "=" with spec-chars :)
$parts = explode(':', $env_var);
if (!$this->Application->RewriteURLs() || ($this->Application->RewriteURLs() && $this->Get('rewrite') != 'on')) {
$vars = array_merge_recursive2($vars, $this->extractSIDAndTemplate($parts));
}
if ($parts) {
$passed = Array ();
foreach ($parts as $mixed_part) {
list ($passed[], $processed_vars) = $this->_parseEnvPart($mixed_part);
$vars = array_merge_recursive2($vars, $processed_vars);
}
$vars[$pass_name] = implode(',', array_unique($passed));
}
}
else {
$t = $this->getTemplateName('index');
$vars['t'] = $t;
}
return $vars;
}
/**
* Converts enviroment part into variable array (based on query map for given prefix)
*
* @param string $mixed_part
* @return Array
*/
function _parseEnvPart($mixed_part)
{
// In-portal old style env conversion - adds '-' between prefix and first var
$mixed_part = str_replace('_&+$$+&_', ':', $mixed_part);
$mixed_part = preg_replace("/^([a-zA-Z]+)([0-9]+)-(.*)/", "$1-$2-$3", $mixed_part);
// replace escaped "-" symbol not to explode by it
$escaped_part = str_replace('\-', '_&+$$+&_', $mixed_part);
$escaped_part = explode('-', $escaped_part);
$mixed_part = Array();
foreach ($escaped_part as $escaped_val) {
$mixed_part[] = str_replace('_&+$$+&_', '-', $escaped_val);
}
$prefix_special = array_shift($mixed_part); // l.pick, l
$query_map = $this->Application->EventManager->setQueryMap($prefix_special);
$vars = Array ();
// if config is not defined for prefix in QueryString, then don't process it
if ($query_map) {
foreach ($query_map as $index => $var_name) {
// l_id, l_page, l_bla-bla-bla
$val = $mixed_part[$index - 1];
if ($val == '') $val = false;
$vars[$prefix_special.'_'.$var_name] = $val;
}
}
return Array ($prefix_special, $vars);
}
/**
* Removes tpl part from template name + resolved template ID to name
*
* @param string $t
* @return string
* @access private
*/
function getTemplateName($t)
{
if (isset($this->Get['t']) && $this->Get['t']) {
// template name is passed directly in url (GET method)
$t = $this->Get['t'];
}
// if t was set through env, even in mod_rewrite mode!
if ($this->Get('env') && $this->Application->RewriteURLs() && $this->Get('t')) {
$t = $this->Get('t');
}
if (is_numeric($t)) {
$sql = 'SELECT CONCAT(FilePath, \'/\', FileName)
FROM '.TABLE_PREFIX.'ThemeFiles
WHERE FileId = '.$t;
$t = $this->Conn->GetOne($sql);
}
return preg_replace('/\.tpl$/', '', $t);
}
/**
* Saves variables from array specified
* into common variable storage place
*
* @param Array $array
* @return Array
* @access private
*/
function AddVars($array)
{
$array = $this->StripSlashes($array);
foreach($array as $key => $value)
{
$this->Set($key,$value);
}
return $array;
}
function MergeVars($array, $strip_slashes=true)
{
if ($strip_slashes) $array = $this->StripSlashes($array);
foreach($array as $key => $value)
{
$this->_Params = array_merge_recursive2($this->_Params, Array($key=>$value));
}
return $array;
}
function StripSlashes($array)
{
//if( !get_magic_quotes_gpc() ) return $array;
foreach($array as $key=>$value)
{
if( is_array($value) )
{
$array[$key] = $this->StripSlashes($value);
}
else
{
// $value = iconv('UTF-8', 'windows-1257', $value);
if( get_magic_quotes_gpc() ) $value = stripslashes($value);
if(!$this->Admin) $value = htmlspecialchars($value);
$array[$key] = $value;
}
//$array[$key]=is_array($value)?$this->StripSlashes($value):stripslashes($value);
}
return $array;
}
/**
* Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls
*
* @param bool $access_error Method is called during no_permission, require login, session expiration link preparation
* @return Array
*/
function getRedirectParams($access_error = false)
{
$unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action');
+ if (!$this->_sidInQueryString) {
+ $unset_vars[] = 'sid';
+ }
+
$ret = $this->Get;
foreach ($unset_vars as $var_name) {
if( isset($ret[$var_name]) ) {
unset( $ret[$var_name] );
}
}
if ($access_error) {
$ret = $this->_removePassThroughVariables($ret);
if ($this->Application->IsAdmin()) {
// place 1 of 2 (also in UsersEventHandler::OnSessionExpire)
$ret['m_cat_id'] = 0; // category means nothing on admin login screen
$ret['m_wid'] = ''; // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets)
$ret['pass'] = 'm'; // don't pass any other (except "m") prefixes to admin login template
}
}
return $ret;
}
/**
* Removes all pass_though variables from redirect params
*
* @param Array $url_params
* @return Array
*/
function _removePassThroughVariables($url_params)
{
$pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : '';
if (!$pass_through) {
return $url_params;
}
$pass_through = explode(',', $pass_through . ',pass_through');
foreach ($pass_through as $pass_through_var) {
unset($url_params[$pass_through_var]);
}
$url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again
return $url_params;
}
function writeRequestLog($filename)
{
$folder_path = dirname(FULL_PATH.'/'.$filename);
if (is_writable($folder_path)) {
$fp = fopen(FULL_PATH.'/'.$filename, 'a');
if ($fp) {
$session =& $this->Application->recallObject('Session');
$user_id = $session->GetField('PortalUserId');
$admin_mark = $this->Application->IsAdmin() ? 'ADMIN' : 'FRONT';
$data = '['.date('D M d H:i:s Y').'] '.$admin_mark.'; ip: '.$_SERVER['REMOTE_ADDR'].'; user_id: '.$user_id.'; sid: '.$this->Application->GetSID().'; request: '."\n";
if ($this->Get) {
$data .= "_GET:\n".print_r($this->Get, true);
}
if ($this->Post) {
$data .= "_POST:\n".print_r($this->Post, true);
}
if ($this->Cookie) {
$data .= "_COOKIE:\n".print_r($this->Cookie, true);
}
$data .= str_repeat('=', 100)."\n";
fwrite($fp, $data);
fclose($fp);
}
else {
trigger_error('Requrest Log directory not writable', E_USER_WARNING);
}
}
else {
trigger_error('Requrest Log directory not writable', E_USER_WARNING);
}
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.0.x/core/kernel/db/db_event_handler.php (revision 12367)
+++ branches/5.0.x/core/kernel/db/db_event_handler.php (revision 12368)
@@ -1,2516 +1,2516 @@
<?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('EH_CUSTOM_PROCESSING_BEFORE',1);
define('EH_CUSTOM_PROCESSING_AFTER',2);
/**
* Note:
* 1. When adressing variables from submit containing
* Prefix_Special as part of their name use
* $event->getPrefixSpecial(true) instead of
* $event->Prefix_Special as usual. This is due PHP
* is converting "." symbols in variable names during
* submit info "_". $event->getPrefixSpecial optional
* 1st parameter returns correct corrent Prefix_Special
* for variables beeing submitted such way (e.g. variable
* name that will be converted by PHP: "users.read_only_id"
* will be submitted as "users_read_only_id".
*
* 2. When using $this->Application-LinkVar on variables submitted
* from form which contain $Prefix_Special then note 1st item. Example:
* LinkVar($event->getPrefixSpecial(true).'_varname',$event->Prefix_Special.'_varname')
*
*/
/**
* EventHandler that is used to process
* any database related events
*
*/
class kDBEventHandler extends kEventHandler {
/**
* Description
*
* @var kDBConnection
* @access public
*/
var $Conn;
/**
* Adds ability to address db connection
*
* @return kDBEventHandler
* @access public
*/
function kDBEventHandler()
{
parent::kBase();
$this->Conn =& $this->Application->GetADODBConnection();
}
/**
* Checks permissions of user
*
* @param kEvent $event
*/
function CheckPermission(&$event)
{
if (!$this->Application->IsAdmin()) {
$allow_events = Array('OnSearch', 'OnSearchReset', 'OnNew');
if (in_array($event->Name, $allow_events)) {
// allow search on front
return true;
}
}
$section = $event->getSection();
if (!preg_match('/^CATEGORY:(.*)/', $section)) {
// only if not category item events
if ((substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave')) {
if ($this->isNewItemCreate($event)) {
return $this->Application->CheckPermission($section.'.add', 1);
}
else {
return $this->Application->CheckPermission($section.'.add', 1) || $this->Application->CheckPermission($section.'.edit', 1);
}
}
}
if ($event->Name == 'OnPreCreate') {
// save category_id before item create (for item category selector not to destroy permission checking category)
$this->Application->LinkVar('m_cat_id');
}
if ($event->Name == 'OnSaveWidths') {
return $this->Application->IsAdmin() && $this->Application->LoggedIn();
}
return parent::CheckPermission($event);
}
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnLoad' => Array('self' => 'view', 'subitem' => 'view'),
'OnItemBuild' => Array('self' => 'view', 'subitem' => 'view'),
'OnBuild' => Array('self' => true),
'OnNew' => Array('self' => 'add', 'subitem' => 'add|edit'),
'OnCreate' => Array('self' => 'add', 'subitem' => 'add|edit'),
'OnUpdate' => Array('self' => 'edit', 'subitem' => 'add|edit'),
'OnSetPrimary' => Array('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnDelete' => Array('self' => 'delete', 'subitem' => 'add|edit'),
'OnDeleteAll' => Array('self' => 'delete', 'subitem' => 'add|edit'),
'OnMassDelete' => Array('self' => 'delete', 'subitem' => 'add|edit'),
'OnMassClone' => Array('self' => 'add', 'subitem' => 'add|edit'),
'OnCut' => array('self'=>'edit', 'subitem' => 'edit'),
'OnCopy' => array('self'=>'edit', 'subitem' => 'edit'),
'OnPaste' => array('self'=>'edit', 'subitem' => 'edit'),
'OnSelectItems' => Array('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnProcessSelected' => Array('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnSelectUser' => Array('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnMassApprove' => Array('self' => 'advanced:approve|edit', 'subitem' => 'advanced:approve|add|edit'),
'OnMassDecline' => Array('self' => 'advanced:decline|edit', 'subitem' => 'advanced:decline|add|edit'),
'OnMassMoveUp' => Array('self' => 'advanced:move_up|edit', 'subitem' => 'advanced:move_up|add|edit'),
'OnMassMoveDown' => Array('self' => 'advanced:move_down|edit', 'subitem' => 'advanced:move_down|add|edit'),
'OnPreCreate' => Array('self' => 'add|add.pending', 'subitem' => 'edit|edit.pending'),
'OnEdit' => Array('self' => 'edit|edit.pending', 'subitem' => 'edit|edit.pending'),
'OnExport' => Array('self' => 'view|advanced:export'),
'OnExportBegin' => Array('self' => 'view|advanced:export'),
'OnExportProgress' => Array('self' => 'view|advanced:export'),
'OnSetAutoRefreshInterval' => Array ('self' => true, 'subitem' => true),
'OnAutoRefreshToggle' => Array ('self' => true, 'subitem' => true),
// theese event do not harm, but just in case check them too :)
'OnCancelEdit' => Array('self' => true, 'subitem' => true),
'OnCancel' => Array('self' => true, 'subitem' => true),
'OnReset' => Array('self' => true, 'subitem' => true),
'OnSetSorting' => Array('self' => true, 'subitem' => true),
'OnSetSortingDirect' => Array('self' => true, 'subitem' => true),
'OnResetSorting' => Array('self' => true, 'subitem' => true),
'OnSetFilter' => Array('self' => true, 'subitem' => true),
'OnApplyFilters' => Array('self' => true, 'subitem' => true),
'OnRemoveFilters' => Array('self' => true, 'subitem' => true),
'OnSetFilterPattern' => Array('self' => true, 'subitem' => true),
'OnSetPerPage' => Array('self' => true, 'subitem' => true),
'OnSetPage' => Array('self' => true, 'subitem' => true),
'OnSearch' => Array('self' => true, 'subitem' => true),
'OnSearchReset' => Array('self' => true, 'subitem' => true),
'OnGoBack' => Array('self' => true, 'subitem' => true),
// it checks permission itself since flash uploader does not send cookies
'OnUploadFile' => Array ('self' => true, 'subitem' => true),
'OnDeleteFile' => Array ('self' => true, 'subitem' => true),
'OnViewFile' => Array ('self' => true, 'subitem' => true),
'OnSaveWidths' => Array ('self' => true, 'subitem' => true),
'OnValidateMInputFields' => Array ('self' => 'view'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
function mapEvents()
{
$events_map = Array(
'OnRemoveFilters' => 'FilterAction',
'OnApplyFilters' => 'FilterAction',
'OnMassApprove'=>'iterateItems',
'OnMassDecline'=>'iterateItems',
'OnMassMoveUp'=>'iterateItems',
'OnMassMoveDown'=>'iterateItems',
);
$this->eventMethods = array_merge($this->eventMethods, $events_map);
}
/**
* Returns ID of current item to be edited
* by checking ID passed in get/post as prefix_id
* or by looking at first from selected ids, stored.
* Returned id is also stored in Session in case
* it was explicitly passed as get/post
*
* @param kEvent $event
* @return int
*/
function getPassedID(&$event)
{
if ($event->getEventParam('raise_warnings') === false) {
$event->setEventParam('raise_warnings', 1);
}
if (preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1])) {
// <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object)
$main_object =& $this->Application->recallObject($regs[1]);
/* @var $main_object kDBItem */
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
return $main_object->GetDBField($id_field);
}
// 1. get id from post (used in admin)
$ret = $this->Application->GetVar($event->getPrefixSpecial(true).'_id');
if (($ret !== false) && ($ret != '')) {
return $ret;
}
// 2. get id from env (used in front)
$ret = $this->Application->GetVar($event->getPrefixSpecial().'_id');
if (($ret !== false) && ($ret != '')) {
return $ret;
}
// recall selected ids array and use the first one
$ids = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
if ($ids != '') {
$ids = explode(',',$ids);
if ($ids) {
$ret = array_shift($ids);
}
}
else { // if selected ids are not yet stored
$this->StoreSelectedIDs($event);
return $this->Application->GetVar($event->getPrefixSpecial().'_id'); // StoreSelectedIDs sets this variable
}
return $ret;
}
/**
* Prepares and stores selected_ids string
* in Session and Application Variables
* by getting all checked ids from grid plus
* id passed in get/post as prefix_id
*
* @param kEvent $event
* @param Array $direct_ids
*
* @return Array ids stored
*/
function StoreSelectedIDs(&$event, $direct_ids = null)
{
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
$ids = $event->getEventParam('ids');
if (isset($direct_ids) || ($ids !== false)) {
// save ids directly if they given + reset array indexes
$resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false);
if ($resulting_ids) {
$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids));
$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name);
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $resulting_ids[0]);
return $resulting_ids;
}
return Array ();
}
$ret = Array();
// May be we don't need this part: ?
$passed = $this->Application->GetVar($event->getPrefixSpecial(true).'_id');
if($passed !== false && $passed != '')
{
array_push($ret, $passed);
}
$ids = Array();
// get selected ids from post & save them to session
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info)
{
$id_field = $this->Application->getUnitOption($event->Prefix,'IDField');
foreach($items_info as $id => $field_values)
{
if( getArrayValue($field_values,$id_field) ) array_push($ids,$id);
}
//$ids=array_keys($items_info);
}
$ret = array_unique(array_merge($ret, $ids));
$this->Application->SetVar($event->getPrefixSpecial().'_selected_ids', implode(',',$ret));
- $this->Application->LinkVar($event->getPrefixSpecial().'_selected_ids', $session_name);
+ $this->Application->LinkVar($event->getPrefixSpecial().'_selected_ids', $session_name, '', !$ret); // optional when IDs are missing
// This is critical - otherwise getPassedID will return last ID stored in session! (not exactly true)
// this smells... needs to be refactored
$first_id = getArrayValue($ret,0);
if (($first_id === false) && ($event->getEventParam('raise_warnings') == 1)) {
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendTrace();
}
trigger_error('Requested ID for prefix <b>'.$event->getPrefixSpecial().'</b> <span class="debug_error">not passed</span>',E_USER_NOTICE);
}
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id);
return $ret;
}
/**
* Returns stored selected ids as an array
*
* @param kEvent $event
* @param bool $from_session return ids from session (written, when editing was started)
* @return array
*/
function getSelectedIDs(&$event, $from_session = false)
{
if ($from_session) {
$wid = $this->Application->GetTopmostWid($event->Prefix);
$var_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
$ret = $this->Application->RecallVar($var_name);
}
else {
$ret = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
}
return explode(',', $ret);
}
/**
* Returs associative array of submitted fields for current item
* Could be used while creating/editing single item -
* meaning on any edit form, except grid edit
*
* @param kEvent $event
*/
function getSubmittedFields(&$event)
{
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
$field_values = $items_info ? array_shift($items_info) : Array();
return $field_values;
}
/**
* Removes any information about current/selected ids
* from Application variables and Session
*
* @param kEvent $event
*/
function clearSelectedIDs(&$event)
{
$prefix_special = $event->getPrefixSpecial();
$ids = implode(',', $this->getSelectedIDs($event, true));
$event->setEventParam('ids', $ids);
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($prefix_special.'_selected_ids_'.$wid, '_');
$this->Application->RemoveVar($session_name);
$this->Application->SetVar($prefix_special.'_selected_ids', '');
$this->Application->SetVar($prefix_special.'_id', ''); // $event->getPrefixSpecial(true).'_id' too may be
}
/*function SetSaveEvent(&$event)
{
$this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnUpdate');
$this->Application->LinkVar($event->Prefix_Special.'_SaveEvent');
}*/
/**
* Common builder part for Item & List
*
* @param kDBBase $object
* @param kEvent $event
* @access private
*/
function dbBuild(&$object, &$event)
{
$object->Configure( $event->getEventParam('populate_ml_fields') || $this->Application->getUnitOption($event->Prefix, 'PopulateMlFields') );
$this->PrepareObject($object, $event);
// force live table if specified or is original item
$live_table = $event->getEventParam('live_table') || $event->Special == 'original';
if( $this->UseTempTables($event) && !$live_table )
{
$object->SwitchToTemp();
}
// This strange constuction creates hidden field for storing event name in form submit
// It pass SaveEvent to next screen, otherwise after unsuccsefull create it will try to update rather than create
$current_event = $this->Application->GetVar($event->Prefix_Special.'_event');
// $this->Application->setEvent($event->Prefix_Special, $current_event);
$this->Application->setEvent($event->Prefix_Special, '');
$save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate';
$this->Application->SetVar($event->Prefix_Special.'_SaveEvent',$save_event);
}
/**
* 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;
}
return true;
}
/**
* Shows not found template content
*
* @param kEvent $event
*
*/
function _errorNotFound(&$event)
{
if ($event->getEventParam('raise_warnings') === 0) {
// when it's possible, that autoload fails do nothing
return ;
}
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_WARNING);
header('HTTP/1.0 404 Not Found');
while (ob_get_level()) {
ob_end_clean();
}
// object is used inside template parsing, so break out any parsing and return error document
$error_template = $this->Application->ConfigValue('ErrorTemplate');
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$this->Application->SetVar('t', $error_template);
$this->Application->SetVar('m_cat_id', $themes_helper->getPageByTemplate($error_template));
$this->Application->HTML = $this->Application->ParseBlock( Array ('name' => $error_template) );
$this->Application->Done();
exit;
}
/**
* Builds item (loads if needed)
*
* @param kEvent $event
* @access protected
*/
function OnItemBuild(&$event)
{
$object =& $event->getObject();
$this->dbBuild($object,$event);
$sql = $this->ItemPrepareQuery($event);
$sql = $this->Application->ReplaceLanguageTags($sql);
$object->setSelectSQL($sql);
// 2. loads if allowed
$auto_load = $this->Application->getUnitOption($event->Prefix,'AutoLoad');
$skip_autload = $event->getEventParam('skip_autoload');
if ($auto_load && !$skip_autload) {
$perm_status = true;
$user_id = $this->Application->RecallVar('user_id');
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
$status_checked = false;
if ($user_id == -1 || $this->CheckPermission($event)) {
// don't autoload item, when user doesn't have view permission
$this->LoadItem($event);
$status_checked = true;
if ($user_id != -1 && !$this->Application->IsAdmin() && !$this->checkItemStatus($event)) {
$perm_status = false;
}
}
else {
$perm_status = false;
}
if (!$perm_status) {
// when no permission to view item -> redirect to no pemrission template
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix ['.$event->getPrefixSpecial().'] in <strong>'.($status_checked ? 'checkItemStatus' : 'CheckPermission').'</strong>', E_USER_WARNING);
$next_template = $this->Application->IsAdmin() ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
$this->Application->Redirect($next_template, Array('next_template' => $this->Application->GetVar('t')));
}
}
$actions =& $this->Application->recallObject('kActions');
$actions->Set($event->Prefix_Special.'_GoTab', '');
$actions->Set($event->Prefix_Special.'_GoId', '');
}
/**
* Build subtables array from configs
*
* @param kEvent $event
*/
function OnTempHandlerBuild(&$event)
{
$object =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $object kTempTablesHandler */
$object->BuildTables( $event->Prefix, $this->getSelectedIDs($event) );
}
/**
* Checks, that object used in event should use temp tables
*
* @param kEvent $event
* @return bool
*/
function UseTempTables(&$event)
{
$top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual
$special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event);
return $this->Application->IsTempMode($event->Prefix, $special);
}
/**
* Returns table prefix from event (temp or live)
*
* @param kEvent $event
* @return string
* @todo Needed? Should be refactored (by Alex)
*/
function TablePrefix(&$event)
{
return $this->UseTempTables($event) ? $this->Application->GetTempTablePrefix('prefix:'.$event->Prefix).TABLE_PREFIX : TABLE_PREFIX;
}
/**
* Load item if id is available
*
* @param kEvent $event
*/
function LoadItem(&$event)
{
$object =& $event->getObject();
$id = $this->getPassedID($event);
if ($object->isLoaded() && !is_array($id) && ($object->GetID() == $id)) {
// object is already loaded by same id
return ;
}
if ($object->Load($id)) {
$actions =& $this->Application->recallObject('kActions');
$actions->Set($event->Prefix_Special.'_id', $object->GetID() );
}
else {
$object->setID($id);
}
}
/**
* Builds list
*
* @param kEvent $event
* @access protected
*/
function OnListBuild(&$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
$this->dbBuild($object,$event);
$sql = $this->ListPrepareQuery($event);
$sql = $this->Application->ReplaceLanguageTags($sql);
$object->setSelectSQL($sql);
$object->Counted = false; // when requery="1" should re-count records too!
$object->ClearOrderFields(); // prevents duplicate order fields, when using requery="1"
$object->linkToParent( $this->getMainSpecial($event) );
$this->AddFilters($event);
$this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex.
$this->SetPagination($event);
$this->SetSorting($event);
// $object->CalculateTotals(); // Now called in getTotals to avoid extra query
$actions =& $this->Application->recallObject('kActions');
$actions->Set('remove_specials['.$event->Prefix_Special.']', '0');
$actions->Set($event->Prefix_Special.'_GoTab', '');
}
/**
* Get's special of main item for linking with subitem
*
* @param kEvent $event
* @return string
*/
function getMainSpecial(&$event)
{
$main_special = $event->getEventParam('main_special');
if ($main_special === false) {
// main item's special not passed
if (substr($event->Special, -5) == '-item') {
// temp handler added "-item" to given special -> process that here
return substr($event->Special, 0, -5);
}
// by default subitem's special is used for main item searching
return $event->Special;
}
return $main_special;
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @access protected
* @see OnListBuild
*/
function SetCustomQuery(&$event)
{
}
/**
* Set's new perpage for grid
*
* @param kEvent $event
*/
function OnSetPerPage(&$event)
{
$per_page = $this->Application->GetVar($event->getPrefixSpecial(true).'_PerPage');
$this->Application->StoreVar($event->getPrefixSpecial().'_PerPage', $per_page);
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_PerPage.'.$view_name, $per_page);
}
/**
* Occurs when page is changed (only for hooking)
*
* @param kEvent $event
*/
function OnSetPage(&$event)
{
$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
$event->SetRedirectParam($event->getPrefixSpecial().'_Page', $page);
}
/**
* Set's correct page for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetPagination(&$event)
{
// get PerPage (forced -> session -> config -> 10)
$per_page = $this->getPerPage($event);
$object =& $event->getObject();
$object->SetPerPage($per_page);
- $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1);
+ $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1, true); // true for optional
$page = $this->Application->GetVar($event->getPrefixSpecial().'_Page');
if (!$page) {
$page = $this->Application->GetVar($event->getPrefixSpecial(true).'_Page');
}
if (!$page) {
$page = $this->Application->RecallVar($event->getPrefixSpecial().'_Page');
}
else {
$this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page);
}
if( !$event->getEventParam('skip_counting') )
{
$pages = $object->GetTotalPages();
if($page > $pages)
{
$this->Application->StoreVar($event->getPrefixSpecial().'_Page', 1);
$page = 1;
}
}
/*$per_page = $event->getEventParam('per_page');
if ($per_page == 'list_next') {
$cur_page = $page;
$cur_per_page = $per_page;
$object->SetPerPage(1);
$object =& $this->Application->recallObject($event->Prefix);
$cur_item_index = $object->CurrentIndex;
$page = ($cur_page-1) * $cur_per_page + $cur_item_index + 1;
$object->SetPerPage(1);
}*/
$object->SetPage($page);
}
/**
* Returns current per-page setting for list
*
* @param kEvent $event
* @return int
*/
function getPerPage(&$event)
{
// 1. per-page is passed as tag parameter to PrintList, InitList, etc.
$per_page = $event->getEventParam('per_page');
/*if ($per_page == 'list_next') {
$per_page = '';
}*/
// 2. per-page variable name is store into config variable
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
if ($config_mapping) {
switch ( $per_page ){
case 'short_list' :
$per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']);
break;
case 'default' :
$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
break;
}
}
if (!$per_page) {
// per-page is stored to persistent session
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->getPrefixSpecial();
$per_page = $this->Application->RecallPersistentVar($storage_prefix.'_PerPage.'.$view_name, ALLOW_DEFAULT_SETTINGS);
if (!$per_page) {
// per-page is stored to current session
$per_page = $this->Application->RecallVar($storage_prefix.'_PerPage');
}
if (!$per_page) {
if ($config_mapping) {
if (!isset($config_mapping['PerPage'])) {
trigger_error('Incorrect mapping of <span class="debug_error">PerPage</span> key in config for prefix <b>'.$event->Prefix.'</b>', E_USER_WARNING);
}
$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
}
if (!$per_page) {
// none of checked above per-page locations are useful, then try default value
$default_per_page = $event->getEventParam('default_per_page');
$per_page = is_numeric($default_per_page) ? $default_per_page : 10;
}
}
}
return $per_page;
}
/**
* Set's correct sorting for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetSorting(&$event)
{
$event->setPseudoClass('_List');
$object =& $event->getObject();
$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->Prefix_Special;
$cur_sort1 = $this->Application->RecallVar($storage_prefix.'_Sort1');
$cur_sort1_dir = $this->Application->RecallVar($storage_prefix.'_Sort1_Dir');
$cur_sort2 = $this->Application->RecallVar($storage_prefix.'_Sort2');
$cur_sort2_dir = $this->Application->RecallVar($storage_prefix.'_Sort2_Dir');
$sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings');
$sorting_prefix = getArrayValue($list_sortings, $event->Special) ? $event->Special : '';
$tag_sort_by = $event->getEventParam('sort_by');
if ($tag_sort_by) {
if ($tag_sort_by == 'random') {
$object->AddOrderField('RAND()', '');
}
else {
$tag_sort_by = explode('|', $tag_sort_by);
foreach ($tag_sort_by as $sorting_element) {
list ($by, $dir) = explode(',', $sorting_element);
$object->AddOrderField($by, $dir);
}
}
}
if ($sorting_configs && isset ($sorting_configs['DefaultSorting1Field'])){
$list_sortings[$sorting_prefix]['Sorting'] = Array(
$this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']),
$this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']),
);
}
// Use default if not specified
if ( !$cur_sort1 || !$cur_sort1_dir)
{
if ( $sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting') ) {
reset($sorting);
$cur_sort1 = key($sorting);
$cur_sort1_dir = current($sorting);
if (next($sorting)) {
$cur_sort2 = key($sorting);
$cur_sort2_dir = current($sorting);
}
}
}
if ( $forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting') ) {
foreach ($forced_sorting as $field => $dir) {
$object->AddOrderField($field, $dir);
}
}
if($cur_sort1 != '' && $cur_sort1_dir != '')
{
$object->AddOrderField($cur_sort1, $cur_sort1_dir);
}
if($cur_sort2 != '' && $cur_sort2_dir != '')
{
$object->AddOrderField($cur_sort2, $cur_sort2_dir);
}
}
/**
* Add filters found in session
*
* @param kEvent $event
*/
function AddFilters(&$event)
{
$object =& $event->getObject();
$edit_mark = rtrim($this->Application->GetSID().'_'.$this->Application->GetTopmostWid($event->Prefix), '_');
// add search filter
$filter_data = $this->Application->RecallVar($event->getPrefixSpecial().'_search_filter');
if ($filter_data) {
$filter_data = unserialize($filter_data);
foreach ($filter_data as $filter_field => $filter_params) {
$filter_type = ($filter_params['type'] == 'having') ? HAVING_FILTER : WHERE_FILTER;
$filter_value = str_replace(EDIT_MARK, $edit_mark, $filter_params['value']);
$object->addFilter($filter_field, $filter_value, $filter_type, FLT_SEARCH);
}
}
// add custom filter
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name);
if ($custom_filters) {
$grid_name = $event->getEventParam('grid');
$custom_filters = unserialize($custom_filters);
if (isset($custom_filters[$grid_name])) {
foreach ($custom_filters[$grid_name] as $field_name => $field_options) {
list ($filter_type, $field_options) = each($field_options);
if (isset($field_options['value']) && $field_options['value']) {
$filter_type = ($field_options['sql_filter_type'] == 'having') ? HAVING_FILTER : WHERE_FILTER;
$filter_value = str_replace(EDIT_MARK, $edit_mark, $field_options['value']);
$object->addFilter($field_name, $filter_value, $filter_type, FLT_CUSTOM);
}
}
}
}
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
if($view_filter)
{
$view_filter = unserialize($view_filter);
$temp_filter =& $this->Application->makeClass('kMultipleFilter');
$filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu');
$group_key = 0; $group_count = count($filter_menu['Groups']);
while($group_key < $group_count)
{
$group_info = $filter_menu['Groups'][$group_key];
$temp_filter->setType( constant('FLT_TYPE_'.$group_info['mode']) );
$temp_filter->clearFilters();
foreach ($group_info['filters'] as $flt_id)
{
$sql_key = getArrayValue($view_filter,$flt_id) ? 'on_sql' : 'off_sql';
if ($filter_menu['Filters'][$flt_id][$sql_key] != '')
{
$temp_filter->addFilter('view_filter_'.$flt_id, $filter_menu['Filters'][$flt_id][$sql_key]);
}
}
$object->addFilter('view_group_'.$group_key, $temp_filter, $group_info['type'] , FLT_VIEW);
$group_key++;
}
}
}
/**
* Set's new sorting for list
*
* @param kEvent $event
* @access protected
*/
function OnSetSorting(&$event)
{
$cur_sort1 = $this->Application->RecallVar($event->Prefix_Special.'_Sort1');
$cur_sort1_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort1_Dir');
$use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting');
if ($use_double_sorting) {
$cur_sort2 = $this->Application->RecallVar($event->Prefix_Special.'_Sort2');
$cur_sort2_dir = $this->Application->RecallVar($event->Prefix_Special.'_Sort2_Dir');
}
$passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true).'_Sort1');
if ($cur_sort1 == $passed_sort1) {
$cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc';
}
else {
if ($use_double_sorting) {
$cur_sort2 = $cur_sort1;
$cur_sort2_dir = $cur_sort1_dir;
}
$cur_sort1 = $passed_sort1;
$cur_sort1_dir = 'asc';
}
$this->Application->StoreVar($event->Prefix_Special.'_Sort1', $cur_sort1);
$this->Application->StoreVar($event->Prefix_Special.'_Sort1_Dir', $cur_sort1_dir);
if ($use_double_sorting) {
$this->Application->StoreVar($event->Prefix_Special.'_Sort2', $cur_sort2);
$this->Application->StoreVar($event->Prefix_Special.'_Sort2_Dir', $cur_sort2_dir);
}
}
/**
* Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu)
*
* @param kEvent $event
*/
function OnSetSortingDirect(&$event)
{
$combined = $this->Application->GetVar($event->Prefix.'_CombinedSorting');
if ($combined) {
list($field, $dir) = explode('|', $combined);
$this->Application->StoreVar($event->Prefix.'_Sort1', $field);
$this->Application->StoreVar($event->Prefix.'_Sort1_Dir', $dir);
return ;
}
$field_pos = $this->Application->GetVar($event->Prefix.'_SortPos');
$this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos, $event->Prefix.'_Sort'.$field_pos);
$this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos.'_Dir', $event->Prefix.'_Sort'.$field_pos.'_Dir');
}
/**
* Reset grid sorting to default (from config)
*
* @param kEvent $event
*/
function OnResetSorting(&$event)
{
$this->Application->RemoveVar($event->Prefix_Special.'_Sort1');
$this->Application->RemoveVar($event->Prefix_Special.'_Sort1_Dir');
$this->Application->RemoveVar($event->Prefix_Special.'_Sort2');
$this->Application->RemoveVar($event->Prefix_Special.'_Sort2_Dir');
}
/**
* Sets grid refresh interval
*
* @param kEvent $event
*/
function OnSetAutoRefreshInterval(&$event)
{
$refresh_interval = $this->Application->GetVar('refresh_interval');
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_interval);
}
/**
* Changes auto-refresh state for grid
*
* @param kEvent $event
*/
function OnAutoRefreshToggle(&$event)
{
$refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals');
if (!$refresh_intervals) {
return ;
}
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name);
if ($auto_refresh === false) {
$refresh_intervals = explode(',', $refresh_intervals);
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_intervals[0]);
}
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name, $auto_refresh ? 0 : 1);
}
/**
* Creates needed sql query to load item,
* if no query is defined in config for
* special requested, then use default
* query
*
* @param kEvent $event
* @access protected
*/
function ItemPrepareQuery(&$event)
{
$sqls = $this->Application->getUnitOption($event->Prefix, 'ItemSQLs', Array ());
$special = array_key_exists($event->Special, $sqls) ? $event->Special : '';
if (!array_key_exists($special, $sqls)) {
// preferred special not found in ItemSQLs -> use analog from ListSQLs
return $this->ListPrepareQuery($event);
}
return $sqls[$special];
}
/**
* Creates needed sql query to load list,
* if no query is defined in config for
* special requested, then use default
* query
*
* @param kEvent $event
* @access protected
*/
function ListPrepareQuery(&$event)
{
$sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs', Array ());
return $sqls[ array_key_exists($event->Special, $sqls) ? $event->Special : '' ];
}
/**
* Apply custom processing to item
*
* @param kEvent $event
*/
function customProcessing(&$event, $type)
{
}
/* Edit Events mostly used in Admin */
/**
* Creates new kDBItem
*
* @param kEvent $event
* @access protected
*/
function OnCreate(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
list($id,$field_values) = each($items_info);
$object->SetFieldsFromHash($field_values);
}
$this->customProcessing($event,'before');
//look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default
if( $object->Create($event->getEventParam('ForceCreateId')) )
{
if( $object->IsTempTable() ) $object->setTempID();
$this->customProcessing($event,'after');
$event->status=erSUCCESS;
$event->redirect_params = Array('opener'=>'u');
}
else
{
$event->status = erFAIL;
$event->redirect = false;
$this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnCreate');
$object->setID($id);
}
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @access protected
*/
function OnUpdate(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$object->SetFieldsFromHash($field_values);
$this->customProcessing($event, 'before');
if ( $object->Update($id) ) {
$this->customProcessing($event, 'after');
$event->status = erSUCCESS;
}
else {
$event->status = erFAIL;
$event->redirect = false;
break;
}
}
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Delete's kDBItem object
*
* @param kEvent $event
* @access protected
*/
function OnDelete(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$temp->DeleteItems($event->Prefix, $event->Special, Array($this->getPassedID($event)));
}
/**
* Deletes all records from table
*
* @param kEvent $event
*/
function OnDeleteAll(&$event)
{
$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName');
$ids = $this->Conn->GetCol($sql);
if ($ids) {
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
}
}
/**
* Prepares new kDBItem object
*
* @param kEvent $event
* @access protected
*/
function OnNew(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$object->Clear(0);
$this->Application->SetVar($event->Prefix_Special.'_SaveEvent', 'OnCreate');
if ($event->getEventParam('top_prefix') != $event->Prefix) {
// this is subitem prefix, so use main item special
$table_info = $object->getLinkedInfo( $this->getMainSpecial($event) );
}
else {
$table_info = $object->getLinkedInfo();
}
$object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']);
$event->redirect = false;
}
/**
* Cancel's kDBItem Editing/Creation
*
* @param kEvent $event
* @access protected
*/
function OnCancel(&$event)
{
$object =& $event->getObject(Array('skip_autoload' => true));
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ($items_info) {
$delete_ids = Array();
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
foreach ($items_info as $id => $field_values) {
$object->Load($id);
// record created for using with selector (e.g. Reviews->Select User), and not validated => Delete it
if ($object->isLoaded() && !$object->Validate() && ($id <= 0) ) {
$delete_ids[] = $id;
}
}
if ($delete_ids) {
$temp->DeleteItems($event->Prefix, $event->Special, $delete_ids);
}
}
$event->redirect_params = Array('opener'=>'u');
}
/**
* 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;
}
$event->status=erSUCCESS;
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$ids = $this->StoreSelectedIDs($event);
$event->setEventParam('ids', $ids);
$this->customProcessing($event, 'before');
$ids = $event->getEventParam('ids');
if($ids)
{
$temp->DeleteItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
/**
* Sets window id (of first opened edit window) to temp mark in uls
*
* @param kEvent $event
*/
function setTempWindowID(&$event)
{
$prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true));
foreach ($prefixes as $prefix) {
$mode = $this->Application->GetVar($prefix . '_mode');
if ($mode == 't') {
$wid = $this->Application->GetVar('m_wid');
$this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid);
break;
}
}
}
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
*/
function OnEdit(&$event)
{
$this->setTempWindowID($event);
$this->StoreSelectedIDs($event);
$var_name = $event->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
$this->Application->RemoveVar($var_name);
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$temp->PrepareEdit();
$event->redirect = false;
}
/**
* Saves content of temp table into live and
* redirects to event' default redirect (normally grid template)
*
* @param kEvent $event
*/
function OnSave(&$event)
{
$event->CallSubEvent('OnPreSave');
if ($event->status == erSUCCESS) {
$skip_master = false;
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$changes_var_name = $this->Prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
if (!$this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$live_ids = $temp->SaveEdit($event->getEventParam('master_ids') ? $event->getEventParam('master_ids') : Array());
if ($live_ids === false) {
// coping from table failed, because we have another coping process to same table, that wasn't finished
$event->status = erFAIL;
return ;
}
// Deleteing files scheduled for delete
$var_name = $event->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : array();
foreach ($schedule as $data) {
if ($data['action'] == 'delete') {
unlink($data['file']);
}
}
if ($live_ids) {
// ensure, that newly created item ids are avalable as if they were selected from grid
// NOTE: only works if main item has subitems !!!
$this->StoreSelectedIDs($event, $live_ids);
}
$this->SaveLoggedChanges($changes_var_name);
}
else {
$this->Application->RemoveVar($changes_var_name);
}
$this->clearSelectedIDs($event);
$event->redirect_params = Array('opener' => 'u');
$this->Application->RemoveVar($event->getPrefixSpecial().'_modified');
// all temp tables are deleted here => all after hooks should think, that it's live mode now
$this->Application->SetVar($event->Prefix.'_mode', '');
}
}
function SaveLoggedChanges($changes_var_name)
{
$ses_log_id = $this->Application->RecallVar('_SessionLogId_');
if (!$ses_log_id) {
return ;
}
$changes = $this->Application->RecallVar($changes_var_name);
$changes = $changes ? unserialize($changes) : Array ();
if (!$changes) {
return ;
}
$add_fields = Array (
'PortalUserId' => $this->Application->RecallVar('user_id'),
'SessionLogId' => $ses_log_id,
);
$changelog_table = $this->Application->getUnitOption('change-log', 'TableName');
$sessionlog_table = $this->Application->getUnitOption('session-log', 'TableName');
foreach ($changes as $rec) {
$this->Conn->doInsert(array_merge($rec, $add_fields), $changelog_table);
}
$sql = 'UPDATE '.$sessionlog_table.'
SET AffectedItems = AffectedItems + '.count($changes).'
WHERE SessionLogId = '.$ses_log_id;
$this->Conn->Query($sql);
$this->Application->RemoveVar($changes_var_name);
}
/**
* Cancels edit
* Removes all temp tables and clears selected ids
*
* @param kEvent $event
*/
function OnCancelEdit(&$event)
{
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$temp->CancelEdit();
$this->clearSelectedIDs($event);
$event->redirect_params = Array('opener'=>'u');
$this->Application->RemoveVar($event->getPrefixSpecial().'_modified');
}
/**
* Allows to determine if we are creating new item or editing already created item
*
* @param kEvent $event
* @return bool
*/
function isNewItemCreate(&$event)
{
$object =& $event->getObject( Array ('raise_warnings' => 0) );
return !$object->IsLoaded();
// $item_id = $this->getPassedID($event);
// return ($item_id == '') ? true : false;
}
/**
* Saves edited item into temp table
* If there is no id, new item is created in temp table
*
* @param kEvent $event
*/
function OnPreSave(&$event)
{
//$event->redirect = false;
// if there is no id - it means we need to create an item
if (is_object($event->MasterEvent)) {
$event->MasterEvent->setEventParam('IsNew',false);
}
if ($this->isNewItemCreate($event)) {
$event->CallSubEvent('OnPreSaveCreated');
if (is_object($event->MasterEvent)) {
$event->MasterEvent->setEventParam('IsNew',true);
}
return;
}
$object =& $event->getObject( Array('skip_autoload' => true) );
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
foreach ($items_info as $id => $field_values) {
$object->SetDefaultValues();
$object->Load($id);
$object->SetFieldsFromHash($field_values);
$this->customProcessing($event, 'before');
if( $object->Update($id) )
{
$this->customProcessing($event, 'after');
$event->status=erSUCCESS;
}
else {
$event->status = erFAIL;
$event->redirect = false;
break;
}
}
}
}
/**
* [HOOK] Saves subitem
*
* @param kEvent $event
*/
function OnPreSaveSubItem(&$event)
{
$not_created = $this->isNewItemCreate($event);
$event->CallSubEvent($not_created ? 'OnCreate' : 'OnUpdate');
if ($event->status == erSUCCESS) {
$object =& $event->getObject();
/* @var $object kDBItem */
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID());
}
$event->SetRedirectParam('opener', 's');
}
/**
* Saves edited item in temp table and loads
* item with passed id in current template
* Used in Prev/Next buttons
*
* @param kEvent $event
*/
function OnPreSaveAndGo(&$event)
{
$event->CallSubEvent('OnPreSave');
if ($event->status == erSUCCESS) {
$id = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId');
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
}
}
/**
* Saves edited item in temp table and goes
* to passed tabs, by redirecting to it with OnPreSave event
*
* @param kEvent $event
*/
function OnPreSaveAndGoToTab(&$event)
{
$event->CallSubEvent('OnPreSave');
if ($event->status==erSUCCESS) {
$event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab');
}
}
/**
* Saves editable list and goes to passed tab,
* by redirecting to it with empty event
*
* @param kEvent $event
*/
function OnUpdateAndGoToTab(&$event)
{
$event->setPseudoClass('_List');
$event->CallSubEvent('OnUpdate');
if ($event->status==erSUCCESS) {
$event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab');
}
}
/**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
*/
function OnPreCreate(&$event)
{
$this->setTempWindowID($event);
$this->clearSelectedIDs($event);
$object =& $event->getObject( Array('skip_autoload' => true) );
$temp =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
$temp->PrepareEdit();
$object->setID(0);
$this->Application->SetVar($event->getPrefixSpecial().'_id',0);
$this->Application->SetVar($event->getPrefixSpecial().'_PreCreate', 1);
$event->redirect=false;
}
/**
* Creates a new item in temp table and
* stores item id in App vars and Session on succsess
*
* @param kEvent $event
*/
function OnPreSaveCreated(&$event)
{
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info) $field_values = array_shift($items_info);
$object =& $event->getObject( Array('skip_autoload' => true) );
$object->SetFieldsFromHash($field_values);
$this->customProcessing($event, 'before');
if( $object->Create() )
{
$this->customProcessing($event, 'after');
$event->redirect_params[$event->getPrefixSpecial(true).'_id'] = $object->GetId();
$event->status=erSUCCESS;
}
else
{
$event->status=erFAIL;
$event->redirect=false;
$object->setID(0);
}
}
function OnReset(&$event)
{
//do nothing - should reset :)
if ($this->isNewItemCreate($event)) {
// just reset id to 0 in case it was create
$object =& $event->getObject( Array('skip_autoload' => true) );
$object->setID(0);
$this->Application->SetVar($event->getPrefixSpecial().'_id',0);
}
}
/**
* Apply same processing to each item beeing selected in grid
*
* @param kEvent $event
* @access private
*/
function 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') );
$order_field = $this->Application->getUnitOption($event->Prefix,'OrderField');
if (!$order_field) {
$order_field = 'Priority';
}
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;
case 'OnMassMoveUp':
$object->SetDBField($order_field, $object->GetDBField($order_field) + 1);
break;
case 'OnMassMoveDown':
$object->SetDBField($order_field, $object->GetDBField($order_field) - 1);
break;
}
if ($object->Update()) {
$event->status = erSUCCESS;
}
else {
$event->status = erFAIL;
$event->redirect = false;
break;
}
}
}
$this->clearSelectedIDs($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnMassClone(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$event->status = erSUCCESS;
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$ids = $this->StoreSelectedIDs($event);
if ($ids) {
$temp->CloneItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
function check_array($records, $field, $value)
{
foreach ($records as $record) {
if ($record[$field] == $value) {
return true;
}
}
return false;
}
function OnPreSavePopup(&$event)
{
$object =& $event->getObject();
$this->RemoveRequiredFields($object);
$event->CallSubEvent('OnPreSave');
$this->finalizePopup($event);
}
/* End of Edit events */
// III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item
/**
* Occurse before loading item, 'id' parameter
* allows to get id of item beeing loaded
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemLoad(&$event)
{
}
/**
* Occurse after loading item, 'id' parameter
* allows to get id of item that was loaded
*
* @param kEvent $event
* @access public
*/
function OnAfterItemLoad(&$event)
{
}
/**
* Occurse before creating item
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemCreate(&$event)
{
}
/**
* Occurse after creating item
*
* @param kEvent $event
* @access public
*/
function OnAfterItemCreate(&$event)
{
}
/**
* Occurse before updating item
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemUpdate(&$event)
{
}
/**
* Occurse after updating item
*
* @param kEvent $event
* @access public
*/
function OnAfterItemUpdate(&$event)
{
}
/**
* Occurse before deleting item, id of item beeing
* deleted is stored as 'id' event param
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemDelete(&$event)
{
}
/**
* Occurse after deleting item, id of deleted item
* is stored as 'id' param of event
*
* @param kEvent $event
* @access public
*/
function OnAfterItemDelete(&$event)
{
}
/**
* Occurs after successful item validation
*
* @param kEvent $event
*/
function OnAfterItemValidate(&$event)
{
}
/**
* Occures after an item has been copied to temp
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterCopyToTemp(&$event)
{
}
/**
* Occures before an item is deleted from live table when copying from temp
* (temp handler deleted all items from live and then copy over all items from temp)
* Id of item being deleted is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnBeforeDeleteFromLive(&$event)
{
}
/**
* Occures before an item is copied to live table (after all foreign keys have been updated)
* Id of item being copied is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnBeforeCopyToLive(&$event)
{
}
/**
* !!! NOT FULLY IMPLEMENTED - SEE TEMP HANDLER COMMENTS (search by event name)!!!
* Occures after an item has been copied to live table
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterCopyToLive(&$event)
{
}
/**
* Occures before an item is cloneded
* Id of ORIGINAL item is passed as event' 'id' param
* Do not call object' Update method in this event, just set needed fields!
*
* @param kEvent $event
*/
function OnBeforeClone(&$event)
{
}
/**
* Occures after an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterClone(&$event)
{
}
/**
* Occures after list is queried
*
* @param kEvent $event
*/
function OnAfterListQuery(&$event)
{
}
/**
* Ensures that popup will be closed automatically
* and parent window will be refreshed with template
* passed
*
* @param kEvent $event
* @access public
*/
function finalizePopup(&$event)
{
$event->SetRedirectParam('opener', 'u');
}
/**
* Create search filters based on search query
*
* @param kEvent $event
* @access protected
*/
function OnSearch(&$event)
{
$event->setPseudoClass('_List');
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->performSearch($event);
}
/**
* Clear search keywords
*
* @param kEvent $event
* @access protected
*/
function OnSearchReset(&$event)
{
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->resetSearch($event);
}
/**
* Set's new filter value (filter_id meaning from config)
*
* @param kEvent $event
*/
function OnSetFilter(&$event)
{
$filter_id = $this->Application->GetVar('filter_id');
$filter_value = $this->Application->GetVar('filter_value');
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array();
$view_filter[$filter_id] = $filter_value;
$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
}
function OnSetFilterPattern(&$event)
{
$filters = $this->Application->GetVar($event->getPrefixSpecial(true).'_filters');
if (!$filters) return ;
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array();
$filters = explode(',', $filters);
foreach ($filters as $a_filter) {
list($id, $value) = explode('=', $a_filter);
$view_filter[$id] = $value;
}
$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
$event->redirect = false;
}
/**
* Add/Remove all filters applied to list from "View" menu
*
* @param kEvent $event
*/
function FilterAction(&$event)
{
$view_filter = Array();
$filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu');
switch ($event->Name)
{
case 'OnRemoveFilters':
$filter_value = 1;
break;
case 'OnApplyFilters':
$filter_value = 0;
break;
}
foreach($filter_menu['Filters'] as $filter_key => $filter_params)
{
if(!$filter_params) continue;
$view_filter[$filter_key] = $filter_value;
}
$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnPreSaveAndOpenTranslator(&$event)
{
$this->Application->SetVar('allow_translation', true);
$object =& $event->getObject();
$this->RemoveRequiredFields($object);
$event->CallSubEvent('OnPreSave');
if ($event->status == erSUCCESS) {
$resource_id = $this->Application->GetVar('translator_resource_id');
if ($resource_id) {
$t_prefixes = explode(',', $this->Application->GetVar('translator_prefixes'));
$cdata =& $this->Application->recallObject($t_prefixes[1], null, Array('skip_autoload' => true));
$cdata->Load($resource_id, 'ResourceId');
if (!$cdata->isLoaded()) {
$cdata->SetDBField('ResourceId', $resource_id);
$cdata->Create();
}
$this->Application->SetVar($cdata->getPrefixSpecial().'_id', $cdata->GetID());
}
$event->redirect = $this->Application->GetVar('translator_t');
$event->redirect_params = Array('pass'=>'all,trans,'.$this->Application->GetVar('translator_prefixes'),
$event->getPrefixSpecial(true).'_id' => $object->GetID(),
'trans_event' => 'OnLoad',
'trans_prefix' => $this->Application->GetVar('translator_prefixes'),
'trans_field' => $this->Application->GetVar('translator_field'),
'trans_multi_line' => $this->Application->GetVar('translator_multi_line'),
);
// 1. SAVE LAST TEMPLATE TO SESSION (really needed here, because of tweaky redirect)
$last_template = $this->Application->RecallVar('last_template');
preg_match('/index4\.php\|'.$this->Application->GetSID().'-(.*):/U', $last_template, $rets);
$this->Application->StoreVar('return_template', $this->Application->GetVar('t'));
}
}
function RemoveRequiredFields(&$object)
{
// making all field non-required to achieve successful presave
foreach($object->Fields as $field => $options)
{
if(isset($options['required']))
{
unset($object->Fields[$field]['required']);
}
}
}
/**
* Saves selected user in needed field
*
* @param kEvent $event
*/
function OnSelectUser(&$event)
{
$items_info = $this->Application->GetVar('u');
if ($items_info) {
$user_id = array_shift( array_keys($items_info) );
$object =& $event->getObject();
$this->RemoveRequiredFields($object);
$is_new = !$object->isLoaded();
$is_main = substr($this->Application->GetVar($event->Prefix.'_mode'), 0, 1) == 't';
if ($is_new) {
$new_event = $is_main ? 'OnPreCreate' : 'OnNew';
$event->CallSubEvent($new_event);
$event->redirect = true;
}
$object->SetDBField($this->Application->RecallVar('dst_field'), $user_id);
if ($is_new) {
$object->Create();
if (!$is_main && $object->IsTempTable()) {
$object->setTempID();
}
}
else {
$object->Update();
}
}
$event->SetRedirectParam($event->getPrefixSpecial().'_id', $object->GetID());
$this->finalizePopup($event);
}
/** EXPORT RELATED **/
/**
* Shows export dialog
*
* @param kEvent $event
*/
function OnExport(&$event)
{
$selected_ids = $this->StoreSelectedIDs($event);
if (implode(',', $selected_ids) == '') {
// K4 fix when no ids found bad selected ids array is formed
$selected_ids = false;
}
$this->Application->StoreVar($event->Prefix.'_export_ids', $selected_ids ? implode(',', $selected_ids) : '' );
$this->Application->LinkVar('export_finish_t');
$this->Application->LinkVar('export_progress_t');
$this->Application->StoreVar('export_oroginal_special', $event->Special);
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/*list ($index_file, $env) = explode('|', $this->Application->RecallVar('last_template'));
$finish_url = $this->Application->BaseURL('/admin').$index_file.'?'.ENV_VAR_NAME.'='.$env;
$this->Application->StoreVar('export_finish_url', $finish_url);*/
$redirect_params = Array (
$this->Prefix . '.export_event' => 'OnNew',
'pass' => 'all,' . $this->Prefix . '.export'
);
$event->setRedirectParams($redirect_params);
}
/**
* Apply some special processing to
* object beeing recalled before using
* it in other events that call prepareObject
*
* @param Object $object
* @param kEvent $event
* @access protected
*/
function prepareObject(&$object, &$event)
{
if ($event->Special == 'export' || $event->Special == 'import')
{
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->prepareExportColumns($event);
}
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
*/
function getCustomExportColumns(&$event)
{
return Array();
}
/**
* Export form validation & processing
*
* @param kEvent $event
*/
function OnExportBegin(&$event)
{
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->OnExportBegin($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnExportCancel(&$event)
{
$this->OnGoBack($event);
}
/**
* Allows configuring export options
*
* @param kEvent $event
*/
function OnBeforeExportBegin(&$event)
{
}
function OnDeleteExportPreset(&$event)
{
$object =& $event->GetObject();
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info)
{
list($id,$field_values) = each($items_info);
$preset_key = $field_values['ExportPresets'];
$export_settings = $this->Application->RecallPersistentVar('export_settings');
if (!$export_settings) return ;
$export_settings = unserialize($export_settings);
if (!isset($export_settings[$event->Prefix])) return ;
$to_delete = '';
$export_presets = array(''=>'');
foreach ($export_settings[$event->Prefix] as $key => $val) {
if (implode('|', $val['ExportColumns']) == $preset_key) {
$to_delete = $key;
break;
}
}
if ($to_delete) {
unset($export_settings[$event->Prefix][$to_delete]);
$this->Application->StorePersistentVar('export_settings', serialize($export_settings));
}
}
}
/**
* Saves changes & changes language
*
* @param kEvent $event
*/
function OnPreSaveAndChangeLanguage(&$event)
{
$event->CallSubEvent('OnPreSave');
if ($event->status == erSUCCESS) {
$this->Application->SetVar('m_lang', $this->Application->GetVar('language'));
$pass_vars = Array ('st_id', 'cms_id');
foreach ($pass_vars as $pass_var) {
$data = $this->Application->GetVar($pass_var);
if ($data) {
$event->SetRedirectParam($pass_var, $data);
}
}
}
}
/**
* Used to save files uploaded via swfuploader
*
* @param kEvent $event
*/
function OnUploadFile(&$event)
{
$event->status = erSTOP;
define('DBG_SKIP_REPORTING', 1);
echo "Flash requires that we output something or it won't fire the uploadSuccess event";
if (!$this->Application->HttpQuery->Post) {
// Variables {field, id, flashsid} are always submitted through POST!
// When file size is larger, then "upload_max_filesize" (in php.ini),
// then theese variables also are not submitted -> handle such case.
header('HTTP/1.0 413 File size exceeds allowed limit');
return ;
}
if (!$this->_checkFlashUploaderPermission($event)) {
// 403 Forbidden
header('HTTP/1.0 403 You don\'t have permissions to upload');
return ;
}
$value = $this->Application->GetVar('Filedata');
if (!$value || ($value['error'] != UPLOAD_ERR_OK)) {
// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
// to large for web server to accept, see "upload_max_filesize" in php.ini)
header('HTTP/1.0 413 File size exceeds allowed limit');
return ;
}
$tmp_path = WRITEABLE . '/tmp/';
$fname = $value['name'];
$id = $this->Application->GetVar('id');
if ($id) {
$fname = $id.'_'.$fname;
}
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$upload_dir = $fields[ $this->Application->GetVar('field') ]['upload_dir'];
if (!is_writable($tmp_path) || !is_writable(FULL_PATH . $upload_dir)) {
// 500 Internal Server Error
// check both temp and live upload directory
header('HTTP/1.0 500 Write permissions not set on the server');
return ;
}
move_uploaded_file($value['tmp_name'], $tmp_path.$fname);
}
/**
* Checks, that flash uploader is allowed to perform upload
*
* @param kEvent $event
* @return bool
*/
function _checkFlashUploaderPermission(&$event)
{
// Flash uploader does NOT send correct cookies, so we need to make our own check
$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
$admin_ses =& $this->Application->recallObject('Session.admin');
/* @var $admin_ses Session */
$backup_user_id = $this->Application->RecallVar('user_id'); // 1. backup user
$this->Application->StoreVar('user_id', $admin_ses->RecallVar('user_id')); // 2. fake user_id
$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected'); // 3. event, that have "add|edit" rule
$allowed_to_upload = $this->CheckPermission($check_event); // 4. check permission
$this->Application->StoreVar('user_id', $backup_user_id); // 5. restore user id
return $allowed_to_upload;
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnDeleteFile(&$event)
{
$event->status = erSTOP;
if (strpos($this->Application->GetVar('file'), '../') !== false) {
return ;
}
$object =& $event->getObject( Array ('skip_autoload' => true) );
$options = $object->GetFieldOptions( $this->Application->GetVar('field') );
$var_name = $event->getPrefixSpecial() . '_file_pending_actions' . $this->Application->GetVar('m_wid');
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : Array ();
$schedule[] = Array ('action' => 'delete', 'file' => $path = FULL_PATH . $options['upload_dir'] . $this->Application->GetVar('file'));
$this->Application->StoreVar($var_name, serialize($schedule));
$this->Application->Session->SaveData();
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnViewFile(&$event)
{
$file = $this->Application->GetVar('file');
if ((strpos($file, '../') !== false) || (trim($file) !== $file)) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
return ;
}
if ($this->Application->GetVar('tmp')) {
$path = WRITEABLE . '/tmp/' . $this->Application->GetVar('id') . '_' . $this->Application->GetVar('file');
}
else {
$object =& $event->getObject(array('skip_autoload'=>true));
$options = $object->GetFieldOptions($this->Application->GetVar('field'));
$path = FULL_PATH.$options['upload_dir'].$file;
}
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
if (!file_exists($path)) {
exit;
}
$type = mime_content_type($path);
header('Content-Length: '.filesize($path));
header('Content-Type: '.$type);
safeDefine('DBG_SKIP_REPORTING',1);
readfile($path);
exit();
}
/**
* Validates MInput control fields
*
* @param kEvent $event
*/
function OnValidateMInputFields(&$event)
{
$minput_helper =& $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
$minput_helper->OnValidateMInputFields($event);
}
/**
* Returns auto-complete values for ajax-dropdown
*
* @param kEvent $event
*/
function OnSuggestValues(&$event)
{
$this->Application->XMLHeader();
$field = $this->Application->GetVar('field');
$cur_value = $this->Application->GetVar('cur_value');
if (!$field || !$cur_value) {
exit;
}
$limit = $this->Application->GetVar('limit');
if (!$limit) {
$limit = 20;
}
$object =& $event->getObject();
$sql = 'SELECT DISTINCT '.$field.'
FROM '.$object->TableName.'
WHERE '.$field.' LIKE '.$this->Conn->qstr($cur_value.'%').'
ORDER BY '.$field.'
LIMIT 0,' . $limit;
$data = $this->Conn->GetCol($sql);
echo '<suggestions>';
foreach ($data as $item) {
echo '<item>' . htmlspecialchars($item) . '</item>';
}
echo '</suggestions>';
$event->status = erSTOP;
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnSaveWidths(&$event)
{
$event->status = erSTOP;
$lang =& $this->Application->recallObject('lang.current');
// header('Content-type: text/xml; charset='.$lang->GetDBField('Charset'));
$picker_helper =& $this->Application->RecallObject('ColumnPickerHelper');
/* @var $picker_helper kColumnPickerHelper */
$picker_helper->PreparePicker($event->getPrefixSpecial(), $this->Application->GetVar('grid_name'));
$picker_helper->SaveWidths($event->getPrefixSpecial(), $this->Application->GetVar('widths'));
echo 'OK';
}
/**
* Called from CSV import script after item fields
* are set and validated, but before actual item create/update.
* If event status is erSUCCESS, line will be imported,
* else it will not be imported but added to skipped lines
* and displayed in the end of import.
* Event status is preset from import script.
*
* @param kEvent $event
*/
function OnBeforeCSVLineImport(&$event)
{
// abstract, for hooking
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/db/dbitem.php
===================================================================
--- branches/5.0.x/core/kernel/db/dbitem.php (revision 12367)
+++ branches/5.0.x/core/kernel/db/dbitem.php (revision 12368)
@@ -1,1300 +1,1300 @@
<?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!');
/**
* DBItem
*
* Desciption
* @package kernel4
*/
class kDBItem extends kDBBase {
/**
* Description
*
* @var array Associative array of current item' field values
* @access public
*/
var $FieldValues;
/**
* Unformatted field values, before parse
*
* @var Array
* @access private
*/
var $DirtyFieldValues = Array();
/**
* Holds item values after loading (not affected by submit)
*
* @var Array
* @access private
*/
var $OriginalFieldValues = Array ();
var $FieldErrors;
var $ErrorMsgs = Array();
/**
* If set to true, Update will skip Validation before running
*
* @var array Associative array of current item' field values
* @access public
*/
var $IgnoreValidation = false;
var $Loaded = false;
/**
* Holds item' primary key value
*
* @var int Value of primary key field for current item
* @access public
*/
var $ID;
function kDBItem()
{
parent::kDBBase();
$this->ErrorMsgs['required'] = '!la_err_required!'; //'Field is required';
$this->ErrorMsgs['unique'] = '!la_err_unique!'; //'Field value must be unique';
$this->ErrorMsgs['value_out_of_range'] = '!la_err_value_out_of_range!'; //'Field is out of range, possible values from %s to %s';
$this->ErrorMsgs['length_out_of_range'] = '!la_err_length_out_of_range!'; //'Field is out of range';
$this->ErrorMsgs['bad_type'] = '!la_err_bad_type!'; //'Incorrect data format, please use %s';
$this->ErrorMsgs['invalid_format'] = '!la_err_invalid_format!'; //'Incorrect data format, please use %s';
$this->ErrorMsgs['bad_date_format'] = '!la_err_bad_date_format!'; //'Incorrect date format, please use (%s) ex. (%s)';
$this->ErrorMsgs['primary_lang_required'] = '!la_err_primary_lang_required!';
}
function SetDirtyField($field_name, $field_value)
{
$this->DirtyFieldValues[$field_name] = $field_value;
}
function GetDirtyField($field_name)
{
return $this->DirtyFieldValues[$field_name];
}
function GetOriginalField($field_name, $formatted = false, $format=null)
{
if (array_key_exists($field_name, $this->OriginalFieldValues)) {
// item was loaded before
$value = $this->OriginalFieldValues[$field_name];
}
else {
// no original fields -> use default field value
$value = $this->Fields[$field_name]['default'];
}
if (!$formatted) {
return $value;
}
$options = $this->GetFieldOptions($field_name);
$res = $value;
if (array_key_exists('formatter', $options)) {
$formatter =& $this->Application->recallObject($options['formatter']);
/* @var $formatter kFormatter */
$res = $formatter->Format($value, $field_name, $this, $format);
}
return $res;
}
/**
* Sets original field value (useful for custom virtual fields)
*
* @param string $field_name
*/
function SetOriginalField($field_name, $field_value)
{
$this->OriginalFieldValues[$field_name] = $field_value;
}
/**
* Set's default values for all fields
*
* @param bool $populate_ml_fields create all ml fields from db in config or not
*
* @access public
*/
function SetDefaultValues($populate_ml_fields = false)
{
parent::SetDefaultValues($populate_ml_fields);
if ($populate_ml_fields) {
$this->PopulateMultiLangFields();
}
foreach ($this->Fields as $field => $params) {
if ( isset($params['default']) ) {
$this->SetDBField($field, $params['default']);
}
else {
$this->SetDBField($field, NULL);
}
}
}
/**
* Sets current item field value
* (applies formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
function SetField($name,$value)
{
$options = $this->GetFieldOptions($name);
$parsed = $value;
if ($value == '') {
$parsed = NULL;
}
// kFormatter is always used, to make sure, that numeric value is converted to normal representation
// according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why)
$formatter =& $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter');
$parsed = $formatter->Parse($value, $name, $this);
$this->SetDBField($name,$parsed);
}
/**
* Sets current item field value
* (doesn't apply formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
function SetDBField($name,$value)
{
$this->FieldValues[$name] = $value;
/*if (isset($this->Fields[$name]['formatter'])) {
$formatter =& $this->Application->recallObject($this->Fields[$name]['formatter']);
$formatter->UpdateSubFields($name, $value, $this->Fields[$name], $this);
}*/
}
/**
* Set's field error, if pseudo passed not found then create it with message text supplied.
* Don't owerrite existing pseudo translation.
*
* @param string $field
* @param string $pseudo
* @param string $error_label
*
* @return bool
*/
function SetError($field, $pseudo, $error_label = null, $error_params = null)
{
$error_field = isset($this->Fields[$field]['error_field']) ? $this->Fields[$field]['error_field'] : $field;
if (isset($this->FieldErrors[$error_field]['pseudo'])) {
// don't set more then one error on field
return false;
}
$this->FieldErrors[$error_field]['pseudo'] = $pseudo;
if (isset($error_params)) {
// additional params, that helps to determine error sources
$this->FieldErrors[$error_field]['params'] = $error_params;
}
if (isset($error_label) && !isset($this->ErrorMsgs[$pseudo])) {
// label for error (only when not already set)
$this->ErrorMsgs[$pseudo] = (substr($error_label, 0, 1) == '+') ? substr($error_label, 1) : '!'.$error_label.'!';
}
return true;
}
/**
* Return current item' field value by field name
* (doesn't apply formatter)
*
* @access public
* @param string $name field name to return
* @return mixed
*/
function GetDBField($name)
{
/*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) {
$this->Application->Debugger->appendTrace();
}*/
return $this->FieldValues[$name];
}
function HasField($name)
{
return isset($this->FieldValues[$name]);
}
function GetFieldValues()
{
return $this->FieldValues;
}
/**
* Sets item' fields corresponding to elements in passed $hash values.
*
* The function sets current item fields to values passed in $hash, by matching $hash keys with field names
* of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before acutally setting the fields
*
* @access public
* @param Array $hash
* @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
* @return void
*/
function SetFieldsFromHash($hash, $set_fields = null)
{
// used in formatter which work with multiple fields together
foreach($hash as $field_name => $field_value) {
if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
continue;
}
if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
continue;
}
$this->SetDirtyField($field_name, $field_value);
}
// formats all fields using associated formatters
foreach ($hash as $field_name => $field_value)
{
if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
continue;
}
if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
continue;
}
$this->SetField($field_name,$field_value);
}
}
function SetDBFieldsFromHash($hash, $set_fields = null)
{
foreach ($hash as $field_name => $field_value) {
if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
continue;
}
if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
continue;
}
$this->SetDBField($field_name, $field_value);
}
}
/**
* Returns part of SQL WHERE clause identifing the record, ex. id = 25
*
* @access public
* @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method
* @param Array $keys_hash alternative, then item id, keys hash to load item by
* @return void
* @see kDBItem::Load()
* @see kDBItem::Update()
* @see kDBItem::Delete()
*/
function GetKeyClause($method=null, $keys_hash = null)
{
if( !isset($keys_hash) ) $keys_hash = Array($this->IDField => $this->ID);
$ret = '';
foreach($keys_hash as $field => $value)
{
if (!preg_match('/\./', $field)) {
$ret .= '(`'.$this->TableName.'`.'.$field.' = '.$this->Conn->qstr($value).') AND ';
}
else {
$ret .= '('.$field.' = '.$this->Conn->qstr($value).') AND ';
}
}
return preg_replace('/(.*) AND $/', '\\1', $ret);
}
/**
* Loads item from the database by given id
*
* @access public
* @param mixed $id item id of keys->values hash to load item by
* @param string $id_field_name Optional parameter to load item by given Id field
* @return bool True if item has been loaded, false otherwise
*/
function Load($id, $id_field_name = null)
{
if ( isset($id_field_name) ) {
$this->SetIDField($id_field_name); // set new IDField
}
$keys_sql = '';
if (is_array($id)) {
$keys_sql = $this->GetKeyClause('load', $id);
}
else {
$this->setID($id);
$keys_sql = $this->GetKeyClause('load');
}
if ( isset($id_field_name) ) {
// restore original IDField from unit config
$this->setIDField( $this->Application->getUnitOption($this->Prefix, 'IDField') );
}
if (($id === false) || !$keys_sql) {
return $this->Clear();
}
if (!$this->raiseEvent('OnBeforeItemLoad', $id)) {
return false;
}
$q = $this->GetSelectSQL() . ' WHERE ' . $keys_sql;
$field_values = $this->Conn->GetRow($q);
if ($field_values) {
$this->FieldValues = array_merge_recursive2($this->FieldValues, $field_values);
$this->OriginalFieldValues = $this->FieldValues;
}
else {
return $this->Clear();
}
if (is_array($id) || isset($id_field_name)) {
$this->setID($this->FieldValues[$this->IDField]);
}
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $this->GetID());
$this->Loaded = true;
return true;
}
/**
* Builds select sql, SELECT ... FROM parts only
*
* @access public
* @return string
*/
function GetSelectSQL()
{
$sql = $this->addCalculatedFields($this->SelectClause);
return parent::GetSelectSQL($sql);
}
function UpdateFormattersMasterFields()
{
foreach ($this->Fields as $field => $options) {
if (isset($options['formatter'])) {
$formatter =& $this->Application->recallObject($options['formatter']);
$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
}
}
}
/**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
* @param mixed $force_id
* @return bool
*/
function skipField($field_name, $force_id = false)
{
$skip = false;
// 1. skipping 'virtual' field
$skip = $skip || array_key_exists($field_name, $this->VirtualFields);
// 2. don't write empty field value to db, when "skip_empty" option is set
$field_value = array_key_exists($field_name, $this->FieldValues) ? $this->FieldValues[$field_name] : false;
$skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['skip_empty'] : false;
$skip = $skip || (!$field_value && $skip_empty);
// 3. skipping field not in Fields (nor virtual, nor real)
$skip = $skip || !array_key_exists($field_name, $this->Fields);
return $skip;
}
/**
* Updates previously loaded record with current item' values
*
* @access public
* @param int Primery Key Id to update
* @return bool
*/
function Update($id = null, $system_update = false)
{
if (isset($id)) {
$this->setID($id);
}
if (!$this->raiseEvent('OnBeforeItemUpdate')) {
return false;
}
if (!isset($this->ID)) {
// ID could be set inside OnBeforeItemUpdate event, so don't combine this check with previous one
return false;
}
// validate before updating
if (!$this->Validate()) {
return false;
}
if (!$this->raiseEvent('OnAfterItemValidate')) {
return false;
}
if (!$this->FieldValues) {
// nothing to update
return true;
}
$sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ($this->skipField($field_name)) {
continue;
}
if ( is_null($field_value) ) {
if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$field_value = $this->Fields[$field_name]['default'];
}
}
$sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', ';
}
$sql = 'UPDATE ' . $this->TableName . '
SET ' . substr($sql, 0, -2) . '
WHERE ' . $this->GetKeyClause('update');
if ($this->Conn->ChangeQuery($sql) === false) {
// there was and sql error
return false;
}
$affected = $this->Conn->getAffectedRows();
if (!$system_update && $affected == 1) {
$this->setModifiedFlag(clUPDATE);
}
$this->saveCustomFields();
$this->raiseEvent('OnAfterItemUpdate');
$this->Loaded = true;
if ($this->mode != 't') {
$this->Application->resetCounters($this->TableName);
}
return true;
}
function ValidateField($field)
{
$options = $this->Fields[$field];
/*if (isset($options['formatter'])) {
$formatter =& $this->Application->recallObject($options['formatter']);
$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
}*/
$error_field = isset($options['error_field']) ? $options['error_field'] : $field;
$res = !isset($this->FieldErrors[$error_field]['pseudo']) || !$this->FieldErrors[$error_field]['pseudo'];
$res = $res && $this->ValidateRequired($field, $options);
$res = $res && $this->ValidateType($field, $options);
$res = $res && $this->ValidateRange($field, $options);
$res = $res && $this->ValidateUnique($field, $options);
$res = $res && $this->CustomValidation($field, $options);
return $res;
}
/**
* Validate all item fields based on
* constraints set in each field options
* in config
*
* @return bool
* @access private
*/
function Validate()
{
$this->UpdateFormattersMasterFields(); //order is critical - should be called BEFORE checking errors
if ($this->IgnoreValidation) {
return true;
}
$global_res = true;
foreach ($this->Fields as $field => $params) {
$res = $this->ValidateField($field);
$global_res = $global_res && $res;
}
if (!$global_res && $this->Application->isDebugMode()) {
$error_msg = ' Validation failed in prefix <strong>'.$this->Prefix.'</strong>,
FieldErrors follow (look at items with <strong>"pseudo"</strong> key set)<br />
You may ignore this notice if submitted data really has a validation error';
trigger_error(trim($error_msg), E_USER_NOTICE);
$this->Application->Debugger->dumpVars($this->FieldErrors);
}
return $global_res;
}
/**
* Check field value by user-defined alghoritm
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
*/
function CustomValidation($field, $params)
{
return true;
}
/**
* Check if item has errors
*
* @param Array $skip_fields fields to skip during error checking
* @return bool
*/
function HasErrors($skip_fields)
{
$global_res = false;
foreach ($this->Fields as $field => $field_params) {
// If Formatter has set some error messages during values parsing
if ( !( in_array($field, $skip_fields) ) &&
isset($this->FieldErrors[$field]['pseudo']) && $this->FieldErrors[$field] != '') {
$global_res = true;
}
}
return $global_res;
}
/**
* Check if value in field matches field type specified in config
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
*/
function ValidateType($field, $params)
{
$res = true;
$val = $this->FieldValues[$field];
if ( $val != '' &&
isset($params['type']) &&
preg_match("#int|integer|double|float|real|numeric|string#", $params['type'])
) {
if ($params['type'] == 'numeric') {
trigger_error('Invalid field type <strong>'.$params['type'].'</strong> (in ValidateType method), please use <strong>float</strong> instead', E_USER_NOTICE);
$params['type'] = 'float';
}
$res = is_numeric($val);
if ($params['type']=='string' || $res) {
$f = 'is_'.$params['type'];
settype($val, $params['type']);
$res = $f($val) && ($val == $this->FieldValues[$field]);
}
if (!$res) {
$this->SetError($field, 'bad_type', null, $params['type']);
}
}
return $res;
}
/**
* Check if value is set for required field
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
function ValidateRequired($field, $params)
{
$res = true;
if (isset($params['required']) && $params['required']) {
$check_value = $this->FieldValues[$field];
if ($this->Application->ConfigValue('TrimRequiredFields')) {
$check_value = trim($check_value);
}
$res = ((string)$check_value != '');
}
if (!$res) {
$this->SetError($field, 'required');
}
return $res;
}
/**
* Validates that current record has unique field combination among other table records
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
function ValidateUnique($field, $params)
{
$res = true;
$unique_fields = getArrayValue($params,'unique');
if($unique_fields !== false)
{
$where = Array();
array_push($unique_fields,$field);
foreach($unique_fields as $unique_field)
{
// if field is not empty or if it is required - we add where condition
if ((string)$this->GetDBField($unique_field) != '' || (isset($this->Fields[$unique_field]['required']) && $this->Fields[$unique_field]['required'])) {
$where[] = '`'.$unique_field.'` = '.$this->Conn->qstr( $this->GetDBField($unique_field) );
}
else {
// not good if we check by less fields than indicated
return true;
}
}
// This can ONLY happen if all unique fields are empty and not required.
// In such case we return true, because if unique field is not required there may be numerous empty values
// if (!$where) return true;
$sql = 'SELECT COUNT(*) FROM %s WHERE ('.implode(') AND (',$where).') AND ('.$this->IDField.' <> '.(int)$this->ID.')';
$res_temp = $this->Conn->GetOne( str_replace('%s', $this->TableName, $sql) );
$current_table_only = getArrayValue($params, 'current_table_only'); // check unique record only in current table
$res_live = $current_table_only ? 0 : $this->Conn->GetOne( str_replace('%s', $this->Application->GetLiveName($this->TableName), $sql) );
$res = ($res_temp == 0) && ($res_live == 0);
if (!$res) {
$this->SetError($field, 'unique');
}
}
return $res;
}
/**
* Check if field value is in range specified in config
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
function ValidateRange($field, $params)
{
$res = true;
$val = $this->FieldValues[$field];
if ( isset($params['type']) && preg_match("#int|integer|double|float|real#", $params['type']) && strlen($val) > 0 ) {
if ( isset($params['max_value_inc'])) {
$res = $res && $val <= $params['max_value_inc'];
$max_val = $params['max_value_inc'].' (inclusive)';
}
if ( isset($params['min_value_inc'])) {
$res = $res && $val >= $params['min_value_inc'];
$min_val = $params['min_value_inc'].' (inclusive)';
}
if ( isset($params['max_value_exc'])) {
$res = $res && $val < $params['max_value_exc'];
$max_val = $params['max_value_exc'].' (exclusive)';
}
if ( isset($params['min_value_exc'])) {
$res = $res && $val > $params['min_value_exc'];
$min_val = $params['min_value_exc'].' (exclusive)';
}
}
if (!$res) {
if ( !isset($min_val) ) $min_val = '-&infin;';
if ( !isset($max_val) ) $max_val = '&infin;';
$this->SetError($field, 'value_out_of_range', null, Array ($min_val, $max_val));
return $res;
}
if ( isset($params['max_len'])) {
$res = $res && mb_strlen($val) <= $params['max_len'];
}
if ( isset($params['min_len'])) {
$res = $res && mb_strlen($val) >= $params['min_len'];
}
if (!$res) {
$error_params = Array (getArrayValue($params, 'min_len'), getArrayValue($params, 'max_len'));
$this->SetError($field, 'length_out_of_range', null, $error_params);
return $res;
}
return $res;
}
/**
* Return error message for field
*
* @param string $field
* @return string
* @access public
*/
function GetErrorMsg($field, $force_escape = null)
{
if( !isset($this->FieldErrors[$field]) ) return '';
$err = getArrayValue($this->FieldErrors[$field], 'pseudo');
if (!$err) return '';
// if special error msg defined in config
if( isset($this->Fields[$field]['error_msgs'][$err]) )
{
$msg = $this->Fields[$field]['error_msgs'][$err];
}
else //fall back to defaults
{
if( !isset($this->ErrorMsgs[$err]) ) {
trigger_error('No user message is defined for pseudo error <b>'.$err.'</b><br>', E_USER_WARNING);
return $err; //return the pseudo itself
}
$msg = $this->ErrorMsgs[$err];
}
$msg = $this->Application->ReplaceLanguageTags($msg, $force_escape);
if ( isset($this->FieldErrors[$field]['params']) )
{
return vsprintf($msg, $this->FieldErrors[$field]['params']);
}
return $msg;
}
/**
* Creates a record in the database table with current item' values
*
* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
* @access public
* @return bool
*/
function Create($force_id = false, $system_create = false)
{
if (!$this->raiseEvent('OnBeforeItemCreate')) {
return false;
}
// Validating fields before attempting to create record
if (!$this->Validate()) {
return false;
}
if (!$this->raiseEvent('OnAfterItemValidate')) {
return false;
}
if (is_int($force_id)) {
$this->FieldValues[$this->IDField] = $force_id;
}
elseif (!$force_id || !is_bool($force_id)) {
$this->FieldValues[$this->IDField] = $this->generateID();
}
$fields_sql = '';
$values_sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ($this->skipField($field_name, $force_id)) {
continue;
}
if (is_null($field_value)) {
if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']);
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
else {
if (($field_name == $this->IDField) && ($field_value == 0)) {
// don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value
$values_sql .= 'DEFAULT';
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
$fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement
$values_sql .= ', ';
}
$sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ')
VALUES (' . substr($values_sql, 0, -2) . ')';
//Executing the query and checking the result
if ($this->Conn->ChangeQuery($sql) === false) {
return false;
}
$insert_id = $this->Conn->getInsertID();
if ($insert_id == 0) {
// insert into temp table (id is not auto-increment field)
$insert_id = $this->FieldValues[$this->IDField];
}
$this->setID($insert_id);
if (!$system_create){
$this->setModifiedFlag(clCREATE);
}
$this->saveCustomFields();
if ($this->mode != 't') {
$this->Application->resetCounters($this->TableName);
}
$this->raiseEvent('OnAfterItemCreate');
$this->Loaded = true;
return true;
}
/**
* Deletes the record from databse
*
* @access public
* @return bool
*/
function Delete($id = null)
{
if (isset($id)) {
$this->setID($id);
}
if (!$this->raiseEvent('OnBeforeItemDelete')) {
return false;
}
$sql = 'DELETE FROM ' . $this->TableName . '
WHERE ' . $this->GetKeyClause('Delete');
$ret = $this->Conn->ChangeQuery($sql);
$affected_rows = $this->Conn->getAffectedRows();
$this->setModifiedFlag(clDELETE); // will change affected rows, so get it before this line
if ($affected_rows > 0) {
// something was actually deleted
$this->raiseEvent('OnAfterItemDelete');
}
if ($this->mode != 't') {
$this->Application->resetCounters($this->TableName);
}
return $ret;
}
function PopulateMultiLangFields()
{
$ml_helper =& $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$lang_count = $ml_helper->getLanguageCount();
foreach ($this->Fields as $field => $options)
{
// master field is set only for CURRENT language
if (isset($options['formatter']) && $options['formatter'] == 'kMultiLanguage' && isset($options['master_field'])) {
if (preg_match('/^l([0-9]+)_(.*)/', $field, $regs)) {
$l = $regs[1];
$name = $regs[2];
// MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode, we display ML fields directly
// so we set it back to itself, otherwise error will not be displayed
$this->Fields['l' . $l . '_' . $name]['error_field'] = 'l' . $l . '_' . $name;
for ($i = 1; $i <= $lang_count; $i++) {
if ($i == $l || !$ml_helper->LanguageFound($i)) continue;
$f_options = $options;
$f_options['error_field'] = 'l' . $i . '_' . $name; // set error field back to itself - see comment above
if ($i != $this->Application->GetDefaultLanguageId()) {
unset($f_options['required']); // all non-primary language field set to non-required
}
$this->Fields['l' . $i . '_' . $name] = $f_options;
}
}
}
}
}
/**
* Sets new name for item in case if it is beeing copied
* in same table
*
* @param array $master Table data from TempHandler
* @param int $foreign_key ForeignKey value to filter name check query by
* @param string $title_field FieldName to alter, by default - TitleField of the prefix
* @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name
* @access private
*/
function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s')
{
if (!isset($title_field)) {
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return;
}
$new_name = $this->GetDBField($title_field);
$original_checked = false;
do {
if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) {
$new_name = sprintf($format, ($regs[1]+1), $regs[2]);
}
elseif ($original_checked) {
$new_name = sprintf($format, '', $new_name);
}
// if we are cloning in temp table this will look for names in temp table,
// since object' TableName contains correct TableName (for temp also!)
// if we are cloning live - look in live
$query = 'SELECT '.$title_field.' FROM '.$this->TableName.'
WHERE '.$title_field.' = '.$this->Conn->qstr($new_name);
$foreign_key_field = getArrayValue($master, 'ForeignKey');
$foreign_key_field = is_array($foreign_key_field) ? $foreign_key_field[ $master['ParentPrefix'] ] : $foreign_key_field;
if ($foreign_key_field && isset($foreign_key)) {
$query .= ' AND '.$foreign_key_field.' = '.$foreign_key;
}
$res = $this->Conn->GetOne($query);
/*// if not found in live table, check in temp table if applicable
if ($res === false && $object->Special == 'temp') {
$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
$res = $this->Conn->GetOne($query);
}*/
$original_checked = true;
} while ($res !== false);
$this->SetDBField($title_field, $new_name);
}
function raiseEvent($name, $id = null, $additional_params = Array())
{
if( !isset($id) ) $id = $this->GetID();
$event = new kEvent( Array('name'=>$name,'prefix'=>$this->Prefix,'special'=>$this->Special) );
$event->setEventParam('id', $id);
if ($additional_params) {
foreach ($additional_params as $ap_name => $ap_value) {
$event->setEventParam($ap_name, $ap_value);
}
}
$this->Application->HandleEvent($event);
return $event->status == erSUCCESS ? true : false;
}
/**
* Set's new ID for item
*
* @param int $new_id
* @access public
*/
function setID($new_id)
{
$this->ID = $new_id;
$this->SetDBField($this->IDField, $new_id);
}
/**
* Generate and set new temporary id
*
* @access private
*/
function setTempID()
{
$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$this->IDField.') FROM '.$this->TableName);
if($new_id > 0) $new_id = 0;
--$new_id;
$this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID());
if ($this->ShouldLogChanges()) {
// Updating TempId in ChangesLog, if changes are disabled
$ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix).'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
if ($changes) {
foreach ($changes as $key => $rec) {
if ($rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID()) {
$changes[$key]['ItemId'] = $new_id;
}
}
}
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
$this->SetID($new_id);
}
/**
* Set's modification flag for main prefix of current prefix to true
*
* @access private
* @author Alexey
*/
function setModifiedFlag($mode = null)
{
$main_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
- $this->Application->StoreVar($main_prefix.'_modified', '1');
+ $this->Application->StoreVar($main_prefix.'_modified', '1', !$this->Application->IsAdmin());
if ($this->ShouldLogChanges()) {
$this->LogChanges($main_prefix, $mode);
if (!$this->IsTempTable()) {
$handler =& $this->Application->recallObject($this->Prefix.'_EventHandler');
$ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
$handler->SaveLoggedChanges($ses_var_name);
}
}
}
/**
* Determines, that changes made to this item should be written to change log
*
* @return bool
*/
function ShouldLogChanges()
{
$log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog');
return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges');
}
function LogChanges($main_prefix, $mode)
{
if (!$mode) {
return ;
}
$ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : array();
$general = array(
'Prefix' => $this->Prefix,
'ItemId' => $this->GetID(),
'OccuredOn' => adodb_mktime(),
'MasterPrefix' => $main_prefix,
'MasterId' => $this->Prefix == $main_prefix ? $this->GetID() : $this->Application->GetVar($main_prefix.'_id'), // is that correct (Kostja)??
'Action' => $mode,
);
switch ($mode) {
case clUPDATE:
$changes[] = array_merge($general, Array(
'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetChangedFields())),
));
break;
case clCREATE:
$changes[] = array_merge($general, Array(
'Changes' => serialize($this->GetTitleField()),
));
break;
case clDELETE:
$changes[] = array_merge($general, Array(
'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetRealFields())),
));
}
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
function GetTitleField()
{
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if ($title_field && $this->GetField($title_field)) {
return Array($title_field => $this->GetField($title_field));
}
}
function GetRealFields()
{
if (function_exists('array_diff_key')) {
$db_fields = array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields);
}
else {
$db_fields = array();
foreach ($this->FieldValues as $key => $value) {
if (array_key_exists($key, $this->VirtualFields) || array_key_exists($key, $this->CalculatedFields)) continue;
$db_fields[$key] = $value;
}
}
return $db_fields;
}
function GetChangedFields()
{
$changes = array();
$diff = array_diff_assoc($this->GetRealFields(), $this->OriginalFieldValues);
foreach ($diff as $field => $new_value) {
$changes[$field] = array('old' => $this->GetOriginalField($field, true), 'new' => $this->GetField($field));
}
return $changes;
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
function GetID()
{
return $this->ID;
}
/**
* Generates ID for new items before inserting into database
*
* @return int
* @access private
*/
function generateID()
{
return 0;
}
/**
* Returns true if item was loaded successfully by Load method
*
* @return bool
*/
function isLoaded()
{
return $this->Loaded;
}
/**
* Checks if field is required
*
* @param string $field
* @return bool
*/
function isRequired($field)
{
return getArrayValue( $this->Fields[$field], 'required' );
}
/**
* Sets new required flag to field
*
* @param string $field
* @param bool $is_required
*/
function setRequired($field, $is_required = true)
{
$this->Fields[$field]['required'] = $is_required;
}
function Clear($new_id = null)
{
$this->Loaded = false;
$this->FieldValues = Array();
$this->OriginalFieldValues = Array ();
$this->SetDefaultValues(); // will wear off kDBItem::setID effect, so set it later
$this->FieldErrors = Array();
$this->setID($new_id);
return $this->Loaded;
}
function Query($force = false)
{
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendTrace();
}
trigger_error('<b>Query</b> method is called in class <b>'.get_class($this).'</b> for prefix <b>'.$this->getPrefixSpecial().'</b>', E_USER_ERROR);
}
function saveCustomFields()
{
if (!$this->customFields) {
return true;
}
$cdata_key = rtrim($this->Prefix.'-cdata.'.$this->Special, '.');
$cdata =& $this->Application->recallObject($cdata_key, null, Array('skip_autoload' => true, 'populate_ml_fields' => true));
$resource_id = $this->GetDBField('ResourceId');
$cdata->Load($resource_id, 'ResourceId');
$cdata->SetDBField('ResourceId', $resource_id);
$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
foreach ($this->customFields as $custom_id => $custom_name) {
$force_primary = isset($cdata->Fields['cust_'.$custom_id]['force_primary']) && $cdata->Fields['cust_'.$custom_id]['force_primary'];
$cdata->SetDBField($ml_formatter->LangFieldName('cust_'.$custom_id, $force_primary), $this->GetDBField('cust_'.$custom_name));
}
if ($cdata->isLoaded()) {
$ret = $cdata->Update();
}
else {
$ret = $cdata->Create();
if ($cdata->mode == 't') $cdata->setTempID();
}
return $ret;
}
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @return Array
*/
function GetCol($field)
{
return Array (0 => $this->GetDBField($field));
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/db/cat_event_handler.php
===================================================================
--- branches/5.0.x/core/kernel/db/cat_event_handler.php (revision 12367)
+++ branches/5.0.x/core/kernel/db/cat_event_handler.php (revision 12368)
@@ -1,2653 +1,2653 @@
<?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 kCatDBEventHandler extends kDBEventHandler {
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnSaveSettings' => Array ('self' => 'add|edit|advanced:import'),
'OnResetSettings' => Array ('self' => 'add|edit|advanced:import'),
'OnBeforeDeleteOriginal' => Array ('self' => 'edit|advanced:approve'),
'OnCopy' => Array ('self' => true),
'OnDownloadFile' => Array ('self' => 'view'),
'OnCancelAction' => Array ('self' => true),
'OnItemBuild' => Array ('self' => true),
'OnMakeVote' => Array ('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Load item if id is available
*
* @param kEvent $event
*/
function LoadItem(&$event)
{
$object =& $event->getObject();
$id = $this->getPassedID($event);
if ($object->Load($id)) {
$actions =& $this->Application->recallObject('kActions');
$actions->Set($event->Prefix_Special.'_id', $object->GetID() );
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ($use_pending_editing && $event->Special != 'original') {
$this->Application->SetVar($event->Prefix.'.original_id', $object->GetDBField('OrgId'));
}
}
else {
$object->setID($id);
}
}
/**
* Checks 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 == 'OnExport') {
// save category_id before doing export
$this->Application->LinkVar('m_cat_id');
}
if (in_array($event->Name, $this->_getMassPermissionEvents())) {
$items = $this->_getPermissionCheckInfo($event);
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
if (($event->Name == 'OnSave') && array_key_exists(0, $items)) {
// adding new item (ID = 0)
$perm_value = $perm_helper->AddCheckPermission($items[0]['CategoryId'], $event->Prefix) > 0;
}
else {
// leave only items, that can be edited
$ids = Array ();
$check_method = in_array($event->Name, Array ('OnMassDelete', 'OnCut')) ? 'DeleteCheckPermission' : 'ModifyCheckPermission';
foreach ($items as $item_id => $item_data) {
if ($perm_helper->$check_method($item_data['CreatedById'], $item_data['CategoryId'], $event->Prefix) > 0) {
$ids[] = $item_id;
}
}
if (!$ids) {
// no items left for editing -> no permission
return $perm_helper->finalizePermissionCheck($event, false);
}
$perm_value = true;
$event->setEventParam('ids', $ids); // will be used later by "kDBEventHandler::StoreSelectedIDs" method
}
return $perm_helper->finalizePermissionCheck($event, $perm_value);
}
$export_events = Array ('OnSaveSettings', 'OnResetSettings', 'OnExportBegin');
if (in_array($event->Name, $export_events)) {
// when import settings before selecting target import category
return $this->Application->CheckPermission('in-portal:main_import.view');
}
if ($event->Name == 'OnProcessSelected') {
if ($this->Application->RecallVar('dst_field') == 'ImportCategory') {
// when selecting target import category
return $this->Application->CheckPermission('in-portal:main_import.view');
}
}
return parent::CheckPermission($event);
}
/**
* Returns events, that require item-based (not just event-name based) permission check
*
* @return Array
*/
function _getMassPermissionEvents()
{
return Array (
'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove',
'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown',
'OnCut',
);
}
/**
* Returns category item IDs, that require permission checking
*
* @param kEvent $event
* @return string
*/
function _getPermissionCheckIDs(&$event)
{
if ($event->Name == 'OnSave') {
$selected_ids = implode(',', $this->getSelectedIDs($event, true));
if (!$selected_ids) {
$selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave)
}
}
else {
// OnEdit, OnMassDelete events, when items are checked in grid
$selected_ids = implode(',', $this->StoreSelectedIDs($event));
}
return $selected_ids;
}
/**
* Returns information used in permission checking
*
* @param kEvent $event
* @return Array
*/
function _getPermissionCheckInfo(&$event)
{
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
// when saving data from temp table to live table check by data from temp table
$item_ids = $this->_getPermissionCheckIDs($event);
$items = $perm_helper->GetCategoryItemData($event->Prefix, $item_ids, $event->Name == 'OnSave');
if (!$items) {
// when item not present in temp table, then permission is not checked, because there are no data in db to check
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list ($id, $fields_hash) = each($items_info);
if (array_key_exists('CategoryId', $fields_hash)) {
$item_category = $fields_hash['CategoryId'];
}
else {
$item_category = $this->Application->GetVar('m_cat_id');
}
$items[$id] = Array (
'CreatedById' => $this->Application->RecallVar('use_id'),
'CategoryId' => $item_category,
);
}
return $items;
}
/**
* Add selected items to clipboard with mode = COPY (CLONE)
*
* @param kEvent $event
*/
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);
}
/**
* Checks permission for OnPaste event
*
* @param kEvent $event
* @return bool
*/
function _checkPastePermission(&$event)
{
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$category_id = $this->Application->GetVar('m_cat_id');
if ($perm_helper->AddCheckPermission($category_id, $event->Prefix) == 0) {
// no items left for editing -> no permission
return $perm_helper->finalizePermissionCheck($event, false);
}
return true;
}
/**
* Performs category item paste
*
* @param kEvent $event
*/
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;
}
if ($clipboard_data['copy']) {
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$this->Application->SetVar('ResetCatBeforeClone', 1); // used in "kCatDBEventHandler::OnBeforeClone"
$temp->CloneItems($event->Prefix, $event->Special, $clipboard_data['copy']);
}
if ($clipboard_data['cut']) {
$object =& $this->Application->recallObject($event->getPrefixSpecial().'.item', $event->Prefix, Array('skip_autoload' => true));
foreach ($clipboard_data['cut'] as $id) {
$object->Load($id);
$object->MoveToCat();
}
}
}
/**
* Deletes all selected items.
* Automatically recurse into sub-items using temp handler, and deletes sub-items
* by calling its Delete method if sub-item has AutoDelete set to true in its config file
*
* @param kEvent $event
*/
function OnMassDelete(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$event->status=erSUCCESS;
$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);
$object =& $this->Application->recallObject($event->Prefix.'.recycleitem', null, Array ('skip_autoload' => true));
foreach ($ids as $id) {
$object->Load($id);
if (preg_match('/^'.preg_quote($rb->GetDBField('ParentPath'),'/').'/', $object->GetDBField('ParentPath'))) {
$to_delete[] = $id;
continue;
}
$object->MoveToCat($recycle_bin);
}
$ids = $to_delete;
}
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$event->setEventParam('ids', $ids);
$this->customProcessing($event, 'before');
$ids = $event->getEventParam('ids');
if($ids)
{
$temp->DeleteItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
/**
* Return type clauses for list bulding on front
*
* @param kEvent $event
* @return Array
*/
function getTypeClauses(&$event)
{
$types = $event->getEventParam('types');
$types = $types ? explode(',', $types) : Array ();
$except_types = $event->getEventParam('except');
$except_types = $except_types ? explode(',', $except_types) : Array ();
$type_clauses = Array();
$user_id = $this->Application->RecallVar('user_id');
$owner_field = $this->getOwnerField($event->Prefix);
$type_clauses['my_items']['include'] = '%1$s.'.$owner_field.' = '.$user_id;
$type_clauses['my_items']['except'] = '%1$s.'.$owner_field.' <> '.$user_id;
$type_clauses['my_items']['having_filter'] = false;
$type_clauses['pick']['include'] = '%1$s.EditorsPick = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
$type_clauses['pick']['except'] = '%1$s.EditorsPick! = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
$type_clauses['pick']['having_filter'] = false;
$type_clauses['hot']['include'] = '`IsHot` = 1 AND PrimaryCat = 1';
$type_clauses['hot']['except'] = '`IsHot`! = 1 AND PrimaryCat = 1';
$type_clauses['hot']['having_filter'] = true;
$type_clauses['pop']['include'] = '`IsPop` = 1 AND PrimaryCat = 1';
$type_clauses['pop']['except'] = '`IsPop`! = 1 AND PrimaryCat = 1';
$type_clauses['pop']['having_filter'] = true;
$type_clauses['new']['include'] = '`IsNew` = 1 AND PrimaryCat = 1';
$type_clauses['new']['except'] = '`IsNew`! = 1 AND PrimaryCat = 1';
$type_clauses['new']['having_filter'] = true;
$type_clauses['displayed']['include'] = '';
$displayed = $this->Application->GetVar($event->Prefix.'_displayed_ids');
if ($displayed) {
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$type_clauses['displayed']['except'] = '%1$s.'.$id_field.' NOT IN ('.$displayed.')';
}
else {
$type_clauses['displayed']['except'] = '';
}
$type_clauses['displayed']['having_filter'] = false;
if (in_array('search', $types) || in_array('search', $except_types)) {
$event_mapping = Array(
'simple' => 'OnSimpleSearch',
'subsearch' => 'OnSubSearch',
'advanced' => 'OnAdvancedSearch');
if($this->Application->GetVar('INPORTAL_ON') && $this->Application->GetVar('Action') == 'm_simple_subsearch')
{
$type = 'subsearch';
}
else
{
$type = $this->Application->GetVar('search_type') ? $this->Application->GetVar('search_type') : 'simple';
}
if($keywords = $event->getEventParam('keyword_string')) // processing keyword_string param of ListProducts tag
{
$this->Application->SetVar('keywords', $keywords);
$type = 'simple';
}
$search_event = $event_mapping[$type];
$this->$search_event($event);
$search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
$sql = 'SHOW TABLES LIKE "'.$search_table.'"';
if ($this->Conn->Query($sql)) {
$search_res_ids = $this->Conn->GetCol('SELECT ResourceId FROM '.$search_table);
}
if (isset($search_res_ids) && $search_res_ids) {
$type_clauses['search']['include'] = '%1$s.ResourceId IN ('.implode(',', $search_res_ids).') AND PrimaryCat = 1 AND ('.TABLE_PREFIX.'Category.Status = '.STATUS_ACTIVE.')';
$type_clauses['search']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $search_res_ids).') AND PrimaryCat = 1 AND ('.TABLE_PREFIX.'Category.Status = '.STATUS_ACTIVE.')';
}
else {
$type_clauses['search']['include'] = '0';
$type_clauses['search']['except'] = '1';
}
$type_clauses['search']['having_filter'] = false;
}
if (in_array('related', $types) || in_array('related', $except_types)) {
$related_to = $event->getEventParam('related_to');
if (!$related_to) {
$related_prefix = $event->Prefix;
}
else {
$sql = 'SELECT Prefix
FROM '.TABLE_PREFIX.'ItemTypes
WHERE ItemName = '.$this->Conn->qstr($related_to);
$related_prefix = $this->Conn->GetOne($sql);
}
$rel_table = $this->Application->getUnitOption('rel', 'TableName');
$item_type = (int)$this->Application->getUnitOption($event->Prefix, 'ItemType');
if ($item_type == 0) {
trigger_error('<strong>ItemType</strong> not defined for prefix <strong>' . $event->Prefix . '</strong>', E_USER_WARNING);
}
// process case, then this list is called inside another list
$prefix_special = $event->getEventParam('PrefixSpecial');
if (!$prefix_special) {
$prefix_special = $this->Application->Parser->GetParam('PrefixSpecial');
}
$id = false;
if ($prefix_special !== false) {
$processed_prefix = $this->Application->processPrefix($prefix_special);
if ($processed_prefix['prefix'] == $related_prefix) {
// printing related categories within list of items (not on details page)
$list =& $this->Application->recallObject($prefix_special);
/* @var $list kDBList */
$id = $list->GetID();
}
}
if ($id === false) {
// printing related categories for single item (possibly on details page)
if ($related_prefix == 'c') {
$id = $this->Application->GetVar('m_cat_id');
}
else {
$id = $this->Application->GetVar($related_prefix . '_id');
}
}
$p_item =& $this->Application->recallObject($related_prefix.'.current', null, Array('skip_autoload' => true));
$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).') AND PrimaryCat = 1';
$type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).') AND PrimaryCat = 1';
}
else {
$type_clauses['related']['include'] = '0';
$type_clauses['related']['except'] = '1';
}
$type_clauses['related']['having_filter'] = false;
}
if (in_array('favorites', $types) || in_array('favorites', $except_types)) {
$sql = 'SELECT ResourceId
FROM '.$this->Application->getUnitOption('fav', 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id');
$favorite_ids = $this->Conn->GetCol($sql);
if ($favorite_ids) {
$type_clauses['favorites']['include'] = '%1$s.ResourceId IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1';
$type_clauses['favorites']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1';
}
else {
$type_clauses['favorites']['include'] = 0;
$type_clauses['favorites']['except'] = 1;
}
$type_clauses['favorites']['having_filter'] = false;
}
return $type_clauses;
}
/**
* Returns SQL clause, that will help to select only data from specified category & it's children
*
* @param int $category_id
* @return string
*/
function getCategoryLimitClause($category_id)
{
if (!$category_id) {
return false;
}
$tree_indexes = $this->Application->getTreeIndex($category_id);
if (!$tree_indexes) {
// id of non-existing category was given
return 'FALSE';
}
return TABLE_PREFIX.'Category.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight'];
}
/**
* Apply filters to list
*
* @param kEvent $event
*/
function SetCustomQuery(&$event)
{
parent::SetCustomQuery($event);
$object =& $event->getObject();
/* @var $object kDBList */
// add category filter if needed
if ($event->Special != 'showall' && $event->Special != 'user') {
if ($event->getEventParam('parent_cat_id') !== false) {
$parent_cat_id = $event->getEventParam('parent_cat_id');
}
else {
$parent_cat_id = $this->Application->GetVar('c_id');
if (!$parent_cat_id) {
$parent_cat_id = $this->Application->GetVar('m_cat_id');
}
if (!$parent_cat_id) {
$parent_cat_id = 0;
}
}
if ("$parent_cat_id" == '0') {
// replace "0" category with "Content" category id (this way template
$parent_cat_id = $this->Application->findModule('Name', 'Core', 'RootCat');
}
if ((string)$parent_cat_id != 'any') {
if ($event->getEventParam('recursive')) {
$filter_clause = $this->getCategoryLimitClause($parent_cat_id);
if ($filter_clause !== false) {
$object->addFilter('category_filter', $filter_clause);
}
$object->addFilter('primary_filter', 'PrimaryCat = 1');
}
else {
$object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.CategoryId = '.$parent_cat_id );
}
}
else {
$object->addFilter('primary_filter', 'PrimaryCat = 1');
}
}
else {
$object->addFilter('primary_filter', 'PrimaryCat = 1');
// if using recycle bin don't show items from there
$recycle_bin = $this->Application->ConfigValue('RecycleBinFolder');
if ($recycle_bin) {
$object->addFilter('recyclebin_filter', TABLE_PREFIX.'CategoryItems.CategoryId <> '.$recycle_bin);
}
}
if ($event->Special == 'user') {
$editable_user = $this->Application->GetVar('u_id');
$object->addFilter('owner_filter', '%1$s.'.$this->getOwnerField($event->Prefix).' = '.$editable_user);
}
// add permission filter
if ($this->Application->RecallVar('user_id') == -1) {
// for "root" CATEGORY.VIEW permission is checked for items lists too
$view_perm = 1;
}
else {
// for any real user itemlist view permission is checked instead of CATEGORY.VIEW
$count_helper =& $this->Application->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
list ($view_perm, $view_filter) = $count_helper->GetPermissionClause($event->Prefix, 'perm');
$object->addFilter('perm_filter2', $view_filter);
}
$object->addFilter('perm_filter', 'perm.PermId = '.$view_perm);
$types = $event->getEventParam('types');
$this->applyItemStatusFilter($object, $types);
$except_types = $event->getEventParam('except');
$type_clauses = $this->getTypeClauses($event);
// convert prepared type clauses into list filters
$includes_or_filter =& $this->Application->makeClass('kMultipleFilter');
$includes_or_filter->setType(FLT_TYPE_OR);
$excepts_and_filter =& $this->Application->makeClass('kMultipleFilter');
$excepts_and_filter->setType(FLT_TYPE_AND);
$includes_or_filter_h =& $this->Application->makeClass('kMultipleFilter');
$includes_or_filter_h->setType(FLT_TYPE_OR);
$excepts_and_filter_h =& $this->Application->makeClass('kMultipleFilter');
$excepts_and_filter_h->setType(FLT_TYPE_AND);
if ($types) {
$types_array = explode(',', $types);
for ($i = 0; $i < sizeof($types_array); $i++) {
$type = trim($types_array[$i]);
if (isset($type_clauses[$type])) {
if ($type_clauses[$type]['having_filter']) {
$includes_or_filter_h->removeFilter('filter_'.$type);
$includes_or_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['include']);
}else {
$includes_or_filter->removeFilter('filter_'.$type);
$includes_or_filter->addFilter('filter_'.$type, $type_clauses[$type]['include']);
}
}
}
}
if ($except_types) {
$except_types_array = explode(',', $except_types);
for ($i = 0; $i < sizeof($except_types_array); $i++) {
$type = trim($except_types_array[$i]);
if (isset($type_clauses[$type])) {
if ($type_clauses[$type]['having_filter']) {
$excepts_and_filter_h->removeFilter('filter_'.$type);
$excepts_and_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['except']);
}else {
$excepts_and_filter->removeFilter('filter_'.$type);
$excepts_and_filter->addFilter('filter_'.$type, $type_clauses[$type]['except']);
}
}
}
}
/*if ( !$this->Application->IsAdmin() ) {
$object->addFilter('expire_filter', '%1$s.Expire IS NULL OR %1$s.Expire > UNIX_TIMESTAMP()');
}*/
/*$list_type = $event->getEventParam('ListType');
switch($list_type)
{
case 'favorites':
$fav_table = $this->Application->getUnitOption('fav','TableName');
$user_id =& $this->Application->RecallVar('user_id');
$sql = 'SELECT DISTINCT f.ResourceId
FROM '.$fav_table.' f
LEFT JOIN '.$object->TableName.' p ON p.ResourceId = f.ResourceId
WHERE f.PortalUserId = '.$user_id;
$ids = $this->Conn->GetCol($sql);
if(!$ids) $ids = Array(-1);
$object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.PrimaryCat = 1');
$object->addFilter('favorites_filter', '%1$s.`ResourceId` IN ('.implode(',',$ids).')');
break;
case 'search':
$search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
$sql = ' SELECT DISTINCT ResourceId
FROM '.$search_results_table.'
WHERE ItemType=11';
$ids = $this->Conn->GetCol($sql);
if(!$ids) $ids = Array(-1);
$object->addFilter('search_filter', '%1$s.`ResourceId` IN ('.implode(',',$ids).')');
break;
} */
$object->addFilter('includes_filter', $includes_or_filter);
$object->addFilter('excepts_filter', $excepts_and_filter);
$object->addFilter('includes_filter_h', $includes_or_filter_h, HAVING_FILTER);
$object->addFilter('excepts_filter_h', $excepts_and_filter_h, HAVING_FILTER);
}
/**
* Adds filter that filters out items with non-required statuses
*
* @param kDBList $object
* @param string $types
*/
function applyItemStatusFilter(&$object, $types)
{
// Link1 (before modifications) [Status = 1, OrgId = NULL], Link2 (after modifications) [Status = -2, OrgId = Link1_ID]
$pending_editing = $this->Application->getUnitOption($object->Prefix, 'UsePendingEditing');
if ( !$this->Application->IsAdmin() ) {
$types = explode(',', $types);
if (in_array('my_items', $types)) {
$allow_statuses = Array (STATUS_ACTIVE, STATUS_PENDING, STATUS_PENDING_EDITING);
$object->addFilter('status_filter', '%1$s.Status IN ('.implode(',', $allow_statuses).')');
if ($pending_editing) {
$user_id = $this->Application->RecallVar('user_id');
$this->applyPendingEditingFilter($object, $user_id);
}
}
else {
$object->addFilter('status_filter', '(%1$s.Status = ' . STATUS_ACTIVE . ') AND (' . TABLE_PREFIX . 'Category.Status = ' . STATUS_ACTIVE . ')');
if ($pending_editing) {
// if category item uses pending editing abilities, then in no cases show pending copies on front
$object->addFilter('original_filter', '%1$s.OrgId = 0 OR %1$s.OrgId IS NULL');
}
}
}
else {
if ($pending_editing) {
$this->applyPendingEditingFilter($object);
}
}
}
/**
* Adds filter, that removes live items if they have pending editing copies
*
* @param kDBList $object
* @param int $user_id
*/
function applyPendingEditingFilter(&$object, $user_id = null)
{
$sql = 'SELECT OrgId
FROM '.$object->TableName.'
WHERE Status = '.STATUS_PENDING_EDITING.' AND OrgId IS NOT NULL';
if (isset($user_id)) {
$owner_field = $this->getOwnerField($object->Prefix);
$sql .= ' AND '.$owner_field.' = '.$user_id;
}
$pending_ids = $this->Conn->GetCol($sql);
if ($pending_ids) {
$object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')');
}
}
/**
* Adds calculates fields for item statuses
*
* @param kCatDBItem $object
* @param kEvent $event
*/
function prepareObject(&$object, &$event)
{
$this->prepareItemStatuses($event);
$object->addCalculatedField('CachedNavbar', 'l'.$this->Application->GetVar('m_lang').'_CachedNavbar');
if ($event->Special == 'export' || $event->Special == 'import') {
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
$export_helper->prepareExportColumns($event);
}
}
/**
* Creates calculated fields for all item statuses based on config settings
*
* @param kEvent $event
*/
function prepareItemStatuses(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if (!$property_map) {
return ;
}
// new items
$object->addCalculatedField('IsNew', ' IF(%1$s.NewItem = 2,
IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '.
$this->Application->ConfigValue($property_map['NewDays']).
'*3600*24), 1, 0),
%1$s.NewItem
)');
// hot items (cache updated every hour)
$sql = 'SELECT Data
FROM '.TABLE_PREFIX.'Cache
WHERE (VarName = "'.$property_map['HotLimit'].'") AND (Cached >'.(adodb_mktime() - 3600).')';
$hot_limit = $this->Conn->GetOne($sql);
if ($hot_limit === false) {
$hot_limit = $this->CalculateHotLimit($event);
}
$object->addCalculatedField('IsHot', ' IF(%1$s.HotItem = 2,
IF(%1$s.'.$property_map['ClickField'].' >= '.$hot_limit.', 1, 0),
%1$s.HotItem
)');
// popular items
$object->addCalculatedField('IsPop', ' IF(%1$s.PopItem = 2,
IF(%1$s.CachedVotesQty >= '.
$this->Application->ConfigValue($property_map['MinPopVotes']).
' AND %1$s.CachedRating >= '.
$this->Application->ConfigValue($property_map['MinPopRating']).
', 1, 0),
%1$s.PopItem)');
}
function CalculateHotLimit(&$event)
{
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if (!$property_map) {
return;
}
$click_field = $property_map['ClickField'];
$last_hot = $this->Application->ConfigValue($property_map['MaxHotNumber']) - 1;
$sql = 'SELECT '.$click_field.' FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
ORDER BY '.$click_field.' DESC
LIMIT '.$last_hot.', 1';
$res = $this->Conn->GetCol($sql);
$hot_limit = (double)array_shift($res);
$this->Conn->Query('REPLACE INTO '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("'.$property_map['HotLimit'].'", "'.$hot_limit.'", '.adodb_mktime().')');
return $hot_limit;
return 0;
}
/**
* Moves item to preferred category, updates item hits
*
* @param kEvent $event
*/
function OnBeforeItemUpdate(&$event)
{
parent::OnBeforeItemUpdate($event);
// update hits field
$property_map = $this->Application->getUnitOption($event->Prefix, 'ItemPropertyMappings');
if ($property_map) {
$click_field = $property_map['ClickField'];
$object =& $event->getObject();
/* @var $object kCatDBItem */
if( $this->Application->IsAdmin() && ($this->Application->GetVar($click_field.'_original') !== false) &&
floor($this->Application->GetVar($click_field.'_original')) != $object->GetDBField($click_field) )
{
$sql = 'SELECT MAX('.$click_field.') FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
WHERE FLOOR('.$click_field.') = '.$object->GetDBField($click_field);
$hits = ( $res = $this->Conn->GetOne($sql) ) ? $res + 0.000001 : $object->GetDBField($click_field);
$object->SetDBField($click_field, $hits);
}
}
// change category
$target_category = $object->GetDBField('CategoryId');
if ($object->GetOriginalField('CategoryId') != $target_category) {
$object->MoveToCat($target_category);
}
}
/**
* Load price from temp table if product mode is temp table
*
* @param kEvent $event
*/
function OnAfterItemLoad(&$event)
{
$special = substr($event->Special, -6);
$object =& $event->getObject();
/* @var $object kCatDBItem */
if ($special == 'import' || $special == 'export') {
$image_data = $object->getPrimaryImageData();
if ($image_data) {
$thumbnail_image = $image_data[$image_data['LocalThumb'] ? 'ThumbPath' : 'ThumbUrl'];
if ($image_data['SameImages']) {
$full_image = '';
}
else {
$full_image = $image_data[$image_data['LocalImage'] ? 'LocalPath' : 'Url'];
}
$object->SetDBField('ThumbnailImage', $thumbnail_image);
$object->SetDBField('FullImage', $full_image);
$object->SetDBField('ImageAlt', $image_data['AltName']);
}
}
// substituiting pending status value for pending editing
if ($object->HasField('OrgId') && $object->GetDBField('OrgId') > 0 && $object->GetDBField('Status') == -2) {
$options = $object->Fields['Status']['options'];
foreach ($options as $key => $val) {
if ($key == 2) $key = -2;
$new_options[$key] = $val;
}
$object->Fields['Status']['options'] = $new_options;
}
// linking existing images for item with virtual fields
$image_helper =& $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$image_helper->LoadItemImages($object);
// linking existing files for item with virtual fields
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->LoadItemFiles($object);
// set item's additional categories to virtual field (used in editing)
$item_categories = $this->getItemCategories($object->GetDBField('ResourceId'));
$object->SetDBField('MoreCategories', $item_categories ? '|'.implode('|', $item_categories).'|' : '');
}
function OnAfterItemUpdate(&$event)
{
$this->CalculateHotLimit($event);
if ( substr($event->Special, -6) == 'import') {
$this->setCustomExportColumns($event);
}
if (!$this->Application->IsAdmin()) {
$object =& $event->getObject();
/* @var $object kDBItem */
$image_helper =& $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
// process file upload in virtual fields
$file_helper->SaveItemFiles($object);
if ($event->Special != '-item') {
// don't touch categories during cloning
$this->processAdditionalCategories($object, 'update');
}
}
}
/**
* sets values for import process
*
* @param kEvent $event
*/
function OnAfterItemCreate(&$event)
{
if ( substr($event->Special, -6) == 'import') {
$this->setCustomExportColumns($event);
}
if (!$this->Application->IsAdmin()) {
$object =& $event->getObject();
/* @var $object kDBItem */
$image_helper =& $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
// process file upload in virtual fields
$file_helper->SaveItemFiles($object);
if ($event->Special != '-item') {
// don't touch categories during cloning
$this->processAdditionalCategories($object, 'create');
}
}
}
/**
* Make record to search log
*
* @param string $keywords
* @param int $search_type 0 - simple search, 1 - advanced search
*/
function saveToSearchLog($keywords, $search_type = 0)
{
// don't save keywords for each module separately, just one time
// static variable can't help here, because each module uses it's own class instance !
if (!$this->Application->GetVar('search_logged')) {
$sql = 'UPDATE '.TABLE_PREFIX.'SearchLog
SET Indices = Indices + 1
WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search
$this->Conn->Query($sql);
if ($this->Conn->getAffectedRows() == 0) {
$fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLog');
}
$this->Application->SetVar('search_logged', 1);
}
}
/**
* Makes simple search for products
* based on keywords string
*
* @param kEvent $event
* @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!!
*/
function OnSimpleSearch(&$event)
{
if($this->Application->GetVar('INPORTAL_ON') && !($this->Application->GetVar('Action') == 'm_simple_search'))
{
return;
}
$event->redirect = false;
$search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
$keywords = unhtmlentities( trim($this->Application->GetVar('keywords')) );
$query_object =& $this->Application->recallObject('HTTPQuery');
$sql = 'SHOW TABLES LIKE "'.$search_table.'"';
if(!isset($query_object->Get['keywords']) &&
!isset($query_object->Post['keywords']) &&
$this->Conn->Query($sql))
{
return; // used when navigating by pages or changing sorting in search results
}
if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length'))
{
$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table);
$this->Application->SetVar('keywords_too_short', 1);
return; // if no or too short keyword entered, doing nothing
}
$this->Application->StoreVar('keywords', $keywords);
if (!$this->Application->GetVar('INPORTAL_ON')) {
// don't save search log, because in-portal already saved it
$this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search
}
$keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_'));
$event->setPseudoClass('_List');
$object =& $event->getObject();
/* @var $object kDBList */
$this->Application->SetVar($event->getPrefixSpecial().'_Page', 1);
$lang = $this->Application->GetVar('m_lang');
$items_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
$module_name = $this->Application->findModule('Var', $event->Prefix, 'Name');
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('confs', 'TableName') . '
WHERE ModuleName = ' . $this->Conn->qstr($module_name) . ' AND SimpleSearch = 1';
$search_config = $this->Conn->Query($sql, 'FieldName');
$field_list = array_keys($search_config);
$join_clauses = Array();
// field processing
$weight_sum = 0;
$alias_counter = 0;
$custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields');
if ($custom_fields) {
$custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName');
$join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId';
}
// what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table))
$search_config_map = Array();
foreach ($field_list as $key => $field) {
$options = $object->getFieldOptions($field);
$local_table = TABLE_PREFIX.$search_config[$field]['TableName'];
$weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause
// processing multilingual fields
if (getArrayValue($options, 'formatter') == 'kMultiLanguage') {
$field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field;
$field_list[$key] = 'l'.$lang.'_'.$field;
if (!isset($search_config[$field]['ForeignField'])) {
$field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary'];
$search_config_map[ $field_list[$key.'_primary'] ] = $field;
}
}
// processing fields from other tables
if ($foreign_field = $search_config[$field]['ForeignField']) {
$exploded = explode(':', $foreign_field, 2);
if ($exploded[0] == 'CALC') {
// ignoring having type clauses in simple search
unset($field_list[$key]);
continue;
}
else {
$multi_lingual = false;
if ($exploded[0] == 'MULTI') {
$multi_lingual = true;
$foreign_field = $exploded[1];
}
$exploded = explode('.', $foreign_field); // format: table.field_name
$foreign_table = TABLE_PREFIX.$exploded[0];
$alias_counter++;
$alias = 't'.$alias_counter;
if ($multi_lingual) {
$field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1];
$field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field;
$search_config_map[ $field_list[$key] ] = $field;
$search_config_map[ $field_list[$key.'_primary'] ] = $field;
}
else {
$field_list[$key] = $alias.'.'.$exploded[1];
$search_config_map[ $field_list[$key] ] = $field;
}
$join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']);
$join_clause = str_replace('{LocalTable}', $items_table, $join_clause);
$join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.'
ON '.$join_clause;
}
}
else {
// processing fields from local table
if ($search_config[$field]['CustomFieldId']) {
$local_table = 'custom_data';
// search by custom field value on current language
$custom_field_id = array_search($field_list[$key], $custom_fields);
$field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id;
// search by custom field value on primary language
$field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id;
$search_config_map[ $field_list[$key.'_primary'] ] = $field;
}
$field_list[$key] = $local_table.'.'.$field_list[$key];
$search_config_map[ $field_list[$key] ] = $field;
}
}
// keyword string processing
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$where_clause = Array ();
foreach ($field_list as $field) {
if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) {
// local real field
$filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false);
if ($filter_data) {
$where_clause[] = $filter_data['value'];
}
}
elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) {
$custom_field_name = 'cust_' . $search_config_map[$field];
$filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false);
if ($filter_data) {
$where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']);
}
}
else {
$where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field));
}
}
$where_clause = '(' . implode(') OR (', $where_clause) . ')';
$search_scope = $this->Application->GetVar('search_scope');
if ($search_scope == 'category') {
$category_id = $this->Application->GetVar('m_cat_id');
$category_filter = $this->getCategoryLimitClause($category_id);
if ($category_filter !== false) {
$join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'CategoryItems ON '.TABLE_PREFIX.'CategoryItems.ItemResourceId = '.$items_table.'.ResourceId';
$join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'CategoryItems.CategoryId';
$where_clause = '('.$this->getCategoryLimitClause($category_id).') AND '.$where_clause;
}
}
$where_clause = $where_clause.' AND '.$items_table.'.Status=1';
if($this->Application->GetVar('Action') == 'm_simple_subsearch') // subsearch, In-portal
{
if( $event->getEventParam('ResultIds') )
{
$where_clause .= ' AND '.$items_table.'.ResourceId IN ('.implode(',', $event->specificParams['ResultIds']).')';
}
}
if( $event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild' ) // subsearch, k4
{
if( $event->MasterEvent->getEventParam('ResultIds') )
{
$where_clause .= ' AND '.$items_table.'.ResourceId IN ('.implode(',', $event->MasterEvent->getEventParam('ResultIds')).')';
}
}
// making relevance clause
$positive_words = $search_helper->getPositiveKeywords($keywords);
$this->Application->StoreVar('highlight_keywords', serialize($positive_words));
$revelance_parts = Array();
reset($search_config);
foreach ($positive_words as $keyword_index => $positive_word) {
$positive_words[$keyword_index] = mysql_real_escape_string($positive_word);
}
foreach ($field_list as $field) {
if (!array_key_exists($field, $search_config_map)) {
$map_key = $search_config_map[$items_table . '.' . $field];
}
else {
$map_key = $search_config_map[$field];
}
$config_elem = $search_config[ $map_key ];
$weight = $config_elem['Priority'];
$revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)';
foreach ($positive_words as $keyword) {
$revelance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)';
}
}
$conf_postfix = $this->Application->getUnitOption($event->Prefix, 'SearchConfigPostfix');
$rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100;
$rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100;
$rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100;
$relevance_clause = '('.implode(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords;
if ($rel_pop && isset($object->Fields['Hits'])) {
$relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
}
if ($rel_rating && isset($object->Fields['CachedRating'])) {
$relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
}
// building final search query
if (!$this->Application->GetVar('do_not_drop_search_table') && !$this->Application->GetVar('INPORTAL_ON')) {
$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event
$this->Application->SetVar('do_not_drop_search_table', true);
}
$search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"');
if ($search_table_exists) {
$select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) ';
}
else {
$select_intro = 'CREATE TABLE '.$search_table.' AS ';
}
$edpick_clause = $this->Application->getUnitOption($event->Prefix.'.EditorsPick', 'Fields') ? $items_table.'.EditorsPick' : '0';
$sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance,
'.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField').' AS ItemId,
'.$items_table.'.ResourceId,
'.$this->Application->getUnitOption($event->Prefix, 'ItemType').' AS ItemType,
'.$edpick_clause.' AS EdPick
FROM '.$object->TableName.'
'.implode(' ', $join_clauses).'
WHERE '.$where_clause.'
GROUP BY '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField');
$res = $this->Conn->Query($sql);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnSubSearch(&$event)
{
$search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
$sql = 'SHOW TABLES LIKE "'.$search_table.'"';
if($this->Conn->Query($sql))
{
$sql = 'SELECT DISTINCT ResourceId FROM '.$search_table;
$ids = $this->Conn->GetCol($sql);
}
$event->setEventParam('ResultIds', $ids);
$event->CallSubEvent('OnSimpleSearch');
}
/**
* Enter description here...
*
* @param kEvent $event
* @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!!
*/
function OnAdvancedSearch(&$event)
{
$query_object =& $this->Application->recallObject('HTTPQuery');
if(!isset($query_object->Post['andor']))
{
return; // used when navigating by pages or changing sorting in search results
}
$this->Application->RemoveVar('keywords');
$this->Application->RemoveVar('Search_Keywords');
$module_name = $this->Application->findModule('Var', $event->Prefix, 'Name');
$sql = 'SELECT *
FROM '.$this->Application->getUnitOption('confs', 'TableName').'
WHERE (ModuleName = '.$this->Conn->qstr($module_name).') AND (AdvancedSearch = 1)';
$search_config = $this->Conn->Query($sql);
$lang = $this->Application->GetVar('m_lang');
$object =& $event->getObject();
$object->SetPage(1);
$items_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
$search_keywords = $this->Application->GetVar('value'); // will not be changed
$keywords = $this->Application->GetVar('value'); // will be changed down there
$verbs = $this->Application->GetVar('verb');
$glues = $this->Application->GetVar('andor');
$and_conditions = Array();
$or_conditions = Array();
$and_having_conditions = Array();
$or_having_conditions = Array();
$join_clauses = Array();
$highlight_keywords = Array();
$relevance_parts = Array();
$alias_counter = 0;
$custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields');
if ($custom_fields) {
$custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName');
$join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId';
}
$search_log = '';
$weight_sum = 0;
// processing fields and preparing conditions
foreach ($search_config as $record) {
$field = $record['FieldName'];
$join_clause = '';
$condition_mode = 'WHERE';
// field processing
$options = $object->getFieldOptions($field);
$local_table = TABLE_PREFIX.$record['TableName'];
$weight_sum += $record['Priority']; // counting weight sum; used when making relevance clause
// processing multilingual fields
if (getArrayValue($options, 'formatter') == 'kMultiLanguage') {
$field_name = 'l'.$lang.'_'.$field;
}
else {
$field_name = $field;
}
// processing fields from other tables
if ($foreign_field = $record['ForeignField']) {
$exploded = explode(':', $foreign_field, 2);
if($exploded[0] == 'CALC')
{
$user_groups = $this->Application->RecallVar('UserGroups');
$field_name = str_replace('{PREFIX}', TABLE_PREFIX, $exploded[1]);
$join_clause = str_replace('{PREFIX}', TABLE_PREFIX, $record['JoinClause']);
$join_clause = str_replace('{USER_GROUPS}', $user_groups, $join_clause);
$join_clause = ' LEFT JOIN '.$join_clause;
$condition_mode = 'HAVING';
}
else {
$exploded = explode('.', $foreign_field);
$foreign_table = TABLE_PREFIX.$exploded[0];
if($record['CustomFieldId']) {
$exploded[1] = 'l'.$lang.'_'.$exploded[1];
}
$alias_counter++;
$alias = 't'.$alias_counter;
$field_name = $alias.'.'.$exploded[1];
$join_clause = str_replace('{ForeignTable}', $alias, $record['JoinClause']);
$join_clause = str_replace('{LocalTable}', $items_table, $join_clause);
if($record['CustomFieldId'])
{
$join_clause .= ' AND '.$alias.'.CustomFieldId='.$record['CustomFieldId'];
}
$join_clause = ' LEFT JOIN '.$foreign_table.' '.$alias.'
ON '.$join_clause;
}
}
else
{
// processing fields from local table
if ($record['CustomFieldId']) {
$local_table = 'custom_data';
$field_name = 'l'.$lang.'_cust_'.array_search($field_name, $custom_fields);
}
$field_name = $local_table.'.'.$field_name;
}
$condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords);
if ($record['CustomFieldId'] && strlen($condition)) {
// search in primary value of custom field + value in current language
$field_name = $local_table.'.'.'l'.$this->Application->GetDefaultLanguageId().'_cust_'.array_search($field, $custom_fields);
$primary_condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords);
$condition = '('.$condition.' OR '.$primary_condition.')';
}
if ($condition) {
if ($join_clause) {
$join_clauses[] = $join_clause;
}
$relevance_parts[] = 'IF('.$condition.', '.$record['Priority'].', 0)';
if ($glues[$field] == 1) { // and
if ($condition_mode == 'WHERE') {
$and_conditions[] = $condition;
}
else {
$and_having_conditions[] = $condition;
}
}
else { // or
if ($condition_mode == 'WHERE') {
$or_conditions[] = $condition;
}
else {
$or_having_conditions[] = $condition;
}
}
// create search log record
$search_log_data = Array('search_config' => $record, 'verb' => getArrayValue($verbs, $field), 'value' => ($record['FieldType'] == 'range') ? $search_keywords[$field.'_from'].'|'.$search_keywords[$field.'_to'] : $search_keywords[$field]);
$search_log[] = $this->Application->Phrase('la_Field').' "'.$this->getHuman('Field', $search_log_data).'" '.$this->getHuman('Verb', $search_log_data).' '.$this->Application->Phrase('la_Value').' '.$this->getHuman('Value', $search_log_data).' '.$this->Application->Phrase($glues[$field] == 1 ? 'lu_And' : 'lu_Or');
}
}
if ($search_log) {
$search_log = implode('<br />', $search_log);
$search_log = preg_replace('/(.*) '.preg_quote($this->Application->Phrase('lu_and'), '/').'|'.preg_quote($this->Application->Phrase('lu_or'), '/').'$/is', '\\1', $search_log);
$this->saveToSearchLog($search_log, 1); // advanced search
}
$this->Application->StoreVar('highlight_keywords', serialize($highlight_keywords));
// making relevance clause
if($relevance_parts)
{
$conf_postfix = $this->Application->getUnitOption($event->Prefix, 'SearchConfigPostfix');
$rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100;
$rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100;
$rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100;
$relevance_clause = '('.implode(' + ', $relevance_parts).') / '.$weight_sum.' * '.$rel_keywords;
$relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop;
$relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating;
}
else
{
$relevance_clause = '0';
}
// building having clause
if($or_having_conditions)
{
$and_having_conditions[] = '('.implode(' OR ', $or_having_conditions).')';
}
$having_clause = implode(' AND ', $and_having_conditions);
$having_clause = $having_clause ? ' HAVING '.$having_clause : '';
// building where clause
if($or_conditions)
{
$and_conditions[] = '('.implode(' OR ', $or_conditions).')';
}
// $and_conditions[] = $items_table.'.Status = 1';
$where_clause = implode(' AND ', $and_conditions);
if(!$where_clause)
{
if($having_clause)
{
$where_clause = '1';
}
else
{
$where_clause = '0';
$this->Application->SetVar('adv_search_error', 1);
}
}
$where_clause .= ' AND '.$items_table.'.Status = 1';
// building final search query
$search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search';
$this->Conn->Query('DROP TABLE IF EXISTS '.$search_table);
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$pick_field = isset($fields['EditorsPick']) ? $items_table.'.EditorsPick' : '0';
$sql = ' CREATE TABLE '.$search_table.'
SELECT '.$relevance_clause.' AS Relevance,
'.$items_table.'.'.$id_field.' AS ItemId,
'.$items_table.'.ResourceId AS ResourceId,
11 AS ItemType,
'.$pick_field.' AS EdPick
FROM '.$items_table.'
'.implode(' ', $join_clauses).'
WHERE '.$where_clause.'
GROUP BY '.$items_table.'.'.$id_field.
$having_clause;
$res = $this->Conn->Query($sql);
}
function getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, &$highlight_keywords)
{
$field = $record['FieldName'];
$condition_patterns = Array (
'any' => '%s LIKE %s',
'contains' => '%s LIKE %s',
'notcontains' => '(NOT (%1$s LIKE %2$s) OR %1$s IS NULL)',
'is' => '%s = %s',
'isnot' => '(%1$s != %2$s OR %1$s IS NULL)'
);
$condition = '';
switch ($record['FieldType']) {
case 'select':
$keywords[$field] = unhtmlentities( $keywords[$field] );
if ($keywords[$field]) {
$condition = sprintf($condition_patterns['is'], $field_name, $this->Conn->qstr( $keywords[$field] ));
}
break;
case 'multiselect':
$keywords[$field] = unhtmlentities( $keywords[$field] );
if ($keywords[$field]) {
$condition = Array ();
$values = explode('|', substr($keywords[$field], 1, -1));
foreach ($values as $selected_value) {
$condition[] = sprintf($condition_patterns['contains'], $field_name, $this->Conn->qstr('%|'.$selected_value.'|%'));
}
$condition = '('.implode(' OR ', $condition).')';
}
break;
case 'text':
$keywords[$field] = unhtmlentities( $keywords[$field] );
if (mb_strlen($keywords[$field]) >= $this->Application->ConfigValue('Search_MinKeyword_Length')) {
$highlight_keywords[] = $keywords[$field];
if (in_array($verbs[$field], Array('any', 'contains', 'notcontains'))) {
$keywords[$field] = '%'.strtr($keywords[$field], Array('%' => '\\%', '_' => '\\_')).'%';
}
$condition = sprintf($condition_patterns[$verbs[$field]], $field_name, $this->Conn->qstr( $keywords[$field] ));
}
break;
case 'boolean':
if ($keywords[$field] != -1) {
$property_mappings = $this->Application->getUnitOption($this->Prefix, 'ItemPropertyMappings');
$items_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
switch ($field) {
case 'HotItem':
$hot_limit_var = getArrayValue($property_mappings, 'HotLimit');
if ($hot_limit_var) {
$sql = 'SELECT Data
FROM '.TABLE_PREFIX.'Cache
WHERE VarName = "'.$hot_limit_var.'"';
$hot_limit = (int)$this->Conn->GetOne($sql);
$condition = 'IF('.$items_table.'.HotItem = 2,
IF('.$items_table.'.Hits >= '.
$hot_limit.
', 1, 0), '.$items_table.'.HotItem) = '.$keywords[$field];
}
break;
case 'PopItem':
$votes2pop_var = getArrayValue($property_mappings, 'VotesToPop');
$rating2pop_var = getArrayValue($property_mappings, 'RatingToPop');
if ($votes2pop_var && $rating2pop_var) {
$condition = 'IF('.$items_table.'.PopItem = 2, IF('.$items_table.'.CachedVotesQty >= '.
$this->Application->ConfigValue($votes2pop_var).
' AND '.$items_table.'.CachedRating >= '.
$this->Application->ConfigValue($rating2pop_var).
', 1, 0), '.$items_table.'.PopItem) = '.$keywords[$field];
}
break;
case 'NewItem':
$new_days_var = getArrayValue($property_mappings, 'NewDays');
if ($new_days_var) {
$condition = 'IF('.$items_table.'.NewItem = 2,
IF('.$items_table.'.CreatedOn >= (UNIX_TIMESTAMP() - '.
$this->Application->ConfigValue($new_days_var).
'*3600*24), 1, 0), '.$items_table.'.NewItem) = '.$keywords[$field];
}
break;
case 'EditorsPick':
$condition = $items_table.'.EditorsPick = '.$keywords[$field];
break;
}
}
break;
case 'range':
$range_conditions = Array();
if ($keywords[$field.'_from'] && !preg_match("/[^0-9]/i", $keywords[$field.'_from'])) {
$range_conditions[] = $field_name.' >= '.$keywords[$field.'_from'];
}
if ($keywords[$field.'_to'] && !preg_match("/[^0-9]/i", $keywords[$field.'_to'])) {
$range_conditions[] = $field_name.' <= '.$keywords[$field.'_to'];
}
if ($range_conditions) {
$condition = implode(' AND ', $range_conditions);
}
break;
case 'date':
if ($keywords[$field]) {
if (in_array($keywords[$field], Array('today', 'yesterday'))) {
$current_time = getdate();
$day_begin = adodb_mktime(0, 0, 0, $current_time['mon'], $current_time['mday'], $current_time['year']);
$time_mapping = Array('today' => $day_begin, 'yesterday' => ($day_begin - 86400));
$min_time = $time_mapping[$keywords[$field]];
}
else {
$time_mapping = Array (
'last_week' => 604800, 'last_month' => 2628000, 'last_3_months' => 7884000,
'last_6_months' => 15768000, 'last_year' => 31536000,
);
$min_time = adodb_mktime() - $time_mapping[$keywords[$field]];
}
$condition = $field_name.' > '.$min_time;
}
break;
}
return $condition;
}
function getHuman($type, $search_data)
{
$type = ucfirst(strtolower($type));
extract($search_data);
switch ($type) {
case 'Field':
return $this->Application->Phrase($search_config['DisplayName']);
break;
case 'Verb':
return $verb ? $this->Application->Phrase('lu_advsearch_'.$verb) : '';
break;
case 'Value':
switch ($search_config['FieldType']) {
case 'date':
$values = Array(0 => 'lu_comm_Any', 'today' => 'lu_comm_Today',
'yesterday' => 'lu_comm_Yesterday', 'last_week' => 'lu_comm_LastWeek',
'last_month' => 'lu_comm_LastMonth', 'last_3_months' => 'lu_comm_Last3Months',
'last_6_months' => 'lu_comm_Last6Months', 'last_year' => 'lu_comm_LastYear');
$ret = $this->Application->Phrase($values[$value]);
break;
case 'range':
$value = explode('|', $value);
return $this->Application->Phrase('lu_comm_From').' "'.$value[0].'" '.$this->Application->Phrase('lu_comm_To').' "'.$value[1].'"';
break;
case 'boolean':
$values = Array(1 => 'lu_comm_Yes', 0 => 'lu_comm_No', -1 => 'lu_comm_Both');
$ret = $this->Application->Phrase($values[$value]);
break;
default:
$ret = $value;
break;
}
return '"'.$ret.'"';
break;
}
}
/**
* Set's correct page for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetPagination(&$event)
{
// get PerPage (forced -> session -> config -> 10)
$per_page = $this->getPerPage($event);
$object =& $event->getObject();
$object->SetPerPage($per_page);
- $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1);
+ $this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1, true); // true for optional
$page = $this->Application->GetVar($event->getPrefixSpecial().'_Page');
if (!$page)
{
$page = $this->Application->GetVar($event->getPrefixSpecial(true).'_Page');
}
if (!$page)
{
if( $this->Application->RewriteURLs() )
{
$page = $this->Application->GetVar($event->Prefix.'_Page');
if (!$page)
{
$page = $this->Application->RecallVar($event->Prefix.'_Page');
}
- if($page) $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page);
+ if($page) $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page, true); //true for optional
}
else
{
$page = $this->Application->RecallVar($event->getPrefixSpecial().'_Page');
}
}
else {
- $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page);
+ $this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page, true); //true for optional
}
if( !$event->getEventParam('skip_counting') )
{
$pages = $object->GetTotalPages();
if($page > $pages)
{
$this->Application->StoreVar($event->getPrefixSpecial().'_Page', 1);
$page = 1;
}
}
/*$cur_per_page = $per_page;
$per_page = $event->getEventParam('per_page');
if ($per_page == 'list_next') {
$cur_page = $page;
$object =& $this->Application->recallObject($event->Prefix);
$object->SetPerPage(1);
$cur_item_index = $object->CurrentIndex;
$page = ($cur_page-1) * $cur_per_page + $cur_item_index + 1;
$object->SetPerPage(1);
}*/
$object->SetPage($page);
}
/* === RELATED TO IMPORT/EXPORT: BEGIN === */
/**
* Shows export dialog
*
* @param kEvent $event
*/
function OnExport(&$event)
{
$selected_ids = $this->StoreSelectedIDs($event);
if (implode(',', $selected_ids) == '') {
// K4 fix when no ids found bad selected ids array is formed
$selected_ids = false;
}
$selected_cats_ids = $this->Application->GetVar('export_categories');
$this->Application->StoreVar($event->Prefix.'_export_ids', $selected_ids ? implode(',', $selected_ids) : '' );
$this->Application->StoreVar($event->Prefix.'_export_cats_ids', $selected_cats_ids);
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$redirect_params = Array (
$this->Prefix.'.export_event' => 'OnNew',
'pass' => 'all,'.$this->Prefix.'.export'
);
$event->setRedirectParams($redirect_params);
}
/**
* Performs each export step & displays progress percent
*
* @param kEvent $event
*/
function OnExportProgress(&$event)
{
$export_object =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_object kCatDBItemExportHelper */
$event = new kEvent($event->getPrefixSpecial().':OnDummy');
$action_method = 'perform'.ucfirst($event->Special);
$field_values = $export_object->$action_method($event);
// finish code is done from JS now
if ($field_values['start_from'] == $field_values['total_records']) {
if ($event->Special == 'import') {
$this->Application->StoreVar('PermCache_UpdateRequired', 1);
$url_params = Array(
't' => 'catalog/catalog',
'm_cat_id' => $this->Application->RecallVar('ImportCategory'),
'anchor' => 'tab-' . $event->Prefix,
);
$this->Application->EventManager->openerStackChange($url_params);
$event->SetRedirectParam('opener', 'u');
}
elseif ($event->Special == 'export') {
$event->redirect = $this->Application->getUnitOption($event->Prefix, 'ModuleFolder') . '/' . $event->Special . '_finish';
$event->SetRedirectParam('pass', 'all');
}
return ;
}
$export_options = $export_object->loadOptions($event);
echo $export_options['start_from'] * 100 / $export_options['total_records'];
$event->status = erSTOP;
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
*/
function getCustomExportColumns(&$event)
{
return Array( '__VIRTUAL__ThumbnailImage' => 'ThumbnailImage',
'__VIRTUAL__FullImage' => 'FullImage',
'__VIRTUAL__ImageAlt' => 'ImageAlt');
}
/**
* Sets non standart virtual fields (e.g. to other tables)
*
* @param kEvent $event
*/
function setCustomExportColumns(&$event)
{
$this->restorePrimaryImage($event);
}
/**
* Create/Update primary image record in info found in imported data
*
* @param kEvent $event
*/
function restorePrimaryImage(&$event)
{
$object =& $event->getObject();
$has_image_info = $object->GetDBField('ImageAlt') && ($object->GetDBField('ThumbnailImage') || $object->GetDBField('FullImage'));
if (!$has_image_info) {
return false;
}
$image_data = $object->getPrimaryImageData();
$image =& $this->Application->recallObject('img', null, Array('skip_autoload' => true));
if ($image_data) {
$image->Load($image_data['ImageId']);
}
else {
$image->Clear();
$image->SetDBField('Name', 'main');
$image->SetDBField('DefaultImg', 1);
$image->SetDBField('ResourceId', $object->GetDBField('ResourceId'));
}
$image->SetDBField('AltName', $object->GetDBField('ImageAlt'));
if ($object->GetDBField('ThumbnailImage')) {
$thumbnail_field = $this->isURL( $object->GetDBField('ThumbnailImage') ) ? 'ThumbUrl' : 'ThumbPath';
$image->SetDBField($thumbnail_field, $object->GetDBField('ThumbnailImage') );
$image->SetDBField('LocalThumb', $thumbnail_field == 'ThumbPath' ? 1 : 0);
}
if (!$object->GetDBField('FullImage')) {
$image->SetDBField('SameImages', 1);
}
else {
$image->SetDBField('SameImages', 0);
$full_field = $this->isURL( $object->GetDBField('FullImage') ) ? 'Url' : 'LocalPath';
$image->SetDBField($full_field, $object->GetDBField('FullImage') );
$image->SetDBField('LocalImage', $full_field == 'LocalPath' ? 1 : 0);
}
if ($image->isLoaded()) {
$image->Update();
}
else {
$image->Create();
}
}
function isURL($path)
{
return preg_match('#(http|https)://(.*)#', $path);
}
/**
* Prepares item for import/export operations
*
* @param kEvent $event
*/
function OnNew(&$event)
{
parent::OnNew($event);
if ($event->Special == 'import' || $event->Special == 'export') {
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
$export_helper->setRequiredFields($event);
}
}
/**
* Process items selected in item_selector
*
* @param kEvent $event
*/
function OnProcessSelected(&$event)
{
$selected_ids = $this->Application->GetVar('selected_ids');
$dst_field = $this->Application->RecallVar('dst_field');
if ($dst_field == 'ItemCategory') {
// Item Edit -> Categories Tab -> New Categories
$object =& $event->getObject();
$category_ids = explode(',', $selected_ids['c']);
foreach ($category_ids as $category_id) {
$object->assignToCategory($category_id);
}
}
if ($dst_field == 'ImportCategory') {
// Tools -> Import -> Item Import -> Select Import Category
$this->Application->StoreVar('ImportCategory', $selected_ids['c']);
// $this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 1); // not to loose import/export values on form refresh
$url_params = Array (
$event->getPrefixSpecial() . '_id' => 0,
$event->getPrefixSpecial() . '_event' => 'OnExportBegin',
// 'm_opener' => 's',
);
$this->Application->EventManager->openerStackChange($url_params);
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Saves Import/Export settings to session
*
* @param kEvent $event
*/
function OnSaveSettings(&$event)
{
$event->redirect = false;
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
list($id, $field_values) = each($items_info);
$object =& $event->getObject( Array('skip_autoload' => true) );
$object->SetFieldsFromHash($field_values);
$field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!!
$field_values['ImportSource'] = 2;
$field_values['ImportLocalFilename'] = $object->GetDBField('ImportFilename');
$items_info[$id] = $field_values;
$this->Application->StoreVar($event->getPrefixSpecial().'_ItemsInfo', serialize($items_info));
}
}
/**
* Saves Import/Export settings to session
*
* @param kEvent $event
*/
function OnResetSettings(&$event)
{
$this->Application->StoreVar('ImportCategory', $this->Application->findModule('Name', 'Core', 'RootCat'));
}
function OnCancelAction(&$event)
{
$event->redirect_params = Array('pass' => 'all,'.$event->GetPrefixSpecial());
$event->redirect = $this->Application->GetVar('cancel_template');
}
/* === RELATED TO IMPORT/EXPORT: END === */
/**
* Stores item's owner login into separate field together with id
*
* @param kEvent $event
* @param string $id_field
* @param string $cached_field
*/
function cacheItemOwner(&$event, $id_field, $cached_field)
{
$object =& $event->getObject();
$user_id = $object->GetDBField($id_field);
$options = $object->GetFieldOptions($id_field);
if (isset($options['options'][$user_id])) {
$object->SetDBField($cached_field, $options['options'][$user_id]);
}
else {
$id_field = $this->Application->getUnitOption('u', 'IDField');
$table_name = $this->Application->getUnitOption('u', 'TableName');
$sql = 'SELECT Login
FROM '.$table_name.'
WHERE '.$id_field.' = '.$user_id;
$object->SetDBField($cached_field, $this->Conn->GetOne($sql));
}
}
/**
* Saves item beeing edited into temp table
*
* @param kEvent $event
*/
function OnPreSave(&$event)
{
parent::OnPreSave($event);
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ($event->status == erSUCCESS && $use_pending_editing) {
// decision: clone or not clone
$object =& $event->getObject();
if ($object->GetID() == 0 || $object->GetDBField('OrgId') > 0) {
// new items or cloned items shouldn't be cloned again
return true;
}
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
$owner_field = $this->getOwnerField($event->Prefix);
if ($perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix) == 2) {
// 1. clone original item
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($object->GetID()), null, null, null, true);
$ci_table = $this->Application->GetTempName(TABLE_PREFIX.'CategoryItems');
// 2. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem
$sql = 'SELECT ResourceId
FROM '.$object->TableName.'
WHERE '.$object->IDField.' = '.$cloned_ids[0];
$clone_resource_id = $this->Conn->GetOne($sql);
$sql = 'DELETE FROM '.$ci_table.'
WHERE ItemResourceId = '.$clone_resource_id.' AND PrimaryCat = 1';
$this->Conn->Query($sql);
// 3. copy main item categoryitems to cloned item
$sql = ' INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename)
SELECT CategoryId, '.$clone_resource_id.' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename
FROM '.$ci_table.'
WHERE ItemResourceId = '.$object->GetDBField('ResourceId');
$this->Conn->Query($sql);
// 4. put cloned id to OrgId field of item being cloned
$sql = 'UPDATE '.$object->TableName.'
SET OrgId = '.$object->GetID().'
WHERE '.$object->IDField.' = '.$cloned_ids[0];
$this->Conn->Query($sql);
// 5. substitute id of item being cloned with clone id
$this->Application->SetVar($event->getPrefixSpecial().'_id', $cloned_ids[0]);
$selected_ids = $this->getSelectedIDs($event, true);
$selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0];
$this->StoreSelectedIDs($event, $selected_ids);
// 6. delete original item from temp table
$temp_handler->DeleteItems($event->Prefix, $event->Special, Array($object->GetID()));
}
}
}
/**
* Sets default expiration based on module setting
*
* @param kEvent $event
*/
function OnPreCreate(&$event)
{
parent::OnPreCreate($event);
if ($event->status == erSUCCESS) {
$object =& $event->getObject();
$owner_field = $this->getOwnerField($event->Prefix);
$object->SetDBField($owner_field, $this->Application->RecallVar('user_id'));
}
}
/**
* Occures before original item of item in pending editing got deleted (for hooking only)
*
* @param kEvent $event
*/
function OnBeforeDeleteOriginal(&$event)
{
}
/**
* Occures before an item is cloneded
* Id of ORIGINAL item is passed as event' 'id' param
* Do not call object' Update method in this event, just set needed fields!
*
* @param kEvent $event
*/
function OnBeforeClone(&$event)
{
if ($this->Application->GetVar('ResetCatBeforeClone')) {
$object =& $event->getObject();
$object->SetDBField('CategoryId', null);
}
}
/**
* Set status for new category item based on user permission in category
*
* @param kEvent $event
*/
function OnBeforeItemCreate(&$event)
{
if ($this->Application->IsAdmin()) {
return true;
}
$use_pending_editing = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ($use_pending_editing) {
$object =& $event->getObject();
/* @var $object kDBItem */
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$primary_category = $object->GetDBField('CategoryId') > 0 ? $object->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id');
$item_status = $perm_helper->AddCheckPermission($primary_category, $event->Prefix);
if ($item_status == STATUS_DISABLED) {
$event->status = erFAIL;
return false;
}
else {
$object->SetDBField('Status', $item_status);
}
}
}
/**
* Creates category item & redirects to confirmation template (front-end only)
*
* @param kEvent $event
*/
function OnCreate(&$event)
{
parent::OnCreate($event);
$this->SetFrontRedirectTemplate($event, 'suggest');
}
/**
* Returns item's categories (allows to exclude primary category)
*
* @param int $resource_id
* @param bool $with_primary
* @return Array
*/
function getItemCategories($resource_id, $with_primary = false)
{
$sql = 'SELECT CategoryId
FROM '.TABLE_PREFIX.'CategoryItems
WHERE (ItemResourceId = '.$resource_id.')';
if (!$with_primary) {
$sql .= ' AND (PrimaryCat = 0)';
}
return $this->Conn->GetCol($sql);
}
/**
* Adds new and removes old additional categories from category item
*
* @param kCatDBItem $object
*/
function processAdditionalCategories(&$object, $mode)
{
$process_categories = $object->GetDBField('MoreCategories');
if ($process_categories === '') {
// field was not in submit & have default value (when no categories submitted, then value is null)
return ;
}
if ($mode == 'create') {
// prevents first additional category to become primary
$object->assignPrimaryCategory();
}
$process_categories = $process_categories ? explode('|', substr($process_categories, 1, -1)) : Array ();
$existing_categories = $this->getItemCategories($object->GetDBField('ResourceId'));
$add_categories = array_diff($process_categories, $existing_categories);
foreach ($add_categories as $category_id) {
$object->assignToCategory($category_id);
}
$remove_categories = array_diff($existing_categories, $process_categories);
foreach ($remove_categories as $category_id) {
$object->removeFromCategory($category_id);
}
}
/**
* Creates category item & redirects to confirmation template (front-end only)
*
* @param kEvent $event
*/
function OnUpdate(&$event)
{
$use_pending = $this->Application->getUnitOption($event->Prefix, 'UsePendingEditing');
if ($this->Application->IsAdmin() || !$use_pending) {
parent::OnUpdate($event);
$this->SetFrontRedirectTemplate($event, 'modify');
return ;
}
$object =& $event->getObject(Array('skip_autoload' => true));
/* @var $object kCatDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ($items_info) {
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$owner_field = $this->getOwnerField($event->Prefix);
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$edit_perm = $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix);
if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) {
// pending editing enabled + not pending copy -> get/create pending copy & save changes to it
$original_id = $object->GetID();
$original_resource_id = $object->GetDBField('ResourceId');
$file_helper->PreserveItemFiles($field_values);
$object->Load($original_id, 'OrgId');
if (!$object->isLoaded()) {
// 1. user has no pending copy of live item -> clone live item
$cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($original_id), null, null, null, true);
$object->Load($cloned_ids[0]);
$object->SetFieldsFromHash($field_values);
// 1a. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem
$ci_table = $this->Application->getUnitOption('ci', 'TableName');
$sql = 'DELETE FROM '.$ci_table.'
WHERE ItemResourceId = '.$object->GetDBField('ResourceId').' AND PrimaryCat = 1';
$this->Conn->Query($sql);
// 1b. copy main item categoryitems to cloned item
$sql = 'INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename)
SELECT CategoryId, '.$object->GetDBField('ResourceId').' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename
FROM '.$ci_table.'
WHERE ItemResourceId = '.$original_resource_id;
$this->Conn->Query($sql);
// 1c. put cloned id to OrgId field of item being cloned
$object->SetDBField('Status', STATUS_PENDING_EDITING);
$object->SetDBField('OrgId', $original_id);
}
else {
// 2. user has pending copy of live item -> just update field values
$object->SetFieldsFromHash($field_values);
}
// update id in request (used for redirect in mod-rewrite mode)
$this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID());
}
else {
// 3. already editing pending copy -> just update field values
$object->SetFieldsFromHash($field_values);
}
if ($object->Update()) {
$event->status = erSUCCESS;
}
else {
$event->status = erFAIL;
$event->redirect = false;
break;
}
}
}
$this->SetFrontRedirectTemplate($event, 'modify');
}
/**
* Sets next template to one required for front-end after adding/modifying item
*
* @param kEvent $event
* @param string $template_key - {suggest,modify}
*/
function SetFrontRedirectTemplate(&$event, $template_key)
{
if ($this->Application->IsAdmin() || $event->status != erSUCCESS) {
return ;
}
// prepare redirect template
$object =& $event->getObject();
$is_active = ($object->GetDBField('Status') == STATUS_ACTIVE);
$next_template = $is_active ? 'confirm_template' : 'pending_confirm_template';
$event->redirect = $this->Application->GetVar($template_key.'_'.$next_template);
$event->SetRedirectParam('opener', 's');
// send email events
$perm_prefix = $this->Application->getUnitOption($event->Prefix, 'PermItemPrefix');
switch ($event->Name) {
case 'OnCreate':
$event_suffix = $is_active ? 'ADD' : 'ADD.PENDING';
$owner_field = $this->getOwnerField($event->Prefix);
$this->Application->EmailEventAdmin($perm_prefix.'.'.$event_suffix); // there are no ADD.PENDING event for admin :(
$this->Application->EmailEventUser($perm_prefix.'.'.$event_suffix, $object->GetDBField($owner_field));
break;
case 'OnUpdate':
$event_suffix = $is_active ? 'MODIFY' : 'MODIFY.PENDING';
$this->Application->EmailEventAdmin($perm_prefix.'.'.$event_suffix); // there are no ADD.PENDING event for admin :(
$this->Application->EmailEventUser($perm_prefix.'.'.$event_suffix, $object->GetDBField('ModifiedById'));
break;
}
}
/**
* 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) {
foreach ($ids as $id) {
$object->Load($id);
switch ($event->Name) {
case 'OnMassApprove':
$ret = $object->ApproveChanges();
break;
case 'OnMassDecline':
$ret = $object->DeclineChanges();
break;
}
if (!$ret) {
$event->status = erFAIL;
$event->redirect = false;
break;
}
}
}
$this->clearSelectedIDs($event);
}
/**
* Deletes items & preserves clean env
*
* @param kEvent $event
*/
function OnDelete(&$event)
{
parent::OnDelete($event);
if ($event->status == erSUCCESS && !$this->Application->IsAdmin()) {
$event->SetRedirectParam('pass', 'm');
$event->SetRedirectParam('m_cat_id', 0);
}
}
/**
* Checks, that currently loaded item is allowed for viewing (non permission-based)
*
* @param kEvent $event
* @return bool
*/
function checkItemStatus(&$event)
{
$object =& $event->getObject();
if (!$object->isLoaded()) {
$this->_errorNotFound($event);
return true;
}
$status = $object->GetDBField('Status');
$user_id = $this->Application->RecallVar('user_id');
$owner_field = $this->getOwnerField($event->Prefix);
if (($status == STATUS_PENDING_EDITING || $status == STATUS_PENDING) && ($object->GetDBField($owner_field) == $user_id)) {
return true;
}
return $status == STATUS_ACTIVE;
}
/**
* Set's correct sorting for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetSorting(&$event)
{
if (!$this->Application->IsAdmin()) {
$event->setEventParam('same_special', true);
}
parent::SetSorting($event);
}
/**
* 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);
}
function getOwnerField($prefix)
{
$owner_field = $this->Application->getUnitOption($prefix, 'OwnerField');
if (!$owner_field) {
$owner_field = 'CreatedById';
}
return $owner_field;
}
/**
* Creates virtual image fields for item
*
* @param kEvent $event
*/
function OnAfterConfigRead(&$event)
{
parent::OnAfterConfigRead($event);
if (defined('IS_INSTALL') && IS_INSTALL) {
return ;
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->createItemFiles($event->Prefix, true); // create image fields
$file_helper->createItemFiles($event->Prefix, false); // create file fields
// add EditorsPick to ForcedSorting if needed
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
if (array_key_exists('ForceEditorPick', $config_mapping) && $this->Application->ConfigValue($config_mapping['ForceEditorPick'])) {
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings');
$new_forced_sorting = Array ('EditorsPick' => 'DESC');
if (array_key_exists('ForcedSorting', $list_sortings[''])) {
foreach ($list_sortings['']['ForcedSorting'] as $sort_field => $sort_order) {
$new_forced_sorting[$sort_field] = $sort_order;
}
}
$list_sortings['']['ForcedSorting'] = $new_forced_sorting;
$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_primary_category_td', 'filter_block' => 'grid_like_filter');
$grids[$process_grid . 'ShowAll'] = $grid_data;
}
$this->Application->setUnitOption($this->Prefix, 'Grids', $grids);
// add options for CategoryId field (quick way to select item's primary category)
$category_helper =& $this->Application->recallObject('CategoryHelper');
/* @var $category_helper CategoryHelper */
$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
$virtual_fields['CategoryId']['default'] = (int)$this->Application->GetVar('m_cat_id');
$virtual_fields['CategoryId']['options'] = $category_helper->getStructureTreeAsOptions();
$this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields);
}
/**
* Returns file contents associated with item
*
* @param kEvent $event
*/
function OnDownloadFile(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$event->status = erSTOP;
$field = $this->Application->GetVar('field');
if (!preg_match('/^File([\d]+)/', $field)) {
return ;
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$filename = $object->GetField($field, 'full_path');
$file_helper->DownloadFile($filename);
}
/**
* Saves user's vote
*
* @param kEvent $event
*/
function OnMakeVote(&$event)
{
$event->status = erSTOP;
if ($this->Application->GetVar('ajax') != 'yes') {
// this is supposed to call from AJAX only
return ;
}
$rating_helper =& $this->Application->recallObject('RatingHelper');
/* @var $rating_helper RatingHelper */
$object =& $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$object->Load( $this->Application->GetVar('id') );
echo $rating_helper->makeVote($object);
}
}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/application.php
===================================================================
--- branches/5.0.x/core/kernel/application.php (revision 12367)
+++ branches/5.0.x/core/kernel/application.php (revision 12368)
@@ -1,2905 +1,2918 @@
<?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.
*/
/**
* Basic class for Kernel4-based Application
*
* This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
* The class incapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
* <br>
* The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
* This could be guranteed by NOT calling the class constuctor directly, but rather calling kApplication::Instance() method,
* which returns an instance of the application. The method gurantees that it will return exactly the same instance for any call.<br>
* See singleton pattern by GOF.
* @package kernel4
*/
defined('FULL_PATH') or die('restricted access!');
class kApplication {
/**
* Is true, when Init method was called already, prevents double initialization
*
* @var bool
*/
var $InitDone = false;
/**
* Holds internal NParser object
* @access private
* @var NParser
*/
var $Parser;
/**
* Holds parser output buffer
* @access private
* @var string
*/
var $HTML;
/**
* Prevents request from beeing proceeded twice in case if application init is called mere then one time
*
* @var bool
* @todo This is not good anyway (by Alex)
*/
var $RequestProcessed = false;
/**
* The main Factory used to create
* almost any class of kernel and
* modules
*
* @access private
* @var kFactory
*/
var $Factory;
/**
* All ConfigurationValues table content (hash) here
*
* @var Array
* @access private
*/
var $ConfigHash = Array();
/**
* Ids of config variables used in current run (for caching)
*
* @var Array
* @access private
*/
var $ConfigCacheIds = array();
/**
* Template names, that will be used instead of regular templates
*
* @var Array
*/
var $ReplacementTemplates = Array ();
/**
* Mod-Rewrite listeners used during url building and parsing
*
* @var Array
*/
var $RewriteListeners = Array ();
/**
* Reference to debugger
*
* @var Debugger
*/
var $Debugger = null;
/**
* Holds all phrases used
* in code and template
*
* @var PhrasesCache
*/
var $Phrases;
/**
* Modules table content, key - module name
*
* @var Array
*/
var $ModuleInfo = Array();
/**
* Holds DBConnection
*
* @var kDBConnection
*/
var $Conn = null;
/**
* Maintains list of user-defined error handlers
*
* @var Array
*/
var $errorHandlers = Array();
// performance needs:
/**
* Holds a refererence to httpquery
*
* @var kHttpQuery
*/
var $HttpQuery = null;
/**
* Holds a reference to UnitConfigReader
*
* @var kUnitConfigReader
*/
var $UnitConfigReader = null;
/**
* Holds a reference to Session
*
* @var Session
*/
var $Session = null;
/**
* Holds a ref to kEventManager
*
* @var kEventManager
*/
var $EventManager = null;
/**
* Ref to itself, needed because everybody used to write $this->Application, even inside kApplication
*
* @var kApplication
*/
var $Application = null;
/**
* Ref for TemplatesChache
*
* @var TemplatesCache
*/
var $TemplatesCache = null;
var $CompilationCache = array(); //used when compiling templates
var $CachedProcessors = array(); //used when running compiled templates
var $LambdaElements = 1; // for autonumbering unnamed RenderElements [any better place for this prop? KT]
/**
* Holds current NParser tag while parsing, can be used in error messages to display template file and line
*
* @var _BlockTag
*/
var $CurrentNTag = null;
/**
* Memcache object pointer
*
* @var Memcache
*/
var $Memcached = null;
/**
* Returns kApplication instance anywhere in the script.
*
* This method should be used to get single kApplication object instance anywhere in the
* Kernel-based application. The method is guranteed to return the SAME instance of kApplication.
* Anywhere in the script you could write:
* <code>
* $application =& kApplication::Instance();
* </code>
* or in an object:
* <code>
* $this->Application =& kApplication::Instance();
* </code>
* to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
* To use descendand of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
* BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
* create and return default KernelApplication instance.
* @static
* @access public
* @return kApplication
*/
function &Instance()
{
static $instance = false;
if(!$instance)
{
safeDefine('APPLICATION_CLASS', 'kApplication');
$class = APPLICATION_CLASS;
$instance = new $class();
$instance->Application =& $instance;
}
return $instance;
}
function InitMemcached()
{
return ;
$memcached_servers = 'localhost:11211'; // $this->Application->ConfigValue('MemcachedServers');
if ($memcached_servers && class_exists('Memcache')) {
$this->Memcached = new Memcache();
$servers = explode(';', $memcached_servers);
foreach ($servers as $server) {
list ($server, $port) = strpos($server, ':') !== false ? explode(':', $server, 2) : Array ($server, 11211);
$this->Memcached->addServer($server, $port);
}
}
//try to set something to cache, if not working - set $this->Memcached to null
}
function CacheSet($name, $value, $expiration)
{
if (isset($this->Memcached)) {
return $this->Memcached->set($name, $value, 0, $expiration);
}
return false;
}
function CacheGet($name)
{
if (isset($this->Memcached)) {
return $this->Memcached->get($name);
}
return false;
}
/**
* Initializes the Application
*
* @access public
* @see kHTTPQuery
* @see Session
* @see TemplatesCache
* @return bool Was Init actually made now or before
*/
function Init()
{
if($this->InitDone) return false;
$this->InitMemcached();
if (!constOn('SKIP_OUT_COMPRESSION')) {
ob_start(); // collect any output from method (other then tags) into buffer
}
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
$this->Debugger->appendMemoryUsage('Application before Init:');
}
if (!$this->isDebugMode() && !constOn('DBG_ZEND_PRESENT')) {
error_reporting(0);
ini_set('display_errors', 0);
}
if (!constOn('DBG_ZEND_PRESENT')) {
$error_handler = set_error_handler( Array (&$this, 'handleError') );
if ($error_handler) {
// wrap around previous error handler, if any was set
$this->errorHandlers[] = $error_handler;
}
}
$this->Conn = new kDBConnection(SQL_TYPE, Array(&$this, 'handleSQLError') );
$this->Conn->debugMode = $this->isDebugMode();
$this->Conn->Connect(SQL_SERVER, SQL_USER, SQL_PASS, SQL_DB);
$this->Factory = new kFactory();
$this->registerDefaultClasses();
$this->Phrases = new PhrasesCache();
$this->EventManager =& $this->Factory->makeClass('EventManager');
$this->Factory->Storage['EventManager'] =& $this->EventManager;
$this->RegisterDefaultBuildEvents();
$this->SetDefaultConstants();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Before UnitConfigReader');
}
$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
$this->UnitConfigReader->scanModules(MODULES_PATH);
$this->registerModuleConstants();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('After UnitConfigReader');
}
$rewrite_on = $this->ConfigValue('UseModRewrite');
// admin=1 - when front is browsed using admin session
$admin_on = getArrayValue($_REQUEST, 'admin') || $this->IsAdmin();
define('MOD_REWRITE', $rewrite_on && !$admin_on ? 1 : 0);
$this->HttpQuery =& $this->recallObject('HTTPQuery');
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
}
$this->Session =& $this->recallObject('Session');
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Processed Session');
}
if (!$this->RecallVar('UserGroups')) {
$user_groups = trim($this->Session->GetField('GroupList'), ',');
if (!$user_groups) {
$user_groups = $this->ConfigValue('User_GuestGroup');
}
$this->Session->SetField('GroupList', $user_groups);
- $this->StoreVar('UserGroups', $user_groups);
+ $this->StoreVar('UserGroups', $user_groups, true); // true for optional
}
$this->HttpQuery->AfterInit();
$this->Session->ValidateExpired();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
}
$this->LoadCache();
$this->InitConfig();
$this->Phrases->Init('phrases');
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Loaded cache and phrases');
}
$this->UnitConfigReader->AfterConfigRead();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendTimestamp('Processed AfterConfigRead');
}
/*// Module items are recalled during url parsing & PhrasesCache is needed already there,
// because it's used in their build events. That's why phrases cache initialization is
// called from kHTTPQuery in case when mod_rewrite is used
if (!$this->RewriteURLs()) {
$this->Phrases = new PhrasesCache();
}*/
if ($this->GetVar('m_cat_id') === false) $this->SetVar('m_cat_id', 0);
- if( !$this->RecallVar('curr_iso') ) $this->StoreVar('curr_iso', $this->GetPrimaryCurrency() );
+ if (!$this->RecallVar('curr_iso')) {
+ $this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
+ }
$this->SetVar('visits_id', $this->RecallVar('visit_id') );
$language =& $this->recallObject( 'lang.current', null, Array('live_table' => true) );
if (preg_match('/utf-8/', $language->GetDBField('Charset'))) {
setlocale(LC_ALL, 'en_US.UTF-8');
mb_internal_encoding('UTF-8');
}
$this->ValidateLogin();
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->profileFinish('kernel4_startup');
}
$this->InitDone = true;
$this->HandleEvent( new kEvent('adm:OnStartup') );
return true;
}
/**
* Returns module information. Searches module by requested field
*
* @param string $field
* @param mixed $value
* @param string field value to returns, if not specified, then return all fields
* @param string field to return
* @return Array
*/
function findModule($field, $value, $return_field = null)
{
$found = false;
foreach ($this->ModuleInfo as $module_name => $module_info) {
if (strtolower($module_info[$field]) == strtolower($value)) {
$found = true;
break;
}
}
if ($found) {
return isset($return_field) ? $module_info[$return_field] : $module_info;
}
return false;
}
function refreshModuleInfo()
{
if (defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules')) {
$this->registerModuleConstants();
return false;
}
$modules_helper =& $this->recallObject('ModulesHelper');
/* @var $modules_helper kModulesHelper */
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Modules
WHERE Loaded = 1
ORDER BY LoadOrder';
$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
$sql = 'SELECT *
FROM '.TABLE_PREFIX.'Modules
WHERE '.$modules_helper->getWhereClause().'
ORDER BY LoadOrder';
$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
$this->registerModuleConstants();
}
/**
* Checks if passed language id if valid and sets it to primary otherwise
*
*/
function VerifyLanguageId()
{
$language_id = $this->GetVar('m_lang');
if (!$language_id) {
$language_id = 'default';
}
$this->SetVar('lang.current_id', $language_id );
$this->SetVar('m_lang', $language_id );
$lang_mode = $this->GetVar('lang_mode');
$this->SetVar('lang_mode', '');
$lang =& $this->recallObject('lang.current');
if ( !$lang->IsLoaded() || (!$this->Application->IsAdmin() && !$lang->GetDBField('Enabled')) ) {
if (!defined('IS_INSTALL')) $this->ApplicationDie('Unknown or disabled language');
}
$this->SetVar('lang_mode',$lang_mode);
}
/**
* Checks if passed theme id if valid and sets it to primary otherwise
*
*/
function VerifyThemeId()
{
if ($this->Application->IsAdmin()) {
safeDefine('THEMES_PATH', '/core/admin_templates');
return;
}
$path = $this->GetFrontThemePath();
if ($path === false) {
$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
}
safeDefine('THEMES_PATH', $path);
/*$theme_id = $this->GetVar('m_theme');
if (!$theme_id) {
$theme_id = $this->GetDefaultThemeId();
if (!$theme_id) {
if (!defined('IS_INSTALL')) $this->ApplicationDie('No Primary Theme Selected');
}
}
$this->SetVar('m_theme', $theme_id);
$this->SetVar('theme.current_id', $theme_id ); // KOSTJA: this is to fool theme' getPassedId
$theme =& $this->recallObject('theme.current');
if (!$theme->IsLoaded() || !$theme->GetDBField('Enabled')) {
if (!defined('IS_INSTALL')) $this->ApplicationDie('Unknown or disabled theme');
}
safeDefine('THEMES_PATH', '/themes/'.$theme->GetDBField('Name'));*/
}
function GetFrontThemePath($force=0)
{
static $path=null;
if (!$force && isset($path)) return $path;
$theme_id = $this->GetVar('m_theme');
if (!$theme_id) {
// $theme_id = $this->GetDefaultThemeId(1); //1 to force front-end mode!
$theme_id = 'default';
}
$this->SetVar('m_theme', $theme_id);
$this->SetVar('theme.current_id', $theme_id ); // KOSTJA: this is to fool theme' getPassedId
$theme =& $this->recallObject('theme.current');
if (!$theme->IsLoaded() || !$theme->GetDBField('Enabled')) {
return false;
}
$path = '/themes/'.$theme->GetDBField('Name');
return $path;
}
function GetDefaultLanguageId()
{
static $language_id = 0;
if ($language_id > 0) {
return $language_id;
}
$table = $this->getUnitOption('lang', 'TableName');
$id_field = $this->getUnitOption('lang', 'IDField');
$sql = 'SELECT '.$id_field.'
FROM '.$table.'
WHERE (PrimaryLang = 1) AND (Enabled = 1)';
$language_id = $this->Conn->GetOne($sql);
if (!$language_id && defined('IS_INSTALL') && IS_INSTALL) {
$language_id = 1;
}
return $language_id;
}
function GetDefaultThemeId($force_front=0)
{
static $theme_id = 0;
if ($theme_id > 0) {
return $theme_id;
}
if (constOn('DBG_FORCE_THEME')) {
$theme_id = DBG_FORCE_THEME;
}
elseif (!$force_front && $this->IsAdmin()) {
$theme_id = 999;
}
else {
$table = $this->getUnitOption('theme','TableName');
$id_field = $this->getUnitOption('theme','IDField');
$sql = 'SELECT '.$id_field.'
FROM '.$table.'
WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
$theme_id = $this->Conn->GetOne($sql);
}
return $theme_id;
}
function GetPrimaryCurrency()
{
if ($this->isModuleEnabled('In-Commerce')) {
$table = $this->getUnitOption('curr', 'TableName');
return $this->Conn->GetOne('SELECT ISO FROM '.$table.' WHERE IsPrimary = 1');
}
else {
return 'USD';
}
}
/**
* Registers default classes such as ItemController, GridController and LoginController
*
* Called automatically while initializing Application
* @access private
* @return void
*/
function RegisterDefaultClasses()
{
$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'Cache', 'Params');
$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery', 'Params');
$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session.php');
$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session.php', 'SessionStorage');
$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php','m_TagProcessor', 'kTagProcessor');
$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php', null, 'kTagProcessor');
$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php', null, Array ('kHelper', 'kDBTagProcessor'));
$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender', 'kHelper');
$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
if (file_exists(MODULES_PATH . '/in-commerce/units/currencies/currency_rates.php')) {
$this->registerClass('kCurrencyRates', MODULES_PATH . '/in-commerce/units/currencies/currency_rates.php');
}
// do not move to config - this helper is used before configs are read
$this->registerClass('kModulesHelper', KERNEL_PATH . '/../units/helpers/modules_helper.php', 'ModulesHelper');
}
function RegisterDefaultBuildEvents()
{
$event_manager =& $this->recallObject('EventManager');
$event_manager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
}
/**
* Returns item's filename that corresponds id passed. If possible, then get it from cache
*
* @param string $prefix
* @param int $id
* @return string
*/
function getFilename($prefix, $id, $category_id=null)
{
$filename = $this->getCache('filenames', $prefix.'_'.$id);
if ($filename === false) {
$table = $this->getUnitOption($prefix, 'TableName');
$id_field = $this->getUnitOption($prefix, 'IDField');
if ($prefix == 'c') {
if(!$id) {
$this->setCache('filenames', $prefix.'_'.$id, '');
return '';
}
// this allows to save 2 sql queries for each category
$sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
FROM '.$table.'
WHERE '.$id_field.' = '.$this->Conn->qstr($id);
$category_data = $this->Conn->GetRow($sql);
// only direct links to category pages work (symlinks, container pages and so on won't work)
$filename = $category_data['NamedParentPath'];
$this->setCache('category_templates', $id, $filename);
$this->setCache('category_designs', $id, ltrim($category_data['CachedTemplate'], '/'));
$this->setCache('category_tree', $id, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
}
else {
$resource_id = $this->Conn->GetOne('SELECT ResourceId FROM '.$table.' WHERE '.$id_field.' = '.$this->Conn->qstr($id));
if (is_null($category_id)) $category_id = $this->GetVar('m_cat_id');
$sql = 'SELECT Filename FROM '.TABLE_PREFIX.'CategoryItems WHERE ItemResourceId = '.$resource_id.' AND CategoryId = '.$category_id;
$filename = $this->Conn->GetOne($sql);
/*if (!$filename) {
$sql = 'SELECT Filename FROM '.TABLE_PREFIX.'CategoryItems WHERE ItemResourceId = '.$resource_id.' AND PrimaryCat = 1';
$filename = $this->Conn->GetOne($sql);
}*/
/*$sql = 'SELECT Filename
FROM '.$table.'
WHERE '.$id_field.' = '.$this->Conn->qstr($id);
$filename = $this->Conn->GetOne($sql);*/
}
$this->setCache('filenames', $prefix.'_'.$id, $filename);
}
return $filename;
}
/**
* Adds new value to cache $cache_name and identified by key $key
*
* @param string $cache_name cache name
* @param int $key key name to add to cache
* @param mixed $value value of chached record
*/
function setCache($cache_name, $key, $value, $expiration=3600)
{
$cache =& $this->recallObject('Cache');
/* @var $cache kCache */
return $cache->setCache($cache_name, $key, $value, $expiration);
}
/**
* Returns cached $key value from cache named $cache_name
*
* @param string $cache_name cache name
* @param int $key key name from cache
* @return mixed
*/
function getCache($cache_name, $key)
{
$cache =& $this->recallObject('Cache');
return $cache->getCache($cache_name, $key);
}
/**
* Defines default constants if it's not defined before - in config.php
*
* @access private
*/
function SetDefaultConstants() // it's defined in startup.php - can be removed??
{
safeDefine('SERVER_NAME', $_SERVER['HTTP_HOST']);
}
/**
* Registers each module specific constants if any found
*
*/
function registerModuleConstants()
{
if (file_exists(KERNEL_PATH.'/constants.php')) {
k4_include_once(KERNEL_PATH.'/constants.php');
}
if (!$this->ModuleInfo) return false;
foreach($this->ModuleInfo as $module_name => $module_info)
{
$module_path = '/'.$module_info['Path'];
$contants_file = FULL_PATH.$module_path.'constants.php';
if( file_exists($contants_file) ) k4_include_once($contants_file);
}
return true;
}
function ProcessRequest()
{
$event_manager =& $this->recallObject('EventManager');
/* @var $event_manager kEventManager */
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_SHOW_HTTPQUERY')) {
$this->Debugger->appendHTML('HTTPQuery:');
$this->Debugger->dumpVars($this->HttpQuery->_Params);
}
$event_manager->ProcessRequest();
$event_manager->RunRegularEvents(reBEFORE);
$this->RequestProcessed = true;
}
/**
* Actually runs the parser against current template and stores parsing result
*
* This method gets t variable passed to the script, loads the template given in t variable and
* parses it. The result is store in {@link $this->HTML} property.
* @access public
* @return void
*/
function Run()
{
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
$this->Debugger->appendMemoryUsage('Application before Run:');
}
if ($this->IsAdmin()) {
// for permission checking in events & templates
$this->LinkVar('module'); // for common configuration templates
$this->LinkVar('module_key'); // for common search templates
$this->LinkVar('section'); // for common configuration templates
if ($this->GetVar('m_opener') == 'p') {
$this->LinkVar('main_prefix'); // window prefix, that opened selector
$this->LinkVar('dst_field'); // field to set value choosed in selector
// $this->LinkVar('return_template'); // template to go, when something was coosen from popup (from finalizePopup)
// $this->LinkVar('return_m'); // main env part to restore after popup will be closed (from finalizePopup)
}
if ($this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax')) {
// hide debug output from ajax requests automatically
define('DBG_SKIP_REPORTING', 1);
}
}
elseif ($this->GetVar('admin')) {
// viewing front-end through admin's frame
$admin_session =& $this->Application->recallObject('Session.admin');
$user = (int)$admin_session->RecallVar('user_id'); // in case, when no valid admin session found
$perm_helper =& $this->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
if ($perm_helper->CheckUserPermission($user, 'CATEGORY.MODIFY', 0, 0)) {
// user can edit cms blocks
$editing_mode = $this->GetVar('editing_mode');
define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
$this->Phrases->setPhraseEditing();
}
}
safeDefine('EDITING_MODE', ''); // user can't edit anything
if (!$this->RequestProcessed) $this->ProcessRequest();
$this->InitParser();
$t = $this->GetVar('t');
if (!$this->TemplatesCache->TemplateExists($t) && !$this->IsAdmin()) {
$cms_handler =& $this->recallObject('st_EventHandler');
/* @var $cms_handler CategoriesEventHandler */
$t = ltrim($cms_handler->GetDesignTemplate(), '/');
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
}
}
/*else {
$cms_handler->SetCatByTemplate();
}*/
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
$this->Debugger->appendMemoryUsage('Application before Parsing:');
}
$this->HTML = $this->Parser->Run($t);
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
$this->Debugger->appendMemoryUsage('Application after Parsing:');
}
}
function InitParser($theme_name = false)
{
if( !is_object($this->Parser) ) {
$this->Parser =& $this->recallObject('NParser');
$this->TemplatesCache =& $this->recallObject('TemplatesCache');
}
$this->TemplatesCache->forceThemeName = $theme_name;
}
/**
* Send the parser results to browser
*
* Actually send everything stored in {@link $this->HTML}, to the browser by echoing it.
* @access public
* @return void
*/
function Done()
{
$this->HandleEvent( new kEvent('adm:OnBeforeShutdown') );
if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
$this->Debugger->appendMemoryUsage('Application before Done:');
}
if (defined('DEBUG_MODE') && $this->isDebugMode()) {
$this->EventManager->RunRegularEvents(reAFTER);
$this->Session->SaveData();
if (constOn('DBG_CACHE')) {
$cache =& $this->recallObject('Cache');
$cache->printStatistics();
}
$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
}
else {
$this->HTML = ob_get_clean().$this->HTML;
}
if ($this->UseOutputCompression()) {
header('Content-Encoding: gzip');
$compression_level = $this->ConfigValue('OutputCompressionLevel');
if ($compression_level < 0 || $compression_level > 9) $compression_level = 7;
echo gzencode($this->HTML, $compression_level);
}
else {
echo $this->HTML;
}
$this->UpdateCache();
flush();
if (!$this->isDebugMode()) {
$this->EventManager->RunRegularEvents(reAFTER);
$this->Session->SaveData();
}
if (defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->IsAdmin()) {
$this->_storeStatistics();
}
}
/**
* Stores script execution statistics to database
*
*/
function _storeStatistics()
{
global $start;
$script_time = getmicrotime() - $start;
$query_statistics = $this->Conn->getQueryStatistics(); // time & count
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'StatisticsCapture
WHERE TemplateName = "' . $this->GetVar('t') . '"';
$data = $this->Conn->GetRow($sql);
if ($data) {
$this->_updateAverageStatistics($data, 'ScriptTime', $script_time);
$this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']);
$this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']);
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']);
}
else {
$data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time;
$data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time'];
$data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count'];
$data['TemplateName'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture');
}
}
/**
* Calculates average time for statistics
*
* @param Array $data
* @param string $field_prefix
* @param float $current_value
*/
function _updateAverageStatistics(&$data, $field_prefix, $current_value)
{
$data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1);
if ($current_value < $data[$field_prefix . 'Min']) {
$data[$field_prefix . 'Min'] = $current_value;
}
if ($current_value > $data[$field_prefix . 'Max']) {
$data[$field_prefix . 'Max'] = $current_value;
}
}
function logSlowQuery($slow_sql, $time)
{
$query_crc = crc32($slow_sql);
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'SlowSqlCapture
WHERE QueryCrc = ' . $query_crc;
$data = $this->Conn->Query($sql, null, true);
if ($data) {
$this->_updateAverageStatistics($data, 'Time', $time);
$template_names = explode(',', $data['TemplateNames']);
array_push($template_names, $this->GetVar('t'));
$data['TemplateNames'] = implode(',', array_unique($template_names));
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']);
}
else {
$data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time;
$data['SqlQuery'] = $slow_sql;
$data['QueryCrc'] = $query_crc;
$data['TemplateNames'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture');
}
}
/**
* Checks if output compression options is available
*
* @return string
*/
function UseOutputCompression()
{
if (constOn('IS_INSTALL') || constOn('DBG_ZEND_PRESENT') || constOn('SKIP_OUT_COMPRESSION')) return false;
return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
}
// Facade
/**
* Returns current session id (SID)
* @access public
* @return longint
*/
function GetSID()
{
$session =& $this->recallObject('Session');
return $session->GetID();
}
function DestroySession()
{
$session =& $this->recallObject('Session');
$session->Destroy();
}
/**
* Returns variable passed to the script as GET/POST/COOKIE
*
* @access public
* @param string $name Name of variable to retrieve
* @param int $default default value returned in case if varible not present
* @return mixed
*/
function GetVar($name, $default = false)
{
return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default;
}
/**
* Returns ALL variables passed to the script as GET/POST/COOKIE
*
* @access public
* @return array
*/
function GetVars()
{
return $this->HttpQuery->GetParams();
}
/**
* Set the variable 'as it was passed to the script through GET/POST/COOKIE'
*
* This could be useful to set the variable when you know that
* other objects would relay on variable passed from GET/POST/COOKIE
* or you could use SetVar() / GetVar() pairs to pass the values between different objects.<br>
*
* This method is formerly known as $this->Session->SetProperty.
* @param string $var Variable name to set
* @param mixed $val Variable value
* @access public
* @return void
*/
function SetVar($var,$val)
{
return $this->HttpQuery->Set($var, $val);
}
/**
* Deletes kHTTPQuery variable
*
* @param string $var
* @todo think about method name
*/
function DeleteVar($var)
{
return $this->HttpQuery->Remove($var);
}
/**
* Deletes Session variable
*
* @param string $var
*/
function RemoveVar($var)
{
return $this->Session->RemoveVar($var);
}
function RemovePersistentVar($var)
{
return $this->Session->RemovePersistentVar($var);
}
/**
* Restores Session variable to it's db version
*
* @param string $var
*/
function RestoreVar($var)
{
return $this->Session->RestoreVar($var);
}
/**
* Returns session variable value
*
* Return value of $var variable stored in Session. An optional default value could be passed as second parameter.
*
* @see SimpleSession
* @access public
* @param string $var Variable name
* @param mixed $default Default value to return if no $var variable found in session
* @return mixed
*/
function RecallVar($var,$default=false)
{
return $this->Session->RecallVar($var,$default);
}
function RecallPersistentVar($var, $default = false)
{
return $this->Session->RecallPersistentVar($var, $default);
}
/**
* Stores variable $val in session under name $var
*
* Use this method to store variable in session. Later this variable could be recalled.
* @see RecallVar
* @access public
* @param string $var Variable name
* @param mixed $val Variable value
*/
function StoreVar($var, $val, $optional = false)
{
$session =& $this->recallObject('Session');
$this->Session->StoreVar($var, $val, $optional);
}
function StorePersistentVar($var, $val)
{
$this->Session->StorePersistentVar($var, $val);
}
function StoreVarDefault($var, $val, $optional=false)
{
$session =& $this->recallObject('Session');
$this->Session->StoreVarDefault($var, $val, $optional);
}
/**
* Links HTTP Query variable with session variable
*
* If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session.
* This method could be used for making sure that GetVar will return query or session value for given
* variable, when query variable should overwrite session (and be stored there for later use).<br>
* This could be used for passing item's ID into popup with multiple tab -
* in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id').
* After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session
* @access public
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
*/
function LinkVar($var, $ses_var = null, $default = '', $optional = false)
{
if (!isset($ses_var)) $ses_var = $var;
if ($this->GetVar($var) !== false) {
$this->StoreVar($ses_var, $this->GetVar($var), $optional);
}
else {
$this->SetVar($var, $this->RecallVar($ses_var, $default));
}
}
/**
* Returns variable from HTTP Query, or from session if not passed in HTTP Query
*
* The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed.
* Returns the default value if variable does not exist in session and was not passed in HTTP Query
*
* @see LinkVar
* @access public
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
* @return mixed
*/
function GetLinkedVar($var, $ses_var = null, $default = '')
{
$this->LinkVar($var, $ses_var, $default);
return $this->GetVar($var);
}
function AddBlock($name, $tpl)
{
$this->cache[$name] = $tpl;
}
function ProcessParsedTag($prefix, $tag, $params)
{
$processor = $this->Parser->GetProcessor($prefix);
return $processor->ProcessParsedTag($tag, $params, $prefix);
}
/**
* Return ADODB Connection object
*
* Returns ADODB Connection object already connected to the project database, configurable in config.php
* @access public
* @return kDBConnection
*/
function &GetADODBConnection()
{
return $this->Conn;
}
/**
* Allows to parse given block name or include template
*
- * @param Array $params Parameters to pass to block/template. Reserved parameter "name" used to specify block/template name.
+ * @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
* @param Array $pass_params Forces to pass current parser params to this block/template. Use with cauntion, because you can accidently pass "block_no_data" parameter.
* @param bool $as_template
* @return string
*/
function ParseBlock($params, $pass_params = 0, $as_template = false)
{
if (substr($params['name'], 0, 5) == 'html:') {
return substr($params['name'], 6);
}
return $this->Parser->ParseBlock($params, $pass_params, $as_template);
}
/**
+ * Allows to include template with a given name and given parameters
+ *
+ * @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
+ * @return string
+ */
+ function IncludeTemplate($params)
+ {
+ return $this->Parser->IncludeTemplate($params, isset($block_params['is_silent']) ? 1 : 0);
+ }
+
+ /**
* Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all
*
* @param string $prefix
* @param string $index_file
* @param Array $params
* @return string
*/
function getIndexFile($prefix, $index_file, &$params)
{
if (isset($params['index_file'])) {
$index_file = $params['index_file'];
unset($params['index_file']);
return $index_file;
}
if (isset($index_file)) {
return $index_file;
}
if (defined('INDEX_FILE')) {
return INDEX_FILE;
}
$cut_prefix = trim(BASE_PATH, '/').'/'.trim($prefix, '/');
return trim(preg_replace('/'.preg_quote($cut_prefix, '/').'(.*)/', '\\1', $_SERVER['PHP_SELF']), '/');
}
/**
* Return href for template
*
* @access public
* @param string $t Template path
* @var string $prefix index.php prefix - could be blank, 'admin'
*/
function HREF($t, $prefix='', $params=null, $index_file=null)
{
if(!$t) $t = $this->GetVar('t'); // moved from kMainTagProcessor->T()
$t = preg_replace('/^Content\//i', '', $t);
/*if ($this->GetVar('skip_last_template')) {
$params['opener'] = 'p';
$this->SetVar('m_opener', 'p');
}
if ($t == 'incs/close_popup') {
// because this template closes the popup and we don't need popup mark here anymore
$params['m_opener'] = 's';
}*/
if( substr($t, -4) == '.tpl' ) $t = substr($t, 0, strlen($t) - 4 );
if ( $this->IsAdmin() && $prefix == '') $prefix = ADMIN_DIRECTORY;
if ( $this->IsAdmin() && $prefix == '_FRONT_END_') $prefix = '';
$index_file = $this->getIndexFile($prefix, $index_file, $params);
if (isset($params['_auto_prefix_'])) {
unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here
}
$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
if ($ssl !== null) {
$session =& $this->recallObject('Session');
$cookie_url = trim($session->CookieDomain.$session->CookiePath, '/.');
if ($ssl) {
$target_url = defined('ADMIN') && ADMIN ? $this->ConfigValue('AdminSSL_URL') : false;
if (!$target_url) {
$target_url = $this->ConfigValue('SSL_URL');
}
}
else {
$target_url = 'http://'.DOMAIN.$this->ConfigValue('Site_Path');
}
if (!preg_match('#'.preg_quote($cookie_url).'#', $target_url)) {
$session->SetMode(smGET_ONLY);
}
}
if (isset($params['opener']) && $params['opener'] == 'u') {
$wid = $this->Application->GetVar('m_wid');
$stack_name = rtrim('opener_stack_'.$wid, '_');
$opener_stack = $this->RecallVar($stack_name);
if ($opener_stack && $opener_stack != serialize(Array())) {
$opener_stack = unserialize($opener_stack);
list($index_file, $env) = explode('|', $opener_stack[count($opener_stack) - 1]);
$ret = $this->BaseURL($prefix, $ssl).$index_file.'?'.ENV_VAR_NAME.'='.$env;
if ( getArrayValue($params,'escape') ) $ret = addslashes($ret);
if (isset($params['m_opener']) && $params['m_opener'] == 'u') {
array_pop($opener_stack);
if (!$opener_stack) {
$this->RemoveVar($stack_name);
// remove popups last templates, because popup is closing now
$this->RemoveVar('last_template_'.$wid);
$this->RemoveVar('last_template_popup_'.$wid);
// don't save popups last templates again :)
$this->SetVar('skip_last_template', 1);
}
else {
$this->StoreVar($stack_name, serialize($opener_stack));
}
/*// store window relations
$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
if (array_key_exists($wid, $window_relations)) {
unset($window_relations[$wid]);
$this->Application->StoreVar('window_relations', serialize($window_relations));
}*/
}
return $ret;
}
else {
//define('DBG_REDIRECT', 1);
$t = $this->GetVar('t');
}
}
$pass = isset($params['pass']) ? $params['pass'] : '';
$pass_events = isset($params['pass_events']) ? $params['pass_events'] : false; // pass events with url
$map_link = '';
if( isset($params['anchor']) )
{
$map_link = '#'.$params['anchor'];
unset($params['anchor']);
}
if ( isset($params['no_amp']) )
{
$params['__URLENCODE__'] = $params['no_amp'];
unset($params['no_amp']);
}
$no_rewrite = false;
if( isset($params['__NO_REWRITE__']) )
{
$no_rewrite = true;
unset($params['__NO_REWRITE__']);
}
$force_rewrite = false;
if( isset($params['__MOD_REWRITE__']) )
{
$force_rewrite = true;
unset($params['__MOD_REWRITE__']);
}
$force_no_sid = false;
if( isset($params['__NO_SID__']) )
{
$force_no_sid = true;
unset($params['__NO_SID__']);
}
// append pass through variables to each link to be build
// $params = array_merge_recursive2($this->getPassThroughVariables($params), $params);
$params = array_merge($this->getPassThroughVariables($params), $params);
if ($force_rewrite || ($this->RewriteURLs($ssl) && !$no_rewrite)) {
static $rewrite_listeners_done = false;
if (!$rewrite_listeners_done) {
$mod_rewrite_helper =& $this->recallObject('ModRewriteHelper');
/* @var $mod_rewrite_helper kModRewriteHelper */
$mod_rewrite_helper->initRewriteListeners();
$rewrite_listeners_done = true;
}
$session =& $this->recallObject('Session');
if ($session->NeedQueryString() && !$force_no_sid) {
$params['sid'] = $this->GetSID();
}
$url = $this->BuildEnv_NEW($t, $params, $pass, $pass_events);
$ret = $this->BaseURL($prefix, $ssl).$url.$map_link;
}
else {
unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off
$env = $this->BuildEnv($t, $params, $pass, $pass_events);
$ret = $this->BaseURL($prefix, $ssl).$index_file.'?'.$env.$map_link;
}
return $ret;
}
/**
* Returns variables with values that should be passed throught with this link + variable list
*
* @param Array $params
* @return Array
*/
function getPassThroughVariables(&$params)
{
static $cached_pass_through = null;
if (isset($params['no_pass_through']) && $params['no_pass_through']) {
unset($params['no_pass_through']);
return Array();
}
// because pass through is not changed during script run, then we can cache it
if (is_null($cached_pass_through)) {
$cached_pass_through = Array();
$pass_through = $this->Application->GetVar('pass_through');
if ($pass_through) {
// names of variables to pass to each link
$cached_pass_through['pass_through'] = $pass_through;
$pass_through = explode(',', $pass_through);
foreach ($pass_through as $pass_through_var) {
$cached_pass_through[$pass_through_var] = $this->Application->GetVar($pass_through_var);
}
}
}
return $cached_pass_through;
}
/**
* Returns sorted array of passed prefixes (to build url from)
*
* @param string $pass
* @return Array
*/
function getPassInfo($pass = 'all')
{
if (!$pass) $pass = 'all';
$pass = trim(
preg_replace(
'/(?<=,|\\A)all(?=,|\\z)/',
trim($this->GetVar('passed'), ','),
trim($pass, ',')
),
',');
if (!$pass) {
return Array();
}
$pass_info = array_unique( explode(',', $pass) ); // array( prefix[.special], prefix[.special] ...
// we need to keep that sorting despite the sorting below, because this sorts prefixes with same priority by name
sort($pass_info, SORT_STRING); // to be prefix1,prefix1.special1,prefix1.special2,prefix3.specialX
foreach ($pass_info as $prefix) {
list($prefix_only) = explode('.', $prefix, 1);
$sorted[$prefix] = $this->getUnitOption($prefix_only, 'RewritePriority', 0);
}
arsort($sorted);
$pass_info = array_keys($sorted);
// ensure that "m" prefix is at the beginning
$main_index = array_search('m', $pass_info);
if ($main_index !== false) {
unset($pass_info[$main_index]);
array_unshift($pass_info, 'm');
}
return $pass_info;
}
function BuildEnv_NEW($t, $params, $pass='all', $pass_events = false)
{
// $session =& $this->recallObject('Session');
$force_admin = getArrayValue($params,'admin') || $this->GetVar('admin');
// if($force_admin) $sid = $this->GetSID();
$ret = '';
$env = '';
$encode = false;
if (isset($params['__URLENCODE__'])) {
$encode = $params['__URLENCODE__'];
unset($params['__URLENCODE__']);
}
if (isset($params['__SSL__'])) {
unset($params['__SSL__']);
}
$catalog_item_found = false;
$pass_info = $this->getPassInfo($pass);
if ($pass_info) {
if ($pass_info[0] == 'm') {
array_shift($pass_info);
}
$inject_parts = Array (); // url parts for beginning of url
$params['t'] = $t; // make template available for rewrite listeners
$params['pass_template'] = true; // by default we keep given template in resulting url
if (!array_key_exists('pass_category', $params)) {
$params['pass_category'] = false; // by default we don't keep categories in url
}
foreach ($pass_info as $pass_index => $pass_element) {
list ($prefix) = explode('.', $pass_element);
$catalog_item = $this->findModule('Var', $prefix) && $this->getUnitOption($prefix, 'CatalogItem');
if (array_key_exists($prefix, $this->RewriteListeners)) {
// if next prefix is same as current, but with special => exclude current prefix from url
$next_prefix = array_key_exists($pass_index + 1, $pass_info) ? $pass_info[$pass_index + 1] : false;
if ($next_prefix) {
$next_prefix = substr($next_prefix, 0, strlen($prefix) + 1);
if ($prefix . '.' == $next_prefix) {
continue;
}
}
// rewrited url part
$url_part = $this->BuildModuleEnv_NEW($pass_element, $params, $pass_events);
if (is_string($url_part) && $url_part) {
$ret .= $url_part . '/';
if ($catalog_item) {
// pass category later only for catalog items
$catalog_item_found = true;
}
}
elseif (is_array($url_part)) {
// rewrite listener want to insert something at the beginning of url too
if ($url_part[0]) {
$inject_parts[] = $url_part[0];
}
if ($url_part[1]) {
$ret .= $url_part[1] . '/';
}
if ($catalog_item) {
// pass category later only for catalog items
$catalog_item_found = true;
}
} elseif ($url_part === false) {
// rewrite listener decided not to rewrite given $pass_element
$env .= ':' . $this->BuildModuleEnv($pass_element, $params, $pass_events);
}
}
else {
$env .= ':' . $this->BuildModuleEnv($pass_element, $params, $pass_events);
}
}
if ($catalog_item_found || preg_match('/c\.[-\d]*/', implode(',', $pass_info))) {
// "c" prefix is present -> keep category
$params['pass_category'] = true;
}
$params['inject_parts'] = $inject_parts;
$ret = $this->BuildModuleEnv_NEW('m', $params, $pass_events) . '/' . $ret;
$cat_processed = array_key_exists('category_processed', $params) && $params['category_processed'];
// remove tempporary parameters used by listeners
unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']);
if ($catalog_item_found || !$cat_processed || !defined('EXP_DIR_URLS')) {
// this catalog item detail page OR there is no category given
$ret = trim($ret, '/') . '.html';
}
else {
// url ends with "/" and not with ".html"
$ret = trim($ret, '/') . '/';
}
if ($env) {
$params[ENV_VAR_NAME] = ltrim($env, ':');
}
}
unset($params['pass'], $params['opener'], $params['m_event']);
if ($force_admin) {
$params['admin'] = 1;
}
if (array_key_exists('escape', $params) && $params['escape']) {
$ret = addslashes($ret);
unset($params['escape']);
}
$ret = str_replace('%2F', '/', urlencode($ret));
$params_str = '';
$join_string = $encode ? '&' : '&amp;';
foreach ($params as $param => $value) {
$params_str .= $join_string . $param . '=' . $value;
}
if ($params_str) {
$ret .= '?' . substr($params_str, strlen($join_string));
}
if ($encode) {
$ret = str_replace('\\', '%5C', $ret);
}
return $ret;
}
function BuildModuleEnv_NEW($prefix_special, &$params, $keep_events = false)
{
list ($prefix) = explode('.', $prefix_special);
$url_parts = Array ();
$listener = $this->RewriteListeners[$prefix];
$ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $keep_events);
return $ret;
}
/**
* Builds env part that corresponds prefix passed
*
* @param string $prefix_special item's prefix & [special]
* @param Array $params url params
* @param bool $pass_events
*/
function BuildModuleEnv($prefix_special, &$params, $pass_events = false)
{
list($prefix) = explode('.', $prefix_special);
$query_vars = $this->getUnitOption($prefix, 'QueryString');
//if pass events is off and event is not implicity passed
if( !$pass_events && !isset($params[$prefix_special.'_event']) ) {
$params[$prefix_special.'_event'] = ''; // remove event from url if requested
//otherwise it will use value from get_var
}
if(!$query_vars) return '';
$tmp_string = Array(0 => $prefix_special);
foreach($query_vars as $index => $var_name)
{
//if value passed in params use it, otherwise use current from application
$var_name = $prefix_special.'_'.$var_name;
$tmp_string[$index] = isset( $params[$var_name] ) ? $params[$var_name] : $this->GetVar($var_name);
if ( isset($params[$var_name]) ) unset( $params[$var_name] );
}
$escaped = array();
foreach ($tmp_string as $tmp_val) {
$escaped[] = str_replace(Array('-',':'), Array('\-','\:'), $tmp_val);
}
$ret = implode('-', $escaped);
if ($this->getUnitOption($prefix, 'PortalStyleEnv') == true)
{
$ret = preg_replace('/^([a-zA-Z]+)-([0-9]+)-(.*)/','\\1\\2-\\3', $ret);
}
return $ret;
}
function BuildEnv($t, $params, $pass='all', $pass_events = false, $env_var = true)
{
$session =& $this->recallObject('Session');
$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : 0;
$sid = $session->NeedQueryString() && !$this->RewriteURLs($ssl) ? $this->GetSID() : '';
// if (getArrayValue($params,'admin') == 1) $sid = $this->GetSID();
$ret = '';
if ($env_var) {
$ret = ENV_VAR_NAME.'=';
}
$ret .= $sid . '-'; // SID-TEMPLATE
$encode = false;
if (isset($params['__URLENCODE__'])) {
$encode = $params['__URLENCODE__'];
unset($params['__URLENCODE__']);
}
if (isset($params['__SSL__'])) {
unset($params['__SSL__']);
}
$env_string = '';
$category_id = isset($params['m_cat_id']) ? $params['m_cat_id'] : $this->GetVar('m_cat_id');
$item_id = false;
$pass_info = $this->getPassInfo($pass);
if ($pass_info) {
if ($pass_info[0] == 'm') array_shift($pass_info);
foreach ($pass_info as $pass_element) {
list($prefix) = explode('.', $pass_element);
$require_rewrite = $this->findModule('Var', $prefix);
if ($require_rewrite) {
$item_id = isset($params[$pass_element.'_id']) ? $params[$pass_element.'_id'] : $this->GetVar($pass_element.'_id');
}
$env_string .= ':'.$this->BuildModuleEnv($pass_element, $params, $pass_events);
}
}
if (strtolower($t) == '__default__') {
// to put category & item templates into cache
$filename = $this->getFilename('c', $category_id);
if (is_numeric($item_id)) {
$mod_rw_helper =& $this->Application->recallObject('ModRewriteHelper');
/* @var $mod_rw_helper kModRewriteHelper */
$t = $mod_rw_helper->GetItemTemplate($category_id, $pass_element); // $pass_element should be the last processed element
// $t = $this->getCache('item_templates', $category_id);
}
elseif ($category_id) {
$t = strtolower( preg_replace('/^Content\//i', '', $this->getCache('category_templates', $category_id)) );
}
else {
$t = 'index';
}
}
$ret .= $t.':'.$this->BuildModuleEnv('m', $params, $pass_events).$env_string;
unset($params['pass'], $params['opener'], $params['m_event']);
if ($this->GetVar('admin') && !isset($params['admin'])) {
$params['admin'] = 1;
if (!array_key_exists('editing_mode', $params)) {
$params['editing_mode'] = EDITING_MODE;
}
}
if (array_key_exists('escape', $params) && $params['escape']) {
$ret = addslashes($ret);
unset($params['escape']);
}
$join_string = $encode ? '&' : '&amp;';
$params_str = '';
foreach ($params as $param => $value)
{
$params_str .= $join_string.$param.'='.$value;
}
$ret .= $params_str;
if ($encode) {
$ret = str_replace('\\', '%5C', $ret);
}
return $ret;
}
function BaseURL($prefix='', $ssl=null)
{
if ($ssl === null) {
return PROTOCOL.SERVER_NAME.(defined('PORT')?':'.PORT : '').rtrim(BASE_PATH, '/').$prefix.'/';
}
else {
if ($ssl) {
$base_url = defined('ADMIN') && ADMIN ? $this->ConfigValue('AdminSSL_URL') : false;
if (!$base_url) {
$base_url = $this->ConfigValue('SSL_URL');
}
return rtrim($base_url, '/').$prefix.'/';
}
else {
return 'http://'.DOMAIN.(defined('PORT')?':'.PORT : '').rtrim( $this->ConfigValue('Site_Path'), '/').$prefix.'/';
}
}
}
function Redirect($t='', $params=null, $prefix='', $index_file=null)
{
$js_redirect = getArrayValue($params, 'js_redirect');
if (preg_match("/external:(.*)/", $t, $rets)) {
$location = $rets[1];
}
else {
if ($t == '' || $t === true) $t = $this->GetVar('t');
// pass prefixes and special from previous url
if( isset($params['js_redirect']) ) unset($params['js_redirect']);
if (!isset($params['pass'])) $params['pass'] = 'all';
if ($this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t')) {
// redirects to the same template as current
$params['ajax'] = 'yes';
}
$params['__URLENCODE__'] = 1;
$location = $this->HREF($t, $prefix, $params, $index_file);
//echo " location : $location <br>";
}
$a_location = $location;
$location = "Location: $location";
if ($this->isDebugMode() && constOn('DBG_REDIRECT')) {
$this->Debugger->appendTrace();
echo "<b>Debug output above!!!</b> Proceed to redirect: <a href=\"$a_location\">$a_location</a><br>";
}
else {
if ($js_redirect) {
$this->SetVar('t', 'redirect');
$this->SetVar('redirect_to_js', addslashes($a_location) );
$this->SetVar('redirect_to', $a_location);
return true;
}
else {
if ($this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t')) {
// redirection to other then current template during ajax request
echo '#redirect#'.$a_location;
}
elseif (headers_sent() != '') {
// some output occured -> redirect using javascript
echo '<script type="text/javascript">window.location.href = \''.$a_location.'\';</script>';
}
else {
// no output before -> redirect using HTTP header
// header('HTTP/1.1 302 Found');
header("$location");
}
}
}
ob_end_flush();
// session expiration is called from session initialization,
// that's why $this->Session may be not defined here
$session =& $this->Application->recallObject('Session');
/* @var $session Session */
$this->HandleEvent( new kEvent('adm:OnBeforeShutdown') );
$session->SaveData();
exit;
}
function Phrase($label, $allow_editing = true)
{
return $this->Phrases->GetPhrase($label, $allow_editing);
}
/**
* Replace language tags in exclamation marks found in text
*
* @param string $text
* @param bool $force_escape force escaping, not escaping of resulting string
* @return string
* @access public
*/
function ReplaceLanguageTags($text, $force_escape=null)
{
// !!!!!!!!
// if( !is_object($this->Phrases) ) $this->Debugger->appendTrace();
return $this->Phrases->ReplaceLanguageTags($text,$force_escape);
}
/**
* Checks if user is logged in, and creates
* user object if so. User object can be recalled
* later using "u.current" prefix_special. Also you may
* get user id by getting "u.current_id" variable.
*
* @access private
*/
function ValidateLogin()
{
$session =& $this->recallObject('Session');
$user_id = $session->GetField('PortalUserId');
if (!$user_id && $user_id != -1) $user_id = -2;
$this->SetVar('u.current_id', $user_id);
if (!$this->IsAdmin()) {
// needed for "profile edit", "registration" forms ON FRONT ONLY
$this->SetVar('u_id', $user_id);
}
- $this->StoreVar('user_id', $user_id);
+ $this->StoreVar('user_id', $user_id, $user_id == -2); // storing Guest user_id (-2) is optional
if ($this->GetVar('expired') == 1) {
// this parameter is set only from admin
$user =& $this->recallObject('u.current');
$user->SetError('ValidateLogin', 'session_expired', 'la_text_sess_expired');
}
if (($user_id != -2) && constOn('DBG_REQUREST_LOG') ) {
$http_query =& $this->recallObject('HTTPQuery');
$http_query->writeRequestLog(DBG_REQUREST_LOG);
}
if ($user_id != -2) {
// normal users + root
$this->LoadPersistentVars();
}
}
/**
* Loads current user persistent session data
*
*/
function LoadPersistentVars()
{
$this->Session->LoadPersistentVars();
}
function LoadCache() {
$cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->IsAdmin();
$query = sprintf("SELECT PhraseList, ConfigVariables FROM %s WHERE Template = %s",
TABLE_PREFIX.'PhraseCache',
$this->Conn->qstr(md5($cache_key)));
$res = $this->Conn->GetRow($query);
if ($res) {
$this->Caches['PhraseList'] = $res['PhraseList'] ? explode(',', $res['PhraseList']) : array();
$config_ids = $res['ConfigVariables'] ? explode(',', $res['ConfigVariables']) : array();
if (isset($this->Caches['ConfigVariables'])) {
$config_ids = array_diff($config_ids, $this->Caches['ConfigVariables']);
}
}
else {
$config_ids = array();
}
$this->Caches['ConfigVariables'] = $config_ids;
$this->ConfigCacheIds = $config_ids;
}
function UpdateCache()
{
$update = false;
//something changed
$update = $update || $this->Phrases->NeedsCacheUpdate();
$update = $update || (count($this->ConfigCacheIds) && $this->ConfigCacheIds != $this->Caches['ConfigVariables']);
if ($update) {
$cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->IsAdmin();
$query = sprintf("REPLACE %s (PhraseList, CacheDate, Template, ConfigVariables)
VALUES (%s, %s, %s, %s)",
TABLE_PREFIX.'PhraseCache',
$this->Conn->Qstr(join(',', $this->Phrases->Ids)),
adodb_mktime(),
$this->Conn->Qstr(md5($cache_key)),
$this->Conn->qstr(implode(',', array_unique($this->ConfigCacheIds))));
$this->Conn->Query($query);
}
}
function InitConfig()
{
if (isset($this->Caches['ConfigVariables']) && count($this->Caches['ConfigVariables']) > 0) {
$this->ConfigHash = array_merge($this->ConfigHash, $this->Conn->GetCol(
'SELECT VariableValue, VariableName FROM '.TABLE_PREFIX.'ConfigurationValues
WHERE VariableId IN ('.implode(',', $this->Caches['ConfigVariables']).')', 'VariableName'));
}
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
*/
function ConfigValue($name)
{
$res = array_key_exists($name, $this->ConfigHash) ? $this->ConfigHash[$name] : false;
if ($res !== false) {
return $res;
}
if (defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('ConfigurationValues')) {
return false;
}
$sql = 'SELECT VariableId, VariableValue
FROM '.TABLE_PREFIX.'ConfigurationValues
WHERE VariableName = '.$this->Conn->qstr($name);
$res = $this->Conn->GetRow($sql);
if ($res !== false) {
$this->ConfigHash[$name] = $res['VariableValue'];
$this->ConfigCacheIds[] = $res['VariableId'];
return $res['VariableValue'];
}
return false;
}
function UpdateConfigCache()
{
if ($this->ConfigCacheIds) {
}
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @access public
* @author Alex
*/
function HandleEvent(&$event, $params=null, $specificParams=null)
{
if ( isset($params) ) {
$event = new kEvent( $params, $specificParams );
}
if (!isset($this->EventManager)) {
$this->EventManager =& $this->recallObject('EventManager');
}
$this->EventManager->HandleEvent($event);
}
/**
* Registers new class in the factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $file Filename in what $real_class is declared
* @param string $pseudo_class Name under this class object will be accessed using getObject method
* @param Array $dependecies List of classes required for this class functioning
* @access public
* @author Alex
*/
function registerClass($real_class, $file, $pseudo_class = null, $dependecies = Array() )
{
$this->Factory->registerClass($real_class, $file, $pseudo_class, $dependecies);
}
/**
* Add $class_name to required classes list for $depended_class class.
* All required class files are included before $depended_class file is included
*
* @param string $depended_class
* @param string $class_name
* @author Alex
*/
function registerDependency($depended_class, $class_name)
{
$this->Factory->registerDependency($depended_class, $class_name);
}
/**
* Registers Hook from subprefix event to master prefix event
*
* @param string $hookto_prefix
* @param string $hookto_special
* @param string $hookto_event
* @param string $mode
* @param string $do_prefix
* @param string $do_special
* @param string $do_event
* @param string $conditional
* @access public
* @todo take care of a lot parameters passed
* @author Kostja
*/
function registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional)
{
$event_manager =& $this->recallObject('EventManager');
$event_manager->registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional);
}
/**
* Allows one TagProcessor tag act as other TagProcessor tag
*
* @param Array $tag_info
* @author Kostja
*/
function registerAggregateTag($tag_info)
{
$aggregator =& $this->recallObject('TagsAggregator', 'kArray');
$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], Array($tag_info['LocalPrefix'], $tag_info['LocalTagName'], getArrayValue($tag_info, 'LocalSpecial')));
}
/**
* Returns object using params specified,
* creates it if is required
*
* @param string $name
* @param string $pseudo_class
* @param Array $event_params
* @return Object
* @author Alex
*/
function &recallObject($name,$pseudo_class=null,$event_params=Array())
{
$result =& $this->Factory->getObject($name, $pseudo_class, $event_params);
return $result;
}
/**
* Returns object using Variable number of params,
* all params starting with 4th are passed to object consturctor
*
* @param string $name
* @param string $pseudo_class
* @param Array $event_params
* @return Object
* @author Alex
*/
function &recallObjectP($name,$pseudo_class=null,$event_params=Array())
{
$func_args = func_get_args();
$result =& ref_call_user_func_array( Array(&$this->Factory, 'getObjectP'), $func_args );
return $result;
}
/**
* Returns tag processor for prefix specified
*
* @param string $prefix
* @return kDBTagProcessor
*/
function &recallTagProcessor($prefix)
{
$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
$result =& $this->recallObject($prefix . '_TagProcessor');
return $result;
}
/**
* Checks if object with prefix passes was already created in factory
*
* @param string $name object presudo_class, prefix
* @return bool
* @author Kostja
*/
function hasObject($name)
{
return isset($this->Factory->Storage[$name]);
}
/**
* Removes object from storage by given name
*
* @param string $name Object's name in the Storage
* @author Kostja
*/
function removeObject($name)
{
$this->Factory->DestroyObject($name);
}
/**
* Get's real class name for pseudo class,
* includes class file and creates class
* instance
*
* @param string $pseudo_class
* @return Object
* @access public
* @author Alex
*/
function &makeClass($pseudo_class)
{
$func_args = func_get_args();
$result =& ref_call_user_func_array( Array(&$this->Factory, 'makeClass'), $func_args);
return $result;
}
/**
* Checks if application is in debug mode
*
* @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
* @return bool
* @author Alex
* @access public
*/
function isDebugMode($check_debugger = true)
{
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
if ($check_debugger) {
$debug_mode = $debug_mode && is_object($this->Debugger);
}
return $debug_mode;
}
/**
* Checks if it is admin
*
* @return bool
* @author Alex
*/
function IsAdmin()
{
return constOn('ADMIN');
}
/**
* Apply url rewriting used by mod_rewrite or not
*
* @param bool $ssl Force ssl link to be build
* @return bool
*/
function RewriteURLs($ssl = false)
{
// case #1,#4:
// we want to create https link from http mode
// we want to create https link from https mode
// conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
// case #2,#3:
// we want to create http link from https mode
// we want to create http link from http mode
// conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
$allow_rewriting =
(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
|| // or allow rewriting for redirect TO httpS or when already in httpS
(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
return constOn('MOD_REWRITE') && $allow_rewriting;
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $option
* @param mixed $default
* @return string
* @access public
* @author Alex
*/
function getUnitOption($prefix, $option, $default = false)
{
/*if (!isset($this->UnitConfigReader)) {
$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
}*/
return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $name
* @param string $value
* @author Alex
* @access public
*/
function setUnitOption($prefix, $option, $value)
{
// $unit_config_reader =& $this->recallObject('kUnitConfigReader');
return $this->UnitConfigReader->setUnitOption($prefix,$option,$value);
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
* @author Alex
*/
function getUnitOptions($prefix)
{
// $unit_config_reader =& $this->recallObject('kUnitConfigReader');
return $this->UnitConfigReader->getUnitOptions($prefix);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
function prefixRegistred($prefix)
{
/*if (!isset($this->UnitConfigReader)) {
$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
}*/
return $this->UnitConfigReader->prefixRegistred($prefix);
}
/**
* Splits any mixing of prefix and
* special into correct ones
*
* @param string $prefix_special
* @return Array
* @access public
* @author Alex
*/
function processPrefix($prefix_special)
{
return $this->Factory->processPrefix($prefix_special);
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @access public
*/
function setEvent($prefix_special,$event_name)
{
$event_manager =& $this->recallObject('EventManager');
$event_manager->setEvent($prefix_special,$event_name);
}
/**
* SQL Error Handler
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access private
* @author Alex
*/
function handleSQLError($code, $msg, $sql)
{
if ( isset($this->Debugger) )
{
$errorLevel = constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ? E_USER_ERROR : E_USER_WARNING;
$this->Debugger->appendTrace();
$error_msg = '<span class="debug_error">'.$msg.' ('.$code.')</span><br><a href="javascript:$Debugger.SetClipboard(\''.htmlspecialchars($sql).'\');"><b>SQL</b></a>: '.$this->Debugger->formatSQL($sql);
$long_id = $this->Debugger->mapLongError($error_msg);
trigger_error( mb_substr($msg.' ('.$code.') ['.$sql.']',0,1000).' #'.$long_id, $errorLevel);
return true;
}
else
{
//$errorLevel = constOn('IS_INSTALL') ? E_USER_WARNING : E_USER_ERROR;
$errorLevel = E_USER_WARNING;
trigger_error('<b>SQL Error</b> in sql: '.$sql.', code <b>'.$code.'</b> ('.$msg.')', $errorLevel);
/*echo '<b>xProcessing SQL</b>: '.$sql.'<br>';
echo '<b>Error ('.$code.'):</b> '.$msg.'<br>';*/
return $errorLevel == E_USER_ERROR ? false : true;
}
}
/**
* Default error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param Array $errcontext
*/
function handleError($errno, $errstr, $errfile = '', $errline = '', $errcontext = '')
{
if (defined('SILENT_LOG') && SILENT_LOG) {
if ( !(defined('DBG_IGNORE_STRICT_ERRORS') && DBG_IGNORE_STRICT_ERRORS && defined('E_STRICT') && ($errno == E_STRICT)) ) {
$fp = fopen(FULL_PATH.'/silent_log.txt','a');
$time = adodb_date('d/m/Y H:i:s');
fwrite($fp, '['.$time.'] #'.$errno.': '.strip_tags($errstr).' in ['.$errfile.'] on line '.$errline."\n");
fclose($fp);
}
}
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
if (!$this->errorHandlers || ($debug_mode && $skip_reporting)) {
// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
$ignore_fatal_errors = defined('DBG_IGNORE_FATAL_ERRORS') && DBG_IGNORE_FATAL_ERRORS;
if (($errno == E_USER_ERROR) && !$ignore_fatal_errors) {
echo (' <div style="background-color: #FEFFBF; margin: auto; padding: 10px; border: 2px solid red; text-align: center">
<strong>Fatal Error: </strong>'."$errstr in $errfile on line $errline".'
</div>');
exit;
}
if (!$this->errorHandlers) {
return true;
}
}
$res = false;
$i = 0; // while (not foreach) because it is array of references in some cases
$eh_count = count($this->errorHandlers);
while ($i < $eh_count) {
if ( is_array($this->errorHandlers[$i]) ) {
$object =& $this->errorHandlers[$i][0];
$method = $this->errorHandlers[$i][1];
$res = $object->$method($errno, $errstr, $errfile, $errline, $errcontext);
}
else {
$function = $this->errorHandlers[$i];
$res = $function($errno, $errstr, $errfile, $errline, $errcontext);
}
$i++;
}
return $res;
}
/**
* Returns & blocks next ResourceId available in system
*
* @return int
* @access public
* @author Alex
*/
function NextResourceId()
{
$table_name = TABLE_PREFIX.'IdGenerator';
$this->Conn->Query('LOCK TABLES '.$table_name.' WRITE');
$this->Conn->Query('UPDATE '.$table_name.' SET lastid = lastid + 1');
$id = $this->Conn->GetOne('SELECT lastid FROM '.$table_name);
if($id === false)
{
$this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)');
$id = 2;
}
$this->Conn->Query('UNLOCK TABLES');
return $id - 1;
}
/**
* Returns genealogical main prefix for subtable prefix passes
* OR prefix, that has been found in REQUEST and some how is parent of passed subtable prefix
*
* @param string $current_prefix
* @param string $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
* @return string
* @access public
* @author Kostja / Alex
*/
function GetTopmostPrefix($current_prefix, $real_top = false)
{
// 1. get genealogical tree of $current_prefix
$prefixes = Array ($current_prefix);
while ( $parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix') ) {
if (!$this->prefixRegistred($parent_prefix)) {
// stop searching, when parent prefix is not registered
break;
}
$current_prefix = $parent_prefix;
array_unshift($prefixes, $current_prefix);
}
if ($real_top) {
return $current_prefix;
}
// 2. find what if parent is passed
$passed = explode(',', $this->GetVar('all_passed'));
foreach ($prefixes as $a_prefix) {
if (in_array($a_prefix, $passed)) {
return $a_prefix;
}
}
return $current_prefix;
}
/**
* Triggers email event of type Admin
*
* @param string $email_event_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return unknown
*/
function &EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = false)
{
$event =& $this->EmailEvent($email_event_name, 1, $to_user_id, $send_params);
return $event;
}
/**
* Triggers email event of type User
*
* @param string $email_event_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return unknown
*/
function &EmailEventUser($email_event_name, $to_user_id = null, $send_params = false)
{
$event =& $this->EmailEvent($email_event_name, 0, $to_user_id, $send_params);
return $event;
}
/**
* Triggers general email event
*
* @param string $email_event_name
* @param int $email_event_type ( 0 for User, 1 for Admin)
* @param int $to_user_id
* @param array $send_params associative array of direct send params,
* possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return unknown
*/
function &EmailEvent($email_event_name, $email_event_type, $to_user_id = -1, $send_params = false)
{
$params = array(
'EmailEventName' => $email_event_name,
'EmailEventToUserId' => $to_user_id,
'EmailEventType' => $email_event_type,
);
if ($send_params) {
$params['DirectSendParams'] = $send_params;
}
$event_str = isset($send_params['use_special']) ? 'emailevents.'.$send_params['use_special'].':OnEmailEvent' : 'emailevents:OnEmailEvent';
$this->HandleEvent($event, $event_str, $params);
return $event;
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
*/
function LoggedIn()
{
// no session during expiration process
return is_null($this->Session) ? false : $this->Session->LoggedIn();
}
/**
* Check current user permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
*/
function CheckPermission($name, $type = 1, $cat_id = null)
{
$perm_helper =& $this->recallObject('PermissionsHelper');
return $perm_helper->CheckPermission($name, $type, $cat_id);
}
/**
* Set's any field of current visit
*
* @param string $field
* @param mixed $value
*/
function setVisitField($field, $value)
{
if ($this->IsAdmin() || !$this->ConfigValue('UseVisitorTracking')) {
// admin logins are not registred in visits list
return ;
}
$visit =& $this->recallObject('visits', null, Array ('raise_warnings' => 0));
/* @var $visit kDBItem */
if ($visit->isLoaded()) {
$visit->SetDBField($field, $value);
$visit->Update();
}
}
/**
* Allows to check if in-portal is installed
*
* @return bool
*/
function isInstalled()
{
return $this->InitDone && (count($this->ModuleInfo) > 0);
}
/**
* Allows to determine if module is installed & enabled
*
* @param string $module_name
* @return bool
*/
function isModuleEnabled($module_name)
{
return $this->findModule('Name', $module_name) !== false;
}
function reportError($class, $method)
{
$this->Debugger->appendTrace();
trigger_error('depricated method <b>'.$class.'->'.$method.'(...)</b>', E_USER_ERROR);
}
/**
* Returns Window ID of passed prefix main prefix (in edit mode)
*
* @param string $prefix
* @return mixed
*/
function GetTopmostWid($prefix)
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$mode = $this->GetVar($top_prefix.'_mode');
return $mode != '' ? substr($mode, 1) : '';
}
/**
* Get temp table name
*
* @param string $table
* @param mixed $wid
* @return string
*/
function GetTempName($table, $wid = '')
{
if (preg_match('/prefix:(.*)/', $wid, $regs)) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX.'ses_'.$this->GetSID().($wid ? '_'.$wid : '').'_edit_'.$table;
}
function GetTempTablePrefix($wid = '')
{
if (preg_match('/prefix:(.*)/', $wid, $regs)) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX.'ses_'.$this->GetSID().($wid ? '_'.$wid : '').'_edit_';
}
function IsTempTable($table)
{
return preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$table);
}
/**
* Checks, that given prefix is in temp mode
*
* @param string $prefix
* @return bool
*/
function IsTempMode($prefix, $special = '')
{
$top_prefix = $this->Application->GetTopmostPrefix($prefix);
$var_names = Array (
$top_prefix,
rtrim($top_prefix . '_' . $special, '_'), // from post
rtrim($top_prefix . '.' . $special, '.'), // assembled locally
);
$var_names = array_unique($var_names);
$temp_mode = false;
foreach ($var_names as $var_name) {
$value = $this->Application->GetVar($var_name . '_mode');
if ($value && (substr($value, 0, 1) == 't')) {
$temp_mode = true;
break;
}
}
return $temp_mode;
}
/**
* Return live table name based on temp table name
*
* @param string $temp_table
* @return string
*/
function GetLiveName($temp_table)
{
if( preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$temp_table, $rets) )
{
// cut wid from table end if any
return $rets[2];
}
else
{
return $temp_table;
}
}
function CheckProcessors($processors)
{
foreach ($processors as $a_processor)
{
if (!isset($this->CachedProcessors[$a_processor])) {
$this->CachedProcessors[$a_processor] =& $this->recallObject($a_processor.'_TagProcessor');
}
}
}
function TimeZoneAdjustment($time_zone = null)
{
if ($time_zone == 'GMT') {
return (-1) * adodb_date('Z');
}
$target_zone = isset($time_zone) ? $time_zone : $this->ConfigValue('Config_Site_Time');
return 3600 * ($target_zone - $this->ConfigValue('Config_Server_Time'));
}
function ApplicationDie($message = '')
{
$message = ob_get_clean().$message;
if ($this->isDebugMode()) {
$message .= $this->Debugger->printReport(true);
}
echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message;
exit;
}
/* moved from MyApplication */
function getUserGroups($user_id)
{
switch($user_id)
{
case -1:
$user_groups = $this->ConfigValue('User_LoggedInGroup');
break;
case -2:
$user_groups = $this->ConfigValue('User_LoggedInGroup');
$user_groups .= ','.$this->ConfigValue('User_GuestGroup');
break;
default:
$sql = 'SELECT GroupId FROM '.TABLE_PREFIX.'UserGroup WHERE PortalUserId = '.$user_id;
$res = $this->Conn->GetCol($sql);
$user_groups = Array( $this->ConfigValue('User_LoggedInGroup') );
if(is_array($res))
{
$user_groups = array_merge($user_groups, $res);
}
$user_groups = implode(',', $user_groups);
}
return $user_groups;
}
/**
* Allows to detect if page is browsed by spider (293 agents supported)
*
* @return bool
*/
function IsSpider()
{
static $is_spider = null;
if (!isset($is_spider)) {
$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
$robots = file(FULL_PATH.'/core/robots_list.txt');
foreach ($robots as $robot_info) {
$robot_info = explode("\t", $robot_info, 3);
if ($user_agent == trim($robot_info[2])) {
$is_spider = true;
break;
}
}
}
return $is_spider;
}
/**
* Allows to detect table's presense in database
*
* @param string $table_name
* @return bool
*/
function TableFound($table_name)
{
return $this->Conn->TableFound($table_name);
}
/**
* Returns counter value
*
* @param string $name counter name
* @param Array $params counter parameters
* @param string $query_name specify query name directly (don't generate from parmeters)
* @param bool $multiple_results
* @return mixed
*/
function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
{
$count_helper =& $this->Application->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
}
/**
* Resets counter, whitch are affected by one of specified tables
*
* @param string $tables comma separated tables list used in counting sqls
*/
function resetCounters($tables)
{
if (constOn('IS_INSTALL')) {
return ;
}
$count_helper =& $this->Application->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
return $count_helper->resetCounters($tables);
}
/**
* Sends XML header + optionally displays xml heading
*
* @param string $xml_version
* @return string
* @author Alex
*/
function XMLHeader($xml_version = false)
{
$lang =& $this->recallObject('lang.current');
header('Content-type: text/xml; charset='.$lang->GetDBField('Charset'));
return $xml_version ? '<?xml version="'.$xml_version.'" encoding="'.$lang->GetDBField('Charset').'"?>' : '';
}
/**
* Returns category tree
*
* @param int $category_id
* @return Array
*/
function getTreeIndex($category_id)
{
$category_template = $this->getFilename('c', $category_id); // to rebuild "category_tree" cache
$tree_index = $this->getCache('category_tree', $category_id);
if ($tree_index) {
$ret = Array ();
list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
return $ret;
}
return false;
}
}
\ No newline at end of file
Index: branches/5.0.x/core/units/helpers/captcha_helper.php
===================================================================
--- branches/5.0.x/core/units/helpers/captcha_helper.php (revision 12367)
+++ branches/5.0.x/core/units/helpers/captcha_helper.php (revision 12368)
@@ -1,176 +1,182 @@
<?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 kCaptchaHelper extends kHelper {
var $width;
var $height;
function GenerateCaptchaCode($len = 5)
{
$chars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$s = '';
for ($i = 0; $i < $len; $i++) {
$s .= $chars[ rand(0, strlen($chars)-1) ];
}
return $s;
}
function graphics($w, $h)
{
$this->width = $w;
$this->height = $h;
}
function GenerateCaptchaImage($rand, $width, $height, $filter_blur = false)
{
global $site_font_path;
global $site_font_validation;
$image = imagecreate($width, $height);
$bgColor = imagecolorallocate ($image, 255, 255, 255);
$textColor = imagecolorallocate ($image, 0, 0, 0);
// add random noise
for ($i = 0; $i < 20; $i++) {
$rx1 = rand(0, $width);
$rx2 = rand(0, $width);
$ry1 = rand(0, $height);
$ry2 = rand(0, $height);
$rcVal = rand(0, 255);
$rc1 = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(100, 255));
imageline($image, $rx1, $ry1, $rx2, $ry2, $rc1);
}
// write the random number
$dimensions = imagettfbbox($height*0.75, 0, KERNEL_PATH.'/fonts/monofont.ttf', $rand );
imagettftext($image, $height*0.75, 0, floor(($width - $dimensions[4])/2), floor(($height - $dimensions[5])/2), $textColor, KERNEL_PATH.'/fonts/monofont.ttf', $rand);
// $font = imageloadfont(KERNEL_PATH.'/fonts/monofont.ttf');
// imagestring($image, $font, 3, 0, $rand, $textColor);
if ($filter_blur) $this->blur($image, 3);
// send several headers to make sure the image is not cached
// date in the past
header("Expires: Mon, 23 Jul 1993 05:00:00 GMT");
// always modified
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
// HTTP/1.1
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
// HTTP/1.0
header("Pragma: no-cache");
// send the content type header so the image is displayed properly
header('Content-type: image/jpeg');
imagejpeg($image);
imagedestroy($image);
}
function blur(&$gdimg, $radius = 5.0)
{
// Taken from Torstein H�nsi's phpUnsharpMask (see phpthumb.unsharp.php)
$radius = round(max(0, min($radius, 50)) * 2);
if (!$radius) {
return false;
}
$w = ImageSX($gdimg);
$h = ImageSY($gdimg);
if ($imgBlur = ImageCreateTrueColor($w, $h)) {
// Gaussian blur matrix:
// 1 2 1
// 2 4 2
// 1 2 1
// Move copies of the image around one pixel at the time and merge them with weight
// according to the matrix. The same matrix is simply repeated for higher radii.
for ($i = 0; $i < $radius; $i++) {
ImageCopy ($imgBlur, $gdimg, 0, 0, 1, 1, $w - 1, $h - 1); // up left
ImageCopyMerge($imgBlur, $gdimg, 1, 1, 0, 0, $w, $h, 50.00000); // down right
ImageCopyMerge($imgBlur, $gdimg, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left
ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 1, $w, $h - 1, 25.00000); // up right
ImageCopyMerge($imgBlur, $gdimg, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left
ImageCopyMerge($imgBlur, $gdimg, 1, 0, 0, 0, $w, $h, 25.00000); // right
ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 1, $w, $h - 1, 20.00000); // up
ImageCopyMerge($imgBlur, $gdimg, 0, 1, 0, 0, $w, $h, 16.666667); // down
ImageCopyMerge($imgBlur, $gdimg, 0, 0, 0, 0, $w, $h, 50.000000); // center
ImageCopy ($gdimg, $imgBlur, 0, 0, 0, 0, $w, $h);
}
return true;
}
return false;
}
/**
* Generates captcha code for showing on form
*
- * @param kEvent $event
+ * @param string $variable_name
*/
- function prepareCode(&$event)
+ function prepareCode($variable_name)
{
- if ($this->Application->IsAdmin() || $this->Application->RecallVar($event->getPrefixSpecial() . '_captcha_code')) {
+ if ($this->Application->IsAdmin() || $this->Application->RecallVar($variable_name)) {
// when code found don't generate it 2nd time
- return ;
+ return $this->Application->RecallVar($variable_name);
}
- $this->Application->StoreVar($event->getPrefixSpecial() . '_captcha_code', $this->GenerateCaptchaCode());
+ $code = $this->GenerateCaptchaCode();
+ $this->Application->StoreVar($variable_name, $code);
+
+ return $code;
}
/**
* Validates captcha code on form
*
* @param kEvent $event
* @param bool $check_request
* @return bool
*/
function validateCode(&$event, $check_request = true)
{
if ($this->Application->IsAdmin()) {
// no captcha codes in admin
return true;
}
if ($check_request) {
// perform validation only when field is found on form
list ($id, $field_values) = each($this->Application->GetVar($event->getPrefixSpecial()));
if (!array_key_exists('Captcha', $field_values)) {
// when captcha code not submitted
return true;
}
}
$object =& $event->getObject();
/* @var $object kDBItem */
- if ($object->GetDBField('Captcha') != $this->Application->RecallVar($event->getPrefixSpecial() . '_captcha_code')) {
+ $valid_code = $this->Application->RecallVar($event->getPrefixSpecial() . '_captcha_code');
+
+ if (!$object->GetDBField('Captcha') || ($object->GetDBField('Captcha') != $valid_code)) {
+ // empty code OR codes doesn't match
$object->SetError('Captcha', 'captcha_error', 'lu_captcha_error');
$this->Application->StoreVar($event->getPrefixSpecial() . '_captcha_code', $this->GenerateCaptchaCode());
return false;
}
return true;
}
}
\ No newline at end of file
Index: branches/5.0.x/core/units/captcha/captcha_eh.php
===================================================================
--- branches/5.0.x/core/units/captcha/captcha_eh.php (revision 12367)
+++ branches/5.0.x/core/units/captcha/captcha_eh.php (revision 12368)
@@ -1,50 +1,47 @@
<?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 CaptchaEventHandler extends kEventHandler {
/**
* [HOOK] Validates captcha code in item
*
* @param kEvent $event
*/
function OnValidateCode(&$event)
{
$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
$captcha_helper->validateCode($event->MasterEvent);
}
/**
* [HOOK] Initializes captcha code processing routine
*
* @param kEvent $event
*/
function OnPrepareCaptcha(&$event)
{
$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
- // generate captcha code
- $captcha_helper->prepareCode($event->MasterEvent);
-
// create field for captcha code storage
$virtual_fields = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'VirtualFields');
$virtual_fields['Captcha'] = Array ('type' => 'string', 'default' => '');
$this->Application->setUnitOption($event->MasterEvent->Prefix, 'VirtualFields', $virtual_fields);
}
}

Event Timeline