Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Tue, Feb 25, 5:53 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/core/kernel/session/inp_session_storage.php
===================================================================
--- branches/5.2.x/core/kernel/session/inp_session_storage.php (revision 15568)
+++ branches/5.2.x/core/kernel/session/inp_session_storage.php (revision 15569)
@@ -1,100 +1,97 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class InpSessionStorage extends SessionStorage {
public function Init($prefix, $special)
{
parent::Init($prefix, $special);
$this->TableName = TABLE_PREFIX.'UserSessions';
$this->SessionDataTable = TABLE_PREFIX.'UserSessionData';
$this->IDField = '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($timeout = 0)
{
$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($this->TimestampField, $time + $this->SessionTimeout);
// }
}
function GetSessionDefaults()
{
$fields_hash = Array (
'PortalUserId' => $this->Application->isAdmin ? 0 : USER_GUEST,
'Language' => $this->Application->GetDefaultLanguageId(true),
'Theme' => $this->Application->GetDefaultThemeId(),
'GroupId' => $this->Application->ConfigValue('User_GuestGroup'),
'GroupList' => $this->Application->ConfigValue('User_GuestGroup'),
+ 'IpAddress' => $this->Application->getClientIp(),
);
if ( !$this->Application->isAdmin ) {
// Guest users on Front-End belongs to Everyone group too
$fields_hash['GroupList'] .= ',' . $this->Application->ConfigValue('User_LoggedInGroup');
}
- if( isset($_SERVER['REMOTE_ADDR']) ) {
- $fields_hash['IpAddress'] = $_SERVER['REMOTE_ADDR']; // getenv('REMOTE_ADDR') won't work on IIS, so use $_SERVER instead
- }
-
return array_merge($fields_hash, parent::GetSessionDefaults());
}
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.2.x/core/kernel/session/session_storage.php
===================================================================
--- branches/5.2.x/core/kernel/session/session_storage.php (revision 15568)
+++ branches/5.2.x/core/kernel/session/session_storage.php (revision 15569)
@@ -1,505 +1,505 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Implements Session Store in the Database
*
*/
class SessionStorage extends kDBBase {
/**
* Reference to session
*
* @var Session
* @access protected
*/
protected $Session = null;
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;
public function Init($prefix, $special)
{
parent::Init($prefix, $special);
$this->TableName = 'sessions';
$this->IDField = 'sid';
$this->TimestampField = 'expire';
$this->SessionDataTable = 'SessionData';
$this->DataValueField = 'value';
$this->DataVarField = 'var';
}
/**
* Sets reference to session
*
* @param Session $session
*/
public function setSession(&$session)
{
$this->Session =& $session;
$this->SessionTimeout = $session->SessionTimeout;
}
/**
* Calculates browser signature
*
* @return string
*/
function _getBrowserSignature()
{
$signature_parts = Array(
'HTTP_USER_AGENT', 'SERVER_PROTOCOL',
'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_LANGUAGE'
);
$ret = '';
foreach ($signature_parts as $signature_part) {
if (array_key_exists($signature_part, $_SERVER)) {
$ret .= '&|&' . $_SERVER[$signature_part];
}
}
return md5( substr($ret, 3) );
}
function GetSessionDefaults()
{
$fields_hash = Array (
$this->IDField => $this->Session->SID,
$this->TimestampField => $this->Session->Expiration,
);
if (!defined('IS_INSTALL') || !IS_INSTALL) {
// this column was added only in 5.0.1 version,
// so accessing it while database is not upgraded
// will result in admin's inability to login inside
// installator
$fields_hash['BrowserSignature'] = $this->_getBrowserSignature();
}
// default values + values set during this script run
return array_merge($fields_hash, $this->DirectVars);
}
/**
* Stores session to database
*
* @param bool $to_database
*
* @return void
* @access public
*/
public function StoreSession($to_database = true)
{
if ( defined('IS_INSTALL') && IS_INSTALL && $to_database && !$this->Application->TableFound($this->TableName, true) ) {
return;
}
$fields_hash = $this->GetSessionDefaults();
if ( $to_database ) {
$this->Conn->doInsert($fields_hash, $this->TableName);
}
foreach ($fields_hash as $field_name => $field_value) {
$this->SetField($field_name, $field_value);
}
// ensure user groups are stored in a way, that kPermissionsHelper::CheckUserPermission can understand
$this->Session->StoreVar('UserGroups', $this->GetField('GroupList'), !$to_database);
}
function DeleteSession()
{
$this->DeleteSessions( Array ($this->Session->SID), SESSION_LOG_LOGGED_OUT );
$this->DirectVars = $this->ChangedDirectVars = $this->OriginalData = Array();
}
function UpdateSession($timeout = 0)
{
$this->SetField($this->TimestampField, $this->Session->Expiration);
$query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$this->Session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($this->Session->SID);
$this->Conn->Query($query);
}
function LocateSession($sid)
{
$sql = 'SELECT *
FROM ' . $this->TableName . '
WHERE ' . $this->IDField . ' = ' . $this->Conn->qstr($sid);
$result = $this->Conn->GetRow($sql);
if ($result === false) {
return false;
}
// perform security checks to ensure, that session is used by it's creator
if ($this->Application->ConfigValue('SessionBrowserSignatureCheck') && ($result['BrowserSignature'] != $this->_getBrowserSignature()) && $this->Application->GetVar('flashsid') === false) {
return false;
}
- if ($this->Application->ConfigValue('SessionIPAddressCheck') && ($result['IpAddress'] != $_SERVER['REMOTE_ADDR'])) {
+ if ($this->Application->ConfigValue('SessionIPAddressCheck') && ($result['IpAddress'] != $this->Application->getClientIp())) {
// most secure, except for cases where NAT (Network Address Translation)
// is used and two or more computers can have same IP address
return false;
}
$this->DirectVars = $result;
$this->Expiration = $result[$this->TimestampField];
return true;
}
function GetExpiration()
{
return $this->Expiration;
}
function LoadData()
{
$query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($this->Session->SID);
$this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField);
return $this->OriginalData;
}
/**
* Enter description here...
*
* @param string $var_name
* @param mixed $default
* @return mixed
*/
function GetField($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($this->Session->GetID()) );
}
function SetField($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($this->Session->GetID()) );
}
/**
* Saves changes in session to database using single REPLACE query
*
* @return void
* @access public
*/
public function SaveData()
{
if ( !$this->Session->SID ) {
// can't save without sid
return ;
}
$replace = '';
$ses_data = $this->Session->Data->GetParams();
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($this->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($this->Session->GetID());
$this->Conn->Query($query);
}
}
function RemoveFromData($var)
{
if ($this->Session->SessionSet) {
// only, when session is stored in database
$sql = 'DELETE FROM ' . $this->SessionDataTable . '
WHERE ' . $this->IDField . ' = ' . $this->Conn->qstr($this->Session->SID) . ' AND ' . $this->DataVarField . ' = ' . $this->Conn->qstr($var);
$this->Conn->Query($sql);
}
unset($this->OriginalData[$var]);
}
function GetFromData($var, $default = false)
{
return array_key_exists($var, $this->OriginalData) ? $this->OriginalData[$var] : $default;
}
function GetExpiredSIDs()
{
$sql = 'SELECT ' . $this->IDField . '
FROM ' . $this->TableName . '
WHERE ' . $this->TimestampField . ' > ' . adodb_mktime();
return $this->Conn->GetCol($sql);
}
function DeleteExpired()
{
$expired_sids = $this->GetExpiredSIDs();
$this->DeleteSessions($expired_sids);
return $expired_sids;
}
function DeleteSessions($session_ids, $delete_reason = SESSION_LOG_EXPIRED)
{
if (!$session_ids) {
return ;
}
$log_table = $this->Application->getUnitOption('session-log', 'TableName');
if ($log_table) {
// mark session with proper status
$sub_sql = 'SELECT ' . $this->TimestampField . ' - ' . $this->SessionTimeout . '
FROM ' . $this->TableName . '
WHERE ' . $this->IDField . ' = ' . $log_table . '.SessionId';
$sql = 'UPDATE ' . $log_table . '
SET Status = ' . $delete_reason . ', SessionEnd = (' . $sub_sql . ')
WHERE Status = ' . SESSION_LOG_ACTIVE . ' AND SessionId IN (' . implode(',', $session_ids) . ')';
$this->Conn->Query($sql);
}
$where_clause = ' WHERE ' . $this->IDField . ' IN (' . implode(',', $session_ids) . ')';
$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 deleted sessions
foreach ($session_ids as $session_id) {
$debug_file = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache') . '/debug_@' . $session_id . '@.txt';
if (file_exists($debug_file)) {
@unlink($debug_file);
}
}
}
function LoadPersistentVars()
{
$user_id = $this->Session->RecallVar('user_id');
if ($user_id != USER_GUEST) {
// root & normal users
$sql = 'SELECT VariableValue, VariableName
FROM '.TABLE_PREFIX.'UserPersistentSessionData
WHERE PortalUserId = '.$user_id;
$this->PersistentVars = (array)$this->Conn->GetCol($sql, 'VariableName');
}
else {
$this->PersistentVars = Array ();
}
}
/**
* Stores variable to persistent session
*
* @param string $var_name
* @param mixed $var_value
* @param bool $optional
* @return void
* @access public
*/
public function StorePersistentVar($var_name, $var_value, $optional = false)
{
$user_id = $this->Session->RecallVar('user_id');
if ( $user_id == USER_GUEST || $user_id === false ) {
// -2 (when not logged in), false (when after u:OnLogout event)
$this->Session->StoreVar($var_name, $var_value, $optional);
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 . 'UserPersistentSessionData
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 . 'UserPersistentSessionData', $key_clause);
}
else {
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData');
}
}
/**
* Gets persistent variable
*
* @param string $var_name
* @param mixed $default
* @return mixed
* @access public
*/
public function RecallPersistentVar($var_name, $default = false)
{
if ( $this->Session->RecallVar('user_id') == USER_GUEST ) {
if ( $default == ALLOW_DEFAULT_SETTINGS ) {
$default = null;
}
return $this->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 = USER_ROOT;
}
$sql = 'SELECT VariableValue, VariableName
FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
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($var_name, $value); //storing it, so next time we don't load default user setting
}
return $value;
}
return $default;
}
/**
* Removes variable from persistent session
*
* @param string $var_name
* @return void
* @access public
*/
function RemovePersistentVar($var_name)
{
unset($this->PersistentVars[$var_name]);
$user_id = $this->Session->RecallVar('user_id');
if ( $user_id == USER_GUEST || $user_id === false ) {
// -2 (when not logged in), false (when after u:OnLogout event)
$this->Session->RemoveVar($var_name);
}
else {
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE PortalUserId = ' . $user_id . ' AND VariableName = ' . $this->Conn->qstr($var_name);
$this->Conn->Query($sql);
}
}
/**
* Checks of object has given field
*
* @param string $name
* @return bool
* @access protected
*/
protected function HasField($name) { }
/**
* Returns field values
*
* @return Array
* @access protected
*/
protected function GetFieldValues() { }
/**
* Returns unformatted field value
*
* @param string $field
* @return string
* @access protected
*/
protected function GetDBField($field) { }
/**
* Returns true, when list/item was queried/loaded
*
* @return bool
* @access protected
*/
protected function isLoaded() { }
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @return Array
* @access protected
*/
protected function GetCol($field) { }
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php (revision 15568)
+++ branches/5.2.x/core/kernel/application.php (revision 15569)
@@ -1,3041 +1,3052 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Basic class for Kernel4-based Application
*
* This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
* The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
* <br>
* The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
* This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method,
* which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br>
* See singleton pattern by GOF.
*/
class kApplication implements kiCacheable {
/**
* Location of module helper class (used in installator too)
*/
const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php';
/**
* Is true, when Init method was called already, prevents double initialization
*
* @var bool
*/
public $InitDone = false;
/**
* Holds internal NParser object
*
* @var NParser
* @access public
*/
public $Parser;
/**
* Holds parser output buffer
*
* @var string
* @access protected
*/
protected $HTML = '';
/**
* The main Factory used to create
* almost any class of kernel and
* modules
*
* @var kFactory
* @access protected
*/
protected $Factory;
/**
* Template names, that will be used instead of regular templates
*
* @var Array
* @access public
*/
public $ReplacementTemplates = Array ();
/**
* Mod-Rewrite listeners used during url building and parsing
*
* @var Array
* @access public
*/
public $RewriteListeners = Array ();
/**
* Reference to debugger
*
* @var Debugger
* @access public
*/
public $Debugger = null;
/**
* Holds all phrases used
* in code and template
*
* @var PhrasesCache
* @access public
*/
public $Phrases;
/**
* Modules table content, key - module name
*
* @var Array
* @access public
*/
public $ModuleInfo = Array ();
/**
* Holds DBConnection
*
* @var kDBConnection
* @access public
*/
public $Conn = null;
/**
* Reference to event log
*
* @var Array|kLogger
* @access public
*/
protected $_logger = Array ();
// performance needs:
/**
* Holds a reference to httpquery
*
* @var kHttpQuery
* @access public
*/
public $HttpQuery = null;
/**
* Holds a reference to UnitConfigReader
*
* @var kUnitConfigReader
* @access public
*/
public $UnitConfigReader = null;
/**
* Holds a reference to Session
*
* @var Session
* @access public
*/
public $Session = null;
/**
* Holds a ref to kEventManager
*
* @var kEventManager
* @access public
*/
public $EventManager = null;
/**
* Holds a ref to kUrlManager
*
* @var kUrlManager
* @access public
*/
public $UrlManager = null;
/**
* Ref for TemplatesCache
*
* @var TemplatesCache
* @access public
*/
public $TemplatesCache = null;
/**
* Holds current NParser tag while parsing, can be used in error messages to display template file and line
*
* @var _BlockTag
* @access public
*/
public $CurrentNTag = null;
/**
* Object of unit caching class
*
* @var kCacheManager
* @access public
*/
public $cacheManager = null;
/**
* Tells, that administrator has authenticated in administrative console
* Should be used to manipulate data change OR data restrictions!
*
* @var bool
* @access public
*/
public $isAdminUser = false;
/**
* Tells, that admin version of "index.php" was used, nothing more!
* Should be used to manipulate data display!
*
* @var bool
* @access public
*/
public $isAdmin = false;
/**
* Instance of site domain object
*
* @var kDBItem
* @access public
* @todo move away into separate module
*/
public $siteDomain = null;
/**
* Prevent kApplication class to be created directly, only via Instance method
*
* @access private
*/
private function __construct()
{
}
final private function __clone() {}
/**
* Returns kApplication instance anywhere in the script.
*
* This method should be used to get single kApplication object instance anywhere in the
* Kernel-based application. The method is guaranteed to return the SAME instance of kApplication.
* Anywhere in the script you could write:
* <code>
* $application =& kApplication::Instance();
* </code>
* or in an object:
* <code>
* $this->Application =& kApplication::Instance();
* </code>
* to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
* To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
* BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
* create and return default KernelApplication instance.
*
* Pattern: Singleton
*
* @static
* @return kApplication
* @access public
*/
public static function &Instance()
{
static $instance = false;
if ( !$instance ) {
$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
$instance = new $class();
}
return $instance;
}
/**
* Initializes the Application
*
* @param string $factory_class
* @return bool Was Init actually made now or before
* @access public
* @see kHTTPQuery
* @see Session
* @see TemplatesCache
*/
public function Init($factory_class = 'kFactory')
{
if ( $this->InitDone ) {
return false;
}
if ( preg_match('/utf-8/i', CHARSET) ) {
setlocale(LC_ALL, 'en_US.UTF-8');
mb_internal_encoding('UTF-8');
}
$this->isAdmin = kUtil::constOn('ADMIN');
if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
ob_start(); // collect any output from method (other then tags) into buffer
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Init:');
}
$this->_logger = new kLogger($this->_logger);
$this->Factory = new $factory_class();
$this->registerDefaultClasses();
$vars = kUtil::parseConfig(true);
$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection');
$this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array ($this->_logger, 'handleSQLError')));
$this->Conn->setup($vars);
$this->cacheManager = $this->makeClass('kCacheManager');
$this->cacheManager->InitCache();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Before UnitConfigReader');
}
// init config reader and all managers
$this->UnitConfigReader = $this->makeClass('kUnitConfigReader');
$this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read
$this->registerModuleConstants();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('After UnitConfigReader');
}
define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
// start processing request
$this->HttpQuery = $this->recallObject('HTTPQuery');
$this->HttpQuery->process();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
}
$this->Session = $this->recallObject('Session');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed Session');
}
$this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
}
$this->cacheManager->LoadApplicationCache();
$site_timezone = $this->ConfigValue('Config_Site_Time');
if ( $site_timezone ) {
putenv('TZ=' . $site_timezone);
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Loaded cache and phrases');
}
$this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
$this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed AfterConfigRead');
}
if ( $this->GetVar('m_cat_id') === false ) {
$this->SetVar('m_cat_id', 0);
}
if ( !$this->RecallVar('curr_iso') ) {
$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
}
$visit_id = $this->RecallVar('visit_id');
if ( $visit_id !== false ) {
$this->SetVar('visits_id', $visit_id);
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->profileFinish('kernel4_startup');
}
$this->InitDone = true;
$this->HandleEvent(new kEvent('adm:OnStartup'));
return true;
}
/**
* Performs initialization of manager classes, that can be overridden from unit configs
*
* @return void
* @access public
* @throws Exception
*/
public function InitManagers()
{
if ( $this->InitDone ) {
throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
return;
}
$this->UrlManager = $this->makeClass('kUrlManager');
$this->EventManager = $this->makeClass('EventManager');
$this->Phrases = $this->makeClass('kPhraseCache');
$this->RegisterDefaultBuildEvents();
}
/**
* Returns module information. Searches module by requested field
*
* @param string $field
* @param mixed $value
* @param string $return_field field value to returns, if not specified, then return all fields
* @return Array
*/
public function findModule($field, $value, $return_field = null)
{
$found = $module_info = false;
foreach ($this->ModuleInfo as $module_info) {
if ( strtolower($module_info[$field]) == strtolower($value) ) {
$found = true;
break;
}
}
if ( $found ) {
return isset($return_field) ? $module_info[$return_field] : $module_info;
}
return false;
}
/**
* Refreshes information about loaded modules
*
* @return void
* @access public
*/
public function refreshModuleInfo()
{
if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) {
$this->registerModuleConstants();
return;
}
// use makeClass over recallObject, since used before kApplication initialization during installation
$modules_helper = $this->makeClass('ModulesHelper');
/* @var $modules_helper kModulesHelper */
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Modules
WHERE ' . $modules_helper->getWhereClause() . '
ORDER BY LoadOrder';
$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
$this->registerModuleConstants();
}
/**
* Checks if passed language id if valid and sets it to primary otherwise
*
* @return void
* @access public
*/
public 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');
/* @var $lang kDBItem */
if ( !$lang->isLoaded() || (!$this->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
*
* @return void
* @access public
*/
public function VerifyThemeId()
{
if ( $this->isAdmin ) {
kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
return;
}
$path = $this->GetFrontThemePath();
if ( $path === false ) {
$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
}
kUtil::safeDefine('THEMES_PATH', $path);
}
/**
* Returns relative path to current front-end theme
*
* @param bool $force
* @return string
* @access public
*/
public function GetFrontThemePath($force = false)
{
static $path = null;
if ( !$force && isset($path) ) {
return $path;
}
$theme_id = $this->GetVar('m_theme');
if ( !$theme_id ) {
$theme_id = 'default'; // $this->GetDefaultThemeId(1); // 1 to force front-end mode!
}
$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');
/* @var $theme ThemeItem */
if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) {
return false;
}
// assign & then return, since it's static variable
$path = '/themes/' . $theme->GetDBField('Name');
return $path;
}
/**
* Returns primary front/admin language id
*
* @param bool $init
* @return int
* @access public
*/
public function GetDefaultLanguageId($init = false)
{
$cache_key = 'primary_language_info[%LangSerial%]';
$language_info = $this->getCache($cache_key);
if ( $language_info === false ) {
// cache primary language info first
$table = $this->getUnitOption('lang', 'TableName');
$id_field = $this->getUnitOption('lang', 'IDField');
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
FROM ' . $table . '
WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
if ( $language_info !== false ) {
$this->setCache($cache_key, $language_info);
}
}
$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) {
// get from cache
return $language_info[$language_key];
}
$language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false;
if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) {
$language_id = 1;
}
return $language_id;
}
/**
* Returns front-end primary theme id (even, when called from admin console)
*
* @param bool $force_front
* @return int
* @access public
*/
public function GetDefaultThemeId($force_front = false)
{
static $theme_id = 0;
if ( $theme_id > 0 ) {
return $theme_id;
}
if ( kUtil::constOn('DBG_FORCE_THEME') ) {
$theme_id = DBG_FORCE_THEME;
}
elseif ( !$force_front && $this->isAdmin ) {
$theme_id = 999;
}
else {
$cache_key = 'primary_theme[%ThemeSerial%]';
$theme_id = $this->getCache($cache_key);
if ( $theme_id === false ) {
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
FROM ' . $this->getUnitOption('theme', 'TableName') . '
WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
$theme_id = $this->Conn->GetOne($sql);
if ( $theme_id !== false ) {
$this->setCache($cache_key, $theme_id);
}
}
}
return $theme_id;
}
/**
* Returns site primary currency ISO code
*
* @return string
* @access public
* @todo Move into In-Commerce
*/
public function GetPrimaryCurrency()
{
$cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId');
$currency_iso = $this->getCache($cache_key);
if ( $currency_iso === false ) {
if ( $this->isModuleEnabled('In-Commerce') ) {
$this->Conn->nextQueryCachable = true;
$currency_id = $this->siteDomainField('PrimaryCurrencyId');
$sql = 'SELECT ISO
FROM ' . $this->getUnitOption('curr', 'TableName') . '
WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1');
$currency_iso = $this->Conn->GetOne($sql);
}
else {
$currency_iso = 'USD';
}
$this->setCache($cache_key, $currency_iso);
}
return $currency_iso;
}
/**
* Returns site domain field. When none of site domains are found false is returned.
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return mixed
* @todo Move into separate module
*/
public function siteDomainField($field, $formatted = false, $format = null)
{
if ( $this->isAdmin ) {
// don't apply any filtering in administrative console
return false;
}
if ( !$this->siteDomain ) {
$this->siteDomain = $this->recallObject('site-domain.current');
/* @var $site_domain kDBItem */
}
if ( $this->siteDomain->isLoaded() ) {
return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field);
}
return false;
}
/**
* Registers default classes such as kDBEventHandler, kUrlManager
*
* Called automatically while initializing kApplication
*
* @return void
* @access public
*/
public function RegisterDefaultClasses()
{
$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
$this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php');
$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php');
$this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php');
$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php');
$this->registerClass('kSubscriptionManager', KERNEL_PATH . '/managers/subscription_manager.php');
$this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php');
$this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php');
$this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php');
$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php');
$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php');
$this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache');
$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
$this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php');
$this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php');
$this->registerClass('kLogger', KERNEL_PATH . '/utility/logger.php');
$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
// Params class descendants
$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery');
// session
$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage');
// template parser
$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor');
$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php');
$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php');
// database
$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php');
$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
// email sending
$this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php');
$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender');
$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
// do not move to config - this helper is used before configs are read
$this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper');
}
/**
* Registers default build events
*
* @return void
* @access protected
*/
protected function RegisterDefaultBuildEvents()
{
$this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
}
/**
* Returns cached category information by given cache name. All given category
* information is recached, when at least one of 4 caches is missing.
*
* @param int $category_id
* @param string $name cache name = {filenames, category_designs, category_tree}
* @return string
* @access public
*/
public function getCategoryCache($category_id, $name)
{
return $this->cacheManager->getCategoryCache($category_id, $name);
}
/**
* Returns caching type (none, memory, temporary)
*
* @param int $caching_type
* @return bool
* @access public
*/
public function isCachingType($caching_type)
{
return $this->cacheManager->isCachingType($caching_type);
}
/**
* Increments serial based on prefix and it's ID (optional)
*
* @param string $prefix
* @param int $id ID (value of IDField) or ForeignKeyField:ID
* @param bool $increment
* @return string
* @access public
*/
public function incrementCacheSerial($prefix, $id = null, $increment = true)
{
return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
}
/**
* Returns cached $key value from cache named $cache_name
*
* @param int $key key name from cache
* @param bool $store_locally store data locally after retrieved
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds);
}
/**
* Stores new $value in cache with $key name
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function setCache($key, $value, $expiration = 0)
{
return $this->cacheManager->setCache($key, $value, $expiration);
}
/**
* Stores new $value in cache with $key name (only if it's not there)
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function addCache($key, $value, $expiration = 0)
{
return $this->cacheManager->addCache($key, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from cache
*
* @param string $key
* @return void
* @access public
*/
public function deleteCache($key)
{
$this->cacheManager->deleteCache($key);
}
/**
* Reset's all memory cache at once
*
* @return void
* @access public
*/
public function resetCache()
{
$this->cacheManager->resetCache();
}
/**
* Returns value from database cache
*
* @param string $name key name
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getDBCache($name, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getDBCache($name, $max_rebuild_seconds);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @return void
* @access public
*/
public function setDBCache($name, $value, $expiration = false)
{
$this->cacheManager->setDBCache($name, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from database cache
*
* @param string $name
* @return void
* @access public
*/
public function deleteDBCache($name)
{
$this->cacheManager->deleteDBCache($name);
}
/**
* Registers each module specific constants if any found
*
* @return bool
* @access protected
*/
protected function registerModuleConstants()
{
if ( file_exists(KERNEL_PATH . '/constants.php') ) {
kUtil::includeOnce(KERNEL_PATH . '/constants.php');
}
if ( !$this->ModuleInfo ) {
return false;
}
foreach ($this->ModuleInfo as $module_info) {
$constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
if ( file_exists($constants_file) ) {
kUtil::includeOnce($constants_file);
}
}
return true;
}
/**
* Performs redirect to hard maintenance template
*
* @return void
* @access public
*/
public function redirectToMaintenance()
{
$maintenance_page = WRITEBALE_BASE . '/maintenance.html';
$query_string = ''; // $this->isAdmin ? '' : '?next_template=' . urlencode($_SERVER['REQUEST_URI']);
if ( file_exists(FULL_PATH . $maintenance_page) ) {
header('Location: ' . BASE_PATH . $maintenance_page . $query_string);
exit;
}
}
/**
* Actually runs the parser against current template and stores parsing result
*
* This method gets 't' variable passed to the script, loads the template given in 't' variable and
* parses it. The result is store in {@link $this->HTML} property.
*
* @return void
* @access public
*/
public function Run()
{
// process maintenance mode redirect: begin
$maintenance_mode = $this->getMaintenanceMode();
if ( $maintenance_mode == MaintenanceMode::HARD ) {
$this->redirectToMaintenance();
}
elseif ( $maintenance_mode == MaintenanceMode::SOFT ) {
$maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate');
if ( $this->GetVar('t') != $maintenance_template ) {
$redirect_params = Array ();
if ( !$this->isAdmin ) {
$redirect_params['next_template'] = urlencode($_SERVER['REQUEST_URI']);
}
$this->Redirect($maintenance_template, $redirect_params);
}
}
// process maintenance mode redirect: end
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Run:');
}
if ( $this->isAdminUser ) {
// for permission checking in events & templates
$this->LinkVar('module'); // for common configuration templates
$this->LinkVar('module_key'); // for common search templates
$this->LinkVar('section'); // for common configuration templates
if ( $this->GetVar('m_opener') == 'p' ) {
$this->LinkVar('main_prefix'); // window prefix, that opened selector
$this->LinkVar('dst_field'); // field to set value choosed in selector
}
if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) {
// hide debug output from ajax requests automatically
kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
}
}
elseif ( $this->GetVar('admin') ) {
$admin_session = $this->recallObject('Session.admin');
/* @var $admin_session Session */
// store Admin Console User's ID to Front-End's session for cross-session permission checks
$this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
// user can edit cms blocks (when viewing front-end through admin's frame)
$editing_mode = $this->GetVar('editing_mode');
define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
}
}
kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything
$this->Phrases->setPhraseEditing();
$this->EventManager->ProcessRequest();
$this->InitParser();
$t = $this->GetVar('render_template', $this->GetVar('t'));
if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) {
$cms_handler = $this->recallObject('st_EventHandler');
/* @var $cms_handler CategoriesEventHandler */
$t = ltrim($cms_handler->GetDesignTemplate(), '/');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
}
}
/*else {
$cms_handler->SetCatByTemplate();
}*/
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Parsing:');
}
$this->HTML = $this->Parser->Run($t);
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application after Parsing:');
}
}
/**
* Only renders template
*
* @see kDBEventHandler::_errorNotFound()
*/
public function QuickRun()
{
$this->InitParser();
$this->HTML = $this->ParseBlock(Array ('name' => $this->GetVar('t')));
}
/**
* Performs template parser/cache initialization
*
* @param bool|string $theme_name
* @return void
* @access public
*/
public function InitParser($theme_name = false)
{
if ( !is_object($this->Parser) ) {
$this->Parser = $this->recallObject('NParser');
$this->TemplatesCache = $this->recallObject('TemplatesCache');
}
$this->TemplatesCache->forceThemeName = $theme_name;
}
/**
* Send the parser results to browser
*
* Actually send everything stored in {@link $this->HTML}, to the browser by echoing it.
*
* @return void
* @access public
*/
public function Done()
{
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
$debug_mode = defined('DEBUG_MODE') && $this->isDebugMode();
if ( $debug_mode ) {
if ( kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Done:');
}
$this->Session->SaveData(); // adds session data to debugger report
$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
}
else {
// send "Set-Cookie" header before any output is made
$this->Session->SetSession();
$this->HTML = ob_get_clean() . $this->HTML;
}
$this->_outputPage();
$this->cacheManager->UpdateApplicationCache();
if ( !$debug_mode ) {
$this->Session->SaveData();
}
$this->EventManager->runScheduledTasks();
if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) {
$this->_storeStatistics();
}
}
/**
* Outputs generated page content to end-user
*
* @return void
* @access protected
*/
protected function _outputPage()
{
$this->setContentType();
ob_start();
if ( $this->UseOutputCompression() ) {
$compression_level = $this->ConfigValue('OutputCompressionLevel');
if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) {
$compression_level = 7;
}
header('Content-Encoding: gzip');
echo gzencode($this->HTML, $compression_level);
}
else {
// when gzip compression not used connection won't be closed early!
echo $this->HTML;
}
// send headers to tell the browser to close the connection
header('Content-Length: ' . ob_get_length());
header('Connection: close');
// flush all output
ob_end_flush();
if ( ob_get_level() ) {
ob_flush();
}
flush();
// close current session
if ( session_id() ) {
session_write_close();
}
}
/**
* Stores script execution statistics to database
*
* @return void
* @access protected
*/
protected function _storeStatistics()
{
global $start;
$script_time = microtime(true) - $start;
$query_statistics = $this->Conn->getQueryStatistics(); // time & count
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'StatisticsCapture
WHERE TemplateName = ' . $this->Conn->qstr($this->GetVar('t'));
$data = $this->Conn->GetRow($sql);
if ( $data ) {
$this->_updateAverageStatistics($data, 'ScriptTime', $script_time);
$this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']);
$this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']);
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']);
}
else {
$data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time;
$data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time'];
$data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count'];
$data['TemplateName'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture');
}
}
/**
* Calculates average time for statistics
*
* @param Array $data
* @param string $field_prefix
* @param float $current_value
* @return void
* @access protected
*/
protected function _updateAverageStatistics(&$data, $field_prefix, $current_value)
{
$data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1);
if ( $current_value < $data[$field_prefix . 'Min'] ) {
$data[$field_prefix . 'Min'] = $current_value;
}
if ( $current_value > $data[$field_prefix . 'Max'] ) {
$data[$field_prefix . 'Max'] = $current_value;
}
}
/**
* Remembers slow query SQL and execution time into log
*
* @param string $slow_sql
* @param int $time
* @return void
* @access public
*/
public function logSlowQuery($slow_sql, $time)
{
$query_crc = kUtil::crc32($slow_sql);
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'SlowSqlCapture
WHERE QueryCrc = ' . $query_crc;
$data = $this->Conn->Query($sql, null, true);
if ( $data ) {
$this->_updateAverageStatistics($data, 'Time', $time);
$template_names = explode(',', $data['TemplateNames']);
array_push($template_names, $this->GetVar('t'));
$data['TemplateNames'] = implode(',', array_unique($template_names));
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']);
}
else {
$data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time;
$data['SqlQuery'] = $slow_sql;
$data['QueryCrc'] = $query_crc;
$data['TemplateNames'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture');
}
}
/**
* Checks if output compression options is available
*
* @return bool
* @access protected
*/
protected function UseOutputCompression()
{
if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
return false;
}
$accept_encoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($accept_encoding, 'gzip');
}
// Facade
/**
* Returns current session id (SID)
*
* @return int
* @access public
*/
public function GetSID()
{
$session = $this->recallObject('Session');
/* @var $session Session */
return $session->GetID();
}
/**
* Destroys current session
*
* @return void
* @access public
* @see UserHelper::logoutUser()
*/
public function DestroySession()
{
$session = $this->recallObject('Session');
/* @var $session Session */
$session->Destroy();
}
/**
* Returns variable passed to the script as GET/POST/COOKIE
*
* @param string $name Name of variable to retrieve
* @param mixed $default default value returned in case if variable not present
* @return mixed
* @access public
*/
public function GetVar($name, $default = false)
{
return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default;
}
/**
* Returns variable passed to the script as $type
*
* @param string $name Name of variable to retrieve
* @param string $type Get/Post/Cookie
* @param mixed $default default value returned in case if variable not present
* @return mixed
* @access public
*/
public function GetVarDirect($name, $type, $default = false)
{
// $type = ucfirst($type);
$array = $this->HttpQuery->$type;
return isset($array[$name]) ? $array[$name] : $default;
}
/**
* Returns ALL variables passed to the script as GET/POST/COOKIE
*
* @return Array
* @access public
* @deprecated
*/
public function GetVars()
{
return $this->HttpQuery->GetParams();
}
/**
* Set the variable 'as it was passed to the script through GET/POST/COOKIE'
*
* This could be useful to set the variable when you know that
* other objects would relay on variable passed from GET/POST/COOKIE
* or you could use SetVar() / GetVar() pairs to pass the values between different objects.<br>
*
* @param string $var Variable name to set
* @param mixed $val Variable value
* @return void
* @access public
*/
public function SetVar($var,$val)
{
$this->HttpQuery->Set($var, $val);
}
/**
* Deletes kHTTPQuery variable
*
* @param string $var
* @return void
* @todo Think about method name
*/
public function DeleteVar($var)
{
$this->HttpQuery->Remove($var);
}
/**
* Deletes Session variable
*
* @param string $var
* @return void
* @access public
*/
public function RemoveVar($var)
{
$this->Session->RemoveVar($var);
}
/**
* Removes variable from persistent session
*
* @param string $var
* @return void
* @access public
*/
public function RemovePersistentVar($var)
{
$this->Session->RemovePersistentVar($var);
}
/**
* Restores Session variable to it's db version
*
* @param string $var
* @return void
* @access public
*/
public function RestoreVar($var)
{
$this->Session->RestoreVar($var);
}
/**
* Returns session variable value
*
* Return value of $var variable stored in Session. An optional default value could be passed as second parameter.
*
* @param string $var Variable name
* @param mixed $default Default value to return if no $var variable found in session
* @return mixed
* @access public
* @see Session::RecallVar()
*/
public function RecallVar($var,$default=false)
{
return $this->Session->RecallVar($var,$default);
}
/**
* Returns variable value from persistent session
*
* @param string $var
* @param mixed $default
* @return mixed
* @access public
* @see Session::RecallPersistentVar()
*/
public function RecallPersistentVar($var, $default = false)
{
return $this->Session->RecallPersistentVar($var, $default);
}
/**
* Stores variable $val in session under name $var
*
* Use this method to store variable in session. Later this variable could be recalled.
*
* @param string $var Variable name
* @param mixed $val Variable value
* @param bool $optional
* @return void
* @access public
* @see kApplication::RecallVar()
*/
public function StoreVar($var, $val, $optional = false)
{
$session = $this->recallObject('Session');
/* @var $session Session */
$this->Session->StoreVar($var, $val, $optional);
}
/**
* Stores variable to persistent session
*
* @param string $var
* @param mixed $val
* @param bool $optional
* @return void
* @access public
*/
public function StorePersistentVar($var, $val, $optional = false)
{
$this->Session->StorePersistentVar($var, $val, $optional);
}
/**
* Stores default value for session variable
*
* @param string $var
* @param string $val
* @param bool $optional
* @return void
* @access public
* @see Session::RecallVar()
* @see Session::StoreVar()
*/
public function StoreVarDefault($var, $val, $optional = false)
{
$session = $this->recallObject('Session');
/* @var $session Session */
$this->Session->StoreVarDefault($var, $val, $optional);
}
/**
* Links HTTP Query variable with session variable
*
* If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session.
* This method could be used for making sure that GetVar will return query or session value for given
* variable, when query variable should overwrite session (and be stored there for later use).<br>
* This could be used for passing item's ID into popup with multiple tab -
* in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id').
* After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session
*
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
* @param bool $optional
* @return void
* @access public
*/
public function LinkVar($var, $ses_var = null, $default = '', $optional = false)
{
if ( !isset($ses_var) ) {
$ses_var = $var;
}
if ( $this->GetVar($var) !== false ) {
$this->StoreVar($ses_var, $this->GetVar($var), $optional);
}
else {
$this->SetVar($var, $this->RecallVar($ses_var, $default));
}
}
/**
* Returns variable from HTTP Query, or from session if not passed in HTTP Query
*
* The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed.
* Returns the default value if variable does not exist in session and was not passed in HTTP Query
*
* @param string $var HTTP Query (GPC) variable name
* @param mixed $ses_var Session variable name
* @param mixed $default Default variable value
* @return mixed
* @access public
* @see LinkVar
*/
public function GetLinkedVar($var, $ses_var = null, $default = '')
{
$this->LinkVar($var, $ses_var, $default);
return $this->GetVar($var);
}
/**
* Renders given tag and returns it's output
*
* @param string $prefix
* @param string $tag
* @param Array $params
* @return mixed
* @access public
* @see kApplication::InitParser()
*/
public function ProcessParsedTag($prefix, $tag, $params)
{
$processor = $this->Parser->GetProcessor($prefix);
/* @var $processor kDBTagProcessor */
return $processor->ProcessParsedTag($tag, $params, $prefix);
}
/**
* Return ADODB Connection object
*
* Returns ADODB Connection object already connected to the project database, configurable in config.php
*
* @return kDBConnection
* @access public
*/
public function &GetADODBConnection()
{
return $this->Conn;
}
/**
* Allows to parse given block name or include template
*
* @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
* @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter.
* @param bool $as_template
* @return string
* @access public
*/
public function ParseBlock($params, $pass_params = false, $as_template = false)
{
if ( substr($params['name'], 0, 5) == 'html:' ) {
return substr($params['name'], 5);
}
return $this->Parser->ParseBlock($params, $pass_params, $as_template);
}
/**
* Checks, that we have given block defined
*
* @param string $name
* @return bool
* @access public
*/
public function ParserBlockFound($name)
{
return $this->Parser->blockFound($name);
}
/**
* Allows to include template with a given name and given parameters
*
* @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
* @return string
* @access public
*/
public function IncludeTemplate($params)
{
return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0);
}
/**
* Return href for template
*
* @param string $t Template path
* @param string $prefix index.php prefix - could be blank, 'admin'
* @param Array $params
* @param string $index_file
* @return string
*/
public function HREF($t, $prefix = '', $params = Array (), $index_file = null)
{
return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
}
/**
* Returns theme template filename and it's corresponding page_id based on given seo template
*
* @param string $seo_template
* @return string
* @access public
*/
public function getPhysicalTemplate($seo_template)
{
return $this->UrlManager->getPhysicalTemplate($seo_template);
}
/**
* Returns template name, that corresponds with given virtual (not physical) page id
*
* @param int $page_id
* @return string|bool
* @access public
*/
public function getVirtualPageTemplate($page_id)
{
return $this->UrlManager->getVirtualPageTemplate($page_id);
}
/**
* Returns section template for given physical/virtual template
*
* @param string $template
* @param int $theme_id
* @return string
* @access public
*/
public function getSectionTemplate($template, $theme_id = null)
{
return $this->UrlManager->getSectionTemplate($template, $theme_id);
}
/**
* Returns variables with values that should be passed through with this link + variable list
*
* @param Array $params
* @return Array
* @access public
*/
public function getPassThroughVariables(&$params)
{
return $this->UrlManager->getPassThroughVariables($params);
}
/**
* Builds url
*
* @param string $t
* @param Array $params
* @param string $pass
* @param bool $pass_events
* @param bool $env_var
* @return string
* @access public
*/
public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true)
{
return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var);
}
/**
* Process QueryString only, create
* events, ids, based on config
* set template name and sid in
* desired application variables.
*
* @param string $env_var environment string value
* @param string $pass_name
* @return Array
* @access public
*/
public function processQueryString($env_var, $pass_name = 'passed')
{
return $this->UrlManager->plain->parse($env_var, $pass_name);
}
/**
* Parses rewrite url and returns parsed variables
*
* @param string $url
* @param string $pass_name
* @return Array
* @access public
*/
public function parseRewriteUrl($url, $pass_name = 'passed')
{
return $this->UrlManager->rewrite->parse($url, $pass_name);
}
/**
* Returns base part of all urls, build on website
*
* @param string $prefix
* @param bool $ssl
* @param bool $add_port
* @return string
* @access public
*/
public function BaseURL($prefix = '', $ssl = null, $add_port = true)
{
if ( $ssl === null ) {
// stay on same encryption level
return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
if ( $ssl ) {
// going from http:// to https://
$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
if ( !$base_url ) {
$ssl_url = $this->siteDomainField('SSLUrl');
$base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL');
}
return rtrim($base_url, '/') . $prefix . '/';
}
// going from https:// to http://
$domain = $this->siteDomainField('DomainName');
if ( $domain === false ) {
$domain = DOMAIN;
}
return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
/**
* Redirects user to url, that's build based on given parameters
*
* @param string $t
* @param Array $params
* @param string $prefix
* @param string $index_file
* @return void
* @access public
*/
public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
{
$js_redirect = getArrayValue($params, 'js_redirect');
if ( $t == '' || $t === true ) {
$t = $this->GetVar('t');
}
// pass prefixes and special from previous url
if ( array_key_exists('js_redirect', $params) ) {
unset($params['js_redirect']);
}
// allows to send custom responce code along with redirect header
if ( array_key_exists('response_code', $params) ) {
$response_code = (int)$params['response_code'];
unset($params['response_code']);
}
else {
$response_code = 302; // Found
}
if ( !array_key_exists('pass', $params) ) {
$params['pass'] = 'all';
}
if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) {
// redirects to the same template as current
$params['ajax'] = 'yes';
}
$params['__URLENCODE__'] = 1;
$location = $this->HREF($t, $prefix, $params, $index_file);
if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) {
$this->Debugger->appendTrace();
echo '<strong>Debug output above !!!</strong><br/>' . "\n";
if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
echo 'Referer: <strong>' . $_SERVER['HTTP_REFERER'] . '</strong><br/>' . "\n";
}
echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n";
}
else {
if ( $js_redirect ) {
// show "redirect" template instead of redirecting,
// because "Set-Cookie" header won't work, when "Location"
// header is used later
$this->SetVar('t', 'redirect');
$this->SetVar('redirect_to', $location);
// make all additional parameters available on "redirect" template too
foreach ($params as $name => $value) {
$this->SetVar($name, $value);
}
return;
}
else {
if ( $this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t') ) {
// redirection to other then current template during ajax request
kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
echo '#redirect#' . $location;
}
elseif ( headers_sent() != '' ) {
// some output occurred -> redirect using javascript
echo '<script type="text/javascript">window.location.href = \'' . $location . '\';</script>';
}
else {
// no output before -> redirect using HTTP header
// header('HTTP/1.1 302 Found');
header('Location: ' . $location, true, $response_code);
}
}
}
// session expiration is called from session initialization,
// that's why $this->Session may be not defined here
$session = $this->recallObject('Session');
/* @var $session Session */
if ( $this->InitDone ) {
// if redirect happened in the middle of application initialization don't call event,
// that presumes that application was successfully initialized
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
}
$session->SaveData();
ob_end_flush();
exit;
}
/**
* Returns translation of given label
*
* @param string $label
* @param bool $allow_editing return translation link, when translation is missing on current language
* @param bool $use_admin use current Admin Console language to translate phrase
* @return string
* @access public
*/
public function Phrase($label, $allow_editing = true, $use_admin = false)
{
return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin);
}
/**
* Replace language tags in exclamation marks found in text
*
* @param string $text
* @param bool $force_escape force escaping, not escaping of resulting string
* @return string
* @access public
*/
public function ReplaceLanguageTags($text, $force_escape = null)
{
return $this->Phrases->ReplaceLanguageTags($text, $force_escape);
}
/**
* Checks if user is logged in, and creates
* user object if so. User object can be recalled
* later using "u.current" prefix_special. Also you may
* get user id by getting "u.current_id" variable.
*
* @return void
* @access protected
*/
protected function ValidateLogin()
{
$session = $this->recallObject('Session');
/* @var $session Session */
$user_id = $session->GetField('PortalUserId');
if ( !$user_id && $user_id != USER_ROOT ) {
$user_id = USER_GUEST;
}
$this->SetVar('u.current_id', $user_id);
if ( !$this->isAdmin ) {
// needed for "profile edit", "registration" forms ON FRONT ONLY
$this->SetVar('u_id', $user_id);
}
$this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional
$this->isAdminUser = $this->isAdmin && $this->LoggedIn();
if ( $this->GetVar('expired') == 1 ) {
// this parameter is set only from admin
$user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login'));
/* @var $user UsersItem */
$user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired');
}
if ( ($user_id != USER_GUEST) && defined('DBG_REQUREST_LOG') && DBG_REQUREST_LOG ) {
$this->HttpQuery->writeRequestLog(DBG_REQUREST_LOG);
}
if ( $user_id != USER_GUEST ) {
// normal users + root
$this->LoadPersistentVars();
}
$user_timezone = $this->Session->GetField('TimeZone');
if ( $user_timezone ) {
putenv('TZ=' . $user_timezone);
}
}
/**
* Loads current user persistent session data
*
* @return void
* @access public
*/
public function LoadPersistentVars()
{
$this->Session->LoadPersistentVars();
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
* @access public
*/
public function ConfigValue($name)
{
return $this->cacheManager->ConfigValue($name);
}
/**
* Changes value of individual configuration variable (+resets cache, when needed)
*
* @param string $name
* @param string $value
* @param bool $local_cache_only
* @return string
* @access public
*/
public function SetConfigValue($name, $value, $local_cache_only = false)
{
return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @param Array $params
* @param Array $specific_params
* @return void
* @access public
*/
public function HandleEvent($event, $params = null, $specific_params = null)
{
if ( isset($params) ) {
$event = new kEvent($params, $specific_params);
}
$this->EventManager->HandleEvent($event);
}
/**
* Notifies event subscribers, that event has occured
*
* @param kEvent $event
* @return void
*/
public function notifyEventSubscribers(kEvent $event)
{
$this->EventManager->notifySubscribers($event);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function eventImplemented(kEvent $event)
{
return $this->EventManager->eventImplemented($event);
}
/**
* Registers new class in the factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $file Filename in what $real_class is declared
* @param string $pseudo_class Name under this class object will be accessed using getObject method
* @return void
* @access public
*/
public function registerClass($real_class, $file, $pseudo_class = null)
{
$this->Factory->registerClass($real_class, $file, $pseudo_class);
}
/**
* Unregisters existing class from factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $pseudo_class Name under this class object is accessed using getObject method
* @return void
* @access public
*/
public function unregisterClass($real_class, $pseudo_class = null)
{
$this->Factory->unregisterClass($real_class, $pseudo_class);
}
/**
* Add new scheduled task
*
* @param string $short_name name to be used to store last maintenance run info
* @param string $event_string
* @param int $run_schedule run schedule like for Cron
* @param int $status
* @access public
*/
public function registerScheduledTask($short_name, $event_string, $run_schedule, $status = STATUS_ACTIVE)
{
$this->EventManager->registerScheduledTask($short_name, $event_string, $run_schedule, $status);
}
/**
* Registers Hook from subprefix event to master prefix event
*
* Pattern: Observer
*
* @param string $hook_event
* @param string $do_event
* @param int $mode
* @param bool $conditional
* @access public
*/
public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false)
{
$this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional);
}
/**
* Registers build event for given pseudo class
*
* @param string $pseudo_class
* @param string $event_name
* @access public
*/
public function registerBuildEvent($pseudo_class, $event_name)
{
$this->EventManager->registerBuildEvent($pseudo_class, $event_name);
}
/**
* Allows one TagProcessor tag act as other TagProcessor tag
*
* @param Array $tag_info
* @return void
* @access public
*/
public function registerAggregateTag($tag_info)
{
$aggregator = $this->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
$tag_data = Array (
$tag_info['LocalPrefix'],
$tag_info['LocalTagName'],
getArrayValue($tag_info, 'LocalSpecial')
);
$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data);
}
/**
* Returns object using params specified, creates it if is required
*
* @param string $name
* @param string $pseudo_class
* @param Array $event_params
* @param Array $arguments
* @return kBase
*/
public function recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
{
/*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) {
// first time, when object with "_prefix_here_" prefix is accessed
$this->Debugger->appendTrace();
}*/
return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
}
/**
* Returns tag processor for prefix specified
*
* @param string $prefix
* @return kDBTagProcessor
* @access public
*/
public function recallTagProcessor($prefix)
{
$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
return $this->recallObject($prefix . '_TagProcessor');
}
/**
* Checks if object with prefix passes was already created in factory
*
* @param string $name object pseudo_class, prefix
* @return bool
* @access public
*/
public function hasObject($name)
{
return $this->Factory->hasObject($name);
}
/**
* Removes object from storage by given name
*
* @param string $name Object's name in the Storage
* @return void
* @access public
*/
public function removeObject($name)
{
$this->Factory->DestroyObject($name);
}
/**
* Get's real class name for pseudo class, includes class file and creates class instance
*
* Pattern: Factory Method
*
* @param string $pseudo_class
* @param Array $arguments
* @return kBase
* @access public
*/
public function makeClass($pseudo_class, $arguments = Array ())
{
return $this->Factory->makeClass($pseudo_class, $arguments);
}
/**
* Checks if application is in debug mode
*
* @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
* @return bool
* @author Alex
* @access public
*/
public function isDebugMode($check_debugger = true)
{
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
if ($check_debugger) {
$debug_mode = $debug_mode && is_object($this->Debugger);
}
return $debug_mode;
}
/**
* Apply url rewriting used by mod_rewrite or not
*
* @param bool|null $ssl Force ssl link to be build
* @return bool
* @access public
*/
public function RewriteURLs($ssl = false)
{
// case #1,#4:
// we want to create https link from http mode
// we want to create https link from https mode
// conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
// case #2,#3:
// we want to create http link from https mode
// we want to create http link from http mode
// conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
$allow_rewriting =
(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
|| // or allow rewriting for redirect TO httpS or when already in httpS
(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
return kUtil::constOn('MOD_REWRITE') && $allow_rewriting;
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $option
* @param mixed $default
* @return string
* @access public
*/
public function getUnitOption($prefix, $option, $default = false)
{
return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $option
* @param string $value
* @access public
*/
public function setUnitOption($prefix, $option, $value)
{
$this->UnitConfigReader->setUnitOption($prefix,$option,$value);
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
*/
public function getUnitOptions($prefix)
{
return $this->UnitConfigReader->getUnitOptions($prefix);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
public function prefixRegistred($prefix)
{
return $this->UnitConfigReader->prefixRegistred($prefix);
}
/**
* Splits any mixing of prefix and
* special into correct ones
*
* @param string $prefix_special
* @return Array
* @access public
*/
public function processPrefix($prefix_special)
{
return $this->Factory->processPrefix($prefix_special);
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @return void
* @access public
*/
public function setEvent($prefix_special, $event_name)
{
$this->EventManager->setEvent($prefix_special, $event_name);
}
/**
* SQL Error Handler
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
* @throws Exception
* @deprecated
*/
public function handleSQLError($code, $msg, $sql)
{
return $this->_logger->handleSQLError($code, $msg, $sql);
}
/**
* Returns & blocks next ResourceId available in system
*
* @return int
* @access public
*/
public function NextResourceId()
{
$table_name = TABLE_PREFIX . 'IdGenerator';
$this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE');
$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
$id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
if ( $id === false ) {
$this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)');
$id = 2;
}
$this->Conn->Query('UNLOCK TABLES');
return $id - 1;
}
/**
* Returns genealogical main prefix for sub-table prefix passes
* OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix
*
* @param string $current_prefix
* @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
* @return string
* @access public
*/
public function GetTopmostPrefix($current_prefix, $real_top = false)
{
// 1. get genealogical tree of $current_prefix
$prefixes = Array ($current_prefix);
while ($parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix')) {
if ( !$this->prefixRegistred($parent_prefix) ) {
// stop searching, when parent prefix is not registered
break;
}
$current_prefix = $parent_prefix;
array_unshift($prefixes, $current_prefix);
}
if ( $real_top ) {
return $current_prefix;
}
// 2. find what if parent is passed
$passed = explode(',', $this->GetVar('all_passed'));
foreach ($prefixes as $a_prefix) {
if ( in_array($a_prefix, $passed) ) {
return $a_prefix;
}
}
return $current_prefix;
}
/**
* Triggers email event of type Admin
*
* @param string $email_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 kEvent
* @access public
*/
public function EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
{
return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_ADMIN, $to_user_id, $send_params);
}
/**
* 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 kEvent
* @access public
*/
public function EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
{
return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_FRONTEND, $to_user_id, $send_params);
}
/**
* 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 kEvent
* @access protected
*/
protected function _emailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
{
$email = $this->makeClass('kEmail');
/* @var $email kEmail */
if ( !$email->findEvent($email_event_name, $email_event_type) ) {
return false;
}
$email->setParams($send_params);
return $email->send($to_user_id);
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
* @access public
*/
public function LoggedIn()
{
// no session during expiration process
return is_null($this->Session) ? false : $this->Session->LoggedIn();
}
/**
* Check current user permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckPermission($name, $type = 1, $cat_id = null)
{
$perm_helper = $this->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
return $perm_helper->CheckPermission($name, $type, $cat_id);
}
/**
* Check current admin permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckAdminPermission($name, $type = 1, $cat_id = null)
{
$perm_helper = $this->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
return $perm_helper->CheckAdminPermission($name, $type, $cat_id);
}
/**
* Set's any field of current visit
*
* @param string $field
* @param mixed $value
* @return void
* @access public
* @todo move to separate module
*/
public function setVisitField($field, $value)
{
if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) {
// admin logins are not registered in visits list
return;
}
$visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0));
/* @var $visit kDBItem */
if ( $visit->isLoaded() ) {
$visit->SetDBField($field, $value);
$visit->Update();
}
}
/**
* Allows to check if in-portal is installed
*
* @return bool
* @access public
*/
public function isInstalled()
{
return $this->InitDone && (count($this->ModuleInfo) > 0);
}
/**
* Allows to determine if module is installed & enabled
*
* @param string $module_name
* @return bool
* @access public
*/
public function isModuleEnabled($module_name)
{
return $this->findModule('Name', $module_name) !== false;
}
/**
* Returns Window ID of passed prefix main prefix (in edit mode)
*
* @param string $prefix
* @return int
* @access public
*/
public function GetTopmostWid($prefix)
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$mode = $this->GetVar($top_prefix . '_mode');
return $mode != '' ? substr($mode, 1) : '';
}
/**
* Get temp table name
*
* @param string $table
* @param mixed $wid
* @return string
* @access public
*/
public function GetTempName($table, $wid = '')
{
return $this->GetTempTablePrefix($wid) . $table;
}
/**
* Builds temporary table prefix based on given window id
*
* @param string $wid
* @return string
* @access public
*/
public function GetTempTablePrefix($wid = '')
{
if ( preg_match('/prefix:(.*)/', $wid, $regs) ) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
}
/**
* Checks if given table is a temporary table
*
* @param string $table
* @return bool
* @access public
*/
public function IsTempTable($table)
{
static $cache = Array ();
if ( !array_key_exists($table, $cache) ) {
$cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table);
}
return (bool)$cache[$table];
}
/**
* Checks, that given prefix is in temp mode
*
* @param string $prefix
* @param string $special
* @return bool
* @access public
*/
public function IsTempMode($prefix, $special = '')
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$var_names = Array (
$top_prefix,
rtrim($top_prefix . '_' . $special, '_'), // from post
rtrim($top_prefix . '.' . $special, '.'), // assembled locally
);
$var_names = array_unique($var_names);
$temp_mode = false;
foreach ($var_names as $var_name) {
$value = $this->GetVar($var_name . '_mode');
if ( $value && (substr($value, 0, 1) == 't') ) {
$temp_mode = true;
break;
}
}
return $temp_mode;
}
/**
* Return live table name based on temp table name
*
* @param string $temp_table
* @return string
*/
public function GetLiveName($temp_table)
{
if ( preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $temp_table, $rets) ) {
// cut wid from table end if any
return $rets[2];
}
else {
return $temp_table;
}
}
/**
* Stops processing of user request and displays given message
*
* @param string $message
* @access public
*/
public function ApplicationDie($message = '')
{
$message = ob_get_clean() . $message;
if ( $this->isDebugMode() ) {
$message .= $this->Debugger->printReport(true);
}
echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message;
exit;
}
/**
* Returns comma-separated list of groups from given user
*
* @param int $user_id
* @return string
*/
public function getUserGroups($user_id)
{
switch ($user_id) {
case USER_ROOT:
$user_groups = $this->ConfigValue('User_LoggedInGroup');
break;
case USER_GUEST:
$user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup');
break;
default:
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . (int)$user_id;
$res = $this->Conn->GetCol($sql);
$user_groups = Array ($this->ConfigValue('User_LoggedInGroup'));
if ( $res ) {
$user_groups = array_merge($user_groups, $res);
}
$user_groups = implode(',', $user_groups);
}
return $user_groups;
}
/**
* Allows to detect if page is browsed by spider (293 scheduled_tasks supported)
*
* @return bool
* @access public
*/
/*public function IsSpider()
{
static $is_spider = null;
if ( !isset($is_spider) ) {
$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
$robots = file(FULL_PATH . '/core/robots_list.txt');
foreach ($robots as $robot_info) {
$robot_info = explode("\t", $robot_info, 3);
if ( $user_agent == trim($robot_info[2]) ) {
$is_spider = true;
break;
}
}
}
return $is_spider;
}*/
/**
* Allows to detect table's presence in database
*
* @param string $table_name
* @param bool $force
* @return bool
* @access public
*/
public function TableFound($table_name, $force = false)
{
return $this->Conn->TableFound($table_name, $force);
}
/**
* Returns counter value
*
* @param string $name counter name
* @param Array $params counter parameters
* @param string $query_name specify query name directly (don't generate from parameters)
* @param bool $multiple_results
* @return mixed
* @access public
*/
public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
{
$count_helper = $this->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
}
/**
* Resets counter, which are affected by one of specified tables
*
* @param string $tables comma separated tables list used in counting sqls
* @return void
* @access public
*/
public function resetCounters($tables)
{
if ( kUtil::constOn('IS_INSTALL') ) {
return;
}
$count_helper = $this->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
$count_helper->resetCounters($tables);
}
/**
* Sends XML header + optionally displays xml heading
*
* @param string|bool $xml_version
* @return string
* @access public
* @author Alex
*/
public function XMLHeader($xml_version = false)
{
$this->setContentType('text/xml');
return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . CHARSET . '"?>' : '';
}
/**
* Returns category tree
*
* @param int $category_id
* @return Array
* @access public
*/
public function getTreeIndex($category_id)
{
$tree_index = $this->getCategoryCache($category_id, 'category_tree');
if ( $tree_index ) {
$ret = Array ();
list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
return $ret;
}
return false;
}
/**
* Base category of all categories
* Usually replaced category, with ID = 0 in category-related operations.
*
* @return int
* @access public
*/
public function getBaseCategory()
{
// same, what $this->findModule('Name', 'Core', 'RootCat') does
// don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade
return $this->ModuleInfo['Core']['RootCat'];
}
/**
* Deletes all data, that was cached during unit config parsing (excluding unit config locations)
*
* @param Array $config_variables
* @access public
*/
public function DeleteUnitCache($config_variables = null)
{
$this->cacheManager->DeleteUnitCache($config_variables);
}
/**
* Deletes cached section tree, used during permission checking and admin console tree display
*
* @return void
* @access public
*/
public function DeleteSectionCache()
{
$this->cacheManager->DeleteSectionCache();
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->Factory->setFromCache($data);
$this->UnitConfigReader->setFromCache($data);
$this->EventManager->setFromCache($data);
$this->ReplacementTemplates = $data['Application.ReplacementTemplates'];
$this->RewriteListeners = $data['Application.RewriteListeners'];
$this->ModuleInfo = $data['Application.ModuleInfo'];
}
/**
* Gets object data for caching
* The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
*
* @access public
* @return Array
*/
public function getToCache()
{
return array_merge(
$this->Factory->getToCache(),
$this->UnitConfigReader->getToCache(),
$this->EventManager->getToCache(),
Array (
'Application.ReplacementTemplates' => $this->ReplacementTemplates,
'Application.RewriteListeners' => $this->RewriteListeners,
'Application.ModuleInfo' => $this->ModuleInfo,
)
);
}
public function delayUnitProcessing($method, $params)
{
$this->cacheManager->delayUnitProcessing($method, $params);
}
/**
* Returns current maintenance mode state
*
* @param bool $check_ips
* @return int
* @access public
*/
public function getMaintenanceMode($check_ips = true)
{
$exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : '';
$setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT';
if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) {
$exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false;
if ( !$exception_ip ) {
return constant($setting_name);
}
}
return MaintenanceMode::NONE;
}
/**
* Sets content type of the page
*
* @param string $content_type
* @param bool $include_charset
* @return void
* @access public
*/
public function setContentType($content_type = 'text/html', $include_charset = null)
{
static $already_set = false;
if ( $already_set ) {
return;
}
$header = 'Content-type: ' . $content_type;
if ( !isset($include_charset) ) {
$include_charset = $content_type = 'text/html' || $content_type == 'text/plain' || $content_type = 'text/xml';
}
if ( $include_charset ) {
$header .= '; charset=' . CHARSET;
}
$already_set = true;
header($header);
}
/**
* Posts message to event log
*
* @param string $message
* @param int $code
* @param bool $write_now Allows further customization of log record by returning kLog object
* @return bool|int|kLogger
* @access public
*/
public function log($message, $code = null, $write_now = false)
{
$log = $this->_logger->prepare($message, $code)->addSource($this->_logger->createTrace(null, 1));
if ( $write_now ) {
return $log->write();
}
return $log;
}
/**
* Deletes log with given id from database or disk, when database isn't available
*
* @param int $unique_id
* @param int $storage_medium
* @return void
* @access public
* @throws InvalidArgumentException
*/
public function deleteLog($unique_id, $storage_medium = kLogger::LS_AUTOMATIC)
{
$this->_logger->delete($unique_id, $storage_medium);
}
+
+ /**
+ * Returns the client IP address.
+ *
+ * @return string The client IP address
+ * @access public
+ */
+ public function getClientIp()
+ {
+ return $this->HttpQuery->getClientIp();
+ }
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/cache_manager.php
===================================================================
--- branches/5.2.x/core/kernel/managers/cache_manager.php (revision 15568)
+++ branches/5.2.x/core/kernel/managers/cache_manager.php (revision 15569)
@@ -1,852 +1,852 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kCacheManager extends kBase implements kiCacheable {
/**
* Used variables from SystemSettings table
*
* @var Array
* @access protected
*/
protected $configVariables = Array();
/**
* Used variables from SystemSettings table retrieved from unit cache
*
* @var Array
* @access protected
*/
protected $originalConfigVariables = Array ();
/**
* IDs of config variables used in current run (for caching)
*
* @var Array
* @access protected
*/
protected $configIDs = Array ();
/**
* IDs of config variables retrieved from unit cache
*
* @var Array
* @access protected
*/
protected $originalConfigIDs = Array ();
/**
* Object of memory caching class
*
* @var kCache
* @access protected
*/
protected $cacheHandler = null;
protected $temporaryCache = Array (
'registerAggregateTag' => Array (),
'registerScheduledTask' => Array (),
'registerHook' => Array (),
'registerBuildEvent' => Array (),
'registerAggregateTag' => Array (),
);
/**
* Name of database table, where configuration settings are stored
*
* @var string
* @access protected
*/
protected $settingTableName = '';
/**
* Set's references to kApplication and kDBConnection class instances
*
* @access public
*/
public function __construct()
{
parent::__construct();
$this->settingTableName = TABLE_PREFIX . 'SystemSettings';
if ( defined('IS_INSTALL') && IS_INSTALL ) {
// table substitution required, so "root" can perform login to upgrade to 5.2.0, where setting table was renamed
if ( !$this->Application->TableFound(TABLE_PREFIX . 'SystemSettings') ) {
$this->settingTableName = TABLE_PREFIX . 'ConfigurationValues';
}
}
}
/**
* Creates caching manager instance
*
* @access public
*/
public function InitCache()
{
$this->cacheHandler = $this->Application->makeClass('kCache');
}
/**
* Returns cache key, used to cache phrase and configuration variable IDs used on current page
*
* @return string
* @access protected
*/
protected function getCacheKey()
{
// TODO: maybe language part isn't required, since same phrase from different languages have one ID now
return $this->Application->GetVar('t') . $this->Application->GetVar('m_theme') . $this->Application->GetVar('m_lang') . $this->Application->isAdmin;
}
/**
* Loads phrases and configuration variables, that were used on this template last time
*
* @access public
*/
public function LoadApplicationCache()
{
$phrase_ids = $config_ids = Array ();
$sql = 'SELECT PhraseList, ConfigVariables
FROM ' . TABLE_PREFIX . 'PhraseCache
WHERE Template = ' . $this->Conn->qstr( md5($this->getCacheKey()) );
$res = $this->Conn->GetRow($sql);
if ($res) {
if ( $res['PhraseList'] ) {
$phrase_ids = explode(',', $res['PhraseList']);
}
if ( $res['ConfigVariables'] ) {
$config_ids = array_diff( explode(',', $res['ConfigVariables']), $this->originalConfigIDs);
}
}
$this->Application->Phrases->Init('phrases', '', null, $phrase_ids);
$this->configIDs = $this->originalConfigIDs = $config_ids;
$this->InitConfig();
}
/**
* Updates phrases and configuration variables, that were used on this template
*
* @access public
*/
public function UpdateApplicationCache()
{
$update = false;
//something changed
$update = $update || $this->Application->Phrases->NeedsCacheUpdate();
$update = $update || (count($this->configIDs) && $this->configIDs != $this->originalConfigIDs);
if ($update) {
$fields_hash = Array (
'PhraseList' => implode(',', $this->Application->Phrases->Ids),
'CacheDate' => adodb_mktime(),
'Template' => md5( $this->getCacheKey() ),
'ConfigVariables' => implode(',', array_unique($this->configIDs)),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PhraseCache', 'REPLACE');
}
}
/**
* Loads configuration variables, that were used on this template last time
*
* @access protected
*/
protected function InitConfig()
{
if (!$this->originalConfigIDs) {
return ;
}
$sql = 'SELECT VariableValue, VariableName
FROM ' . $this->settingTableName . '
WHERE VariableId IN (' . implode(',', $this->originalConfigIDs) . ')';
$config_variables = $this->Conn->GetCol($sql, 'VariableName');
$this->configVariables = array_merge($this->configVariables, $config_variables);
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
* @access public
*/
public function ConfigValue($name)
{
$site_domain_override = Array (
'DefaultEmailSender' => 'AdminEmail',
'DefaultEmailRecipients' => 'DefaultEmailRecipients',
);
if ( isset($site_domain_override[$name]) ) {
$res = $this->Application->siteDomainField($site_domain_override[$name]);
if ( $res ) {
return $res;
}
}
if ( array_key_exists($name, $this->configVariables) ) {
return $this->configVariables[$name];
}
if ( defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound($this->settingTableName, true) ) {
return false;
}
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT VariableId, VariableValue
FROM ' . $this->settingTableName . '
WHERE VariableName = ' . $this->Conn->qstr($name);
$res = $this->Conn->GetRow($sql);
if ( $res !== false ) {
$this->configIDs[] = $res['VariableId'];
$this->configVariables[$name] = $res['VariableValue'];
return $res['VariableValue'];
}
trigger_error('Usage of undefined configuration variable "<strong>' . $name . '</strong>"', E_USER_NOTICE);
return false;
}
/**
* Changes value of individual configuration variable (+resets cache, when needed)
*
* @param string $name
* @param string $value
* @param bool $local_cache_only
* @return string
* @access public
*/
public function SetConfigValue($name, $value, $local_cache_only = false)
{
$this->configVariables[$name] = $value;
if ( $local_cache_only ) {
return;
}
$fields_hash = Array ('VariableValue' => $value);
$this->Conn->doUpdate($fields_hash, $this->settingTableName, 'VariableName = ' . $this->Conn->qstr($name));
if ( array_key_exists($name, $this->originalConfigVariables) && $value != $this->originalConfigVariables[$name] ) {
$this->DeleteUnitCache();
}
}
/**
* Loads data, that was cached during unit config parsing
*
* @return bool
* @access public
*/
public function LoadUnitCache()
{
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$data = $this->Application->getCache('master:configs_parsed', false, CacheSettings::$unitCacheRebuildTime);
}
else {
$data = $this->Application->getDBCache('configs_parsed', CacheSettings::$unitCacheRebuildTime);
}
if ( $data ) {
$cache = unserialize($data); // 126 KB all modules
unset($data);
$this->Application->InitManagers();
$this->Application->setFromCache($cache);
$aggregator = $this->Application->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
$aggregator->setFromCache($cache);
$this->setFromCache($cache);
unset($cache);
return true;
}
return false;
}
/**
* Empties factory and event manager cache (without storing changes)
*/
public function EmptyUnitCache()
{
// maybe discover keys automatically from corresponding classes
$cache_keys = Array (
'Factory.Files', 'Factory.realClasses',
'ConfigReader.prefixFiles',
'EventManager.beforeHooks', 'EventManager.afterHooks', 'EventManager.scheduledTasks', 'EventManager.buildEvents',
'Application.ReplacementTemplates', 'Application.RewriteListeners', 'Application.ModuleInfo',
'Application.ConfigHash', 'Application.ConfigCacheIds',
);
$empty_cache = Array ();
foreach ($cache_keys as $cache_key) {
$empty_cache[$cache_key] = Array ();
}
$this->Application->setFromCache($empty_cache);
$this->setFromCache($empty_cache);
// otherwise ModulesHelper indirectly used from includeConfigFiles won't work
$this->Application->RegisterDefaultClasses();
}
/**
* Updates data, that was parsed from unit configs this time
*
* @access public
*/
public function UpdateUnitCache()
{
$aggregator = $this->Application->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
$this->preloadConfigVars(); // preloading will put to cache
$cache = array_merge(
$this->Application->getToCache(),
$aggregator->getToCache(),
$this->getToCache()
);
- $cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s');
+ $cache_rebuild_by = SERVER_NAME . ' (' . $this->Application->getClientIp() . ') - ' . adodb_date('d/m/Y H:i:s');
if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
$this->Application->setCache('master:configs_parsed', serialize($cache));
$this->Application->setCache('master:last_cache_rebuild', $cache_rebuild_by);
}
else {
$this->Application->setDBCache('configs_parsed', serialize($cache));
$this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by);
}
}
public function delayUnitProcessing($method, $params)
{
if ($this->Application->InitDone) {
// init already done -> call immediately (happens during installation)
$function = Array (&$this->Application, $method);
call_user_func_array($function, $params);
return ;
}
$this->temporaryCache[$method][] = $params;
}
public function applyDelayedUnitProcessing()
{
foreach ($this->temporaryCache as $method => $method_calls) {
$function = Array (&$this->Application, $method);
foreach ($method_calls as $method_call) {
call_user_func_array($function, $method_call);
}
$this->temporaryCache[$method] = Array ();
}
}
/**
* Deletes all data, that was cached during unit config parsing (excluding unit config locations)
*
* @param Array $config_variables
* @access public
*/
public function DeleteUnitCache($config_variables = null)
{
if ( isset($config_variables) && !array_intersect(array_keys($this->originalConfigVariables), $config_variables) ) {
// prevent cache reset, when given config variables are not in unit cache
return;
}
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime);
}
else {
$this->rebuildDBCache('configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime);
}
}
/**
* Deletes cached section tree, used during permission checking and admin console tree display
*
* @return void
* @access public
*/
public function DeleteSectionCache()
{
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
}
else {
$this->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
}
}
/**
* Preloads 21 widely used configuration variables, so they will get to cache for sure
*
* @access protected
*/
protected function preloadConfigVars()
{
$config_vars = Array (
// session related
'SessionTimeout', 'SessionCookieName', 'SessionCookieDomains', 'SessionBrowserSignatureCheck',
'SessionIPAddressCheck', 'CookieSessions', 'KeepSessionOnBrowserClose', 'User_GuestGroup',
'User_LoggedInGroup', 'RegistrationUsernameRequired',
// output related
'UseModRewrite', 'UseContentLanguageNegotiation', 'UseOutputCompression', 'OutputCompressionLevel',
'Config_Site_Time', 'SystemTagCache', 'DefaultGridPerPage',
// tracking related
'UseChangeLog', 'UseVisitorTracking', 'ModRewriteUrlEnding', 'ForceModRewriteUrlEnding',
'RunScheduledTasksFromCron',
);
$escaped_config_vars = $this->Conn->qstrArray($config_vars);
$sql = 'SELECT VariableId, VariableName, VariableValue
FROM ' . $this->settingTableName . '
WHERE VariableName IN (' . implode(',', $escaped_config_vars) . ')';
$data = $this->Conn->Query($sql, 'VariableId');
foreach ($data as $variable_id => $variable_info) {
$this->configIDs[] = $variable_id;
$this->configVariables[ $variable_info['VariableName'] ] = $variable_info['VariableValue'];
}
}
/**
* Sets data from cache to object
*
* Used for cases, when ConfigValue is called before LoadApplicationCache method (e.g. session init, url engine init)
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->configVariables = $this->originalConfigVariables = $data['Application.ConfigHash'];
$this->configIDs = $this->originalConfigIDs = $data['Application.ConfigCacheIds'];
}
/**
* Gets object data for caching
* The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
*
* @access public
* @return Array
*/
public function getToCache()
{
return Array (
'Application.ConfigHash' => $this->configVariables,
'Application.ConfigCacheIds' => $this->configIDs,
// not in use, since it only represents template specific values, not global ones
// 'Application.Caches.ConfigVariables' => $this->originalConfigIDs,
);
}
/**
* Returns caching type (none, memory, temporary)
*
* @param int $caching_type
* @return bool
* @access public
*/
public function isCachingType($caching_type)
{
return $this->cacheHandler->getCachingType() == $caching_type;
}
/**
* Returns cached $key value from cache named $cache_name
*
* @param int $key key name from cache
* @param bool $store_locally store data locally after retrieved
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
{
return $this->cacheHandler->getCache($key, $store_locally, $max_rebuild_seconds);
}
/**
* Stores new $value in cache with $key name
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function setCache($key, $value, $expiration = 0)
{
return $this->cacheHandler->setCache($key, $value, $expiration);
}
/**
* Stores new $value in cache with $key name (only if not there already)
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function addCache($key, $value, $expiration = 0)
{
return $this->cacheHandler->addCache($key, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheHandler->rebuildCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from cache
*
* @param string $key
* @return void
* @access public
*/
public function deleteCache($key)
{
$this->cacheHandler->delete($key);
}
/**
* Reset's all memory cache at once
*
* @return void
* @access public
*/
public function resetCache()
{
$this->cacheHandler->reset();
}
/**
* Returns value from database cache
*
* @param string $name key name
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getDBCache($name, $max_rebuild_seconds = 0)
{
// no serials in cache key OR cache is outdated
$rebuilding = false;
$wait_seconds = $max_rebuild_seconds;
while (true) {
$cached_data = $this->_getDBCache(Array ($name, $name . '_rebuilding', $name . '_rebuild'));
if ( $cached_data[$name . '_rebuild'] ) {
// cache rebuild requested -> rebuild now
$this->deleteDBCache($name . '_rebuild');
if ( $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M1]') ) {
return false;
}
}
$cache = $cached_data[$name];
$rebuilding = $cached_data[$name . '_rebuilding'];
if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) {
// cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready
$this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M2' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']');
return false;
}
elseif ( $cache !== false ) {
// cache present -> return it
$this->cacheHandler->storeStatistics($name, $rebuilding ? 'h' : 'H');
return $cache;
}
$wait_seconds -= kCache::WAIT_STEP;
sleep(kCache::WAIT_STEP);
}
$this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M3' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']');
return false;
}
/**
* Returns value from database cache
*
* @param string|Array $names key name
* @return mixed
* @access protected
*/
protected function _getDBCache($names)
{
$res = Array ();
$names = (array)$names;
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT Data, Cached, LifeTime, VarName
FROM ' . TABLE_PREFIX . 'SystemCache
WHERE VarName IN (' . implode(',', $this->Conn->qstrArray($names)) . ')';
$cached_data = $this->Conn->Query($sql, 'VarName');
foreach ($names as $name) {
if ( !isset($cached_data[$name]) ) {
$res[$name] = false;
continue;
}
$lifetime = (int)$cached_data[$name]['LifeTime']; // in seconds
if ( ($lifetime > 0) && ($cached_data[$name]['Cached'] + $lifetime < adodb_mktime()) ) {
// delete expired
$this->Conn->nextQueryCachable = true;
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache
WHERE VarName = ' . $this->Conn->qstr($name);
$this->Conn->Query($sql);
$res[$name] = false;
continue;
}
$res[$name] = $cached_data[$name]['Data'];
}
return count($res) == 1 ? array_pop($res) : $res;
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @return void
* @access public
*/
public function setDBCache($name, $value, $expiration = false)
{
$this->cacheHandler->storeStatistics($name, 'WU');
$this->deleteDBCache($name . '_rebuilding');
$this->_setDBCache($name, $value, $expiration);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @param string $insert_type
* @return bool
* @access protected
*/
protected function _setDBCache($name, $value, $expiration = false, $insert_type = 'REPLACE')
{
if ( (int)$expiration <= 0 ) {
$expiration = -1;
}
$fields_hash = Array (
'VarName' => $name,
'Data' => &$value,
'Cached' => adodb_mktime(),
'LifeTime' => (int)$expiration,
);
$this->Conn->nextQueryCachable = true;
return $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SystemCache', $insert_type);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @return bool
* @access protected
*/
protected function _addDBCache($name, $value, $expiration = false)
{
return $this->_setDBCache($name, $value, $expiration, 'INSERT');
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @param string $miss_type
* @return bool
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0, $miss_type = 'M')
{
if ( !isset($mode) || $mode == kCache::REBUILD_NOW ) {
$this->cacheHandler->storeStatistics($name, $miss_type);
if ( !$max_rebuilding_time ) {
return true;
}
if ( !$this->_addDBCache($name . '_rebuilding', 1, $max_rebuilding_time) ) {
$this->cacheHandler->storeStatistics($name, 'l');
return false;
}
$this->deleteDBCache($name . '_rebuild');
$this->cacheHandler->storeStatistics($name, 'L');
}
elseif ( $mode == kCache::REBUILD_LATER ) {
$this->_setDBCache($name . '_rebuild', 1, 0);
$this->deleteDBCache($name . '_rebuilding');
}
return true;
}
/**
* Deletes key from database cache
*
* @param string $name
* @return void
* @access public
*/
public function deleteDBCache($name)
{
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache
WHERE VarName = ' . $this->Conn->qstr($name);
$this->Conn->Query($sql);
}
/**
* Increments serial based on prefix and it's ID (optional)
*
* @param string $prefix
* @param int $id ID (value of IDField) or ForeignKeyField:ID
* @param bool $increment
* @return string
* @access public
*/
public function incrementCacheSerial($prefix, $id = null, $increment = true)
{
$pascal_case_prefix = implode('', array_map('ucfirst', explode('-', $prefix)));
$serial_name = $pascal_case_prefix . (isset($id) ? 'IDSerial:' . $id : 'Serial');
if ($increment) {
if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) {
$this->Application->Debugger->appendHTML('Incrementing serial: <strong>' . $serial_name . '</strong>.');
}
$this->setCache($serial_name, (int)$this->getCache($serial_name) + 1);
if (!defined('IS_INSTALL') || !IS_INSTALL) {
// delete cached mod-rewrite urls related to given prefix and id
$delete_clause = isset($id) ? $prefix . ':' . $id : $prefix;
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls
WHERE Prefixes LIKE ' . $this->Conn->qstr('%|' . $delete_clause . '|%');
$this->Conn->Query($sql);
}
}
return $serial_name;
}
/**
* Returns cached category informaton by given cache name. All given category
* information is recached, when at least one of 4 caches is missing.
*
* @param int $category_id
* @param string $name cache name = {filenames, category_designs, category_tree}
* @return string
* @access public
*/
public function getCategoryCache($category_id, $name)
{
$serial_name = '[%CIDSerial:' . $category_id . '%]';
$cache_key = $name . $serial_name;
$ret = $this->getCache($cache_key);
if ($ret === false) {
if (!$category_id) {
// don't query database for "Home" category (ID = 0), because it doesn't exist in database
return false;
}
// this allows to save 2 sql queries for each category
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
FROM ' . TABLE_PREFIX . 'Categories
WHERE CategoryId = ' . (int)$category_id;
$category_data = $this->Conn->GetRow($sql);
if ($category_data !== false) {
// only direct links to category pages work (symlinks, container pages and so on won't work)
$this->setCache('filenames' . $serial_name, $category_data['NamedParentPath']);
$this->setCache('category_designs' . $serial_name, ltrim($category_data['CachedTemplate'], '/'));
$this->setCache('category_tree' . $serial_name, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
}
}
return $this->getCache($cache_key);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/http_query.php
===================================================================
--- branches/5.2.x/core/kernel/utility/http_query.php (revision 15568)
+++ branches/5.2.x/core/kernel/utility/http_query.php (revision 15569)
@@ -1,761 +1,802 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kHTTPQuery extends Params {
/**
* Cache of QueryString parameters
* from config, that are represented
* in environment variable
*
* @var Array
*/
protected $discoveredUnits = Array ();
/**
* $_POST vars
*
* @var Array
* @access private
*/
var $Post;
/**
* $_GET vars
*
* @var Array
* @access private
*/
var $Get;
/**
* $_COOKIE vars
*
* @var Array
* @access private
*/
var $Cookie;
/**
* $_SERVER vars
*
* @var Array
* @access private
*/
var $Server;
/**
* $_ENV vars
*
* @var Array
* @access private
*/
var $Env;
/**
* Order in what write
* all vars together in
* the same array
*
* @var string
*/
var $Order;
/**
* Uploaded files info
*
* @var Array
* @access private
*/
var $Files;
var $specialsToRemove = Array();
/**
* SessionID is given via "sid" variable in query string
*
* @var bool
*/
var $_sidInQueryString = false;
/**
+ * Trust information, provided by proxy
+ *
+ * @var bool
+ */
+ protected $_trustProxy = false;
+
+ /**
* Loads info from $_POST, $_GET and
* related arrays into common place
*
* @param string $order
* @access public
*/
public function __construct($order = 'CGPF')
{
parent::__construct();
$this->Order = $order;
if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
// when AJAX request is made from jQuery, then create ajax variable,
// so any logic based in it (like redirects) will not break down
$_GET['ajax'] = 'yes';
}
+
+ $vars = kUtil::getConfigVars();
+
+ $this->_trustProxy = isset($vars['TrustProxy']) ? (bool)$vars['TrustProxy'] : false;
}
/**
* Discovers unit form request and returns it's QueryString option on success
*
* @param string $prefix_special
*
* @return Array|bool
* @access public
*/
public function discoverUnit($prefix_special)
{
list($prefix) = explode('.', $prefix_special);
$query_string = $this->getQueryString($prefix);
if ($query_string) {
// only units with QueryString option can be discovered
$this->discoveredUnits[$prefix_special] = $query_string;
return $query_string;
}
unset( $this->discoveredUnits[$prefix] );
return false;
}
/**
* Returns units, passed in request
*
* @param bool $prefix_special_only
* @return Array
* @access protected
*/
public function getDiscoveredUnits($prefix_special_only = true)
{
return $prefix_special_only ? array_keys( $this->discoveredUnits ) : $this->discoveredUnits;
}
/**
* Returns QueryMap for requested unit config.
* In case if unit config is a clone, then get parent item's (from prefix) config to create clone
*
* @param string $prefix
* @return Array
* @access protected
*/
protected function getQueryString($prefix)
{
$ret = $this->Application->getUnitOption($prefix, 'QueryString', Array ());
if ( !$ret && preg_match('/(.*?)-(.*)/', $prefix, $regs) ) {
// "#prefix" (new format), "prefix" (old format)
return $this->_getQueryString('#' . $regs[2]);
}
return $ret;
}
/**
* Returns query string (with safety check against missing prefixes)
*
* @param string $prefix
* @return Array
*/
private function _getQueryString($prefix)
{
if ( $this->Application->prefixRegistred($prefix) ) {
return $this->Application->getUnitOption($prefix, 'QueryString');
}
return substr($prefix, 0, 1) == '#' ? $this->_getQueryString( substr($prefix, 1) ) : Array ();
}
/**
* Removes specials from request
*
* @param Array $array
* @return Array
* @access protected
*/
protected function _removeSpecials($array)
{
$ret = Array ();
$removed = false;
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ( $flag ) {
$removed = true;
list ($prefix, $special) = explode('.', $prefix_special, 2);
foreach ($array as $key => $val) {
$new_key = preg_match("/^" . $prefix . "[._]{1}" . $special . "(.*)/", $key, $regs) ? $prefix . $regs[1] : $key;
$ret[$new_key] = is_array($val) ? $this->_removeSpecials($val) : $val;
}
}
}
return $removed ? $ret : $array;
}
public function process()
{
$this->AddAllVars();
$this->removeSpecials();
ini_set('magic_quotes_gpc', 0);
$this->Application->UrlManager->LoadStructureTemplateMapping();
$this->AfterInit();
}
/**
* All all requested vars to
* common storage place
*
* @return void
* @access protected
*/
protected function AddAllVars()
{
for ($i = 0; $i < strlen($this->Order); $i++) {
switch ($this->Order[$i]) {
case 'G':
$this->Get = $this->AddVars($_GET);
if ( array_key_exists('sid', $_GET) ) {
$this->_sidInQueryString = true;
}
$vars = $this->Application->processQueryString($this->Get(ENV_VAR_NAME));
if ( array_key_exists('sid', $vars) ) {
// used by Session::GetPassedSIDValue
$this->Get['sid'] = $vars['sid'];
}
$this->AddParams($vars);
break;
case 'P':
$this->Post = $this->AddVars($_POST);
$this->convertPostEvents();
$this->_processPostEnvVariables();
break;
case 'C':
$this->Cookie = $this->AddVars($_COOKIE);
break;
/*case 'E';
$this->Env = $this->AddVars($_ENV, false); //do not strip slashes!
break;
case 'S';
$this->Server = $this->AddVars($_SERVER, false); //do not strip slashes!
break;*/
case 'F';
$this->convertFiles();
$this->Files = $this->MergeVars($_FILES); // , false); //do not strip slashes!
break;
}
}
}
/**
* Allow POST variables, that names were transformed by PHP ("." replaced with "_") to
* override variables, that were virtually created through environment variable parsing
*
*/
function _processPostEnvVariables()
{
$passed = $this->Get('passed');
if ( !$passed ) {
return;
}
$passed = explode(',', $passed);
foreach ($passed as $prefix_special) {
if ( strpos($prefix_special, '.') === false ) {
continue;
}
list ($prefix, $special) = explode('.', $prefix_special);
$query_map = $this->getQueryString($prefix);
$post_prefix_special = $prefix . '_' . $special;
foreach ($query_map as $var_name) {
if ( array_key_exists($post_prefix_special . '_' . $var_name, $this->Post) ) {
$this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]);
}
}
}
}
/**
* Removes requested specials from all request variables
*
* @return void
* @access protected
*/
protected function removeSpecials()
{
$this->specialsToRemove = $this->Get('remove_specials');
if ( $this->specialsToRemove ) {
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ( $flag && strpos($prefix_special, '.') === false ) {
unset($this->specialsToRemove[$prefix_special]);
trigger_error('Incorrect usage of "<strong>remove_specials[' . $prefix_special . ']</strong>" field (no special found)', E_USER_NOTICE);
}
}
$this->_Params = $this->_removeSpecials($this->_Params);
}
}
/**
* Finishes initialization of kHTTPQuery class
*
* @return void
* @access protected
* @todo: only uses build-in rewrite listeners, when cache is build for the first time
*/
protected function AfterInit()
{
$rewrite_url = $this->Get('_mod_rw_url_');
if ( $this->Application->RewriteURLs() || $rewrite_url ) {
// maybe call onafterconfigread here
$this->Application->UrlManager->initRewrite();
if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileStart('url_parsing', 'Parsing <b>MOD_REWRITE</b> url');
$this->Application->UrlManager->rewrite->parseRewriteURL();
$description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>' . $this->Get('t') . '</b>)';
$this->Application->Debugger->profileFinish('url_parsing', $description);
}
else {
$this->Application->UrlManager->rewrite->parseRewriteURL();
}
if ( !$rewrite_url && $this->rewriteRedirectRequired() ) {
// rewrite url is missing (e.g. not a script from tools folder)
$url_params = $this->getRedirectParams();
// no idea about how to check, that given template require category to be passed with it, so pass anyway
$url_params['pass_category'] = 1;
$url_params['response_code'] = 301; // Moved Permanently
trigger_error('Non mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" used', E_USER_NOTICE);
$this->Application->Redirect('', $url_params);
}
}
else {
$this->Application->VerifyThemeId();
$this->Application->VerifyLanguageId();
}
}
/**
* Checks, that non-rewrite url was visited and it's automatic rewrite is required
*
* @return bool
*/
function rewriteRedirectRequired()
{
$redirect_conditions = Array (
!$this->IsHTTPSRedirect(), // not https <-> http redirect
!$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains)
!defined('GW_NOTIFY'), // not in payment gateway notification script
preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited
$this->Get('t') != 'index', // not on index page
);
$perform_redirect = true;
foreach ($redirect_conditions as $redirect_condition) {
$perform_redirect = $perform_redirect && $redirect_condition;
if (!$perform_redirect) {
return false;
}
}
return true;
}
/**
* This is redirect from https to http or via versa
*
* @return bool
*/
function IsHTTPSRedirect()
{
$http_referer = array_key_exists('HTTP_REFERER', $_SERVER) ? $_SERVER['HTTP_REFERER'] : false;
return (
( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) )
||
( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) )
);
}
/**
* Checks, that referer is out site
*
* @return bool
*/
function refererIsOurSite()
{
if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) {
// no referer -> don't care what happens
return false;
}
$site_helper = $this->Application->recallObject('SiteHelper');
/* @var $site_helper SiteHelper */
$found = false;
$http_referer = $_SERVER['HTTP_REFERER'];
preg_match('/^(.*?):\/\/(.*?)(\/|$)/', $http_referer, $regs); // 1 - protocol, 2 - domain
if ($regs[1] == 'https') {
$found = $site_helper->getDomainByName('SSLUrl', $http_referer) > 0;
if (!$found) {
// check if referer starts with our ssl url
$ssl_url = $this->Application->ConfigValue('SSL_URL');
$found = $ssl_url && preg_match('/^' . preg_quote($ssl_url, '/') . '/', $http_referer);
}
}
else {
$found = $site_helper->getDomainByName('DomainName', $regs[2]) > 0;
if (!$found) {
$found = $regs[2] == DOMAIN;
}
}
return $found;
}
function convertFiles()
{
if ( !$_FILES ) {
return ;
}
$tmp = Array ();
$file_keys = Array ('error', 'name', 'size', 'tmp_name', 'type');
foreach ($_FILES as $file_name => $file_info) {
if ( is_array($file_info['error']) ) {
$tmp[$file_name] = $this->getArrayLevel($file_info['error'], $file_name);
}
else {
$normal_files[$file_name] = $file_info;
}
}
if ( !$tmp ) {
return ;
}
$files = $_FILES;
$_FILES = Array ();
foreach ($tmp as $prefix => $prefix_files) {
$anchor =& $_FILES;
foreach ($prefix_files['keys'] as $key) {
$anchor =& $anchor[$key];
}
foreach ($prefix_files['value'] as $field_name) {
unset($inner_anchor, $copy);
$work_copy = $prefix_files['keys'];
foreach ($file_keys as $file_key) {
$inner_anchor =& $files[$prefix][$file_key];
if ( isset($copy) ) {
$work_copy = $copy;
}
else {
$copy = $work_copy;
}
array_shift($work_copy);
foreach ($work_copy as $prefix_file_key) {
$inner_anchor =& $inner_anchor[$prefix_file_key];
}
$anchor[$field_name][$file_key] = $inner_anchor[$field_name];
}
}
}
// keys: img_temp, 0, values: LocalPath, ThumbPath
}
function getArrayLevel(&$level, $prefix='')
{
$ret['keys'] = $prefix ? Array($prefix) : Array();
$ret['value'] = Array();
foreach($level as $level_key => $level_value)
{
if( is_array($level_value) )
{
$ret['keys'][] = $level_key;
$tmp = $this->getArrayLevel($level_value);
$ret['keys'] = array_merge($ret['keys'], $tmp['keys']);
$ret['value'] = array_merge($ret['value'], $tmp['value']);
}
else
{
$ret['value'][] = $level_key;
}
}
return $ret;
}
/**
* Overwrites GET events with POST events in case if they are set and not empty
*
* @return void
* @access protected
*/
protected function convertPostEvents()
{
$events = $this->Get('events', Array ());
/* @var $events Array */
if ( is_array($events) ) {
$events = array_filter($events);
foreach ($events as $prefix_special => $event_name) {
$this->Set($prefix_special . '_event', $event_name);
}
}
}
function finalizeParsing($passed = Array())
{
if (!$passed) {
return;
}
foreach ($passed as $passed_prefix) {
$this->discoverUnit($passed_prefix); // from mod-rewrite url parsing
}
$this->Set('passed', implode(',', $this->getDiscoveredUnits()));
}
/**
* Saves variables from array specified
* into common variable storage place
*
* @param Array $array
* @param bool $strip_slashes
* @return Array
* @access private
*/
function AddVars($array, $strip_slashes = true)
{
if ( $strip_slashes ) {
$array = $this->StripSlashes($array);
}
foreach ($array as $key => $value) {
$this->Set($key, $value);
}
return $array;
}
function MergeVars($array, $strip_slashes = true)
{
if ( $strip_slashes ) {
$array = $this->StripSlashes($array);
}
foreach ($array as $key => $value_array) {
// $value_array is an array too
$this->_Params = kUtil::array_merge_recursive($this->_Params, Array ($key => $value_array));
}
return $array;
}
function StripSlashes($array)
{
static $magic_quotes = null;
if (!isset($magic_quotes)) {
$magic_quotes = get_magic_quotes_gpc();
}
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = $this->StripSlashes($value);
}
else {
if ($magic_quotes) {
$value = stripslashes($value);
}
if (!$this->Application->isAdmin) {
$value = htmlspecialchars($value);
}
$array[$key] = $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)
{
$vars = $this->Get;
$unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action');
if (!$this->_sidInQueryString) {
$unset_vars[] = 'sid';
}
// remove system variables
foreach ($unset_vars as $var_name) {
if (array_key_exists($var_name, $vars)) {
unset($vars[$var_name]);
}
}
if ($access_error) {
// place 1 of 2 (also in UsersEventHandler::OnSessionExpire)
$vars = $this->_removePassThroughVariables($vars);
}
// transform arrays
return $this->_transformArrays($vars);
}
/**
* Removes all pass_though variables from redirect params
*
* @param Array $url_params
* @return Array
*/
function _removePassThroughVariables($url_params)
{
$pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : '';
if (!$pass_through) {
return $url_params;
}
$pass_through = explode(',', $pass_through . ',pass_through');
foreach ($pass_through as $pass_through_var) {
unset($url_params[$pass_through_var]);
}
$url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again
return $url_params;
}
function _transformArrays($array, $level_prefix = '')
{
$ret = Array ();
foreach ($array as $var_name => $var_value) {
$new_var_name = $level_prefix ? $level_prefix . '[' . $var_name . ']' : $var_name;
if (is_array($var_value)) {
$ret = array_merge($ret, $this->_transformArrays($var_value, $new_var_name));
}
else {
$ret[$new_var_name] = $var_value;
}
}
return $ret;
}
function writeRequestLog($filename)
{
$log_file = (defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/' . $filename;
if ( is_writable(dirname($log_file)) ) {
$fp = fopen($log_file, 'a');
if ( $fp ) {
$session = $this->Application->recallObject('Session');
/* @var $session 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";
+ $data = '[' . date('D M d H:i:s Y') . '] ' . $admin_mark . '; ip: ' . $this->getClientIp() . '; 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('Request Log directory not writable', E_USER_WARNING);
}
}
else {
trigger_error('Request Log directory not writable', E_USER_WARNING);
}
}
/**
* Checks, that url is empty
*
* @return bool
* @access public
*/
public function isEmptyUrl()
{
if ( $this->Application->RewriteURLs() ) {
return !$this->Get('_mod_rw_url_');
}
return !count($this->Get);
}
+ /**
+ * Returns the client IP address.
+ *
+ * @return string The client IP address
+ * @access public
+ */
+ public function getClientIp()
+ {
+ if ( $this->_trustProxy ) {
+ if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) {
+ return $_SERVER['HTTP_CLIENT_IP'];
+ }
+
+ if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) {
+ $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+
+ foreach ($client_ip as $ip_address) {
+ $clean_ip_address = trim($ip_address);
+
+ if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) {
+ return $clean_ip_address;
+ }
+ }
+
+ return '';
+ }
+ }
+
+ return $_SERVER['REMOTE_ADDR'];
+ }
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/logger.php
===================================================================
--- branches/5.2.x/core/kernel/utility/logger.php (revision 15568)
+++ branches/5.2.x/core/kernel/utility/logger.php (revision 15569)
@@ -1,1364 +1,1365 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2012 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Class for logging system activity
*/
class kLogger extends kBase {
/**
* Prefix of all database related errors
*/
const DB_ERROR_PREFIX = 'SQL Error:';
/**
* Logger state: logging of errors and user-defined messages
*/
const STATE_ENABLED = 1;
/**
* Logger state: logging of user-defined messages only
*/
const STATE_USER_ONLY = 2;
/**
* Logger state: don't log anything
*/
const STATE_DISABLED = 0;
/**
* Log store: automatically determine where log should be written
*/
const LS_AUTOMATIC = 1;
/**
* Log store: always write log to database
*/
const LS_DATABASE = 2;
/**
* Log store: always write log to disk
*/
const LS_DISK = 3;
/**
* Log level: system is unusable
*/
const LL_EMERGENCY = 0;
/**
* Log level: action must be taken immediately
*/
const LL_ALERT = 1;
/**
* Log level: the system is in a critical condition
*/
const LL_CRITICAL = 2;
/**
* Log level: there is an error condition
*/
const LL_ERROR = 3;
/**
* Log level: there is a warning condition
*/
const LL_WARNING = 4;
/**
* Log level: a normal but significant condition
*/
const LL_NOTICE = 5;
/**
* Log level: a purely informational message
*/
const LL_INFO = 6;
/**
* Log level: messages generated to debug the application
*/
const LL_DEBUG = 7;
/**
* Log type: PHP related activity
*/
const LT_PHP = 1;
/**
* Log type: database related activity
*/
const LT_DATABASE = 2;
/**
* Log type: custom activity
*/
const LT_OTHER = 3;
/**
* Log interface: Front
*/
const LI_FRONT = 1;
/**
* Log interface: Admin
*/
const LI_ADMIN = 2;
/**
* Log interface: Cron (Front)
*/
const LI_CRON_FRONT = 3;
/**
* Log interface: Cron (Admin)
*/
const LI_CRON_ADMIN = 4;
/**
* Log interface: API
*/
const LI_API = 5;
/**
* Log notification status: disabled
*/
const LNS_DISABLED = 0;
/**
* Log notification status: pending
*/
const LNS_PENDING = 1;
/**
* Log notification status: sent
*/
const LNS_SENT = 2;
/**
* List of error/exception handlers
*
* @var Array
* @access protected
*/
protected $_handlers = Array ();
/**
* Long messages are saved here, because "trigger_error" doesn't support error messages over 1KB in size
*
* @var Array
* @access protected
*/
protected static $_longMessages = Array ();
/**
* Log record being worked on
*
* @var Array
* @access protected
*/
protected $_logRecord = Array ();
/**
* Maximal level of a message, that can be logged
*
* @var int
* @access protected
*/
protected $_maxLogLevel = self::LL_NOTICE;
/**
* State of the logger
*
* @var int
* @access protected
*/
protected $_state = self::STATE_DISABLED;
/**
* Caches state of debug mode
*
* @var bool
* @access protected
*/
protected $_debugMode = false;
/**
* Ignores backtrace record where following files are mentioned
*
* @var Array
* @access protected
*/
protected $_ignoreInTrace = Array ('logger.php', 'db_connection.php', 'db_load_balancer.php');
/**
* Create event log
*
* @param Array $methods_to_call List of invokable kLogger class method with their parameters (if any)
* @access public
*/
public function __construct($methods_to_call = Array ())
{
parent::__construct();
$vars = kUtil::getConfigVars();
$this->_debugMode = $this->Application->isDebugMode();
$this->setState(isset($vars['EnableSystemLog']) ? $vars['EnableSystemLog'] : self::STATE_DISABLED);
$this->_maxLogLevel = isset($vars['SystemLogMaxLevel']) ? (int)$vars['SystemLogMaxLevel'] : self::LL_NOTICE;
foreach ($methods_to_call as $method_to_call) {
call_user_func_array(Array ($this, $method_to_call[0]), $method_to_call[1]);
}
if ( !kUtil::constOn('DBG_ZEND_PRESENT') && !$this->Application->isDebugMode() ) {
// don't report error on screen if debug mode is turned off
error_reporting(0);
ini_set('display_errors', 0);
}
register_shutdown_function(Array ($this, 'catchLastError'));
}
/**
* Sets state of the logged (enabled/user-only/disabled)
*
* @param $new_state
* @return void
* @access public
*/
public function setState($new_state = null)
{
if ( isset($new_state) ) {
$this->_state = (int)$new_state;
}
if ( $this->_state === self::STATE_ENABLED ) {
$this->_enableErrorHandling();
}
elseif ( $this->_state === self::STATE_DISABLED ) {
$this->_disableErrorHandling();
}
}
/**
* Enable error/exception handling capabilities
*
* @return void
* @access protected
*/
protected function _enableErrorHandling()
{
$this->_disableErrorHandling();
$this->_handlers[self::LL_ERROR] = new kErrorHandlerStack($this);
$this->_handlers[self::LL_CRITICAL] = new kExceptionHandlerStack($this);
}
/**
* Disables error/exception handling capabilities
*
* @return void
* @access protected
*/
protected function _disableErrorHandling()
{
foreach ($this->_handlers as $index => $handler) {
$this->_handlers[$index]->__destruct();
unset($this->_handlers[$index]);
}
}
/**
* Initializes new log record. Use "kLogger::write" to save to db/disk
*
* @param string $message
* @param int $code
* @return kLogger
* @access public
*/
public function prepare($message = '', $code = null)
{
$this->_logRecord = Array (
'LogUniqueId' => kUtil::generateId(),
'LogMessage' => $message,
'LogLevel' => self::LL_INFO,
'LogCode' => $code,
'LogType' => self::LT_OTHER,
'LogHostname' => $_SERVER['HTTP_HOST'],
'LogRequestSource' => php_sapi_name() == 'cli' ? 2 : 1,
'LogRequestURI' => php_sapi_name() == 'cli' ? implode(' ', $GLOBALS['argv']) : $_SERVER['REQUEST_URI'],
'LogUserId' => USER_GUEST,
'IpAddress' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
'LogSessionKey' => 0,
'LogProcessId' => getmypid(),
'LogUserData' => '',
'LogNotificationStatus' => self::LNS_DISABLED,
);
if ( $this->Application->isAdmin ) {
$this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_ADMIN : self::LI_ADMIN;
}
else {
$this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_FRONT : self::LI_FRONT;
}
if ( $this->Application->InitDone ) {
$this->_logRecord['LogUserId'] = $this->Application->RecallVar('user_id');
$this->_logRecord['LogSessionKey'] = $this->Application->GetSID();
+ $this->_logRecord['IpAddress'] = $this->Application->getClientIp();
}
return $this;
}
/**
* Sets one or more fields of log record
*
* @param string|Array $field_name
* @param string|null $field_value
* @return kLogger
* @access public
* @throws UnexpectedValueException
*/
public function setLogField($field_name, $field_value = null)
{
if ( isset($field_value) ) {
$this->_logRecord[$field_name] = $field_value;
}
elseif ( is_array($field_name) ) {
$this->_logRecord = array_merge($this->_logRecord, $field_name);
}
else {
throw new UnexpectedValueException('Invalid arguments');
}
return $this;
}
/**
* Sets user data
*
* @param string $data
* @param bool $as_array
* @return kLogger
* @access public
*/
public function setUserData($data, $as_array = false)
{
if ( $as_array ) {
$data = serialize((array)$data);
}
return $this->setLogField('LogUserData', $data);
}
/**
* Add user data
*
* @param string $data
* @param bool $as_array
* @return kLogger
* @access public
*/
public function addUserData($data, $as_array = false)
{
$new_data = $this->_logRecord['LogUserData'];
if ( $as_array ) {
$new_data = $new_data ? unserialize($new_data) : Array ();
$new_data[] = $data;
$new_data = serialize($new_data);
}
else {
$new_data .= ($new_data ? PHP_EOL : '') . $data;
}
return $this->setLogField('LogUserData', $new_data);
}
/**
* Adds event to log record
*
* @param kEvent $event
* @return kLogger
* @access public
*/
public function addEvent(kEvent $event)
{
$this->_logRecord['LogEventName'] = (string)$event;
return $this;
}
/**
* Adds log source file & file to log record
*
* @param string|Array $file_or_trace file path
* @param int $line file line
* @return kLogger
* @access public
*/
public function addSource($file_or_trace = '', $line = 0)
{
if ( is_array($file_or_trace) ) {
$trace_info = $file_or_trace[0];
$this->_logRecord['LogSourceFilename'] = $trace_info['file'];
$this->_logRecord['LogSourceFileLine'] = $trace_info['line'];
}
else {
$this->_logRecord['LogSourceFilename'] = $file_or_trace;
$this->_logRecord['LogSourceFileLine'] = $line;
}
return $this;
}
/**
* Adds session contents to log record
*
* @param bool $include_optional Include optional session variables
* @return kLogger
* @access public
*/
public function addSessionData($include_optional = false)
{
if ( $this->Application->InitDone ) {
$this->_logRecord['LogSessionData'] = serialize($this->Application->Session->getSessionData($include_optional));
}
return $this;
}
/**
* Adds user request information to log record
*
* @return kLogger
* @access public
*/
public function addRequestData()
{
$request_data = Array ();
$request_variables = Array ('_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE);
foreach ($request_variables as $title => $data) {
if ( !$data ) {
continue;
}
$request_data[$title] = $data;
}
$this->_logRecord['LogRequestData'] = serialize($request_data);
return $this;
}
/**
* Adds trace to log record
*
* @param Array $trace
* @param int $skip_levels
* @param Array $skip_files
* @return kLogger
* @access public
*/
public function addTrace($trace = null, $skip_levels = 1, $skip_files = null)
{
$trace = $this->createTrace($trace, $skip_levels, $skip_files);
foreach ($trace as $trace_index => $trace_info) {
if ( isset($trace_info['args']) ) {
$trace[$trace_index]['args'] = $this->_implodeObjects($trace_info['args']);
}
}
$this->_logRecord['LogBacktrace'] = serialize($this->_removeObjectsFromTrace($trace));
return $this;
}
/**
* Remove objects from trace, since before PHP 5.2.5 there wasn't possible to remove them initially
*
* @param Array $trace
* @return Array
* @access protected
*/
protected function _removeObjectsFromTrace($trace)
{
if ( version_compare(PHP_VERSION, '5.3', '>=') ) {
return $trace;
}
$trace_indexes = array_keys($trace);
foreach ($trace_indexes as $trace_index) {
unset($trace[$trace_index]['object']);
}
return $trace;
}
/**
* Implodes object to prevent memory leaks
*
* @param Array $array
* @return Array
* @access protected
*/
protected function _implodeObjects($array)
{
$ret = Array ();
foreach ($array as $key => $value) {
if ( is_array($value) ) {
$ret[$key] = $this->_implodeObjects($value);
}
elseif ( is_object($value) ) {
if ( $value instanceof kEvent ) {
$ret[$key] = 'Event: ' . (string)$value;
}
elseif ( $value instanceof kBase ) {
$ret[$key] = (string)$value;
}
else {
$ret[$key] = 'Class: ' . get_class($value);
}
}
else {
$ret[$key] = $value;
}
}
return $ret;
}
/**
* Removes first N levels from trace
*
* @param Array $trace
* @param int $levels
* @param Array $files
* @return Array
* @access public
*/
public function createTrace($trace = null, $levels = null, $files = null)
{
if ( !isset($trace) ) {
$trace = debug_backtrace(false);
}
if ( !$trace ) {
// no trace information
return $trace;
}
if ( isset($levels) && is_numeric($levels) ) {
for ($i = 0; $i < $levels; $i++) {
array_shift($trace);
}
}
if ( isset($files) && is_array($files) ) {
while (true) {
$trace_info = $trace[0];
$file = isset($trace_info['file']) ? basename($trace_info['file']) : '';
if ( !in_array($file, $files) ) {
break;
}
array_shift($trace);
}
}
return $trace;
}
/**
* Adds PHP error to log record
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return kLogger
* @access public
*/
public function addError($errno, $errstr, $errfile = null, $errline = null)
{
$errstr = self::expandMessage($errstr, !$this->_debugMode);
$this->_logRecord['LogLevel'] = $this->_getLogLevelByErrorNo($errno);
if ( $this->isLogType(self::LT_DATABASE, $errstr) ) {
list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr);
$this->_logRecord['LogType'] = self::LT_DATABASE;
$this->_logRecord['LogUserData'] = $sql;
$trace = $this->createTrace(null, 4, $this->_ignoreInTrace);
$this->addSource($trace);
$this->addTrace($trace, 0);
}
else {
$this->_logRecord['LogType'] = self::LT_PHP;
$this->addSource((string)$errfile, $errline);
$this->addTrace(null, 4);
}
$this->_logRecord['LogCode'] = $errno;
$this->_logRecord['LogMessage'] = $errstr;
return $this;
}
/**
* Adds PHP exception to log record
*
* @param Exception $exception
* @return kLogger
* @access public
*/
public function addException($exception)
{
$errstr = self::expandMessage($exception->getMessage(), !$this->_debugMode);
$this->_logRecord['LogLevel'] = self::LL_CRITICAL;
if ( $this->isLogType(self::LT_DATABASE, $errstr) ) {
list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr);
$this->_logRecord['LogType'] = self::LT_DATABASE;
$this->_logRecord['LogUserData'] = $sql;
$trace = $this->createTrace($exception->getTrace(), null, $this->_ignoreInTrace);
$this->addSource($trace);
$this->addTrace($trace, 0);
}
else {
$this->_logRecord['LogType'] = self::LT_PHP;
$errno = $exception->getCode();
$this->addSource((string)$exception->getFile(), $exception->getLine());
$this->addTrace($exception->getTrace(), 0);
}
$this->_logRecord['LogCode'] = $errno;
$this->_logRecord['LogMessage'] = $errstr;
return $this;
}
/**
* Allows to map PHP error numbers to syslog log level
*
* @param int $errno
* @return int
* @access protected
*/
protected function _getLogLevelByErrorNo($errno)
{
$error_number_mapping = Array (
self::LL_ERROR => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE),
self::LL_WARNING => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING),
self::LL_NOTICE => Array (E_NOTICE, E_USER_NOTICE, E_STRICT),
);
if ( version_compare(PHP_VERSION, '5.3.0', '>=') ) {
$error_number_mapping[self::LL_NOTICE][] = E_DEPRECATED;
$error_number_mapping[self::LL_NOTICE][] = E_USER_DEPRECATED;
}
foreach ($error_number_mapping as $log_level => $error_numbers) {
if ( in_array($errno, $error_numbers) ) {
return $log_level;
}
}
return self::LL_ERROR;
}
/**
* Changes log level of a log record
*
* @param int $log_level
* @return kLogger
* @access public
*/
public function setLogLevel($log_level)
{
$this->_logRecord['LogLevel'] = $log_level;
return $this;
}
/**
* Writes prepared log to database or disk, when database isn't available
*
* @param int $storage_medium
* @return bool|int
* @access public
* @throws InvalidArgumentException
*/
public function write($storage_medium = self::LS_AUTOMATIC)
{
if ( !$this->_logRecord || $this->_logRecord['LogLevel'] > $this->_maxLogLevel || $this->_state == self::STATE_DISABLED ) {
// nothing to save OR less detailed logging requested OR disabled
return true;
}
$this->_logRecord['LogMemoryUsed'] = memory_get_usage();
$this->_logRecord['LogTimestamp'] = adodb_mktime();
$this->_logRecord['LogDate'] = adodb_date('Y-m-d H:i:s');
if ( $storage_medium == self::LS_AUTOMATIC ) {
$storage_medium = $this->Conn->connectionOpened() ? self::LS_DATABASE : self::LS_DISK;
}
if ( $storage_medium == self::LS_DATABASE ) {
$result = $this->Conn->doInsert($this->_logRecord, TABLE_PREFIX . 'SystemLog');
}
elseif ( $storage_medium == self::LS_DISK ) {
$result = $this->_saveToFile(RESTRICTED . '/system.log');
}
else {
throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"');
}
$unique_id = $this->_logRecord['LogUniqueId'];
if ( $this->_logRecord['LogNotificationStatus'] == self::LNS_SENT ) {
$this->_sendNotification($unique_id);
}
$this->_logRecord = Array ();
return $result ? $unique_id : false;
}
/**
* Catches last error happened before script ended
*
* @return void
* @access public
*/
public function catchLastError()
{
$this->write();
$last_error = error_get_last();
if ( !is_null($last_error) && isset($this->_handlers[self::LL_ERROR]) ) {
$handler = $this->_handlers[self::LL_ERROR];
/* @var $handler kErrorHandlerStack */
$handler->handle($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
}
}
/**
* Deletes log with given id from database or disk, when database isn't available
*
* @param int $unique_id
* @param int $storage_medium
* @return void
* @access public
* @throws InvalidArgumentException
*/
public function delete($unique_id, $storage_medium = self::LS_AUTOMATIC)
{
if ( $storage_medium == self::LS_AUTOMATIC ) {
$storage_medium = $this->Conn->connectionOpened ? self::LS_DATABASE : self::LS_DISK;
}
if ( $storage_medium == self::LS_DATABASE ) {
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemLog
WHERE LogUniqueId = ' . $unique_id;
$this->Conn->Query($sql);
}
elseif ( $storage_medium == self::LS_DISK ) {
// TODO: no way to delete a line from a file
}
else {
throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"');
}
}
/**
* Send notification (delayed or instant) about log record to e-mail from configuration
*
* @param bool $instant
* @return kLogger
* @access public
*/
public function notify($instant = false)
{
$this->_logRecord['LogNotificationStatus'] = $instant ? self::LNS_SENT : self::LNS_PENDING;
return $this;
}
/**
* Sends notification e-mail about message with given $unique_id
*
* @param int $unique_id
* @return void
* @access protected
*/
protected function _sendNotification($unique_id)
{
$notification_email = $this->Application->ConfigValue('SystemLogNotificationEmail');
if ( !$notification_email ) {
trigger_error('System Log notification E-mail not specified', E_USER_NOTICE);
return;
}
$send_params = Array (
'to_name' => $notification_email,
'to_email' => $notification_email,
);
// initialize list outside of e-mail event with right settings
$this->Application->recallObject('system-log.email', 'system-log_List', Array ('unique_id' => $unique_id));
$this->Application->EmailEventAdmin('SYSTEM.LOG.NOTIFY', null, $send_params);
$this->Application->removeObject('system-log.email');
}
/**
* Adds error/exception handler
*
* @param string|Array $handler
* @param bool $is_exception
* @return void
* @access public
*/
public function addErrorHandler($handler, $is_exception = false)
{
$this->_handlers[$is_exception ? self::LL_CRITICAL : self::LL_ERROR]->add($handler);
}
/**
* SQL Error Handler
*
* When not debug mode, then fatal database query won't break anything.
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
* @throws RuntimeException
*/
public function handleSQLError($code, $msg, $sql)
{
$error_msg = self::shortenMessage(self::DB_ERROR_PREFIX . ' #' . $code . ' - ' . $msg . '. SQL: ' . trim($sql));
if ( isset($this->Application->Debugger) ) {
if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) {
throw new RuntimeException($error_msg);
}
else {
$this->Application->Debugger->appendTrace();
}
}
// next line also trigger attached error handlers
trigger_error($error_msg, E_USER_WARNING);
return true;
}
/**
* Packs information about error into a single line
*
* @param string $errno
* @param bool $strip_tags
* @return string
* @access public
*/
public function toString($errno = null, $strip_tags = false)
{
if ( !isset($errno) ) {
$errno = $this->_logRecord['LogCode'];
}
$errstr = $this->_logRecord['LogMessage'];
$errfile = $this->_logRecord['LogSourceFilename'];
$errline = $this->_logRecord['LogSourceFileLine'];
$result = '<strong>' . $errno . ': </strong>' . "{$errstr} in {$errfile} on line {$errline}";
return $strip_tags ? strip_tags($result) : $result;
}
/**
* Saves log to file (e.g. when not possible to save into database)
*
* @param $filename
* @return bool
* @access protected
*/
protected function _saveToFile($filename)
{
$time = adodb_date('Y-m-d H:i:s');
$log_file = new SplFileObject($filename, 'a');
return $log_file->fwrite('[' . $time . '] #' . $this->toString(null, true) . PHP_EOL) > 0;
}
/**
* Checks if log type of current log record matches given one
*
* @param int $log_type
* @param string $log_message
* @return bool
* @access public
*/
public function isLogType($log_type, $log_message = null)
{
if ( $this->_logRecord['LogType'] == $log_type ) {
return true;
}
if ( $log_type == self::LT_DATABASE ) {
if ( !isset($log_message) ) {
$log_message = $this->_logRecord['LogMessage'];
}
return strpos($log_message, self::DB_ERROR_PREFIX) !== false;
}
return false;
}
/**
* Shortens message
*
* @param string $message
* @return string
* @access public
*/
public static function shortenMessage($message)
{
$max_len = ini_get('log_errors_max_len');
if ( strlen($message) > $max_len ) {
$long_key = kUtil::generateId();
self::$_longMessages[$long_key] = $message;
return mb_substr($message, 0, $max_len - strlen($long_key) - 2) . ' #' . $long_key;
}
return $message;
}
/**
* Expands shortened message
*
* @param string $message
* @param bool $clear_cache Allow debugger to expand message after it's been expanded by kLogger
* @return string
* @access public
*/
public static function expandMessage($message, $clear_cache = true)
{
if ( preg_match('/(.*)#([\d]+)$/', $message, $regs) ) {
$long_key = $regs[2];
if ( isset(self::$_longMessages[$long_key]) ) {
$message = self::$_longMessages[$long_key];
if ( $clear_cache ) {
unset(self::$_longMessages[$long_key]);
}
}
}
return $message;
}
/**
* Parses database error message into error number, error message and sql that caused that error
*
* @static
* @param string $message
* @return Array
* @access public
*/
public static function parseDatabaseError($message)
{
$regexp = '/' . preg_quote(self::DB_ERROR_PREFIX) . ' #(.*?) - (.*?)\. SQL: (.*?)$/s';
if ( preg_match($regexp, $message, $regs) ) {
// errno, errstr, sql
return Array ($regs[1], $regs[2], $regs[3]);
}
return Array (0, $message, '');
}
}
/**
* Base class for error or exception handling
*/
abstract class kHandlerStack extends kBase {
/**
* List of added handlers
*
* @var Array
* @access protected
*/
protected $_handlers = Array ();
/**
* Reference to event log, which created this object
*
* @var kLogger
* @access protected
*/
protected $_logger;
/**
* Remembers if handler is activated
*
* @var bool
* @access protected
*/
protected $_enabled = false;
public function __construct(kLogger $logger)
{
parent::__construct();
$this->_logger = $logger;
if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) {
$this->attach();
$this->_enabled = true;
}
}
/**
* Detaches from error handling routines on class destruction
*
* @return void
* @access public
*/
public function __destruct()
{
if ( !$this->_enabled ) {
return;
}
$this->detach();
$this->_enabled = false;
}
/**
* Attach to error handling routines
*
* @abstract
* @return void
* @access protected
*/
abstract protected function attach();
/**
* Detach from error handling routines
*
* @abstract
* @return void
* @access protected
*/
abstract protected function detach();
/**
* Adds new handler to the stack
*
* @param callable $handler
* @return void
* @access public
*/
public function add($handler)
{
$this->_handlers[] = $handler;
}
protected function _handleFatalError($errno)
{
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
if ( !$this->_handlers || ($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)
if ( $this->_isFatalError($errno) ) {
$this->_displayFatalError($errno);
}
if ( !$this->_handlers ) {
return true;
}
}
return null;
}
/**
* Determines if given error is a fatal
*
* @abstract
* @param Exception|int $errno
* @return bool
*/
abstract protected function _isFatalError($errno);
/**
* Displays div with given error message
*
* @param string $errno
* @return void
* @access protected
*/
protected function _displayFatalError($errno)
{
$errno = $this->_getFatalErrorTitle($errno);
$margin = $this->Application->isAdmin ? '8px' : 'auto';
echo '<div style="background-color: #FEFFBF; margin: ' . $margin . '; padding: 10px; border: 2px solid red; text-align: center">' . $this->_logger->toString($errno) . '</div>';
exit;
}
/**
* Returns title to show for a fatal
*
* @abstract
* @param Exception|int $errno
* @return string
*/
abstract protected function _getFatalErrorTitle($errno);
}
/**
* Class, that handles errors
*/
class kErrorHandlerStack extends kHandlerStack {
/**
* Attach to error handling routines
*
* @return void
* @access protected
*/
protected function attach()
{
// set as error handler
$error_handler = set_error_handler(Array ($this, 'handle'));
if ( $error_handler ) {
// wrap around previous error handler, if any was set
$this->_handlers[] = $error_handler;
}
}
/**
* Detach from error handling routines
*
* @return void
* @access protected
*/
protected function detach()
{
restore_error_handler();
}
/**
* Determines if given error is a fatal
*
* @param int $errno
* @return bool
* @access protected
*/
protected function _isFatalError($errno)
{
$fatal_errors = Array (E_USER_ERROR, E_RECOVERABLE_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE);
return in_array($errno, $fatal_errors);
}
/**
* Returns title to show for a fatal
*
* @param int $errno
* @return string
* @access protected
*/
protected function _getFatalErrorTitle($errno)
{
return 'Fatal Error';
}
/**
* Default error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param Array $errcontext
* @return bool
* @access public
*/
public function handle($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
{
$log = $this->_logger->prepare()->addError($errno, $errstr, $errfile, $errline);
if ( $this->_handleFatalError($errno) ) {
$log->write();
return true;
}
$log->write();
$res = false;
/* @var $handler Closure */
foreach ($this->_handlers as $handler) {
if ( is_array($handler) ) {
$object =& $handler[0];
$method = $handler[1];
$res = $object->$method($errno, $errstr, $errfile, $errline, $errcontext);
}
else {
$res = $handler($errno, $errstr, $errfile, $errline, $errcontext);
}
}
return $res;
}
}
/**
* Class, that handles exceptions
*/
class kExceptionHandlerStack extends kHandlerStack {
/**
* Attach to error handling routines
*
* @return void
* @access protected
*/
protected function attach()
{
// set as exception handler
$exception_handler = set_exception_handler(Array ($this, 'handle'));
if ( $exception_handler ) {
// wrap around previous exception handler, if any was set
$this->_handlers[] = $exception_handler;
}
}
/**
* Detach from error handling routines
*
* @return void
* @access protected
*/
protected function detach()
{
restore_exception_handler();
}
/**
* Determines if given error is a fatal
*
* @param Exception $errno
* @return bool
*/
protected function _isFatalError($errno)
{
return true;
}
/**
* Returns title to show for a fatal
*
* @param Exception $errno
* @return string
*/
protected function _getFatalErrorTitle($errno)
{
return get_class($errno);
}
/**
* Handles exception
*
* @param Exception $exception
* @return bool
* @access public
*/
public function handle($exception)
{
$log = $this->_logger->prepare()->addException($exception);
if ( $exception instanceof kRedirectException ) {
/* @var $exception kRedirectException */
$exception->run();
}
if ( $this->_handleFatalError($exception) ) {
$log->write();
return true;
}
$log->write();
$res = false;
/* @var $handler Closure */
foreach ($this->_handlers as $handler) {
if ( is_array($handler) ) {
$object =& $handler[0];
$method = $handler[1];
$res = $object->$method($exception);
}
else {
$res = $handler($exception);
}
}
return $res;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/debugger.php
===================================================================
--- branches/5.2.x/core/kernel/utility/debugger.php (revision 15568)
+++ branches/5.2.x/core/kernel/utility/debugger.php (revision 15569)
@@ -1,1950 +1,1994 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
if( !class_exists('Debugger') ) {
/**
* Contains misc functions, used by debugger (mostly copied from kUtil class)
*/
class DebuggerUtil {
/**
+ * Trust information, provided by proxy
+ *
+ * @var bool
+ */
+ public static $trustProxy = false;
+
+ /**
* Checks if constant is defined and has positive value
*
* @param string $const_name
* @return bool
*/
public static function constOn($const_name)
{
return defined($const_name) && constant($const_name);
}
/**
* Define constant if it was not already defined before
*
* @param string $const_name
* @param string $const_value
* @access public
*/
public static function safeDefine($const_name, $const_value)
{
if ( !defined($const_name) ) {
define($const_name, $const_value);
}
}
/**
* Formats file/memory size in nice way
*
* @param int $bytes
* @return string
* @access public
*/
public static function formatSize($bytes)
{
if ($bytes >= 1099511627776) {
$return = round($bytes / 1024 / 1024 / 1024 / 1024, 2);
$suffix = "TB";
} elseif ($bytes >= 1073741824) {
$return = round($bytes / 1024 / 1024 / 1024, 2);
$suffix = "GB";
} elseif ($bytes >= 1048576) {
$return = round($bytes / 1024 / 1024, 2);
$suffix = "MB";
} elseif ($bytes >= 1024) {
$return = round($bytes / 1024, 2);
$suffix = "KB";
} else {
$return = $bytes;
$suffix = "Byte";
}
$return .= ' '.$suffix;
return $return;
}
/**
* Checks, that user IP address is within allowed range
*
* @param string $ip_list semi-column (by default) separated ip address list
* @param string $separator ip address separator (default ";")
*
* @return bool
*/
public static function ipMatch($ip_list, $separator = ';')
{
- if ( !isset($_SERVER['REMOTE_ADDR']) ) {
- // PHP CLI used -> never match
+ if ( php_sapi_name() == 'cli' ) {
return false;
}
$ip_match = false;
$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();
+ $client_ip = self::getClientIp();
+
foreach ($ip_addresses as $ip_address) {
- if (self::netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) {
+ if ( self::netMatch($ip_address, $client_ip) ) {
$ip_match = true;
break;
}
}
return $ip_match;
}
/**
+ * Returns the client IP address.
+ *
+ * @return string The client IP address
+ * @access public
+ */
+ public static function getClientIp()
+ {
+ if ( self::$trustProxy ) {
+ if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) {
+ return $_SERVER['HTTP_CLIENT_IP'];
+ }
+
+ if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) {
+ $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+
+ foreach ($client_ip as $ip_address) {
+ $clean_ip_address = trim($ip_address);
+
+ if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) {
+ return $clean_ip_address;
+ }
+ }
+
+ return '';
+ }
+ }
+
+ return $_SERVER['REMOTE_ADDR'];
+ }
+
+ /**
* Checks, that given ip belongs to given subnet
*
* @param string $network
* @param string $ip
* @return bool
* @access public
*/
public static function netMatch($network, $ip) {
$network = trim($network);
$ip = trim($ip);
if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) {
$network = gethostbyname($network);
}
if ($network == $ip) {
// comparing two ip addresses directly
return true;
}
$d = strpos($network, '-');
if ($d !== false) {
// ip address range specified
$from = ip2long(trim(substr($network, 0, $d)));
$to = ip2long(trim(substr($network, $d + 1)));
$ip = ip2long($ip);
return ($ip >= $from && $ip <= $to);
}
elseif (strpos($network, '/') !== false) {
// single subnet specified
$ip_arr = explode('/', $network);
if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) {
$ip_arr[0] .= '.0'; // Alternate form 194.1.4/24
}
$network_long = ip2long($ip_arr[0]);
$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
$ip_long = ip2long($ip);
return ($ip_long & $mask) == ($network_long & $mask);
}
return false;
}
}
/**
* Main debugger class, that can be used with any In-Portal (or not) project
*/
class Debugger {
/**
* Holds reference to global KernelApplication instance
*
* @var kApplication
* @access private
*/
private $Application = null;
/**
* Set to true if fatal error occurred
*
* @var bool
* @access private
*/
private $IsFatalError = false;
/**
* Tells if last error (if any) caught by shutdown function was processed
*
* @var bool
* @access private
*/
private $_lastErrorProcessed = false;
/**
* Counts warnings on the page
*
* @var int
* @access public
*/
public $WarningCount = 0;
/**
* Allows to track compile errors, like "stack-overflow"
*
* @var bool
* @access private
*/
private $_compileError = false;
/**
* Debugger data for building report
*
* @var Array
* @access private
*/
private $Data = Array ();
/**
* Holds information about each profiler record (start/end/description)
*
* @var Array
* @access private
*/
private $ProfilerData = Array ();
/**
* Holds information about total execution time per profiler key (e.g. total sql time)
*
* @var Array
* @access private
*/
private $ProfilerTotals = Array ();
/**
* Counts how much each of total types were called (e.g. total error count)
*
* @var Array
* @access private
*/
private $ProfilerTotalCount = Array ();
/**
* Holds information about all profile points registered
*
* @var Array
* @access private
*/
private $ProfilePoints = Array ();
/**
* Prevent recursion when processing debug_backtrace() function results
*
* @var Array
* @access private
*/
private $RecursionStack = Array ();
/**
* Cross browser debugger report scrollbar width detection
*
* @var int
* @access private
*/
private $scrollbarWidth = 0;
/**
* Remembers how much memory & time was spent on including files
*
* @var Array
* @access public
* @see kUtil::includeOnce
*/
public $IncludesData = Array ();
/**
* Remembers maximal include deep level
*
* @var int
* @access public
* @see kUtil::includeOnce
*/
public $IncludeLevel = 0;
/**
* Prevents report generation more then once
*
* @var bool
* @access private
*/
private $reportDone = false;
/**
* Transparent spacer image used in case of none spacer image defined via SPACER_URL constant.
* Used while drawing progress bars (memory usage, time usage, etc.)
*
* @var string
* @access private
*/
private $dummyImage = '';
/**
* Temporary files created by debugger will be stored here
*
* @var string
* @access private
*/
private $tempFolder = '';
/**
* Debug rows will be separated using this string before writing to debug file
*
* @var string
* @access private
*/
private $rowSeparator = '@@';
/**
* Base URL for debugger includes
*
* @var string
* @access private
*/
private $baseURL = '';
/**
* Sub-folder, where In-Portal is installed
*
* @var string
* @access private
*/
private $basePath = '';
/**
* Holds last recorded timestamp (for appendTimestamp)
*
* @var int
* @access private
*/
private $LastMoment;
/**
* Determines, that current request is AJAX request
*
* @var bool
* @access private
*/
private $_isAjax = false;
/**
* Creates instance of debugger
*/
public function __construct()
{
global $start, $dbg_options;
// check if user haven't defined DEBUG_MODE contant directly
if ( defined('DEBUG_MODE') && DEBUG_MODE ) {
die('error: constant DEBUG_MODE defined directly, please use <strong>$dbg_options</strong> array instead');
}
+ if ( class_exists('kUtil') ) {
+ $vars = kUtil::getConfigVars();
+ DebuggerUtil::$trustProxy = isset($vars['TrustProxy']) ? (bool)$vars['TrustProxy'] : false;
+ }
+
// check IP before enabling debug mode
$ip_match = DebuggerUtil::ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : '');
if ( !$ip_match || (isset($_COOKIE['debug_off']) && $_COOKIE['debug_off']) ) {
define('DEBUG_MODE', 0);
return;
}
// debug is allowed for user, continue initialization
$this->InitDebugger();
$this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start);
$this->profileStart('script_runtime', 'Script runtime', $start);
$this->LastMoment = $start;
error_reporting(E_ALL & ~E_STRICT);
// show errors on screen in case if not in Zend Studio debugging
ini_set('display_errors', DebuggerUtil::constOn('DBG_ZEND_PRESENT') ? 0 : 1);
// vertical scrollbar width differs in Firefox and other browsers
$this->scrollbarWidth = $this->isGecko() ? 22 : 25;
$this->appendRequest();
}
/**
* Set's default values to constants debugger uses
*
*/
function InitDebugger()
{
global $dbg_options;
unset($dbg_options['DBG_IP']);
// Detect fact, that this session being debugged by Zend Studio
foreach ($_COOKIE as $cookie_name => $cookie_value) {
if (substr($cookie_name, 0, 6) == 'debug_') {
DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 1);
break;
}
}
DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 0); // set this constant value to 0 (zero) to debug debugger using Zend Studio
// set default values for debugger constants
$dbg_constMap = Array (
'DBG_USE_HIGHLIGHT' => 1, // highlight output same as php code using "highlight_string" function
'DBG_WINDOW_WIDTH' => 700, // set width of debugger window (in pixels) for better viewing large amount of debug data
'DBG_USE_SHUTDOWN_FUNC' => DBG_ZEND_PRESENT ? 0 : 1, // use shutdown function to include debugger code into output
'DBG_HANDLE_ERRORS' => DBG_ZEND_PRESENT ? 0 : 1, // handle all allowed by php (see php manual) errors instead of default handler
'DBG_DOMVIEWER' => '/temp/domviewer.html', // path to DOMViewer on website
'DOC_ROOT' => str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']) ), // windows hack
'DBG_LOCAL_BASE_PATH' => 'w:', // replace DOC_ROOT in filenames (in errors) using this path
'DBG_SHORTCUT' => 'F12', // Defines debugger activation shortcut (any symbols or Ctrl/Alt/Shift are allowed, e.g. Ctrl+Alt+F12)
);
// only for IE, in case if no windows php script editor defined
if (!defined('DBG_EDITOR')) {
// $dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\UltraEdit\uedit32.exe %F/%L';
$dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\Zend\ZendStudio-5.2.0\bin\ZDE.exe %F';
}
// debugger is initialized before kHTTPQuery, so do jQuery headers check here too
if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
$this->_isAjax = true;
}
elseif (array_key_exists('ajax', $_GET) && $_GET['ajax'] == 'yes') {
$this->_isAjax = true;
}
// user defined options override debugger defaults
$dbg_constMap = array_merge($dbg_constMap, $dbg_options);
if ($this->_isAjax && array_key_exists('DBG_SKIP_AJAX', $dbg_constMap) && $dbg_constMap['DBG_SKIP_AJAX']) {
$dbg_constMap['DBG_SKIP_REPORTING'] = 1;
}
// allows to validate unit configs via request variable
if ( !array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) ) {
$dbg_constMap['DBG_VALIDATE_CONFIGS'] = array_key_exists('validate_configs', $_GET) ? (int)$_GET['validate_configs'] : 0;
}
// when validation configs, don't show sqls for better validation error displaying
if ($dbg_constMap['DBG_VALIDATE_CONFIGS']) {
$dbg_constMap['DBG_SQL_PROFILE'] = 0;
}
// when showing explain make shure, that debugger window is large enough
if (array_key_exists('DBG_SQL_EXPLAIN', $dbg_constMap) && $dbg_constMap['DBG_SQL_EXPLAIN']) {
$dbg_constMap['DBG_WINDOW_WIDTH'] = 1000;
}
foreach ($dbg_constMap as $dbg_constName => $dbg_constValue) {
DebuggerUtil::safeDefine($dbg_constName, $dbg_constValue);
}
}
/**
* Performs debugger initialization
*
* @return void
*/
private function InitReport()
{
if ( !class_exists('kApplication') ) {
return;
}
$application =& kApplication::Instance();
// string used to separate debugger records while in file (used in debugger dump filename too)
$this->rowSeparator = '@' . (/*is_object($application->Factory) &&*/ $application->InitDone ? $application->GetSID() : 0) . '@';
// $this->rowSeparator = '@' . rand(0, 100000) . '@';
// include debugger files from this url
$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
$kernel_path = preg_replace($reg_exp, '', KERNEL_PATH, 1);
$this->baseURL = PROTOCOL . SERVER_NAME . (defined('PORT') ? ':' . PORT : '') . rtrim(BASE_PATH, '/') . $kernel_path . '/utility/debugger';
// store debugger cookies at this path
$this->basePath = rtrim(BASE_PATH, '/');
// save debug output in this folder
$this->tempFolder = defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache';
}
/**
* Appends all passed variable values (without variable names) to debug output
*
* @return void
* @access public
*/
public function dumpVars()
{
$dump_mode = 'var_dump';
$dumpVars = func_get_args();
if ( $dumpVars[count($dumpVars) - 1] === 'STRICT' ) {
$dump_mode = 'strict_var_dump';
array_pop($dumpVars);
}
foreach ($dumpVars as $varValue) {
$this->Data[] = Array ('value' => $varValue, 'debug_type' => $dump_mode);
}
}
/**
* Transforms collected data at given index into human-readable HTML to place in debugger report
*
* @param int $dataIndex
* @return string
* @access private
*/
private function prepareHTML($dataIndex)
{
static $errors_displayed = 0;
$Data =& $this->Data[$dataIndex];
if ( $Data['debug_type'] == 'html' ) {
return $Data['html'];
}
switch ($Data['debug_type']) {
case 'error':
$errors_displayed++;
$fileLink = $this->getFileLink($Data['file'], $Data['line']);
$ret = '<b class="debug_error">' . $this->getErrorNameByCode($Data['no']) . ' (#' . $errors_displayed . ')</b>: ' . $Data['str'];
$ret .= ' in <b>' . $fileLink . '</b> on line <b>' . $Data['line'] . '</b>';
return $ret;
break;
case 'exception':
$fileLink = $this->getFileLink($Data['file'], $Data['line']);
$ret = '<b class="debug_error">' . $Data['exception_class'] . '</b>: ' . $Data['str'];
$ret .= ' in <b>' . $fileLink . '</b> on line <b>' . $Data['line'] . '</b>';
return $ret;
break;
case 'var_dump':
return $this->highlightString($this->print_r($Data['value'], true));
break;
case 'strict_var_dump':
return $this->highlightString(var_export($Data['value'], true));
break;
case 'trace':
ini_set('memory_limit', '500M');
$trace =& $Data['trace'];
$i = 0;
$traceCount = count($trace);
$ret = '';
while ( $i < $traceCount ) {
$traceRec =& $trace[$i];
$argsID = 'trace_args_' . $dataIndex . '_' . $i;
$has_args = isset($traceRec['args']);
if ( isset($traceRec['file']) ) {
$func_name = isset($traceRec['class']) ? $traceRec['class'] . $traceRec['type'] . $traceRec['function'] : $traceRec['function'];
$args_link = $has_args ? '<a href="javascript:$Debugger.ToggleTraceArgs(\'' . $argsID . '\');" title="Show/Hide Function Arguments"><b>Function</b></a>' : '<strong>Function</strong>';
$ret .= $args_link . ': ' . $this->getFileLink($traceRec['file'], $traceRec['line'], $func_name);
$ret .= ' in <b>' . basename($traceRec['file']) . '</b> on line <b>' . $traceRec['line'] . '</b><br>';
}
else {
$ret .= 'no file information available';
}
if ( $has_args ) {
// if parameter value is longer then 200 symbols, then leave only first 50
$args = $this->highlightString($this->print_r($traceRec['args'], true));
$ret .= '<div id="' . $argsID . '" style="display: none;">' . $args . '</div>';
}
$i++;
}
return $ret;
break;
case 'profiler':
$profileKey = $Data['profile_key'];
$Data =& $this->ProfilerData[$profileKey];
$runtime = ($Data['ends'] - $Data['begins']); // in seconds
$totals_key = getArrayValue($Data, 'totalsKey');
if ( $totals_key ) {
$total_before = $Data['totalsBefore'];
$total = $this->ProfilerTotals[$totals_key];
$div_width = Array ();
$total_width = ($this->getWindowWidth() - 10);
$div_width['before'] = round(($total_before / $total) * $total_width);
$div_width['current'] = round(($runtime / $total) * $total_width);
$div_width['left'] = round((($total - $total_before - $runtime) / $total) * $total_width);
$subtitle = array_key_exists('subtitle', $Data) ? ' (' . $Data['subtitle'] . ')' : '';
$ret = '<b>Name' . $subtitle . '</b>: ' . $Data['description'] . '<br />';
$additional = isset($Data['additional']) ? $Data['additional'] : Array ();
if ( isset($Data['file']) ) {
array_unshift($additional, Array ('name' => 'File', 'value' => $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line'])));
}
array_unshift($additional, Array ('name' => 'Runtime', 'value' => $runtime . 's'));
$ret .= '<div>'; //FF 3.5 needs this!
foreach ($additional as $mixed_param) {
$ret .= '[<strong>' . $mixed_param['name'] . '</strong>: ' . $mixed_param['value'] . '] ';
}
/*if ( isset($Data['file']) ) {
$ret .= '[<b>Runtime</b>: ' . $runtime . 's] [<b>File</b>: ' . $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line']) . ']<br />';
}
else {
$ret .= '<b>Runtime</b>: ' . $runtime . 's<br />';
}*/
$ret .= '</div>';
$ret .= '<div class="dbg_profiler" style="width: ' . $div_width['before'] . 'px; border-right: 0px; background-color: #298DDF;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>';
$ret .= '<div class="dbg_profiler" style="width: ' . $div_width['current'] . 'px; border-left: 0px; border-right: 0px; background-color: #EF4A4A;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>';
$ret .= '<div class="dbg_profiler" style="width: ' . $div_width['left'] . 'px; border-left: 0px; background-color: #DFDFDF;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>';
return $ret;
}
else {
return '<b>Name</b>: ' . $Data['description'] . '<br><b>Runtime</b>: ' . $runtime . 's';
}
break;
default:
return 'incorrect debug data';
break;
}
}
/**
* Returns debugger report window width excluding scrollbar
*
* @return int
* @access private
*/
private function getWindowWidth()
{
return DBG_WINDOW_WIDTH - $this->scrollbarWidth - 8;
}
/**
* Tells debugger to skip objects that are heavy in plan of memory usage while printing debug_backtrace results
*
* @param Object $object
* @return bool
* @access private
*/
private function IsBigObject(&$object)
{
$skip_classes = Array(
defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication',
'kFactory',
'kUnitConfigReader',
'NParser',
);
foreach ($skip_classes as $class_name) {
if ( strtolower(get_class($object)) == strtolower($class_name) ) {
return true;
}
}
return false;
}
/**
* Advanced version of print_r (for debugger only). Don't print objects recursively
*
* @param Array $array
* @param bool $return_output return output or print it out
* @param int $tab_count offset in tabs
* @return string
* @access private
*/
private function print_r(&$array, $return_output = false, $tab_count = -1)
{
static $first_line = true;
// not an array at all
if ( !is_array($array) ) {
switch ( gettype($array) ) {
case 'NULL':
return 'NULL' . "\n";
break;
case 'object':
return $this->processObject($array, $tab_count);
break;
default:
// number or string
if ( strlen($array) > 200 ) {
$array = substr($array, 0, 50) . ' ...';
}
return $array . "\n";
break;
}
}
$output = '';
$tab_count++;
$output .= "Array\n" . str_repeat(' ', $tab_count) . "(\n";
$tab_count++;
$tabsign = $tab_count ? str_repeat(' ', $tab_count) : '';
$array_keys = array_keys($array);
foreach ($array_keys as $key) {
switch ( gettype($array[$key]) ) {
case 'array':
$output .= $tabsign . '[' . $key . '] = ' . $this->print_r($array[$key], true, $tab_count);
break;
case 'boolean':
$output .= $tabsign . '[' . $key . '] = ' . ($array[$key] ? 'true' : 'false') . "\n";
break;
case 'integer':
case 'double':
case 'string':
if ( strlen($array[$key]) > 200 ) {
$array[$key] = substr($array[$key], 0, 50) . ' ...';
}
$output .= $tabsign . '[' . $key . '] = ' . $array[$key] . "\n";
break;
case 'NULL':
$output .= $tabsign . '[' . $key . "] = NULL\n";
break;
case 'object':
$output .= $tabsign . '[' . $key . "] = ";
$output .= "Object (" . get_class($array[$key]) . ") = \n" . str_repeat(' ', $tab_count + 1) . "(\n";
$output .= $this->processObject($array[$key], $tab_count + 2);
$output .= str_repeat(' ', $tab_count + 1) . ")\n";
break;
default:
$output .= $tabsign . '[' . $key . '] unknown = ' . gettype($array[$key]) . "\n";
break;
}
}
$tab_count--;
$output .= str_repeat(' ', $tab_count) . ")\n";
if ( $first_line ) {
$first_line = false;
$output .= "\n";
}
$tab_count--;
if ( $return_output ) {
return $output;
}
else {
echo $output;
}
return true;
}
/**
* Returns string representation of given object (more like print_r, but with recursion prevention check)
*
* @param Object $object
* @param int $tab_count
* @return string
* @access private
*/
private function processObject(&$object, $tab_count)
{
$object_class = get_class($object);
if ( !in_array($object_class, $this->RecursionStack) ) {
if ( $this->IsBigObject($object) ) {
return 'SKIPPED (class: ' . $object_class . ")\n";
}
$attribute_names = get_class_vars($object_class);
if ( !$attribute_names ) {
return "NO_ATTRIBUTES\n";
}
else {
$output = '';
array_push($this->RecursionStack, $object_class);
$tabsign = $tab_count ? str_repeat(' ', $tab_count) : '';
foreach ($attribute_names as $attribute_name => $attribute_value) {
if ( is_object($object->$attribute_name) ) {
// it is object
$output .= $tabsign . '[' . $attribute_name . '] = ' . $this->processObject($object->$attribute_name, $tab_count + 1);
}
else {
$output .= $tabsign . '[' . $attribute_name . '] = ' . $this->print_r($object->$attribute_name, true, $tab_count);
}
}
array_pop($this->RecursionStack);
return $output;
}
}
else {
// object [in recursion stack]
return '*** RECURSION *** (class: ' . $object_class . ")\n";
}
}
/**
* Format SQL Query using predefined formatting
* and highlighting techniques
*
* @param string $sql
* @return string
* @access public
*/
public function formatSQL($sql)
{
$sql = trim(preg_replace('/(\n|\t| )+/is', ' ', $sql));
// whitespace in the beginning of the regex is to avoid splitting inside words, for example "FROM int_ConfigurationValues" into "FROM intConfiguration\n\tValues"
$formatted_sql = preg_replace('/\s(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY)\s/is', "\n\t$1 ", ' ' . $sql);
$formatted_sql = $this->highlightString($formatted_sql);
if ( defined('DBG_SQL_EXPLAIN') && DBG_SQL_EXPLAIN ) {
if ( substr($sql, 0, 6) == 'SELECT' ) {
$formatted_sql .= '<br/>' . '<strong>Explain</strong>:<br /><br />';
$explain_result = $this->Application->Conn->Query('EXPLAIN ' . $sql, null, true);
$explain_table = '';
foreach ($explain_result as $explain_row) {
if ( !$explain_table ) {
// first row -> draw header
$explain_table .= '<tr class="explain_header"><td>' . implode('</td><td>', array_keys($explain_row)) . '</td></tr>';
}
$explain_table .= '<tr><td>' . implode('</td><td>', $explain_row) . '</td></tr>';
}
$formatted_sql .= '<table class="dbg_explain_table">' . $explain_table . '</table>';
}
}
return $formatted_sql;
}
/**
* Highlights given string using "highlight_string" method
*
* @param string $string
* @return string
* @access public
*/
public function highlightString($string)
{
if ( !(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError ) {
return nl2br($string);
}
$string = str_replace(Array ('\\', '/'), Array ('_no_match_string_', '_n_m_s_'), $string);
$this->_compileError = true; // next line is possible cause of compile error
$string = highlight_string('<?php ' . $string . ' ?>', true);
$this->_compileError = false;
$string = str_replace(Array ('_no_match_string_', '_n_m_s_'), Array ('\\', '/'), $string);
if ( strlen($string) >= 65536 ) {
// preg_replace will fail, when string is longer, then 65KB
return str_replace(Array ('&lt;?php&nbsp;', '?&gt;'), '', $string);
}
return preg_replace('/&lt;\?(.*)php&nbsp;(.*)\?&gt;/Us', '\\2', $string);
}
/**
* Determine by php type of browser used to show debugger
*
* @return bool
* @access private
*/
private function isGecko()
{
// we need isset because we may run scripts from shell with no user_agent at all
return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false;
}
/**
* Returns link for editing php file (from error) in external editor
*
* @param string $file filename with path from root folder
* @param int $lineno line number in file where error is found
* @param string $title text to show on file edit link
* @return string
* @access public
*/
public function getFileLink($file, $lineno = 1, $title = '')
{
if ( !$title ) {
$title = str_replace('/', '\\', $this->getLocalFile($file));
}
if ( $this->isGecko() ) {
return '<a href="file://' . $this->getLocalFile($file) . '">' . $title . '</a>';
}
else {
return '<a href="javascript:$Debugger.editFile(\'' . $this->getLocalFile($file) . '\', ' . $lineno . ');" title="' . $file . '">' . $title . '</a>';
}
}
/**
* Converts filepath on server to filepath in mapped DocumentRoot on developer pc
*
* @param string $remoteFile
* @return string
* @access private
*/
private function getLocalFile($remoteFile)
{
return preg_replace('/^' . preg_quote(DOC_ROOT, '/') . '/', DBG_LOCAL_BASE_PATH, $remoteFile, 1);
}
/**
* Appends call trace till this method call
*
* @param int $levels_to_shift
* @return void
* @access public
*/
public function appendTrace($levels_to_shift = 1)
{
$levels_shifted = 0;
$trace = debug_backtrace();
while ( $levels_shifted < $levels_to_shift ) {
array_shift($trace);
$levels_shifted++;
}
$this->Data[] = Array ('trace' => $trace, 'debug_type' => 'trace');
}
/**
* Appends call trace till this method call
*
* @param Exception $exception
* @return void
* @access private
*/
private function appendExceptionTrace(&$exception)
{
$trace = $exception->getTrace();
$this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace');
}
/**
* Adds memory usage statistics
*
* @param string $msg
* @param int $used
* @return void
* @access public
*/
public function appendMemoryUsage($msg, $used = null)
{
if ( !isset($used) ) {
$used = round(memory_get_usage() / 1024);
}
$this->appendHTML('<b>Memory usage</b> ' . $msg . ' ' . $used . 'Kb');
}
/**
* Appends HTML code without transformations
*
* @param string $html
* @return void
* @access public
*/
public function appendHTML($html)
{
$this->Data[] = Array ('html' => $html, 'debug_type' => 'html');
}
/**
* Returns instance of FirePHP class
*
* @return FirePHP
* @link http://www.firephp.org/HQ/Use.htm
*/
function firePHP()
{
require_once('FirePHPCore/FirePHP.class.php');
return FirePHP::getInstance(true);
}
/**
* Change debugger info that was already generated before.
* Returns true if html was set.
*
* @param int $index
* @param string $html
* @param string $type = {'append','prepend','replace'}
* @return bool
* @access public
*/
public function setHTMLByIndex($index, $html, $type = 'append')
{
if ( !isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html' ) {
return false;
}
switch ( $type ) {
case 'append':
$this->Data[$index]['html'] .= '<br>' . $html;
break;
case 'prepend':
$this->Data[$index]['html'] = $this->Data[$index]['html'] . '<br>' . $html;
break;
case 'replace':
$this->Data[$index]['html'] = $html;
break;
}
return true;
}
/**
* Move $debugLineCount lines of input from debug output
* end to beginning.
*
* @param int $debugLineCount
* @return void
* @access private
*/
private function moveToBegin($debugLineCount)
{
$lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount);
$this->Data = array_merge($lines, $this->Data);
}
/**
* Moves all debugger report lines after $debugLineCount into $new_row position
*
* @param int $new_row
* @param int $debugLineCount
* @return void
* @access private
*/
private function moveAfterRow($new_row, $debugLineCount)
{
$lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount);
$rows_before = array_splice($this->Data, 0, $new_row, $lines);
$this->Data = array_merge($rows_before, $this->Data);
}
/**
* Appends HTTP REQUEST information to debugger report
*
* @return void
* @access private
*/
private function appendRequest()
{
if ( isset($_SERVER['SCRIPT_FILENAME']) ) {
$script = $_SERVER['SCRIPT_FILENAME'];
}
else {
$script = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['PHP_SELF'];
}
$this->appendHTML('ScriptName: <b>' . $this->getFileLink($script, 1, basename($script)) . '</b> (<b>' . dirname($script) . '</b>)');
if ( $this->_isAjax ) {
$this->appendHTML('RequestURI: ' . $_SERVER['REQUEST_URI'] . ' (QS Length:' . strlen($_SERVER['QUERY_STRING']) . ')');
}
$tools_html = ' <table style="width: ' . $this->getWindowWidth() . 'px;">
<tr>
<td>' . $this->_getDomViewerHTML() . '</td>
<td>' . $this->_getToolsHTML() . '</td>
</tr>
</table>';
$this->appendHTML($tools_html);
ob_start();
?>
<table border="0" cellspacing="0" cellpadding="0" class="dbg_flat_table" style="width: <?php echo $this->getWindowWidth(); ?>px;">
<thead style="font-weight: bold;">
<td width="20">Src</td><td>Name</td><td>Value</td>
</thead>
<?php
foreach ($_REQUEST as $key => $value) {
if ( !is_array($value) && trim($value) == '' ) {
$value = '<b class="debug_error">no value</b>';
}
else {
$value = htmlspecialchars($this->print_r($value, true));
}
$in_cookie = isset($_COOKIE[$key]);
$src = isset($_GET[$key]) && !$in_cookie ? 'GE' : (isset($_POST[$key]) && !$in_cookie ? 'PO' : ($in_cookie ? 'CO' : '?'));
echo '<tr><td>' . $src . '</td><td>' . $key . '</td><td>' . $value . '</td></tr>';
}
?>
</table>
<?php
$this->appendHTML(ob_get_contents());
ob_end_clean();
}
/**
* Appends php session content to debugger output
*
* @return void
* @access private
*/
private function appendSession()
{
if ( isset($_SESSION) && $_SESSION ) {
$this->appendHTML('PHP Session: [<b>' . ini_get('session.name') . '</b>]');
$this->dumpVars($_SESSION);
$this->moveToBegin(2);
}
}
/**
* Starts profiling of a given $key
*
* @param string $key
* @param string $description
* @param int $timeStamp
* @return void
* @access public
*/
public function profileStart($key, $description = null, $timeStamp = null)
{
if ( !isset($timeStamp) ) {
$timeStamp = microtime(true);
}
$this->ProfilerData[$key] = Array ('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data));
if ( isset($description) ) {
$this->ProfilerData[$key]['description'] = $description;
}
if ( substr($key, 0, 4) == 'sql_' ) {
// append place from what was called
$trace_results = debug_backtrace();
$trace_count = count($trace_results);
$i = 0;
while ( $i < $trace_count ) {
if ( !isset($trace_results[$i]['file']) ) {
$i++;
continue;
}
$trace_file = basename($trace_results[$i]['file']);
if ( $trace_file != 'db_connection.php' && $trace_file != 'db_load_balancer.php' && $trace_file != 'adodb.inc.php' ) {
break;
}
$i++;
}
$this->ProfilerData[$key]['file'] = $trace_results[$i]['file'];
$this->ProfilerData[$key]['line'] = $trace_results[$i]['line'];
if ( array_key_exists('object', $trace_results[$i + 1]) && isset($trace_results[$i + 1]['object']->Prefix) ) {
$object =& $trace_results[$i + 1]['object'];
/* @var $object kBase */
$prefix_special = rtrim($object->Prefix . '.' . $object->Special, '.');
$this->ProfilerData[$key]['prefix_special'] = $prefix_special;
}
unset($trace_results);
}
$this->Data[] = Array ('profile_key' => $key, 'debug_type' => 'profiler');
}
/**
* Ends profiling for a given $key
*
* @param string $key
* @param string $description
* @param int $timeStamp
* @return void
* @access public
*/
public function profileFinish($key, $description = null, $timeStamp = null)
{
if ( !isset($timeStamp) ) {
$timeStamp = microtime(true);
}
$this->ProfilerData[$key]['ends'] = $timeStamp;
if ( isset($description) ) {
$this->ProfilerData[$key]['description'] = $description;
}
if ( substr($key, 0, 4) == 'sql_' ) {
$func_arguments = func_get_args();
$rows_affected = $func_arguments[3];
$additional = Array ();
if ( $rows_affected > 0 ) {
$additional[] = Array ('name' => 'Affected Rows', 'value' => $rows_affected);
if ( isset($func_arguments[4]) ) {
if ( strlen($func_arguments[4]) > 200 ) {
$func_arguments[4] = substr($func_arguments[4], 0, 50) . ' ...';
}
$additional[] = Array ('name' => 'Result', 'value' => $func_arguments[4]);
}
}
$additional[] = Array ('name' => 'Query Number', 'value' => $func_arguments[5]);
if ( $func_arguments[6] ) {
$this->profilerAddTotal('cachable_queries', $key);
$this->ProfilerData[$key]['subtitle'] = 'cachable';
}
if ( (string)$func_arguments[7] !== '' ) {
$additional[] = Array ('name' => 'Server #', 'value' => $func_arguments[7]);
}
if ( array_key_exists('prefix_special', $this->ProfilerData[$key]) ) {
$additional[] = Array ('name' => 'PrefixSpecial', 'value' => $this->ProfilerData[$key]['prefix_special']);
}
$this->ProfilerData[$key]['additional'] =& $additional;
}
}
/**
* Collects total execution time from profiler record
*
* @param string $total_key
* @param string $key
* @param int $value
* @return void
* @access public
*/
public function profilerAddTotal($total_key, $key = null, $value = null)
{
if ( !isset($this->ProfilerTotals[$total_key]) ) {
$this->ProfilerTotals[$total_key] = 0;
$this->ProfilerTotalCount[$total_key] = 0;
}
if ( !isset($value) ) {
$value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins'];
}
if ( isset($key) ) {
$this->ProfilerData[$key]['totalsKey'] = $total_key;
$this->ProfilerData[$key]['totalsBefore'] = $this->ProfilerTotals[$total_key];
}
$this->ProfilerTotals[$total_key] += $value;
$this->ProfilerTotalCount[$total_key]++;
}
/**
* Traces relative code execution speed between this method calls
*
* @param string $message
* @return void
* @access public
*/
public function appendTimestamp($message)
{
global $start;
$time = microtime(true);
$from_last = $time - $this->LastMoment;
$from_start = $time - $start;
$this->appendHTML(sprintf("<strong>%s</strong> %.5f from last %.5f from start", $message, $from_last, $from_start));
$this->LastMoment = $time;
}
/**
* Returns unique ID for each method call
*
* @return int
* @access public
*/
public function generateID()
{
list($usec, $sec) = explode(' ', microtime());
$id_part_1 = substr($usec, 4, 4);
$id_part_2 = mt_rand(1, 9);
$id_part_3 = substr($sec, 6, 4);
$digit_one = substr($id_part_1, 0, 1);
if ( $digit_one == 0 ) {
$digit_one = mt_rand(1, 9);
$id_part_1 = preg_replace('/^0/', '', $id_part_1);
$id_part_1 = $digit_one . $id_part_1;
}
return $id_part_1 . $id_part_2 . $id_part_3;
}
/**
* Returns error name based on it's code
*
* @param int $error_code
* @return string
* @access private
*/
private function getErrorNameByCode($error_code)
{
$error_map = Array (
'Fatal Error' => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE),
'Warning' => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING),
'Notice' => Array (E_NOTICE, E_USER_NOTICE, E_STRICT),
);
if ( defined('E_DEPRECATED') ) {
// since PHP 5.3
$error_map['Notice'][] = E_DEPRECATED;
$error_map['Notice'][] = E_USER_DEPRECATED;
}
foreach ($error_map as $error_name => $error_codes) {
if ( in_array($error_code, $error_codes) ) {
return $error_name;
}
}
return '';
}
/**
* Returns profile total key (check against missing key too)
*
* @param string $key
* @return int
* @access private
*/
private function getProfilerTotal($key)
{
if ( isset($this->ProfilerTotalCount[$key]) ) {
return (int)$this->ProfilerTotalCount[$key];
}
return 0;
}
/**
* Counts how much calls were made to a place, where this method is called (basic version of profiler)
*
* @param string $title
* @param int $level
* @return void
* @access public
*/
public function ProfilePoint($title, $level = 1)
{
$trace_results = debug_backtrace();
$level = min($level, count($trace_results) - 1);
do {
$point = $trace_results[$level];
$location = $point['file'] . ':' . $point['line'];
$level++;
$has_more = isset($trace_results[$level]);
} while ( $has_more && $point['function'] == $trace_results[$level]['function'] );
if ( !isset($this->ProfilePoints[$title]) ) {
$this->ProfilePoints[$title] = Array ();
}
if ( !isset($this->ProfilePoints[$title][$location]) ) {
$this->ProfilePoints[$title][$location] = 0;
}
$this->ProfilePoints[$title][$location]++;
}
/**
* Generates report
*
* @param bool $returnResult
* @param bool $clean_output_buffer
*
* @return string
* @access public
*/
public function printReport($returnResult = false, $clean_output_buffer = true)
{
if ( $this->reportDone ) {
// don't print same report twice (in case if shutdown function used + compression + fatal error)
return '';
}
$last_error = error_get_last();
if ( !is_null($last_error) && !$this->_lastErrorProcessed ) {
$this->_lastErrorProcessed = true;
$this->saveError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
}
$this->profileFinish('script_runtime');
$this->breakOutofBuffering(!$returnResult);
$debugger_start = memory_get_usage();
if ( defined('SPACER_URL') ) {
$this->dummyImage = SPACER_URL;
}
$this->InitReport(); // set parameters required by AJAX
// defined here, because user can define this constant while script is running, not event before debugger is started
DebuggerUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 0);
DebuggerUtil::safeDefine('DBG_TOOLBAR_BUTTONS', 1);
$this->appendSession(); // show php session if any
// ensure, that 1st line of debug output always is this one:
$top_line = '<table cellspacing="0" cellpadding="0" style="width: ' . $this->getWindowWidth() . 'px; margin: 0px;"><tr><td align="left" width="50%">[<a href="javascript:window.location.reload();">Reload Frame</a>] [<a href="javascript:$Debugger.Toggle(27);">Hide Debugger</a>] [<a href="javascript:$Debugger.Clear();">Clear Debugger</a>]</td><td align="right" width="50%">[Current Time: <b>' . date('H:i:s') . '</b>] [File Size: <b>#DBG_FILESIZE#</b>]</td></tr></table>';
$this->appendHTML($top_line);
$this->moveToBegin(1);
if ( count($this->ProfilePoints) > 0 ) {
foreach ($this->ProfilePoints as $point => $locations) {
arsort($this->ProfilePoints[$point]);
}
$this->appendHTML($this->highlightString($this->print_r($this->ProfilePoints, true)));
}
if ( DebuggerUtil::constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql']) ) {
// sql query profiling was enabled -> show totals
if ( array_key_exists('cachable_queries', $this->ProfilerTotalCount) ) {
$append = ' <strong>Cachable queries</strong>: ' . $this->ProfilerTotalCount['cachable_queries'];
}
else {
$append = '';
}
$this->appendHTML('<b>SQL Total time:</b> ' . $this->ProfilerTotals['sql'] . ' <b>Number of queries</b>: ' . $this->ProfilerTotalCount['sql'] . $append);
}
if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') && isset($this->ProfilerTotals['includes']) ) {
// included file profiling was enabled -> show totals
$this->appendHTML('<b>Included Files Total time:</b> ' . $this->ProfilerTotals['includes'] . ' Number of includes: ' . $this->ProfilerTotalCount['includes']);
}
if ( DebuggerUtil::constOn('DBG_PROFILE_MEMORY') ) {
// detailed memory usage reporting by objects was enabled -> show totals
$this->appendHTML('<b>Memory used by Objects:</b> ' . round($this->ProfilerTotals['objects'] / 1024, 2) . 'Kb');
}
if ( DebuggerUtil::constOn('DBG_INCLUDED_FILES') ) {
$files = get_included_files();
$this->appendHTML('<strong>Included files:</strong>');
foreach ($files as $file) {
$this->appendHTML($this->getFileLink($this->getLocalFile($file)) . ' (' . round(filesize($file) / 1024, 2) . 'Kb)');
}
}
if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') ) {
$totals = $totals_configs = Array ('mem' => 0, 'time' => 0);
$this->appendHTML('<b>Included files statistics:</b>' . (DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)' : ''));
if ( is_array($this->IncludesData['mem']) ) {
if ( DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ) {
array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']);
}
foreach ($this->IncludesData['file'] as $key => $file_name) {
$this->appendHTML(str_repeat('&nbsp;->&nbsp;', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)) . $file_name . ' Mem: ' . sprintf("%.4f Kb", $this->IncludesData['mem'][$key] / 1024) . ' Time: ' . sprintf("%.4f", $this->IncludesData['time'][$key]));
if ( $this->IncludesData['level'][$key] == 0 ) {
$totals['mem'] += $this->IncludesData['mem'][$key];
$totals['time'] += $this->IncludesData['time'][$key];
}
elseif ( $this->IncludesData['level'][$key] == -1 ) {
$totals_configs['mem'] += $this->IncludesData['mem'][$key];
$totals_configs['time'] += $this->IncludesData['time'][$key];
}
}
$this->appendHTML('<b>Sub-Total classes:</b> ' . ' Mem: ' . sprintf("%.4f Kb", $totals['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals['time']));
$this->appendHTML('<b>Sub-Total configs:</b> ' . ' Mem: ' . sprintf("%.4f Kb", $totals_configs['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals_configs['time']));
$this->appendHTML('<span class="error"><b>Grand Total:</b></span> ' . ' Mem: ' . sprintf("%.4f Kb", ($totals['mem'] + $totals_configs['mem']) / 1024) . ' Time: ' . sprintf("%.4f", $totals['time'] + $totals_configs['time']));
}
}
$skip_reporting = DebuggerUtil::constOn('DBG_SKIP_REPORTING') || DebuggerUtil::constOn('DBG_ZEND_PRESENT');
if ( ($this->_isAjax && !DebuggerUtil::constOn('DBG_SKIP_AJAX')) || !$skip_reporting ) {
$debug_file = $this->tempFolder . '/debug_' . $this->rowSeparator . '.txt';
if ( file_exists($debug_file) ) {
unlink($debug_file);
}
$i = 0;
$fp = fopen($debug_file, 'a');
$lineCount = count($this->Data);
while ( $i < $lineCount ) {
fwrite($fp, $this->prepareHTML($i) . $this->rowSeparator);
$i++;
}
fclose($fp);
}
if ( $skip_reporting ) {
// let debugger write report and then don't output anything
$this->reportDone = true;
return '';
}
$application =& kApplication::Instance();
$dbg_path = str_replace(FULL_PATH, '', $this->tempFolder);
$debugger_params = Array (
'RowSeparator' => $this->rowSeparator,
'ErrorsCount' => (int)$this->getProfilerTotal('error_handling'),
'IsFatalError' => $this->IsFatalError,
'SQLCount' => (int)$this->getProfilerTotal('sql'),
'SQLTime' => isset($this->ProfilerTotals['sql']) ? sprintf('%.5f', $this->ProfilerTotals['sql']) : 0,
'ScriptTime' => sprintf('%.5f', $this->ProfilerData['script_runtime']['ends'] - $this->ProfilerData['script_runtime']['begins']),
'ScriptMemory' => DebuggerUtil::formatSize($this->getMemoryUsed($debugger_start)),
'Shortcut' => DBG_SHORTCUT,
);
ob_start();
// the <script .. /script> and hidden div helps browser to break out of script tag or attribute esacped
// with " or ' in case fatal error (or user-error) occurs inside it in compiled template,
// otherwise it has no effect
?>
<div style="display: none" x='nothing'><script></script></div><html><body></body></html>
<link rel="stylesheet" rev="stylesheet" href="<?php echo $this->baseURL; ?>/debugger.css?v2" type="text/css" media="screen" />
<script type="text/javascript" src="<?php echo $this->baseURL; ?>/debugger.js?v4"></script>
<script type="text/javascript">
var $Debugger = new Debugger(<?php echo json_encode($debugger_params); ?>);
$Debugger.createEnvironment(<?php echo DBG_WINDOW_WIDTH; ?>, <?php echo $this->getWindowWidth(); ?>);
$Debugger.DOMViewerURL = '<?php echo constant('DBG_DOMVIEWER'); ?>';
$Debugger.EditorPath = '<?php echo defined('DBG_EDITOR') ? addslashes(DBG_EDITOR) : '' ?>';
$Debugger.DebugURL = '<?php echo $this->baseURL.'/debugger_responce.php?sid='.$this->rowSeparator.'&path='.urlencode($dbg_path); ?>';
$Debugger.EventURL = '<?php echo /*is_object($application->Factory) &&*/ $application->InitDone ? $application->HREF('dummy', '', Array ('pass' => 'm', '__NO_REWRITE__' => 1)) : ''; ?>';
$Debugger.BasePath = '<?php echo $this->basePath; ?>';
<?php
$is_install = defined('IS_INSTALL') && IS_INSTALL;
if ( $this->IsFatalError || (!$is_install && DBG_RAISE_ON_WARNINGS && $this->WarningCount) ) {
echo '$Debugger.Toggle();';
}
if ( DBG_TOOLBAR_BUTTONS ) {
echo '$Debugger.AddToolbar("$Debugger");';
}
?>
window.focus();
</script>
<?php
if ( $returnResult ) {
$ret = ob_get_contents();
if ( $clean_output_buffer ) {
ob_end_clean();
}
$ret .= $this->getShortReport($this->getMemoryUsed($debugger_start));
$this->reportDone = true;
return $ret;
}
else {
if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) {
$this->breakOutofBuffering();
}
elseif ( $clean_output_buffer ) {
ob_clean();
}
echo $this->getShortReport($this->getMemoryUsed($debugger_start));
$this->reportDone = true;
}
return '';
}
function getMemoryUsed($debugger_start)
{
if ( !isset($this->ProfilerTotals['error_handling']) ) {
$memory_used = $debugger_start;
$this->ProfilerTotalCount['error_handling'] = 0;
}
else {
$memory_used = $debugger_start - $this->ProfilerTotals['error_handling'];
}
return $memory_used;
}
/**
* Format's memory usage report by debugger
*
* @param int $memory_used
* @return string
* @access private
*/
private function getShortReport($memory_used)
{
if ( DebuggerUtil::constOn('DBG_TOOLBAR_BUTTONS') ) {
// evenrything is in toolbar - don't duplicate
return '';
}
else {
// toolbar not visible, then show sql & error count too
$info = Array (
'Script Runtime' => 'PROFILE:script_runtime',
'SQL\'s Runtime' => 'PROFILE_T:sql',
'-' => 'SEP:-',
'Notice / Warning' => 'PROFILE_TC:error_handling',
'SQLs Count' => 'PROFILE_TC:sql',
);
}
$ret = ''; // '<tr><td>Application:</td><td><b>' . DebuggerUtil::formatSize($memory_used) . '</b> (' . $memory_used . ')</td></tr>';
foreach ($info as $title => $value_key) {
list ($record_type, $record_data) = explode(':', $value_key, 2);
switch ( $record_type ) {
case 'PROFILE': // profiler totals value
$Data =& $this->ProfilerData[$record_data];
$profile_time = ($Data['ends'] - $Data['begins']); // in seconds
$ret .= '<tr><td>' . $title . ':</td><td><b>' . sprintf('%.5f', $profile_time) . ' s</b></td></tr>';
break;
case 'PROFILE_TC': // profile totals record count
$record_cell = '<td>';
if ( $record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0 ) {
$record_cell = '<td class="debug_error">';
}
$ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . $this->ProfilerTotalCount[$record_data] . '</b></td></tr>';
break;
case 'PROFILE_T': // profile total
$record_cell = '<td>';
$total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0;
$ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . sprintf('%.5f', $total) . ' s</b></td></tr>';
break;
case 'SEP':
$ret .= '<tr><td colspan="2" style="height: 1px; background-color: #000000; padding: 0px;"><img src="' . $this->dummyImage . '" height="1" alt=""/></td></tr>';
break;
}
}
return '<br /><table class="dbg_stats_table"><tr><td style="border-color: #FFFFFF;"><table class="dbg_stats_table" align="left">' . $ret . '</table></td></tr></table>';
}
/**
* User-defined error handler
*
* @throws Exception
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param array $errcontext
* @return bool
* @access public
*/
public function saveError($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
{
$this->ProfilerData['error_handling']['begins'] = memory_get_usage();
$errorType = $this->getErrorNameByCode($errno);
if (!$errorType) {
throw new Exception('Unknown error type [' . $errno . ']');
return false;
}
elseif ( substr($errorType, 0, 5) == 'Fatal' ) {
$this->IsFatalError = true;
$this->appendTrace(4);
}
$this->expandError($errstr, $errfile, $errline);
$this->Data[] = Array (
'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline,
'context' => $errcontext, 'debug_type' => 'error'
);
$this->ProfilerData['error_handling']['ends'] = memory_get_usage();
$this->profilerAddTotal('error_handling', 'error_handling');
if ($errorType == 'Warning') {
$this->WarningCount++;
}
if ( $this->IsFatalError ) {
// append debugger report to data in buffer & clean buffer afterwards
die( $this->breakOutofBuffering(false) . $this->printReport(true) );
}
return true;
}
/**
* Adds exception details into debugger but don't cause fatal error
*
* @param Exception $exception
* @return void
* @access public
*/
public function appendException($exception)
{
$this->ProfilerData['error_handling']['begins'] = memory_get_usage();
$this->appendExceptionTrace($exception);
$errno = $exception->getCode();
$errstr = $exception->getMessage();
$errfile = $exception->getFile();
$errline = $exception->getLine();
$this->expandError($errstr, $errfile, $errline);
$this->Data[] = Array (
'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline,
'exception_class' => get_class($exception), 'debug_type' => 'exception'
);
$this->ProfilerData['error_handling']['ends'] = memory_get_usage();
$this->profilerAddTotal('error_handling', 'error_handling');
}
/**
* User-defined exception handler
*
* @param Exception $exception
* @return void
* @access public
*/
public function saveException($exception)
{
$this->appendException($exception);
$this->IsFatalError = true;
// append debugger report to data in buffer & clean buffer afterwards
die( $this->breakOutofBuffering(false) . $this->printReport(true) );
}
/**
* Transforms short error messages into long ones
*
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return void
* @access private
*/
private function expandError(&$errstr, &$errfile, &$errline)
{
$errstr = kLogger::expandMessage($errstr);
list ($errno, $errstr, $sql) = kLogger::parseDatabaseError($errstr);
if ( $errno != 0 ) {
$errstr = '<span class="debug_error">' . $errstr . ' (' . $errno . ')</span><br/><strong>SQL</strong>: ' . $this->formatSQL($sql);
}
if ( strpos($errfile, 'eval()\'d code') !== false ) {
$errstr = '[<b>EVAL</b>, line <b>' . $errline . '</b>]: ' . $errstr;
$tmpStr = $errfile;
$pos = strpos($tmpStr, '(');
$errfile = substr($tmpStr, 0, $pos);
$pos++;
$errline = substr($tmpStr, $pos, strpos($tmpStr, ')', $pos) - $pos);
}
}
/**
* Break buffering in case if fatal error is happened in the middle
*
* @param bool $flush
* @return string
* @access private
*/
private function breakOutofBuffering($flush = true)
{
$buffer_content = Array ();
while ( ob_get_level() ) {
$buffer_content[] = ob_get_clean();
}
$ret = implode('', array_reverse($buffer_content));
if ( $flush ) {
echo $ret;
flush();
}
return $ret;
}
/**
* Saves given message to "vb_debug.txt" file in DocumentRoot
*
* @param string $msg
* @return void
* @access public
*/
public function saveToFile($msg)
{
$fp = fopen($_SERVER['DOCUMENT_ROOT'] . '/vb_debug.txt', 'a');
fwrite($fp, $msg . "\n");
fclose($fp);
}
/**
* Prints given constant values in a table
*
* @param mixed $constants
* @return void
* @access public
*/
public function printConstants($constants)
{
if ( !is_array($constants) ) {
$constants = explode(',', $constants);
}
$constant_tpl = '<tr><td>%s</td><td><b>%s</b></td></tr>';
$ret = '<table class="dbg_flat_table" style="width: ' . $this->getWindowWidth() . 'px;">';
foreach ($constants as $constant_name) {
$ret .= sprintf($constant_tpl, $constant_name, constant($constant_name));
}
$ret .= '</table>';
$this->appendHTML($ret);
}
/**
* Attaches debugger to Application
*
* @return void
* @access public
*/
public function AttachToApplication()
{
if ( !DebuggerUtil::constOn('DBG_HANDLE_ERRORS') ) {
return;
}
if ( class_exists('kApplication') ) {
$this->Application =& kApplication::Instance();
$this->Application->Debugger = $this;
}
// kLogger will auto-detect these automatically
// error/exception handlers registered before debugger will be removed!
set_error_handler( Array ($this, 'saveError') );
set_exception_handler( Array ($this, 'saveException') );
}
/**
* Returns HTML for tools section
*
* @return string
* @access private
*/
private function _getToolsHTML()
{
$html = '<table>
<tr>
<td>System Tools:</td>
<td>
<select id="reset_cache" style="border: 1px solid #000000;">
<option value=""></option>
<option value="events[adm][OnResetModRwCache]">Reset mod_rewrite Cache</option>
<option value="events[adm][OnResetCMSMenuCache]">Reset SMS Menu Cache</option>
<option value="events[adm][OnResetSections]">Reset Sections Cache</option>
<option value="events[adm][OnResetConfigsCache]">Reset Configs Cache</option>
<option value="events[adm][OnRebuildThemes]">Re-build Themes Files</option>
<option value="events[lang][OnReflectMultiLingualFields]">Re-build Multilanguage Fields</option>
<option value="events[adm][OnDeleteCompiledTemplates]">Delete Compiled Templates</option>
</select>
</td>
<td>
<input type="button" class="button" onclick="$Debugger.resetCache(\'reset_cache\');" value="Go"/>
</td>
</tr>
</table>';
return $html;
}
/**
* Returns HTML for dom viewer section
*
* @return string
* @access private
*/
private function _getDomViewerHTML()
{
$html = '<table>
<tr>
<td>
<a href="http://www.brainjar.com/dhtml/domviewer/" target="_blank">DomViewer</a>:
</td>
<td>
<input id="dbg_domviewer" type="text" value="window" style="border: 1px solid #000000;"/>
</td>
<td>
<button class="button" onclick="return $Debugger.OpenDOMViewer();">Show</button>
</td>
</tr>
</table>';
return $html;
}
}
if ( !function_exists('memory_get_usage') ) {
// PHP 4.x and compiled without --enable-memory-limit option
function memory_get_usage()
{
return -1;
}
}
if ( !DebuggerUtil::constOn('DBG_ZEND_PRESENT') ) {
$debugger = new Debugger();
}
if ( DebuggerUtil::constOn('DBG_USE_SHUTDOWN_FUNC') ) {
register_shutdown_function(Array (&$debugger, 'printReport'));
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/globals.php
===================================================================
--- branches/5.2.x/core/kernel/globals.php (revision 15568)
+++ branches/5.2.x/core/kernel/globals.php (revision 15569)
@@ -1,885 +1,887 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kUtil {
// const KG_TO_POUND = 2.20462262;
const POUND_TO_KG = 0.45359237;
/**
* Similar to array_merge_recursive but keyed-valued are always overwritten.
* Priority goes to the 2nd array.
*
* @param $paArray1 array
* @param $paArray2 array
* @return array
* @access public
*/
public static function array_merge_recursive($paArray1, $paArray2)
{
if (!is_array($paArray1) or !is_array($paArray2)) {
return $paArray2;
}
foreach ($paArray2 AS $sKey2 => $sValue2) {
$paArray1[$sKey2] = isset($paArray1[$sKey2]) ? self::array_merge_recursive($paArray1[$sKey2], $sValue2) : $sValue2;
}
return $paArray1;
}
/**
* Prepend a reference to an element to the beginning of an array.
* Renumbers numeric keys, so $value is always inserted to $array[0]
*
* @param $array array
* @param $value mixed
* @return int
* @access public
*/
public static function array_unshift_ref(&$array, &$value)
{
$return = array_unshift($array,'');
$array[0] =& $value;
return $return;
}
/**
* Rename key in associative array, maintaining keys order
*
* @param Array $array Associative Array
* @param mixed $old Old key name
* @param mixed $new New key name
* @access public
*/
public static function array_rename_key(&$array, $old, $new)
{
$new_array = Array ();
foreach ($array as $key => $val) {
$new_array[ $key == $old ? $new : $key] = $val;
}
$array = $new_array;
}
/**
* Same as print_r, but outputs result on screen or in debugger report (when in debug mode)
*
* @param Array $data
* @param string $label
* @param bool $on_screen
* @access public
*/
public static function print_r($data, $label = '', $on_screen = false)
{
$is_debug = false;
if ( class_exists('kApplication') && !$on_screen ) {
$application =& kApplication::Instance();
$is_debug = $application->isDebugMode();
}
if ( $is_debug && isset($application) ) {
if ( $label ) {
$application->Debugger->appendHTML('<strong>' . $label . '</strong>');
}
$application->Debugger->dumpVars($data);
}
else {
if ( $label ) {
echo '<strong>' . $label . '</strong><br/>';
}
echo '<pre>', print_r($data, true), '</pre>';
}
}
/**
* Define constant if it was not already defined before
*
* @param string $const_name
* @param string $const_value
* @access public
*/
public static function safeDefine($const_name, $const_value)
{
if ( !defined($const_name) ) {
define($const_name, $const_value);
}
}
/**
* Parses "/system/config.php" file and returns the result
*
* @param bool $parse_section
* @return Array
* @access public
*/
public static function parseConfig($parse_section = false)
{
$file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php';
if ( !file_exists($file) ) {
return Array ();
}
if ( file_exists($file) && !is_readable($file) ) {
die('Could Not Open Ini File');
}
$contents = file($file);
if ( $contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n" ) {
// format of "config.php" file before 5.1.0 version
array_shift($contents);
return parse_ini_string(implode('', $contents), $parse_section);
}
$_CONFIG = Array ();
require($file);
if ( $parse_section ) {
if ( isset($_CONFIG['Database']['LoadBalancing']) && $_CONFIG['Database']['LoadBalancing'] ) {
require FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'db_servers.php';
}
return $_CONFIG;
}
$ret = Array ();
foreach ($_CONFIG as $section => $section_variables) {
$ret = array_merge($ret, $section_variables);
}
return $ret;
}
/**
* Returns parsed variables from "config.php" file
*
* @return Array
* @access public
*/
public static function getConfigVars()
{
static $vars = NULL;
if ( !isset($vars) ) {
$vars = self::parseConfig();
}
return $vars;
}
/**
* Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set
*
* @param string $file
* @access public
*/
public static function includeOnce($file)
{
global $debugger;
if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) {
if ( in_array($file, get_included_files()) ) {
return ;
}
global $debugger;
/*$debugger->IncludeLevel++;
$before_mem = memory_get_usage();*/
$debugger->ProfileStart('inc_'.crc32($file), $file);
include_once($file);
$debugger->ProfileFinish('inc_'.crc32($file));
$debugger->profilerAddTotal('includes', 'inc_'.crc32($file));
/*$used_mem = memory_get_usage() - $before_mem;
$debugger->IncludeLevel--;
$debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file);
$debugger->IncludesData['mem'][] = $used_mem;
$debugger->IncludesData['time'][] = $used_time;
$debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/
}
else {
include_once($file);
}
}
/**
* Checks if given string is a serialized array
*
* @param string $string
* @return bool
* @access public
*/
public static function IsSerialized($string)
{
if ( is_array($string) ) {
return false;
}
return preg_match('/a:([\d]+):{/', $string);
}
/**
* Generates password of given length
*
* @param int $length
* @return string
* @access public
*/
public static function generatePassword($length = 10)
{
$pass_length = $length;
$p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z');
$p2 = Array ('a','e','i','o','u');
$p3 = Array ('1','2','3','4','5','6','7','8','9');
$p4 = Array ('(','&',')',';','%'); // if you need real strong stuff
// how much elements in the array
// can be done with a array count but counting once here is faster
$s1 = 21;// this is the count of $p1
$s2 = 5; // this is the count of $p2
$s3 = 9; // this is the count of $p3
$s4 = 5; // this is the count of $p4
// possible readable combinations
$c1 = '121'; // will be like 'bab'
$c2 = '212'; // will be like 'aba'
$c3 = '12'; // will be like 'ab'
$c4 = '3'; // will be just a number '1 to 9' if you dont like number delete the 3
//$c5 = '4'; // uncomment to active the strong stuff
$comb = '4'; // the amount of combinations you made above (and did not comment out)
for ($p = 0; $p < $pass_length;) {
mt_srand((double)microtime() * 1000000);
$strpart = mt_rand(1, $comb);
// checking if the stringpart is not the same as the previous one
if ($strpart != $previous) {
$pass_structure .= ${'c' . $strpart};
// shortcutting the loop a bit
$p = $p + mb_strlen(${'c' . $strpart});
}
$previous = $strpart;
}
// generating the password from the structure defined in $pass_structure
for ($g = 0; $g < mb_strlen($pass_structure); $g++) {
mt_srand((double)microtime() * 1000000);
$sel = mb_substr($pass_structure, $g, 1);
$pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ];
}
return $pass;
}
/**
* submits $url with $post as POST
*
* @param string $url
* @param mixed $data
* @param Array $headers
* @param string $request_type
* @param Array $curl_options
* @return string
* @access public
* @deprecated
*/
public static function curl_post($url, $data, $headers = NULL, $request_type = 'POST', $curl_options = NULL)
{
$application =& kApplication::Instance();
$curl_helper = $application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
if ($request_type == 'POST') {
$curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST);
}
$curl_helper->SetRequestData($data);
if (!is_null($headers)) {
// not an associative array, so don't use kCurlHelper::SetHeaders method
$curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) );
}
if (is_array($curl_options)) {
$curl_helper->setOptions($curl_options);
}
$curl_helper->followLocation = false;
$ret = $curl_helper->Send($url);
$GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode;
$GLOBALS['curl_error'] = $curl_helper->lastErrorMsg;
return $ret;
}
/**
* Checks if constant is defined and has positive value
*
* @param string $const_name
* @return bool
* @access public
*/
public static function constOn($const_name)
{
return defined($const_name) && constant($const_name);
}
/**
* Converts KG to Pounds
*
* @param float $kg
* @param bool $pounds_only
* @return float
* @access public
*/
public static function Kg2Pounds($kg, $pounds_only = false)
{
$major = floor( round($kg / self::POUND_TO_KG, 3) );
$minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2));
if ($pounds_only) {
$major += round($minor * 0.0625, 2);
$minor = 0;
}
return array($major, $minor);
}
/**
* Converts Pounds to KG
*
* @param float $pounds
* @param float $ounces
* @return float
* @access public
*/
public static function Pounds2Kg($pounds, $ounces = 0.00)
{
return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5);
}
/**
* Formats file/memory size in nice way
*
* @param int $bytes
* @return string
* @access public
*/
public static function formatSize($bytes)
{
if ($bytes >= 1099511627776) {
$return = round($bytes / 1024 / 1024 / 1024 / 1024, 2);
$suffix = "TB";
} elseif ($bytes >= 1073741824) {
$return = round($bytes / 1024 / 1024 / 1024, 2);
$suffix = "GB";
} elseif ($bytes >= 1048576) {
$return = round($bytes / 1024 / 1024, 2);
$suffix = "MB";
} elseif ($bytes >= 1024) {
$return = round($bytes / 1024, 2);
$suffix = "KB";
} else {
$return = $bytes;
$suffix = "Byte";
}
$return .= ' '.$suffix;
return $return;
}
/**
* Enter description here...
*
* @param resource $filePointer the file resource to write to
* @param Array $data the data to write out
* @param string $delimiter the field separator
* @param string $enclosure symbol to enclose field data to
* @param string $recordSeparator symbols to separate records with
* @access public
*/
public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
{
fwrite($filePointer, self::getcsvline($data, $delimiter, $enclosure, $recordSeparator));
}
/**
* Enter description here...
*
* @param Array $data the data to write out
* @param string $delimiter the field separator
* @param string $enclosure symbol to enclose field data to
* @param string $recordSeparator symbols to separate records with
* @return string
* @access public
*/
public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
{
foreach($data as $field_index => $field_value) {
// replaces an enclosure with two enclosures
$data[$field_index] = str_replace($enclosure, $enclosure.$enclosure, $field_value);
}
$line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator;
$line = preg_replace('/'.preg_quote($enclosure, '/').'([0-9\.]+)'.preg_quote($enclosure, '/').'/', '$1', $line);
return $line;
}
/**
* Allows to replace #section# within any string with current section
*
* @param string $string
* @return string
* @access public
*/
public static function replaceModuleSection($string)
{
$application =& kApplication::Instance();
$module_section = $application->RecallVar('section');
if ($module_section) {
// substitute section instead of #section# parameter in title preset name
$module_section = explode(':', $module_section);
$section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]);
$string = str_replace('#section#', mb_strtolower($section), $string);
}
return $string;
}
/**
* Checks, that user IP address is within allowed range
*
* @param string $ip_list semi-column (by default) separated ip address list
* @param string $separator ip address separator (default ";")
*
* @return bool
* @access public
*/
public static function ipMatch($ip_list, $separator = ';')
{
- if ( !isset($_SERVER['REMOTE_ADDR']) ) {
- // PHP CLI used -> never match
+ if ( php_sapi_name() == 'cli' ) {
return false;
}
$ip_match = false;
$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();
+ $application =& kApplication::Instance();
+ $client_ip = $application->getClientIp();
+
foreach ($ip_addresses as $ip_address) {
- if (self::netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) {
+ if ( self::netMatch($ip_address, $client_ip) ) {
$ip_match = true;
break;
}
}
return $ip_match;
}
/**
* Checks, that given ip belongs to given subnet
*
* @param string $network
* @param string $ip
* @return bool
* @access public
*/
public static function netMatch($network, $ip)
{
$network = trim($network);
$ip = trim($ip);
if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) {
$network = gethostbyname($network);
}
if ($network == $ip) {
// comparing two ip addresses directly
return true;
}
$d = strpos($network, '-');
if ($d !== false) {
// ip address range specified
$from = ip2long(trim(substr($network, 0, $d)));
$to = ip2long(trim(substr($network, $d + 1)));
$ip = ip2long($ip);
return ($ip >= $from && $ip <= $to);
}
elseif (strpos($network, '/') !== false) {
// single subnet specified
$ip_arr = explode('/', $network);
if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) {
$ip_arr[0] .= '.0'; // Alternate form 194.1.4/24
}
$network_long = ip2long($ip_arr[0]);
$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
$ip_long = ip2long($ip);
return ($ip_long & $mask) == ($network_long & $mask);
}
return false;
}
/**
* Returns mime type corresponding to given file
* @param string $file
* @return string
* @access public
*/
public static function mimeContentType($file)
{
$ret = '';
if ( function_exists('finfo_open') && function_exists('finfo_file') ) {
$mime_magic_resource = finfo_open(FILEINFO_MIME_TYPE);
if ( $mime_magic_resource ) {
$ret = finfo_file($mime_magic_resource, $file);
finfo_close($mime_magic_resource);
}
}
elseif ( function_exists('mime_content_type') ) {
$ret = mime_content_type($file);
}
return $ret ? $ret : self::mimeContentTypeByExtension($file);
}
/**
* Detects mime type of the file purely based on it's extension
*
* @param string $file
* @return string
* @access public
*/
public static function mimeContentTypeByExtension($file)
{
$file_extension = mb_strtolower( pathinfo($file, PATHINFO_EXTENSION) );
$mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf)
(pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf)
(tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript)
(ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip)
(mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap)
(gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff)
(wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml)
(mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822)
(sxw:application/vnd.sun.xml.writer)(sxc:application/vnd.sun.xml.calc)(sxi:application/vnd.sun.xml.impress)
(sxd:application/vnd.sun.xml.draw)(sxm:application/vnd.sun.xml.math)
(odt:application/vnd.oasis.opendocument.text)(oth:application/vnd.oasis.opendocument.text-web)
(odm:application/vnd.oasis.opendocument.text-master)(odg:application/vnd.oasis.opendocument.graphics)
(odp:application/vnd.oasis.opendocument.presentation)(ods:application/vnd.oasis.opendocument.spreadsheet)
(odc:application/vnd.oasis.opendocument.chart)(odf:application/vnd.oasis.opendocument.formula)
(odi:application/vnd.oasis.opendocument.image)';
if ( preg_match('/[\(,]' . $file_extension . '[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs) ) {
return $regs[1];
}
return 'application/octet-stream';
}
/**
* Return param value and removes it from params array
*
* @param string $name
* @param Array $params
* @param bool $default
* @return string
*/
public static function popParam($name, &$params, $default = false)
{
if ( isset($params[$name]) ) {
$value = $params[$name];
unset($params[$name]);
return $value;
}
return $default;
}
/**
* Generate subpath from hashed value
*
* @param string $name
* @param int $levels
* @return string
*/
public static function getHashPathForLevel($name, $levels = 2)
{
if ( $levels == 0 ) {
return '';
}
else {
$path = '';
$hash = md5($name);
for ($i = 0; $i < $levels; $i++) {
$path .= substr($hash, $i, 1) . '/';
}
return $path;
}
}
/**
* Calculates the crc32 polynomial of a string (always positive number)
*
* @param string $str
* @return int
*/
public static function crc32($str)
{
return sprintf('%u', crc32($str));
}
/**
* Returns instance of DateTime class with date set based on timestamp
*
* @static
* @param int $timestamp
* @return DateTime
* @access public
*/
public static function dateFromTimestamp($timestamp)
{
if ( version_compare(PHP_VERSION, '5.3.0', '<') ) {
$date = new DateTime('@' . $timestamp);
$date->setTimezone(new DateTimeZone(getenv('TZ')));
}
else {
$date = new DateTime();
$date->setTimestamp($timestamp);
}
return $date;
}
/**
* Returns timestamp from given DateTime class instance
*
* @static
* @param DateTime $date_time
* @return int|string
* @access public
*/
public static function timestampFromDate(DateTime $date_time)
{
if ( version_compare(PHP_VERSION, '5.3.0', '<') ) {
return $date_time->format('U');
}
return $date_time->getTimestamp();
}
/**
* Generates random numeric id
*
* @static
* @return string
* @access public
*/
public static function generateId()
{
list($usec, $sec) = explode(' ', microtime());
$id_part_1 = substr($usec, 4, 4);
$id_part_2 = mt_rand(1, 9);
$id_part_3 = substr($sec, 6, 4);
$digit_one = substr($id_part_1, 0, 1);
if ( $digit_one == 0 ) {
$digit_one = mt_rand(1, 9);
$id_part_1 = preg_replace('/^0/', '', $id_part_1);
$id_part_1 = $digit_one . $id_part_1;
}
return $id_part_1 . $id_part_2 . $id_part_3;
}
}
/**
* Returns array value if key exists
* Accepts infinite number of parameters
*
* @param Array $array searchable array
* @param int $key array key
* @return string
*/
function getArrayValue(&$array, $key)
{
$ret = isset($array[$key]) ? $array[$key] : false;
if ( $ret && func_num_args() > 2 ) {
for ($i = 2; $i < func_num_args(); $i++) {
$cur_key = func_get_arg($i);
$ret = getArrayValue($ret, $cur_key);
if ( $ret === false ) {
break;
}
}
}
return $ret;
}
if ( !function_exists('parse_ini_string') ) {
/**
* Equivalent for "parse_ini_string" function available since PHP 5.3.0
*
* @param string $ini
* @param bool $process_sections
* @param int $scanner_mode
* @return Array
*/
function parse_ini_string($ini, $process_sections = false, $scanner_mode = NULL)
{
# Generate a temporary file.
$tempname = tempnam('/tmp', 'ini');
$fp = fopen($tempname, 'w');
fwrite($fp, $ini);
$ini = parse_ini_file($tempname, !empty($process_sections));
fclose($fp);
@unlink($tempname);
return $ini;
}
}
if ( !function_exists('memory_get_usage') ) {
// PHP 4.x and compiled without --enable-memory-limit option
function memory_get_usage() { return -1; }
}
if ( !function_exists('imagecreatefrombmp') ) {
// just in case if GD will add this function in future
function imagecreatefrombmp($filename)
{
//Ouverture du fichier en mode binaire
if (! $f1 = fopen($filename,"rb")) return FALSE;
//1 : Chargement des ent�tes FICHIER
$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14));
if ($FILE['file_type'] != 19778) return FALSE;
//2 : Chargement des ent�tes BMP
$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
'/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
'/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40));
$BMP['colors'] = pow(2,$BMP['bits_per_pixel']);
if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
$BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
$BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
$BMP['decal'] = 4-(4*$BMP['decal']);
if ($BMP['decal'] == 4) $BMP['decal'] = 0;
//3 : Chargement des couleurs de la palette
$PALETTE = array();
if ($BMP['colors'] < 16777216)
{
$PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
}
//4 : Cr�ation de l'image
$IMG = fread($f1,$BMP['size_bitmap']);
$VIDE = chr(0);
$res = imagecreatetruecolor($BMP['width'],$BMP['height']);
$P = 0;
$Y = $BMP['height']-1;
while ($Y >= 0)
{
$X=0;
while ($X < $BMP['width'])
{
if ($BMP['bits_per_pixel'] == 24)
$COLOR = unpack("V",substr($IMG,$P,3).$VIDE);
elseif ($BMP['bits_per_pixel'] == 16)
{
$COLOR = unpack("n",substr($IMG,$P,2));
$COLOR[1] = $PALETTE[$COLOR[1]+1];
}
elseif ($BMP['bits_per_pixel'] == 8)
{
$COLOR = unpack("n",$VIDE.substr($IMG,$P,1));
$COLOR[1] = $PALETTE[$COLOR[1]+1];
}
elseif ($BMP['bits_per_pixel'] == 4)
{
$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F);
$COLOR[1] = $PALETTE[$COLOR[1]+1];
}
elseif ($BMP['bits_per_pixel'] == 1)
{
$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7;
elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6;
elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5;
elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4;
elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3;
elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2;
elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1;
elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
$COLOR[1] = $PALETTE[$COLOR[1]+1];
}
else
return FALSE;
imagesetpixel($res,$X,$Y,$COLOR[1]);
$X++;
$P += $BMP['bytes_per_pixel'];
}
$Y--;
$P+=$BMP['decal'];
}
//Fermeture du fichier
fclose($f1);
return $res;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/visits/visits_event_handler.php
===================================================================
--- branches/5.2.x/core/units/visits/visits_event_handler.php (revision 15568)
+++ branches/5.2.x/core/units/visits/visits_event_handler.php (revision 15569)
@@ -1,143 +1,143 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class VisitsEventHandler extends kDBEventHandler {
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnItemBuild' => Array ('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Registers user visit to site
*
* @param kEvent $event
*
* @return void
* @access protected
*/
protected function OnRegisterVisit($event)
{
if ( $this->Application->isAdmin || !$this->Application->ConfigValue('UseVisitorTracking') || $this->Application->RecallVar('visit_id') ) {
// admin logins are not registered in visits list
return ;
}
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$object->SetDBField('VisitDate_date', adodb_mktime());
$object->SetDBField('VisitDate_time', adodb_mktime());
$object->SetDBField('Referer', getArrayValue($_SERVER, 'HTTP_REFERER'));
- $object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('IPAddress', $this->Application->getClientIp());
if ( $object->Create() ) {
$this->Application->StoreVar('visit_id', $object->GetID());
$this->Application->SetVar('visits_id', $object->GetID());
}
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
$types = $event->getEventParam('types');
if ( $types == 'myvisitors' ) {
$user_id = $this->Application->RecallVar('user_id');
$object->addFilter('myitems_user1', 'au.PortalUserId = ' . $user_id);
$object->addFilter('myitems_user2', 'au.PortalUserId >0');
//$object->AddGroupByField('VisitDate');
$object->AddGroupByField('%1$s.VisitId');
}
if ( $types == 'myvisitororders' && $event->Special == 'incommerce' ) {
$user_id = $this->Application->RecallVar('user_id');
$object->addFilter('myitems_orders', 'ord.OrderId IS NOT NULL');
$object->addFilter('myitems_user1', 'au.PortalUserId = ' . $user_id);
$object->addFilter('myitems_user2', 'au.PortalUserId >0');
$object->addFilter('myitems_orders_processed', 'ord.Status = 4');
}
}
/**
* Apply some special processing to object being
* recalled before using it in other events that
* call prepareObject
*
* @param kDBItem|kDBList $object
* @param kEvent $event
* @return void
* @access protected
*/
protected function prepareObject(&$object, kEvent $event)
{
$types = $event->getEventParam('types');
if ( method_exists($object, 'AddGroupByField') ) {
if ( (!$types || $types == 'myvisitors') && $object->Special == 'incommerce' ) {
$object->addCalculatedField('OrderTotalAmountSum', 'SUM(IF(ord.Status = 4, ord.SubTotal+ord.ShippingCost+ord.VAT, 0))');
$object->addCalculatedField('OrderAffiliateCommissionSum', 'SUM( IF(ord.Status = 4,ord.AffiliateCommission,0))');
$object->addCalculatedField('OrderCountByVisit', 'SUM( IF(ord.Status = 4, 1, 0) )');
}
if ( !$types ) {
$object->AddGroupByField('%1$s.VisitId');
}
}
}
/**
* [HOOK] Updates user_id in current visit
*
* @param kEvent $event
*/
function OnUserLogin($event)
{
if ($event->MasterEvent->status == kEvent::erSUCCESS) {
$user_id = $this->Application->RecallVar('user_id');
if ($user_id > 0) {
// for real users only, not root,guest
$this->Application->setVisitField('PortalUserId', $user_id);
}
}
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/users/users_event_handler.php
===================================================================
--- branches/5.2.x/core/units/users/users_event_handler.php (revision 15568)
+++ branches/5.2.x/core/units/users/users_event_handler.php (revision 15569)
@@ -1,1970 +1,1970 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class UsersEventHandler extends kDBEventHandler
{
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
// admin
'OnSetPersistantVariable' => Array('self' => 'view'), // because setting to logged in user only
'OnUpdateRootPassword' => Array('self' => true),
'OnUpdatePassword' => Array('self' => true),
'OnSaveSelected' => Array ('self' => 'view'),
'OnGeneratePassword' => Array ('self' => 'view'),
// front
'OnRefreshForm' => Array('self' => true),
'OnForgotPassword' => Array('self' => true),
'OnSubscribeQuery' => Array('self' => true),
'OnSubscribeUser' => Array('self' => true),
'OnRecommend' => Array('self' => true),
'OnItemBuild' => Array('self' => true),
'OnMassResetSettings' => Array('self' => 'edit'),
'OnMassCloneUsers' => Array('self' => 'add'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Returns fields, that are not allowed to be changed from request
*
* @param Array $hash
* @return Array
* @access protected
*/
protected function getRequestProtectedFields($hash)
{
$fields = parent::getRequestProtectedFields($hash);
$fields = array_merge($fields, Array ('PrevEmails', 'ResourceId', 'IPAddress', 'IsBanned', 'PwResetConfirm', 'PwRequestTime', 'OldStyleLogin'));
if ( !$this->Application->isAdmin ) {
$fields = array_merge($fields, Array ('UserType', 'Status', 'EmailVerified', 'IsBanned'));
}
return $fields;
}
/**
* Builds item (loads if needed)
*
* Pattern: Prototype Manager
*
* @param kEvent $event
* @access protected
*/
protected function OnItemBuild(kEvent $event)
{
parent::OnItemBuild($event);
$object = $event->getObject();
/* @var $object kDBItem */
if ( $event->Special == 'forgot' || $object->getFormName() == 'registration' ) {
$this->_makePasswordRequired($event);
}
}
/**
* Shows only admins when required
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
if ( $event->Special == 'regular' ) {
$object->addFilter('primary_filter', '%1$s.UserType = ' . UserType::USER);
}
if ( $event->Special == 'admins' ) {
$object->addFilter('primary_filter', '%1$s.UserType = ' . UserType::ADMIN);
}
if ( !$this->Application->isAdminUser ) {
$object->addFilter('status_filter', '%1$s.Status = ' . STATUS_ACTIVE);
}
if ( $event->Special == 'online' ) {
$object->addFilter('online_users_filter', 's.PortalUserId IS NOT NULL');
}
if ( $event->Special == 'group' ) {
$group_id = $this->Application->GetVar('g_id');
if ( $group_id !== false ) {
// show only users, that user doesn't belong to current group
$sql = 'SELECT PortalUserId
FROM ' . $this->Application->GetTempName(TABLE_PREFIX . 'UserGroupRelations', 'prefix:g') . '
WHERE GroupId = ' . (int)$group_id;
$user_ids = $this->Conn->GetCol($sql);
if ( $user_ids ) {
$object->addFilter('already_member_filter', '%1$s.PortalUserId NOT IN (' . implode(',', $user_ids) . ')');
}
}
}
}
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( $event->Name == 'OnLogin' || $event->Name == 'OnLoginAjax' || $event->Name == 'OnLogout' ) {
// permission is checked in OnLogin event directly
return true;
}
if ( $event->Name == 'OnResetRootPassword' ) {
return defined('DBG_RESET_ROOT') && DBG_RESET_ROOT;
}
if ( $event->Name == 'OnLoginAs' ) {
$admin_session = $this->Application->recallObject('Session.admin');
/* @var $admin_session Session */
return $admin_session->LoggedIn();
}
if ( !$this->Application->isAdminUser ) {
$user_id = $this->Application->RecallVar('user_id');
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( ($event->Name == 'OnCreate' || $event->Name == 'OnRegisterAjax') && $user_id == USER_GUEST ) {
// "Guest" can create new users
return true;
}
if ( $event->Name == 'OnUpdate' && $user_id > 0 ) {
$user_dummy = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true));
/* @var $user_dummy UsersItem */
foreach ($items_info as $id => $field_values) {
if ( $id != $user_id ) {
// registered users can update their record only
return false;
}
$user_dummy->Load($id);
$status_field = $user_dummy->getStatusField();
if ( $user_dummy->GetDBField($status_field) != STATUS_ACTIVE ) {
// not active user is not allowed to update his record (he could not activate himself manually)
return false;
}
if ( isset($field_values[$status_field]) && $user_dummy->GetDBField($status_field) != $field_values[$status_field] ) {
// user can't change status by himself
return false;
}
}
return true;
}
if ( $event->Name == 'OnResetLostPassword' && $event->Special == 'forgot' && $user_id == USER_GUEST ) {
// non-logged in users can reset their password, when reset code is valid
return is_numeric($this->getPassedID($event));
}
if ( $event->Name == 'OnUpdate' && $user_id <= 0 ) {
// guests are not allowed to update their record, because they don't have it :)
return false;
}
}
return parent::CheckPermission($event);
}
/**
* Handles session expiration (redirects to valid template)
*
* @param kEvent $event
*/
function OnSessionExpire($event)
{
$this->Application->resetCounters('UserSessions');
// place 2 of 2 (also in kHTTPQuery::getRedirectParams)
$admin_url_params = Array (
'm_cat_id' => 0, // category means nothing on admin login screen
'm_wid' => '', // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets)
'pass' => 'm', // don't pass any other (except "m") prefixes to admin session expiration template
'expired' => 1, // expiration mark to show special error on login screen
'no_pass_through' => 1, // this way kApplication::HREF won't add them again
);
if ($this->Application->isAdmin) {
$this->Application->Redirect('index', $admin_url_params, '', 'index.php');
}
if ($this->Application->GetVar('admin') == 1) {
// Front-End showed in admin's right frame
$session_admin = $this->Application->recallObject('Session.admin');
/* @var $session_admin Session */
if (!$session_admin->LoggedIn()) {
// front-end session created from admin session & both expired
$this->Application->DeleteVar('admin');
$this->Application->Redirect('index', $admin_url_params, '', 'admin/index.php');
}
}
// Front-End session expiration
$get = $this->Application->HttpQuery->getRedirectParams();
$t = $this->Application->GetVar('t');
$get['js_redirect'] = $this->Application->ConfigValue('UseJSRedirect');
$this->Application->Redirect($t ? $t : 'index', $get);
}
/**
* [SCHEDULED TASK] Deletes expired sessions
*
* @param kEvent $event
*/
function OnDeleteExpiredSessions($event)
{
if (defined('IS_INSTALL') && IS_INSTALL) {
return ;
}
$this->Application->Session->DeleteExpired();
}
/**
* Checks user data and logs it in if allowed
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLogin($event)
{
$object = $event->getObject( Array ('form_name' => 'login') );
/* @var $object kDBItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$username = $object->GetDBField('UserLogin');
$password = $object->GetDBField('UserPassword');
$remember_login = $object->GetDBField('UserRememberLogin') == 1;
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->event =& $event;
$result = $user_helper->loginUser($username, $password, false, $remember_login);
if ($result != LoginResult::OK) {
$event->status = kEvent::erFAIL;
$object->SetError('UserLogin', $result == LoginResult::NO_PERMISSION ? 'no_permission' : 'invalid_password');
}
if ( is_object($event->MasterEvent) && ($event->MasterEvent->Name == 'OnLoginAjax') ) {
// used to insert just logged-in user e-mail on "One Step Checkout" form in "Modern Store" theme
$user =& $user_helper->getUserObject();
$event->SetRedirectParam('user_email', $user->GetDBField('Email'));
}
}
/**
* Performs user login from ajax request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLoginAjax($event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnLogin'); //, Array ('do_refresh' => 1));
}
/**
* [HOOK] Auto-Logins Front-End user when "Remember Login" cookie is found
*
* @param kEvent $event
*/
function OnAutoLoginUser($event)
{
$remember_login_cookie = $this->Application->GetVar('remember_login');
if (!$remember_login_cookie || $this->Application->isAdmin || $this->Application->LoggedIn()) {
return ;
}
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->loginUser('', '', false, false, $remember_login_cookie);
}
/**
* Called when user logs in using old in-portal
*
* @param kEvent $event
*/
function OnInpLogin($event)
{
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('LoginUser', $event->getEventParam('user'), $event->getEventParam('pass') );
if ($event->redirect && is_string($event->redirect)) {
// some real template specified instead of true
$this->Application->Redirect($event->redirect, $event->getRedirectParams());
}
}
/**
* Called when user logs in using old in-portal
*
* @param kEvent $event
*/
function OnInpLogout($event)
{
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('LogoutUser');
}
/**
* Performs user logout
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLogout($event)
{
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->event =& $event;
$user_helper->logoutUser();
}
/**
* Redirects user after successful registration to confirmation template (on Front only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->afterItemChanged($event);
$this->assignToPrimaryGroup($event);
}
/**
* Performs user registration
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
if ( $this->Application->isAdmin ) {
parent::OnCreate($event);
return ;
}
$object = $event->getObject( Array('form_name' => 'registration') );
/* @var $object UsersItem */
$field_values = $this->getSubmittedFields($event);
$user_email = getArrayValue($field_values, 'Email');
$subscriber_id = $user_email ? $this->getSubscriberByEmail($user_email) : false;
if ( $subscriber_id ) {
// update existing subscriber
$object->Load($subscriber_id);
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup'));
$this->Application->SetVar($event->getPrefixSpecial(true), Array ($object->GetID() => $field_values));
}
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$status = $object->isLoaded() ? $object->Update() : $object->Create();
if ( !$status ) {
$event->status = kEvent::erFAIL;
$event->redirect = false;
$object->setID( (int)$object->GetID() );
}
$this->setNextTemplate($event, true);
if ( ($event->status == kEvent::erSUCCESS) && $event->redirect ) {
$this->assignToPrimaryGroup($event);
$object->SendEmailEvents();
$this->autoLoginUser($event);
}
}
/**
* Processes user registration from ajax request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRegisterAjax(kEvent $event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnCreate', Array ('do_refresh' => 1));
}
/**
* Returns subscribed user ID by given e-mail address
*
* @param string $email
* @return int|bool
* @access protected
*/
protected function getSubscriberByEmail($email)
{
$verify_user = $this->Application->recallObject('u.verify', null, Array ('skip_autoload' => true));
/* @var $verify_user UsersItem */
$verify_user->Load($email, 'Email');
return $verify_user->isLoaded() && $verify_user->isSubscriberOnly() ? $verify_user->GetID() : false;
}
/**
* Login user if possible, if not then redirect to corresponding template
*
* @param kEvent $event
*/
function autoLoginUser($event)
{
$object = $event->getObject();
/* @var $object UsersItem */
if ( $object->GetDBField('Status') == STATUS_ACTIVE ) {
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load($object->GetID());
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
/**
* Set's new unique resource id to user
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$this->beforeItemChanged($event);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$object->isSubscriberOnly() ) {
// don't check state-to-country relations for subscribers
$cs_helper->CheckStateField($event, 'State', 'Country');
}
if ( $object->getFormName() != 'login' ) {
$this->_makePasswordRequired($event);
}
$cs_helper->PopulateStates($event, 'State', 'Country');
$this->setUserGroup($object);
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
if ( !$user_helper->checkBanRules($object) ) {
$object->SetError('Username', 'banned');
}
- $object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('IPAddress', $this->Application->getClientIp());
if ( !$this->Application->isAdmin ) {
$object->SetDBField('FrontLanguage', $this->Application->GetVar('m_lang'));
}
}
/**
* Sets primary group of the user
*
* @param kDBItem $object
*/
protected function setUserGroup(&$object)
{
if ($object->Special == 'subscriber') {
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_SubscriberGroup'));
return ;
}
// set primary group to user
if ( !$this->Application->isAdminUser ) {
$group_id = $object->GetDBField('PrimaryGroupId');
if ($group_id) {
// check, that group is allowed for Front-End
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroups
WHERE GroupId = ' . (int)$group_id . ' AND FrontRegistration = 1';
$group_id = $this->Conn->GetOne($sql);
}
if (!$group_id) {
// when group not selected OR not allowed -> use default group
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup'));
}
}
}
/**
* Assigns a user to it's primary group
*
* @param kEvent $event
*/
protected function assignToPrimaryGroup($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$primary_group_id = $object->GetDBField('PrimaryGroupId');
if ($primary_group_id) {
$ug_table = TABLE_PREFIX . 'UserGroupRelations';
if ( $object->IsTempTable() ) {
$ug_table = $this->Application->GetTempName($ug_table, 'prefix:' . $event->Prefix);
}
$fields_hash = Array (
'PortalUserId' => $object->GetID(),
'GroupId' => $primary_group_id,
);
$this->Conn->doInsert($fields_hash, $ug_table, 'REPLACE');
}
}
/**
* Set's new unique resource id to user
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemValidate(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$resource_id = $object->GetDBField('ResourceId');
if ( !$resource_id ) {
$object->SetDBField('ResourceId', $this->Application->NextResourceId());
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnRecommend($event)
{
$object = $event->getObject( Array ('form_name' => 'recommend') );
/* @var $object kDBItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if ( !$object->ValidateField('RecommendEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$send_params = Array (
'to_email' => $object->GetDBField('RecommendEmail'),
'to_name' => $object->GetDBField('RecommendEmail'),
);
$user_id = $this->Application->RecallVar('user_id');
$email_sent = $this->Application->EmailEventUser('USER.SUGGEST', $user_id, $send_params);
$this->Application->EmailEventAdmin('USER.SUGGEST');
if ( $email_sent ) {
$event->SetRedirectParam('pass', 'all');
$event->redirect = $this->Application->GetVar('template_success');
}
else {
$event->status = kEvent::erFAIL;
$object->SetError('RecommendEmail', 'send_error');
}
}
/**
* Saves address changes and mades no redirect
*
* @param kEvent $event
*/
function OnUpdateAddress($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);
if ( $id > 0 ) {
$object->Load($id);
}
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$object->setID($id);
$object->Validate();
}
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'State', 'Country');
$event->redirect = false;
}
/**
* Validate subscriber's email & store it to session -> redirect to confirmation template
*
* @param kEvent $event
*/
function OnSubscribeQuery($event)
{
$object = $event->getObject( Array ('form_name' => 'subscription') );
/* @var $object UsersItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if ( !$object->ValidateField('SubscriberEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$user_email = $object->GetDBField('SubscriberEmail');
$object->Load($user_email, 'Email');
$event->SetRedirectParam('subscriber_email', $user_email);
if ( $object->isLoaded() && $object->isSubscribed() ) {
$event->redirect = $this->Application->GetVar('unsubscribe_template');
}
else {
$event->redirect = $this->Application->GetVar('subscribe_template');
}
$event->SetRedirectParam('pass', 'm');
}
/**
* Subscribe/Unsubscribe user based on email stored in previous step
*
* @param kEvent $event
*/
function OnSubscribeUser($event)
{
$object = $event->getObject( Array ('form_name' => 'subscription') );
/* @var $object UsersItem */
$user_email = $this->Application->GetVar('subscriber_email');
$object->SetDBField('SubscriberEmail', $user_email);
if ( !$object->ValidateField('SubscriberEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$username_required = $object->isRequired('Username');
$this->RemoveRequiredFields($object);
$object->Load($user_email, 'Email');
if ( $object->isLoaded() ) {
if ( $object->isSubscribed() ) {
if ( $event->getEventParam('no_unsubscribe') ) {
// for customization code from FormsEventHandler
return ;
}
if ( $object->isSubscriberOnly() ) {
$temp_handler = $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, '', Array($object->GetID()));
}
else {
$this->RemoveSubscriberGroup( $object->GetID() );
}
$event->redirect = $this->Application->GetVar('unsubscribe_ok_template');
}
else {
$this->AddSubscriberGroup($object);
$event->redirect = $this->Application->GetVar('subscribe_ok_template');
}
}
else {
$object->generatePassword();
$object->SetDBField('Email', $user_email);
if ( $username_required ) {
$object->SetDBField('Username', str_replace('@', '_at_', $user_email));
}
$object->SetDBField('Status', STATUS_ACTIVE); // make user subscriber Active by default
if ( $object->Create() ) {
$this->AddSubscriberGroup($object);
$event->redirect = $this->Application->GetVar('subscribe_ok_template');
}
}
}
/**
* Adding user to subscribers group
*
* @param UsersItem $object
*/
function AddSubscriberGroup(&$object)
{
if ( !$object->isSubscriberOnly() ) {
$fields_hash = Array (
'PortalUserId' => $object->GetID(),
'GroupId' => $this->Application->ConfigValue('User_SubscriberGroup'),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserGroupRelations');
}
$this->Application->EmailEventAdmin('USER.SUBSCRIBE');
$this->Application->EmailEventUser('USER.SUBSCRIBE', $object->GetID());
}
/**
* Removing user from subscribers group
*
* @param int $user_id
*/
function RemoveSubscriberGroup($user_id)
{
$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . $user_id . ' AND GroupId = ' . $group_id;
$this->Conn->Query($sql);
$this->Application->EmailEventAdmin('USER.UNSUBSCRIBE');
$this->Application->EmailEventUser('USER.UNSUBSCRIBE', $user_id);
}
/**
* Validates forgot password form and sends password reset confirmation e-mail
*
* @param kEvent $event
* @return void
*/
function OnForgotPassword($event)
{
$object = $event->getObject( Array ('form_name' => 'forgot_password') );
/* @var $object kDBItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$user_object = $this->Application->recallObject('u.tmp', null, Array('skip_autoload' => true));
/* @var $user_object UsersItem */
$found = $allow_reset = false;
$username = $object->GetDBField('ForgotLogin');
$email = $object->GetDBField('ForgotEmail');
if ( strlen($username) ) {
$user_object->Load($username, 'Username');
}
elseif ( strlen($email) ) {
$user_object->Load($email, 'Email');
}
if ( $user_object->isLoaded() ) {
$min_pwd_reset_delay = $this->Application->ConfigValue('Users_AllowReset');
$found = ($user_object->GetDBField('Status') == STATUS_ACTIVE) && strlen( $user_object->GetDBField('Password') );
if ( !$user_object->GetDBField('PwResetConfirm') ) {
// no reset made -> allow
$allow_reset = true;
}
else {
// reset made -> wait N minutes, then allow
$allow_reset = adodb_mktime() > $user_object->GetDBField('PwRequestTime') + $min_pwd_reset_delay;
}
}
if ($found && $allow_reset) {
$this->Application->EmailEventUser('USER.PSWDC', $user_object->GetID());
$event->redirect = $this->Application->GetVar('template_success');
return ;
}
if ( !strlen($username) && !strlen($email) ) {
$object->SetError('ForgotLogin', 'required');
$object->SetError('ForgotEmail', 'required');
}
else {
if ( strlen($username) ) {
$object->SetError('ForgotLogin', $found ? 'reset_denied' : 'unknown_username');
}
if ( strlen($email) ) {
$object->SetError('ForgotEmail', $found ? 'reset_denied' : 'unknown_email');
}
}
if ( !$object->ValidateField('ForgotLogin') || !$object->ValidateField('ForgotEmail') ) {
$event->status = kEvent::erFAIL;
}
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
parent::OnUpdate($event);
if ( !$this->Application->isAdmin ) {
$this->setNextTemplate($event);
}
}
/**
* Checks state against country
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->beforeItemChanged($event);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->CheckStateField($event, 'State', 'Country');
$cs_helper->PopulateStates($event, 'State', 'Country');
$object = $event->getObject();
/* @var $object kDBItem */
if ( $event->Special == 'forgot' ) {
$object->SetDBField('PwResetConfirm', '');
$object->SetDBField('PwRequestTime_date', NULL);
$object->SetDBField('PwRequestTime_time', NULL);
}
$changed_fields = array_keys($object->GetChangedFields());
if ( $changed_fields && !in_array('Modified', $changed_fields) ) {
$object->SetDBField('Modified_date', adodb_mktime());
$object->SetDBField('Modified_time', adodb_mktime());
}
if ( !$this->Application->isAdmin && in_array('Email', $changed_fields) && ($event->Special != 'email-restore') ) {
$object->SetDBField('EmailVerified', 0);
}
}
/**
* Occurs before item is changed
*
* @param kEvent $event
*/
function beforeItemChanged($event)
{
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$this->Application->isAdmin && $object->getFormName() == 'registration' ) {
// sets new user's status based on config options
$status_map = Array (1 => STATUS_ACTIVE, 2 => STATUS_DISABLED, 3 => STATUS_PENDING, 4 => STATUS_PENDING);
$object->SetDBField('Status', $status_map[ $this->Application->ConfigValue('User_Allow_New') ]);
if ( $this->Application->ConfigValue('User_Password_Auto') ) {
$object->generatePassword( rand(5, 8) );
}
if ( $this->Application->ConfigValue('RegistrationCaptcha') ) {
$captcha_helper = $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
$captcha_helper->validateCode($event, false);
}
if ( $event->Name == 'OnBeforeItemUpdate' ) {
// when a subscriber-only users performs normal registration, then assign him to Member group
$this->setUserGroup($object);
}
}
}
/**
* Sets redirect template based on user status & user request contents
*
* @param kEvent $event
* @param bool $for_registration
*/
function setNextTemplate($event, $for_registration = false)
{
$event->SetRedirectParam('opener', 's');
$object = $event->getObject();
/* @var $object UsersItem */
$next_template = false;
if ( $object->GetDBField('Status') == STATUS_ACTIVE && $this->Application->GetVar('next_template') ) {
$next_template = $this->Application->GetVar('next_template');
}
elseif ( $for_registration ) {
switch ( $this->Application->ConfigValue('User_Allow_New') ) {
case 1: // Immediate
$next_template = $this->Application->GetVar('registration_confirm_template');
break;
case 3: // Upon Approval
case 4: // Email Activation
$next_template = $this->Application->GetVar('registration_confirm_pending_template');
break;
}
}
if ($next_template) {
$event->redirect = $next_template;
}
}
/**
* Delete users from groups if their membership is expired
*
* @param kEvent $event
*/
function OnCheckExpiredMembership($event)
{
// send pre-expiration reminders: begin
$pre_expiration = adodb_mktime() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24;
$sql = 'SELECT PortalUserId, GroupId
FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (ExpirationReminderSent = 0) AND (MembershipExpires < '.$pre_expiration.')';
$skip_clause = $event->getEventParam('skip_clause');
if ($skip_clause) {
$sql .= ' AND !('.implode(') AND !(', $skip_clause).')';
}
$records = $this->Conn->Query($sql);
if ($records) {
$conditions = Array();
foreach ($records as $record) {
$this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRATION.NOTICE', $record['PortalUserId']);
$this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE');
$conditions[] = '(PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'].')';
}
$sql = 'UPDATE '.TABLE_PREFIX.'UserGroupRelations
SET ExpirationReminderSent = 1
WHERE '.implode(' OR ', $conditions);
$this->Conn->Query($sql);
}
// send pre-expiration reminders: end
// remove users from groups with expired membership: begin
$sql = 'SELECT PortalUserId
FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
$user_ids = $this->Conn->GetCol($sql);
if ($user_ids) {
foreach ($user_ids as $id) {
$this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRED', $id);
$this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRED');
}
}
$sql = 'DELETE FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
$this->Conn->Query($sql);
// remove users from groups with expired membership: end
}
/**
* Used to keep user registration form data, while showing affiliate registration form fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRefreshForm($event)
{
$event->redirect = false;
$item_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list($id, $field_values) = each($item_info);
$object = $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$object->setID($id);
$object->IgnoreValidation = true;
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
}
/**
* Sets persistant variable
*
* @param kEvent $event
*/
function OnSetPersistantVariable($event)
{
$field = $this->Application->GetVar('field');
$value = $this->Application->GetVar('value');
$this->Application->StorePersistentVar($field, $value);
$force_tab = $this->Application->GetVar('SetTab');
if ($force_tab) {
$this->Application->StoreVar('force_tab', $force_tab);
}
}
/**
* Return user from order by special .ord
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent $event)
{
switch ($event->Special) {
case 'ord':
$order = $this->Application->recallObject('ord');
/* @var $order OrdersItem */
return $order->GetDBField('PortalUserId');
break;
case 'profile':
$id = $this->Application->GetVar('user_id');
if ( !$id ) {
// if none user_id given use current user id
$id = $this->Application->RecallVar('user_id');
}
return $id;
break;
case 'forgot':
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$id = $user_helper->validateUserCode($this->Application->GetVar('user_key'), 'forgot_password');
if ( is_numeric($id) ) {
return $id;
}
break;
}
if ( preg_match('/^(login|register|recommend|subscribe|forgot)/', $event->Special) ) {
// this way we can have 2+ objects stating with same special, e.g. "u.login-sidebox" and "u.login-main"
return USER_GUEST;
}
return parent::getPassedID($event);
}
/**
* Allows to change root password
*
* @param kEvent $event
*/
function OnUpdateRootPassword($event)
{
$this->OnUpdatePassword($event);
}
/**
* Allows to change root password
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdatePassword($event)
{
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( !$items_info ) {
return;
}
list ($id, $field_values) = each($items_info);
$user_id = $this->Application->RecallVar('user_id');
if ( $id == $user_id && ($user_id > 0 || $user_id == USER_ROOT) ) {
$user_dummy = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true));
/* @var $user_dummy kDBItem */
$user_dummy->Load($id);
$status_field = $user_dummy->getStatusField();
if ( $user_dummy->GetDBField($status_field) != STATUS_ACTIVE ) {
// not active user is not allowed to update his record (he could not activate himself manually)
return ;
}
}
if ( $user_id == USER_ROOT ) {
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object UsersItem */
// put salt to user's config
$field_options = $object->GetFieldOptions('RootPassword');
$field_options['salt'] = 'b38';
// this is internal hack to allow root/root passwords for dev
if ( $this->Application->isDebugMode() && $field_values['RootPassword'] == 'root' ) {
$field_options['min_length'] = 4;
}
$object->SetFieldOptions('RootPassword', $field_options);
$verify_options = $object->GetFieldOptions('VerifyRootPassword');
$verify_options['salt'] = 'b38';
$object->SetFieldOptions('VerifyRootPassword', $verify_options);
$this->RemoveRequiredFields($object);
$object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass'));
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$object->setID(-1);
if ( $object->Validate() ) {
// validation on, password match too
$fields_hash = Array ('VariableValue' => $object->GetDBField('RootPassword'));
$conf_table = $this->Application->getUnitOption('conf', 'TableName');
$this->Conn->doUpdate($fields_hash, $conf_table, 'VariableName = "RootPass"');
$event->SetRedirectParam('opener', 'u');
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
return ;
}
}
else {
$object = $event->getObject();
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if ( !$object->Update() ) {
$event->status = kEvent::erFAIL;
$event->redirect = false;
}
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Resets grid settings, remembered in each user record
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnMassResetSettings($event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$ids = $this->StoreSelectedIDs($event);
$default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId');
if ( in_array($default_user_id, $ids) ) {
array_splice($ids, array_search($default_user_id, $ids), 1);
}
if ( $ids ) {
$q = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId IN (' . join(',', $ids) . ') AND
(VariableName LIKE "%_columns_%"
OR
VariableName LIKE "%_filter%"
OR
VariableName LIKE "%_PerPage%")';
$this->Conn->Query($q);
}
$this->clearSelectedIDs($event);
}
/**
* Checks, that currently loaded item is allowed for viewing (non permission-based)
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function checkItemStatus(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
if ( !$object->isLoaded() ) {
return true;
}
$virtual_users = Array (USER_ROOT, USER_GUEST);
return ($object->GetDBField('Status') == STATUS_ACTIVE) || in_array($object->GetID(), $virtual_users);
}
/**
* Sends approved/declined email event on user status change
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$this->afterItemChanged($event);
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$this->Application->isAdmin && ($event->Special != 'email-restore') ) {
$this->sendEmailChangeEvent($event);
}
if ( !$this->Application->isAdmin || $object->IsTempTable() ) {
return;
}
$this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status'));
}
/**
* Occurs, after item is changed
*
* @param kEvent $event
*/
protected function afterItemChanged($event)
{
$this->saveUserImages($event);
$object = $event->getObject();
/* @var $object UsersItem */
if ( $object->GetDBField('EmailPassword') && $object->GetDBField('Password_plain') ) {
$email_passwords = $this->Application->RecallVar('email_passwords');
$email_passwords = $email_passwords ? unserialize($email_passwords) : Array ();
$email_passwords[ $object->GetID() ] = $object->GetDBField('Password_plain');
$this->Application->StoreVar('email_passwords', serialize($email_passwords));
}
// update user subscription status (via my profile or new user registration)
if ( !$this->Application->isAdmin && !$object->isSubscriberOnly() ) {
if ( $object->GetDBField('SubscribeToMailing') && !$object->isSubscribed() ) {
$this->AddSubscriberGroup($object);
}
elseif ( !$object->GetDBField('SubscribeToMailing') && $object->isSubscribed() ) {
$this->RemoveSubscriberGroup( $object->GetID() );
}
}
}
/**
* Stores user's original Status before overwriting with data from temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteFromLive(kEvent $event)
{
parent::OnBeforeDeleteFromLive($event);
$user_id = $event->getEventParam('id');
$user_status = $this->Application->GetVar('user_status', Array ());
if ( $user_id > 0 ) {
$user_status[$user_id] = $this->getUserStatus($user_id);
$this->Application->SetVar('user_status', $user_status);
}
}
/**
* Sends approved/declined email event on user status change (in temp tables during editing)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterCopyToLive(kEvent $event)
{
parent::OnAfterCopyToLive($event);
$temp_id = $event->getEventParam('temp_id');
$email_passwords = $this->Application->RecallVar('email_passwords');
if ( $email_passwords ) {
$email_passwords = unserialize($email_passwords);
if ( isset($email_passwords[$temp_id]) ) {
$object = $event->getObject();
/* @var $object kDBItem */
$object->SwitchToLive();
$object->Load( $event->getEventParam('id') );
$object->SetField('Password', $email_passwords[$temp_id]);
$object->SetField('VerifyPassword', $email_passwords[$temp_id]);
$this->Application->EmailEventUser($temp_id > 0 ? 'USER.NEW.PASSWORD': 'USER.ADD.BYADMIN', $object->GetID());
unset($email_passwords[$temp_id]);
$this->Application->StoreVar('email_passwords', serialize($email_passwords));
}
}
if ( $temp_id > 0 ) {
// only send status change e-mail on user update
$new_status = $this->getUserStatus($temp_id);
$user_status = $this->Application->GetVar('user_status');
$this->sendStatusChangeEvent($temp_id, $user_status[$temp_id], $new_status);
}
}
/**
* Returns user status (active, pending, disabled) based on ID and temp mode setting
*
* @param int $user_id
* @return int
*/
function getUserStatus($user_id)
{
$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
$sql = 'SELECT Status
FROM '.$table_name.'
WHERE '.$id_field.' = '.$user_id;
return $this->Conn->GetOne($sql);
}
/**
* Sends approved/declined email event on user status change
*
* @param int $user_id
* @param int $prev_status
* @param int $new_status
*/
function sendStatusChangeEvent($user_id, $prev_status, $new_status)
{
$status_events = Array (
STATUS_ACTIVE => 'USER.APPROVE',
STATUS_DISABLED => 'USER.DENY',
);
$email_event = isset($status_events[$new_status]) ? $status_events[$new_status] : false;
if (($prev_status != $new_status) && $email_event) {
$this->Application->EmailEventUser($email_event, $user_id);
$this->Application->EmailEventAdmin($email_event);
}
// deletes sessions from users, that are no longer active
if (($prev_status != $new_status) && ($new_status != STATUS_ACTIVE)) {
$sql = 'SELECT SessionKey
FROM ' . TABLE_PREFIX . 'UserSessions
WHERE PortalUserId = ' . $user_id;
$session_ids = $this->Conn->GetCol($sql);
$this->Application->Session->DeleteSessions($session_ids);
}
}
/**
* Sends restore/validation email event on user email change
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function sendEmailChangeEvent(kEvent $event)
{
$object = $event->getObject();
/* @var $object UsersItem */
$new_email = $object->GetDBField('Email');
$prev_email = $object->GetOriginalField('Email');
if ( !$new_email || ($prev_email == $new_email) ) {
return;
}
$prev_emails = $object->GetDBField('PrevEmails');
$prev_emails = $prev_emails ? unserialize($prev_emails) : Array ();
$fields_hash = Array (
'PrevEmails' => serialize($prev_emails),
'EmailVerified' => 0,
);
$user_id = $object->GetID();
if ( $prev_email ) {
$hash = md5(TIMENOW + $user_id);
$prev_emails[$hash] = $prev_email;
$fields_hash['PrevEmails'] = serialize($prev_emails);
$send_params = Array (
'hash' => $hash,
'to_email' => $prev_email,
'to_name' => trim($object->GetDBField('FirstName') . ' ' . $object->GetDBField('LastName')),
);
$this->Application->EmailEventUser('USER.EMAIL.CHANGE.UNDO', null, $send_params);
}
if ( $new_email ) {
$this->Application->EmailEventUser('USER.EMAIL.CHANGE.VERIFY', $user_id);
}
// direct DB update, since USER.EMAIL.CHANGE.VERIFY puts verification code in user record, that we don't want to loose
$this->Conn->doUpdate($fields_hash, $object->TableName, 'PortalUserId = ' . $user_id);
}
/**
* OnAfterConfigRead for users
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
$forms = $this->Application->getUnitOption($event->Prefix, 'Forms');
$form_fields =& $forms['default']['Fields'];
// 1. arrange user registration countries
$site_helper = $this->Application->recallObject('SiteHelper');
/* @var $site_helper SiteHelper */
$first_country = $site_helper->getDefaultCountry('', false);
if ($first_country === false) {
$first_country = $this->Application->ConfigValue('User_Default_Registration_Country');
}
if ($first_country) {
// update user country dropdown sql
$form_fields['Country']['options_sql'] = preg_replace('/ORDER BY (.*)/', 'ORDER BY IF (CountryStateId = '.$first_country.', 1, 0) DESC, \\1', $form_fields['Country']['options_sql']);
}
// 2. set default user registration group
$form_fields['PrimaryGroupId']['default'] = $this->Application->ConfigValue('User_NewGroup');
// 3. allow avatar upload on Front-End
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->createItemFiles($event->Prefix, true); // create image fields
if ($this->Application->isAdminUser) {
// 4. when in administrative console, then create all users with Active status
$form_fields['Status']['default'] = STATUS_ACTIVE;
// 5. remove groups tab on editing forms when AdvancedUserManagement config variable not set
if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
$edit_tab_presets = $this->Application->getUnitOption($event->Prefix, 'EditTabPresets');
foreach ($edit_tab_presets as $preset_name => $preset_tabs) {
if (array_key_exists('groups', $preset_tabs)) {
unset($edit_tab_presets[$preset_name]['groups']);
if (count($edit_tab_presets[$preset_name]) == 1) {
// only 1 tab left -> remove it too
$edit_tab_presets[$preset_name] = Array ();
}
}
}
$this->Application->setUnitOption($event->Prefix, 'EditTabPresets', $edit_tab_presets);
}
}
if ( $this->Application->ConfigValue('RegistrationUsernameRequired') ) {
// Username becomes required only, when it's used in registration process
$max_username = $this->Application->ConfigValue('MaxUserName');
$form_fields['Username']['required'] = 1;
$form_fields['Username']['min_len'] = $this->Application->ConfigValue('Min_UserName');
$form_fields['Username']['max_len'] = $max_username ? $max_username : 255;
}
$this->Application->setUnitOption($event->Prefix, 'Forms', $forms);
}
/**
* OnMassCloneUsers
*
* @param kEvent $event
*/
function OnMassCloneUsers($event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$temp_handler = $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$ids = $this->StoreSelectedIDs($event);
$temp_handler->CloneItems($event->Prefix, '', $ids);
$this->clearSelectedIDs($event);
}
/**
* When cloning users, reset password (set random)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(kEvent $event)
{
parent::OnBeforeClone($event);
$object = $event->getObject();
/* @var $object UsersItem */
$object->generatePassword();
$object->SetDBField('ResourceId', 0); // this will reset it
// change email because it should be unique
$object->NameCopy(Array (), $object->GetID(), 'Email', 'copy%1$s.%2$s');
}
/**
* Saves selected ids to session
*
* @param kEvent $event
*/
function OnSaveSelected($event)
{
$this->StoreSelectedIDs($event);
// remove current ID, otherwise group selector will use it in filters
$this->Application->DeleteVar($event->getPrefixSpecial(true) . '_id');
}
/**
* Sets primary group of selected users
*
* @param kEvent $event
*/
function OnProcessSelected($event)
{
$event->SetRedirectParam('opener', 'u');
$user_ids = $this->getSelectedIDs($event, true);
$this->clearSelectedIDs($event);
$dst_field = $this->Application->RecallVar('dst_field');
if ( $dst_field != 'PrimaryGroupId' ) {
return;
}
$group_ids = array_keys($this->Application->GetVar('g'));
$primary_group_id = $group_ids ? array_shift($group_ids) : false;
if ( !$user_ids || !$primary_group_id ) {
return;
}
$table_name = $this->Application->getUnitOption('ug', 'TableName');
// 1. mark group as primary
$sql = 'UPDATE ' . TABLE_PREFIX . 'Users
SET PrimaryGroupId = ' . $primary_group_id . '
WHERE PortalUserId IN (' . implode(',', $user_ids) . ')';
$this->Conn->Query($sql);
$sql = 'SELECT PortalUserId
FROM ' . $table_name . '
WHERE (GroupId = ' . $primary_group_id . ') AND (PortalUserId IN (' . implode(',', $user_ids) . '))';
$existing_members = $this->Conn->GetCol($sql);
// 2. add new members to a group
$new_members = array_diff($user_ids, $existing_members);
foreach ($new_members as $user_id) {
$fields_hash = Array (
'GroupId' => $primary_group_id,
'PortalUserId' => $user_id,
);
$this->Conn->doInsert($fields_hash, $table_name);
}
}
/**
* Loads user images
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
// linking existing images for item with virtual fields
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$object = $event->getObject();
/* @var $object UsersItem */
$image_helper->LoadItemImages($object);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'State', 'Country');
// get user subscription status
$object->SetDBField('SubscribeToMailing', $object->isSubscribed() ? 1 : 0);
if ( !$this->Application->isAdmin ) {
$object->SetFieldOption('FrontLanguage', 'options', $this->getEnabledLanguages());
}
}
/**
* Returns list of enabled languages with their names
*
* @return Array
* @access protected
*/
protected function getEnabledLanguages()
{
$cache_key = 'user_languages[%LangSerial%]';
$ret = $this->Application->getCache($cache_key);
if ( $ret === false ) {
$languages = $this->Application->recallObject('lang.enabled', 'lang_List');
/* @var $languages kDBList */
$ret = Array ();
foreach ($languages as $language_info) {
$ret[$languages->GetID()] = $language_info['LocalName'];
}
$this->Application->setCache($cache_key, $ret);
}
return $ret;
}
/**
* Save user images
*
* @param kEvent $event
*/
function saveUserImages($event)
{
if (!$this->Application->isAdmin) {
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$object = $event->getObject();
/* @var $object kDBItem */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
}
}
/**
* Makes password required for new users
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent $event)
{
parent::OnPreCreate($event);
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$object = $event->getObject();
/* @var $object kDBItem */
$user_type = $this->Application->GetVar('user_type');
if ( $user_type ) {
$object->SetDBField('UserType', $user_type);
if ( $user_type == UserType::ADMIN ) {
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_AdminGroup'));
}
}
if ( $this->Application->ConfigValue('User_Password_Auto') ) {
$object->SetDBField('EmailPassword', 1);
}
$this->_makePasswordRequired($event);
}
/**
* Makes password required for new users
*
* @param kEvent $event
*/
function _makePasswordRequired($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$required_fields = Array ('Password', 'Password_plain', 'VerifyPassword', 'VerifyPassword_plain');
$object->setRequired($required_fields);
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(kEvent $event)
{
$id = $this->getPassedID($event);
if ( $id < 0 ) {
// when root, guest and so on
$object = $event->getObject();
/* @var $object kDBItem */
$object->Clear($id);
return;
}
parent::LoadItem($event);
}
/**
* Occurs just after login (for hooking)
*
* @param kEvent $event
*/
function OnAfterLogin($event)
{
if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) {
$event->MasterEvent->SetRedirectParam('login', 1);
}
}
/**
* Occurs just before logout (for hooking)
*
* @param kEvent $event
*/
function OnBeforeLogout($event)
{
if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) {
$event->MasterEvent->SetRedirectParam('logout', 1);
}
}
/**
* Generates password
*
* @param kEvent $event
*/
function OnGeneratePassword($event)
{
$event->status = kEvent::erSTOP;
if ( $this->Application->isAdminUser ) {
echo kUtil::generatePassword();
}
}
/**
* Changes user's password and logges him in
*
* @param kEvent $event
*/
function OnResetLostPassword($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$event->CallSubEvent('OnUpdate');
if ( $event->status == kEvent::erSUCCESS ) {
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load( $object->GetID() );
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
/**
* Generates new Root password and email it
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnResetRootPassword($event)
{
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
/* @var $password_formatter kPasswordFormatter */
$new_root_password = kUtil::generatePassword();
$new_root_password_encrypted = $password_formatter->EncryptPassword($new_root_password, 'b38');
$this->Application->SetConfigValue('RootPass', $new_root_password_encrypted);
$this->Application->EmailEventAdmin('ROOT.RESET.PASSWORD', null, Array ('password' => $new_root_password));
$event->SetRedirectParam('reset', 1);
$event->SetRedirectParam('pass', 'm');
}
/**
* Perform login of user, selected in Admin Console, on Front-End in a separate window
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLoginAs(kEvent $event)
{
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load( $this->Application->GetVar('user_id') );
if ( !$user->isLoaded() ) {
return ;
}
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
Index: branches/5.2.x/core/units/helpers/spam_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/spam_helper.php (revision 15568)
+++ branches/5.2.x/core/units/helpers/spam_helper.php (revision 15569)
@@ -1,190 +1,190 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class SpamHelper extends kHelper {
/**
* Table name where spam control information is kept
*
* @var string
* @access protected
*/
protected $TableName;
/**
* ResourceId field of current item
*
* @var int
* @access protected
*/
protected $ResourceId = 0;
/**
* ResourceId from all items in list
*
* @var Array
* @access protected
*/
protected $ListResourceIDs = Array ();
/**
* Type of information to put into spam control
*
* @var string
* @access protected
*/
protected $DataType = '';
/**
* Default spam control record expiration
*
* @var int
* @access protected
*/
protected $Expiration = 0;
/**
* Items expiration cache (for faster access)
*
* @var array
* @access protected
*/
protected $ExpirationCache = Array ();
public function __construct()
{
parent::__construct();
$this->TableName = TABLE_PREFIX . 'SpamControl';
}
/**
* Initializes helper for concrete item
*
* @param int $resource_id
* @param string $data_type
* @param int $expiration
* @param Array $list_resource_ids
* @access public
*/
public function InitHelper($resource_id, $data_type, $expiration, $list_resource_ids = Array ())
{
$this->DataType = $data_type;
$this->ResourceId = $resource_id;
$this->ListResourceIDs = $list_resource_ids ? $list_resource_ids : Array ($resource_id);
if ( preg_match('/(.*):(.*)/', $expiration, $regs) ) {
$delay_value = $this->Application->ConfigValue($regs[1]);
$delay_interval = $this->Application->ConfigValue($regs[2]);
$expiration = $delay_value * $delay_interval;
}
$this->Expiration = adodb_mktime() + $expiration;
}
/**
* Returns WHERE clause that identified each spam control record
*
* @param bool $as_array return result as array, not string
*
* @return string
* @access protected
*/
protected function GetKeyClause($as_array = false)
{
$user_id = $this->Application->RecallVar('user_id');
if ( $user_id == 0 ) {
$user_id = USER_GUEST;
}
$keys = Array (
- 'IPaddress' => $_SERVER['REMOTE_ADDR'],
+ 'IPaddress' => $this->Application->getClientIp(),
'PortalUserId' => $user_id,
'DataType' => $this->DataType,
);
if ( $as_array ) {
$keys['ItemResourceId'] = $this->ResourceId;
return $keys;
}
$ret = '';
foreach ($keys as $field_name => $field_value) {
$ret .= '(' . $field_name . ' = ' . $this->Conn->qstr($field_value) . ') AND ';
}
$ret .= '(ItemResourceId IN (' . implode(',', $this->ListResourceIDs) . '))';
return $ret;
}
/**
* Allows to add current item in spam control
*
* @return void
* @access public
*/
public function AddToSpamControl()
{
$fields_hash = $this->GetKeyClause(true);
$fields_hash['Expire'] = $this->Expiration;
$this->Conn->doInsert($fields_hash, $this->TableName);
// create empty expiration cache for given data type in case, when InSpamControl method wasn't called
if ( !isset($this->ExpirationCache[$this->DataType]) ) {
$this->ExpirationCache[$this->DataType] = Array ();
}
$this->ExpirationCache[$this->DataType][$this->ResourceId] = $this->Expiration;
}
/**
* Allows to check if current item is in spam control
*
* @return bool
* @access public
*/
public function InSpamControl()
{
if ( !array_key_exists($this->DataType, $this->ExpirationCache) ) {
$key_clause = $this->GetKeyClause();
$sql = 'SELECT Expire, ItemResourceId
FROM ' . $this->TableName . '
WHERE ' . $key_clause;
$this->ExpirationCache[$this->DataType] = $this->Conn->GetCol($sql, 'ItemResourceId');
}
else {
$key_clause = '';
}
$cache =& $this->ExpirationCache[$this->DataType];
$expires = array_key_exists($this->ResourceId, $cache) ? $cache[$this->ResourceId] : false;
if ( $expires && $expires < adodb_mktime() ) {
// spam control record is expired
$sql = 'DELETE FROM ' . $this->TableName . '
WHERE ' . $key_clause;
$this->Conn->Query($sql);
return false;
}
return $expires ? true : false;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/helpers/language_import_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/language_import_helper.php (revision 15568)
+++ branches/5.2.x/core/units/helpers/language_import_helper.php (revision 15569)
@@ -1,1258 +1,1258 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
/**
* Language pack format version description
*
* v1
* ==========
* All language properties are separate nodes inside <LANGUAGE> node. There are
* two more nodes PHRASES and EVENTS for phrase and email event translations.
*
* v2
* ==========
* All data, that will end up in Language table is now attributes of LANGUAGE node
* and is name exactly as field name, that will be used to store that data.
*
* v4
* ==========
* Hint & Column translation added to each phrase translation
*
* v5
* ==========
* Use separate xml nodes for subject, headers, html & plain translations
*
* v6
* ==========
* Added e-mail design templates
*
*/
defined('FULL_PATH') or die('restricted access!');
define('LANG_OVERWRITE_EXISTING', 1);
define('LANG_SKIP_EXISTING', 2);
class LanguageImportHelper extends kHelper {
/**
* Current Language in import
*
* @var LanguagesItem
*/
var $lang_object = null;
/**
* Current user's IP address
*
* @var string
*/
var $ip_address = '';
/**
* Event type + name mapping to id (from system)
*
* @var Array
*/
var $events_hash = Array ();
/**
* Language pack import mode
*
* @var int
*/
var $import_mode = LANG_SKIP_EXISTING;
/**
* Language IDs, that were imported
*
* @var Array
*/
var $_languages = Array ();
/**
* Temporary table names to perform import on
*
* @var Array
*/
var $_tables = Array ();
/**
* Phrase types allowed for import/export operations
*
* @var Array
*/
var $phrase_types_allowed = Array ();
/**
* Encoding, used for language pack exporting
*
* @var string
*/
var $_exportEncoding = 'base64';
/**
* Exported data limits (all or only specified ones)
*
* @var Array
*/
var $_exportLimits = Array (
'phrases' => false,
'emailevents' => false,
'country-state' => false,
);
/**
* Debug language pack import process
*
* @var bool
*/
var $_debugMode = false;
/**
* Latest version of language pack format. Versions are not backwards compatible!
*
* @var int
*/
var $_latestVersion = 6;
/**
* Prefix-based serial numbers, that should be changed after import is finished
*
* @var Array
*/
var $changedPrefixes = Array ();
public function __construct()
{
parent::__construct();
// "core/install/english.lang", phrase count: 3318, xml parse time on windows: 10s, insert time: 0.058s
set_time_limit(0);
ini_set('memory_limit', -1);
$this->lang_object = $this->Application->recallObject('lang.import', null, Array ('skip_autoload' => true));
if (!(defined('IS_INSTALL') && IS_INSTALL)) {
// perform only, when not in installation mode
$this->_updateEventsCache();
}
- $this->ip_address = getenv('HTTP_X_FORWARDED_FOR') ? getenv('HTTP_X_FORWARDED_FOR') : getenv('REMOTE_ADDR');
+ $this->ip_address = $this->Application->getClientIp();
// $this->_debugMode = $this->Application->isDebugMode();
}
/**
* Performs import of given language pack (former Parse method)
*
* @param string $filename
* @param string $phrase_types
* @param Array $module_ids
* @param int $import_mode
* @return bool
*/
function performImport($filename, $phrase_types, $module_ids, $import_mode = LANG_SKIP_EXISTING)
{
// define the XML parsing routines/functions to call based on the handler path
if (!file_exists($filename) || !$phrase_types /*|| !$module_ids*/) {
return false;
}
if ($this->_debugMode) {
$start_time = microtime(true);
$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")');
}
if (defined('IS_INSTALL') && IS_INSTALL) {
// new events could be added during module upgrade
$this->_updateEventsCache();
}
$phrase_types = explode('|', substr($phrase_types, 1, -1) );
// $module_ids = explode('|', substr($module_ids, 1, -1) );
$this->phrase_types_allowed = array_flip($phrase_types);
$this->import_mode = $import_mode;
$this->_parseXML($filename);
// copy data from temp tables to live
foreach ($this->_languages as $language_id) {
$this->_performUpgrade($language_id, 'phrases', 'PhraseKey', Array ('l%s_Translation', 'l%s_HintTranslation', 'l%s_ColumnTranslation', 'PhraseType'));
$this->_performUpgrade($language_id, 'emailevents', 'EventId', Array ('l%s_Subject', 'Headers', 'l%s_HtmlBody', 'l%s_PlainTextBody'));
$this->_performUpgrade($language_id, 'country-state', 'CountryStateId', Array ('l%s_Name'));
}
$this->_initImportTables(true);
$this->changedPrefixes = array_unique($this->changedPrefixes);
foreach ($this->changedPrefixes as $prefix) {
$this->Application->incrementCacheSerial($prefix);
}
if ($this->_debugMode) {
$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time));
}
return true;
}
/**
* Creates XML file with exported language data (former Create method)
*
* @param string $filename filename to export into
* @param Array $phrase_types phrases types to export from modules passed in $module_ids
* @param Array $language_ids IDs of languages to export
* @param Array $module_ids IDs of modules to export phrases from
*/
function performExport($filename, $phrase_types, $language_ids, $module_ids)
{
$fp = fopen($filename,'w');
if (!$fp || !$phrase_types || !$module_ids || !$language_ids) {
return false;
}
$phrase_types = explode('|', substr($phrase_types, 1, -1) );
$module_ids = explode('|', substr($module_ids, 1, -1) );
$ret = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
$ret .= '<LANGUAGES Version="' . $this->_latestVersion . '">' . "\n";
$export_fields = $this->_getExportFields();
// get languages
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('lang','TableName') . '
WHERE LanguageId IN (' . implode(',', $language_ids) . ')';
$languages = $this->Conn->Query($sql, 'LanguageId');
// get phrases
$phrase_modules = $module_ids;
array_push($phrase_modules, ''); // for old language packs without module
$phrase_modules = $this->Conn->qstrArray($phrase_modules);
// apply phrase selection limit
if ($this->_exportLimits['phrases']) {
$escaped_phrases = $this->Conn->qstrArray($this->_exportLimits['phrases']);
$limit_where = 'Phrase IN (' . implode(',', $escaped_phrases) . ')';
}
else {
$limit_where = 'TRUE';
}
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('phrases','TableName') . '
WHERE PhraseType IN (' . implode(',', $phrase_types) . ') AND Module IN (' . implode(',', $phrase_modules) . ') AND ' . $limit_where . '
ORDER BY Phrase';
$phrases = $this->Conn->Query($sql, 'PhraseId');
// email events
$module_sql = preg_replace('/(.*),/U', 'INSTR(Module,\'\\1\') OR ', implode(',', $module_ids) . ',');
// apply event selection limit
if ($this->_exportLimits['emailevents']) {
$escaped_email_events = $this->Conn->qstrArray($this->_exportLimits['emailevents']);
$limit_where = '`Event` IN (' . implode(',', $escaped_email_events) . ')';
}
else {
$limit_where = 'TRUE';
}
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('emailevents', 'TableName') . '
WHERE `Type` IN (' . implode(',', $phrase_types) . ') AND (' . substr($module_sql, 0, -4) . ') AND ' . $limit_where . '
ORDER BY `Event`, `Type`';
$events = $this->Conn->Query($sql, 'EventId');
if ( in_array('Core', $module_ids) ) {
if ($this->_exportLimits['country-state']) {
$escaped_countries = $this->Conn->qstrArray($this->_exportLimits['country-state']);
$limit_where = '`IsoCode` IN (' . implode(',', $escaped_countries) . ')';
}
else {
$limit_where = 'TRUE';
}
$country_table = $this->Application->getUnitOption('country-state', 'TableName');
// countries
$sql = 'SELECT *
FROM ' . $country_table . '
WHERE Type = ' . DESTINATION_TYPE_COUNTRY . ' AND ' . $limit_where . '
ORDER BY `IsoCode`';
$countries = $this->Conn->Query($sql, 'CountryStateId');
// states
$sql = 'SELECT state.*
FROM ' . $country_table . ' state
JOIN ' . $country_table . ' country ON country.CountryStateId = state.StateCountryId
WHERE state.Type = ' . DESTINATION_TYPE_STATE . ' AND ' . str_replace('`IsoCode`', 'country.`IsoCode`', $limit_where) . '
ORDER BY state.`IsoCode`';
$states = $this->Conn->Query($sql, 'CountryStateId');
foreach ($states as $state_id => $state_data) {
$country_id = $state_data['StateCountryId'];
if ( !array_key_exists('States', $countries[$country_id]) ) {
$countries[$country_id]['States'] = Array ();
}
$countries[$country_id]['States'][] = $state_id;
}
}
foreach ($languages as $language_id => $language_info) {
// language
$ret .= "\t" . '<LANGUAGE Encoding="' . $this->_exportEncoding . '"';
foreach ($export_fields as $export_field) {
$ret .= ' ' . $export_field . '="' . htmlspecialchars($language_info[$export_field], NULL, 'UTF-8') . '"';
}
$ret .= '>' . "\n";
// filename replacements
$replacements = $language_info['FilenameReplacements'];
if ( $replacements ) {
$ret .= "\t\t" . '<REPLACEMENTS>' . $this->_exportConvert($replacements) . '</REPLACEMENTS>' . "\n";
}
// e-mail design templates
if ( $language_info['HtmlEmailTemplate'] || $language_info['TextEmailTemplate'] ) {
$ret .= "\t\t" . '<EMAILDESIGNS>' . "\n";
if ( $language_info['HtmlEmailTemplate'] ) {
$ret .= "\t\t\t" . '<HTML>' . $this->_exportConvert($language_info['HtmlEmailTemplate']) . '</HTML>' . "\n";
}
if ( $language_info['TextEmailTemplate'] ) {
$ret .= "\t\t\t" . '<TEXT>' . $this->_exportConvert($language_info['TextEmailTemplate']) . '</TEXT>' . "\n";
}
$ret .= "\t\t" . '</EMAILDESIGNS>' . "\n";
}
// phrases
if ($phrases) {
$ret .= "\t\t" . '<PHRASES>' . "\n";
foreach ($phrases as $phrase_id => $phrase) {
$translation = $phrase['l' . $language_id . '_Translation'];
$hint_translation = $phrase['l' . $language_id . '_HintTranslation'];
$column_translation = $phrase['l' . $language_id . '_ColumnTranslation'];
if (!$translation) {
// phrase is not translated on given language
continue;
}
if ( $this->_exportEncoding == 'base64' ) {
$hint_translation = base64_encode($hint_translation);
$column_translation = base64_encode($column_translation);
}
else {
$hint_translation = htmlspecialchars($hint_translation, NULL, 'UTF-8');
$column_translation = htmlspecialchars($column_translation, NULL, 'UTF-8');
}
$attributes = Array (
'Label="' . $phrase['Phrase'] . '"',
'Module="' . $phrase['Module'] . '"',
'Type="' . $phrase['PhraseType'] . '"'
);
if ( $phrase['l' . $language_id . '_HintTranslation'] ) {
$attributes[] = 'Hint="' . $hint_translation . '"';
}
if ( $phrase['l' . $language_id . '_ColumnTranslation'] ) {
$attributes[] = 'Column="' . $column_translation . '"';
}
$ret .= "\t\t\t" . '<PHRASE ' . implode(' ', $attributes) . '>' . $this->_exportConvert($translation) . '</PHRASE>' . "\n";
}
$ret .= "\t\t" . '</PHRASES>' . "\n";
}
// email events
if ($events) {
$ret .= "\t\t" . '<EVENTS>' . "\n";
foreach ($events as $event_data) {
$fields_hash = Array (
'HEADERS' => $event_data['Headers'],
'SUBJECT' => $event_data['l' . $language_id . '_Subject'],
'HTMLBODY' => $event_data['l' . $language_id . '_HtmlBody'],
'PLAINTEXTBODY' => $event_data['l' . $language_id . '_PlainTextBody'],
);
$data = '';
foreach ($fields_hash as $xml_node => $xml_content) {
if ( $xml_content ) {
$data .= "\t\t\t\t" . '<' . $xml_node . '>' . $this->_exportConvert($xml_content) . '</' . $xml_node . '>' . "\n";
}
}
if ( $data ) {
$ret .= "\t\t\t" . '<EVENT Event="' . $event_data['Event'] . '" Type="' . $event_data['Type'] . '">' . "\n" . $data . "\t\t\t" . '</EVENT>' . "\n";
}
}
$ret .= "\t\t" . '</EVENTS>' . "\n";
}
if (in_array('Core', $module_ids) && $countries) {
$ret .= "\t\t" . '<COUNTRIES>' . "\n";
foreach ($countries as $country_data) {
$translation = $country_data['l' . $language_id . '_Name'];
if (!$translation) {
// country is not translated on given language
continue;
}
$data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation;
if (array_key_exists('States', $country_data)) {
$ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '">' . "\n";
foreach ($country_data['States'] as $state_id) {
$translation = $states[$state_id]['l' . $language_id . '_Name'];
if (!$translation) {
// state is not translated on given language
continue;
}
$data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation;
$ret .= "\t\t\t\t" . '<STATE Iso="' . $states[$state_id]['IsoCode'] . '" Translation="' . $data . '"/>' . "\n";
}
$ret .= "\t\t\t" . '</COUNTRY>' . "\n";
}
else {
$ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '"/>' . "\n";
}
}
$ret .= "\t\t" . '</COUNTRIES>' . "\n";
}
$ret .= "\t" . '</LANGUAGE>' . "\n";
}
$ret .= '</LANGUAGES>';
fwrite($fp, $ret);
fclose($fp);
return true;
}
/**
* Converts string before placing into export file
*
* @param string $string
* @return string
* @access protected
*/
protected function _exportConvert($string)
{
return $this->_exportEncoding == 'base64' ? base64_encode($string) : '<![CDATA[' . $string . ']]>';
}
/**
* Sets language pack encoding (not charset) used during export
*
* @param string $encoding
*/
function setExportEncoding($encoding)
{
$this->_exportEncoding = $encoding;
}
/**
* Sets language pack data limit for export
*
* @param string $prefix
* @param string $data
*/
function setExportLimit($prefix, $data = null)
{
if ( !isset($data) ) {
$key_field = $prefix == 'phrases' ? 'Phrase' : 'Event';
$ids = $this->getExportIDs($prefix);
$sql = 'SELECT ' . $key_field . '
FROM ' . $this->Application->getUnitOption($prefix, 'TableName') . '
WHERE ' . $this->Application->getUnitOption($prefix, 'IDField') . ' IN (' . $ids . ')';
$rows = $this->Conn->GetIterator($sql);
if ( count($rows) ) {
$data = '';
foreach ($rows as $row) {
$data .= ',' . $row[$key_field];
}
$data = substr($data, 1);
}
}
if ( !is_array($data) ) {
$data = str_replace(',', "\n", $data);
$data = preg_replace("/\n+/", "\n", str_replace("\r", '', trim($data)));
$data = $data ? array_map('trim', explode("\n", $data)) : Array ();
}
$this->_exportLimits[$prefix] = $data;
}
/**
* Performs upgrade of given language pack part
*
* @param int $language_id
* @param string $prefix
* @param string $unique_field
* @param Array $data_fields
*/
function _performUpgrade($language_id, $prefix, $unique_field, $data_fields)
{
$live_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], false);
$temp_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], true);
if (!$temp_records) {
// no data for given language
return ;
}
// perform insert for records, that are missing in live table
$to_insert = array_diff($temp_records, $live_records);
if ($to_insert) {
$to_insert = $this->Conn->qstrArray($to_insert);
$sql = 'INSERT INTO ' . $this->Application->getUnitOption($prefix, 'TableName') . '
SELECT *
FROM ' . $this->_tables[$prefix] . '
WHERE ' . $unique_field . ' IN (' . implode(',', $to_insert) . ')';
$this->Conn->Query($sql);
// new records were added
$this->changedPrefixes[] = $prefix;
}
// perform update for records, that are present in live table
$to_update = array_diff($temp_records, $to_insert);
if ($to_update) {
$to_update = $this->Conn->qstrArray($to_update);
$sql = 'UPDATE ' . $this->Application->getUnitOption($prefix, 'TableName') . ' live
SET ';
foreach ($data_fields as $index => $data_field) {
$data_field = sprintf($data_field, $language_id);
$sql .= ' live.' . $data_field . ' = (
SELECT temp' . $index . '.' . $data_field . '
FROM ' . $this->_tables[$prefix] . ' temp' . $index . '
WHERE temp' . $index . '.' . $unique_field . ' = live.' . $unique_field . '
),';
}
$sql = substr($sql, 0, -1); // cut last comma
$where_clause = Array (
// this won't make any difference, but just in case
$unique_field . ' IN (' . implode(',', $to_update) . ')',
);
if ($this->import_mode == LANG_SKIP_EXISTING) {
// empty OR not set
$data_field = sprintf($data_fields[0], $language_id);
$where_clause[] = '(' . $data_field . ' = "") OR (' . $data_field . ' IS NULL)';
}
if ($where_clause) {
$sql .= "\n" . 'WHERE (' . implode(') AND (', $where_clause) . ')';
}
$this->Conn->Query($sql);
if ($this->Conn->getAffectedRows() > 0) {
// existing records were updated
$this->changedPrefixes[] = $prefix;
}
}
}
/**
* Returns data from given table used for language pack upgrade
*
* @param int $language_id
* @param string $prefix
* @param string $unique_field
* @param string $data_field
* @param bool $temp_mode
* @return Array
*/
function _getTableData($language_id, $prefix, $unique_field, $data_field, $temp_mode = false)
{
$data_field = sprintf($data_field, $language_id);
$table_name = $this->Application->getUnitOption($prefix, 'TableName');
if ($temp_mode) {
// for temp table get only records, that have contents on given language (not empty and isset)
$sql = 'SELECT ' . $unique_field . '
FROM ' . $this->Application->GetTempName($table_name, 'prefix:' . $prefix) . '
WHERE (' . $data_field . ' <> "") AND (' . $data_field . ' IS NOT NULL)';
}
else {
// for live table get all records, no matter on what language
$sql = 'SELECT ' . $unique_field . '
FROM ' . $table_name;
}
return $this->Conn->GetCol($sql);
}
function _parseXML($filename)
{
if ( $this->_debugMode ) {
$start_time = microtime(true);
$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")');
}
$languages = simplexml_load_file($filename);
if ( $languages === false) {
// invalid language pack contents
return false;
}
// PHP 5.3 version would be: $languages->count()
if ( count($languages->children()) ) {
$this->_processLanguages($languages);
$this->_processLanguageData($languages);
}
if ( $this->_debugMode ) {
$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time));
}
return true;
}
/**
* Creates temporary tables, used during language import
*
* @param bool $drop_only
*/
function _initImportTables($drop_only = false)
{
$this->_tables['phrases'] = $this->_prepareTempTable('phrases', $drop_only);
$this->_tables['emailevents'] = $this->_prepareTempTable('emailevents', $drop_only);
$this->_tables['country-state'] = $this->_prepareTempTable('country-state', $drop_only);
}
/**
* Create temp table for prefix, if table already exists, then delete it and create again
*
* @param string $prefix
* @param bool $drop_only
* @return string Name of created temp table
* @access protected
*/
protected function _prepareTempTable($prefix, $drop_only = false)
{
$id_field = $this->Application->getUnitOption($prefix, 'IDField');
$table = $this->Application->getUnitOption($prefix,'TableName');
$temp_table = $this->Application->GetTempName($table);
$sql = 'DROP TABLE IF EXISTS %s';
$this->Conn->Query( sprintf($sql, $temp_table) );
if (!$drop_only) {
$sql = 'CREATE TABLE ' . $temp_table . ' SELECT * FROM ' . $table . ' WHERE 0';
$this->Conn->Query($sql);
$sql = 'ALTER TABLE %1$s CHANGE %2$s %2$s INT(11) NOT NULL DEFAULT "0"';
$this->Conn->Query( sprintf($sql, $temp_table, $id_field) );
switch ($prefix) {
case 'phrases':
$unique_field = 'PhraseKey';
break;
case 'emailevents':
$unique_field = 'EventId';
break;
case 'country-state':
$unique_field = 'CountryStateId';
break;
default:
throw new Exception('Unknown prefix "<strong>' . $prefix . '</strong>" during language pack import');
break;
}
$sql = 'ALTER TABLE ' . $temp_table . ' ADD UNIQUE (' . $unique_field . ')';
$this->Conn->Query($sql);
}
return $temp_table;
}
/**
* Prepares mapping between event name+type and their ids in database
*
*/
function _updateEventsCache()
{
$sql = 'SELECT EventId, CONCAT(Event,"_",Type) AS EventMix
FROM ' . TABLE_PREFIX . 'EmailEvents';
$this->events_hash = $this->Conn->GetCol($sql, 'EventMix');
}
/**
* Returns language fields to be exported
*
* @return Array
*/
function _getExportFields()
{
return Array (
'PackName', 'LocalName', 'DateFormat', 'ShortDateFormat', 'TimeFormat', 'ShortTimeFormat',
'InputDateFormat', 'InputTimeFormat', 'DecimalPoint', 'ThousandSep', 'UnitSystem', 'Locale',
'UserDocsUrl'
);
}
/**
* Processes parsed XML
*
* @param SimpleXMLElement $languages
*/
function _processLanguages($languages)
{
$version = (int)$languages['Version'];
if ( !$version ) {
// version missing -> guess it
if ( $languages->DATEFORMAT->getName() ) {
$version = 1;
}
elseif ( (string)$languages->LANGUAGE['Charset'] != '' ) {
$version = 2;
}
}
if ( $version == 1 ) {
$field_mapping = Array (
'DATEFORMAT' => 'DateFormat',
'TIMEFORMAT' => 'TimeFormat',
'INPUTDATEFORMAT' => 'InputDateFormat',
'INPUTTIMEFORMAT' => 'InputTimeFormat',
'DECIMAL' => 'DecimalPoint',
'THOUSANDS' => 'ThousandSep',
'CHARSET' => 'Charset',
'UNITSYSTEM' => 'UnitSystem',
'DOCS_URL' => 'UserDocsUrl',
);
}
else {
$export_fields = $this->_getExportFields();
}
foreach ($languages as $language_node) {
$fields_hash = Array (
'PackName' => (string)$language_node['PackName'],
'LocalName' => (string)$language_node['PackName'],
'Encoding' => (string)$language_node['Encoding'],
'SynchronizationModes' => Language::SYNCHRONIZE_DEFAULT,
);
if ( $version > 1 ) {
foreach ($export_fields as $export_field) {
if ( (string)$language_node[$export_field] ) {
$fields_hash[$export_field] = (string)$language_node[$export_field];
}
}
}
$container_nodes = Array ('PHRASES', 'EVENTS', 'COUNTRIES');
foreach ($language_node as $sub_node) {
/* @var $sub_node SimpleXMLElement */
if ( in_array($sub_node->getName(), $container_nodes) ) {
continue;
}
switch ($sub_node->getName()) {
case 'REPLACEMENTS':
// added since v2
$replacements = (string)$sub_node;
if ( $fields_hash['Encoding'] != 'plain' ) {
$replacements = base64_decode($replacements);
}
$fields_hash['FilenameReplacements'] = $replacements;
break;
case 'EMAILDESIGNS':
// added since v6
$this->_decodeEmailDesignTemplate($fields_hash, 'HtmlEmailTemplate', (string)$sub_node->HTML);
$this->_decodeEmailDesignTemplate($fields_hash, 'TextEmailTemplate', (string)$sub_node->TEXT);
break;
default:
if ( $version == 1 ) {
$fields_hash[$field_mapping[$sub_node->Name]] = (string)$sub_node;
}
break;
}
}
$this->_processLanguage($fields_hash);
}
if ( !defined('IS_INSTALL') || !IS_INSTALL ) {
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
// create ML columns for new languages
$ml_helper->resetState();
$ml_helper->massCreateFields();
}
// create temp tables after new language columns were added
$this->_initImportTables();
}
/**
* Processes parsed XML
*
* @param SimpleXMLElement $languages
*/
function _processLanguageData($languages)
{
foreach ($languages as $language_node) {
$encoding = (string)$language_node['Encoding'];
$language_id = $this->_languages[kUtil::crc32((string)$language_node['PackName'])];
$container_nodes = Array ('PHRASES', 'EVENTS', 'COUNTRIES');
foreach ($language_node as $sub_node) {
/* @var $sub_node SimpleXMLElement */
if ( !in_array($sub_node->getName(), $container_nodes) || !count($sub_node->children()) ) {
// PHP 5.3 version would be: !$sub_node->count()
continue;
}
switch ($sub_node->getName()) {
case 'PHRASES':
$this->_processPhrases($sub_node, $language_id, $encoding);
break;
case 'EVENTS':
$this->_processEvents($sub_node, $language_id, $encoding);
break;
case 'COUNTRIES':
$this->_processCountries($sub_node, $language_id, $encoding);
break;
}
}
}
}
/**
* Decodes e-mail template design from language pack
*
* @param Array $fields_hash
* @param string $field
* @param string $design_template
*/
protected function _decodeEmailDesignTemplate(&$fields_hash, $field, $design_template)
{
if ( $fields_hash['Encoding'] != 'plain' ) {
$design_template = base64_decode($design_template);
}
if ( $design_template ) {
$fields_hash[$field] = $design_template;
}
}
/**
* Performs phases import
*
* @param SimpleXMLElement $phrases
* @param int $language_id
* @param string $language_encoding
*/
function _processPhrases($phrases, $language_id, $language_encoding)
{
static $other_translations = Array ();
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileStart('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import');
}
foreach ($phrases as $phrase_node) {
/* @var $phrase_node SimpleXMLElement */
$phrase_key = mb_strtoupper($phrase_node['Label']);
$fields_hash = Array (
'Phrase' => (string)$phrase_node['Label'],
'PhraseKey' => $phrase_key,
'PhraseType' => (int)$phrase_node['Type'],
'Module' => (string)$phrase_node['Module'] ? (string)$phrase_node['Module'] : 'Core',
'LastChanged' => TIMENOW,
'LastChangeIP' => $this->ip_address,
);
$translation = (string)$phrase_node;
$hint_translation = (string)$phrase_node['Hint'];
$column_translation = (string)$phrase_node['Column'];
if ( array_key_exists($fields_hash['PhraseType'], $this->phrase_types_allowed) ) {
if ( $language_encoding != 'plain' ) {
$translation = base64_decode($translation);
$hint_translation = base64_decode($hint_translation);
$column_translation = base64_decode($column_translation);
}
if ( !array_key_exists($phrase_key, $other_translations) ) {
// ensure translation in every language to make same column count in every insert
$other_translations[$phrase_key] = Array ();
foreach ($this->_languages as $other_language_id) {
$other_translations[$phrase_key]['l' . $other_language_id . '_Translation'] = '';
$other_translations[$phrase_key]['l' . $other_language_id . '_HintTranslation'] = '';
$other_translations[$phrase_key]['l' . $other_language_id . '_ColumnTranslation'] = '';
}
}
$other_translations[$phrase_key]['l' . $language_id . '_Translation'] = $translation;
$other_translations[$phrase_key]['l' . $language_id . '_HintTranslation'] = $hint_translation;
$other_translations[$phrase_key]['l' . $language_id . '_ColumnTranslation'] = $column_translation;
$fields_hash = array_merge($fields_hash, $other_translations[$phrase_key]);
$this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE', false);
}
}
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileFinish('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import');
}
$this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE');
}
/**
* Performs email event import
*
* @param SimpleXMLElement $events
* @param int $language_id
* @param string $language_encoding
*/
function _processEvents($events, $language_id, $language_encoding)
{
static $other_translations = Array ();
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileStart('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import');
}
$email_message_helper = $this->Application->recallObject('kEmailMessageHelper');
/* @var $email_message_helper kEmailMessageHelper */
foreach ($events as $event_node) {
/* @var $event_node SimpleXMLElement */
$message_type = (string)$event_node['MessageType'];
$event_id = $this->_getEventId((string)$event_node['Event'], (int)$event_node['Type']);
if ( !$event_id ) {
continue;
}
$fields_hash = Array (
'EventId' => $event_id,
'Event' => (string)$event_node['Event'],
'Type' => (int)$event_node['Type'],
);
if ( $message_type == '' ) {
$parsed = $email_message_helper->parseTemplate($event_node, '');
$parsed = array_map($language_encoding == 'plain' ? 'rtrim' : 'base64_decode', $parsed);
}
else {
$template = $language_encoding == 'plain' ? rtrim($event_node) : base64_decode($event_node);
$parsed = $email_message_helper->parseTemplate($template, $message_type);
}
if ( !array_key_exists($event_id, $other_translations) ) {
// ensure translation in every language to make same column count in every insert
$other_translations[$event_id] = Array ();
foreach ($this->_languages as $other_language_id) {
$other_translations[$event_id]['l' . $other_language_id . '_Subject'] = '';
$other_translations[$event_id]['l' . $other_language_id . '_HtmlBody'] = '';
$other_translations[$event_id]['l' . $other_language_id . '_PlainTextBody'] = '';
}
}
$other_translations[$event_id]['l' . $language_id . '_Subject'] = $parsed['Subject'];
$other_translations[$event_id]['l' . $language_id . '_HtmlBody'] = $parsed['HtmlBody'];
$other_translations[$event_id]['l' . $language_id . '_PlainTextBody'] = $parsed['PlainTextBody'];
if ( $parsed['Headers'] ) {
$other_translations[$event_id]['Headers'] = $parsed['Headers'];
}
elseif ( !$parsed['Headers'] && !array_key_exists('Headers', $other_translations[$event_id]) ) {
$other_translations[$event_id]['Headers'] = $parsed['Headers'];
}
$fields_hash = array_merge($fields_hash, $other_translations[$event_id]);
$this->Conn->doInsert($fields_hash, $this->_tables['emailevents'], 'REPLACE', false);
}
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileFinish('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import');
}
if ( isset($fields_hash) ) {
// at least one email event in language pack was found in database
$this->Conn->doInsert($fields_hash, $this->_tables['emailevents'], 'REPLACE');
}
}
/**
* Performs country_state translation import
*
* @param SimpleXMLElement $country_states
* @param int $language_id
* @param string $language_encoding
* @param bool $process_states
* @return void
*/
function _processCountries($country_states, $language_id, $language_encoding, $process_states = false)
{
static $other_translations = Array ();
foreach ($country_states as $country_state_node) {
/* @var $country_state_node SimpleXMLElement */
if ( $process_states ) {
$country_state_id = $this->_getStateId((string)$country_states['Iso'], (string)$country_state_node['Iso']);
}
else {
$country_state_id = $this->_getCountryId((string)$country_state_node['Iso']);
}
if ( !$country_state_id ) {
continue;
}
if ( $language_encoding == 'plain' ) {
$translation = rtrim($country_state_node['Translation']);
}
else {
$translation = base64_decode($country_state_node['Translation']);
}
$fields_hash = Array ('CountryStateId' => $country_state_id);
if ( !array_key_exists($country_state_id, $other_translations) ) {
// ensure translation in every language to make same column count in every insert
$other_translations[$country_state_id] = Array ();
foreach ($this->_languages as $other_language_id) {
$other_translations[$country_state_id]['l' . $other_language_id . '_Name'] = '';
}
}
$other_translations[$country_state_id]['l' . $language_id . '_Name'] = $translation;
$fields_hash = array_merge($fields_hash, $other_translations[$country_state_id]);
$this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE', false);
// PHP 5.3 version would be: $country_state_node->count()
if ( !$process_states && count($country_state_node->children()) ) {
$this->_processCountries($country_state_node, $language_id, $language_encoding, true);
}
}
$this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE');
}
/**
* Creates/updates language based on given fields and returns it's id
*
* @param Array $fields_hash
* @return int
*/
function _processLanguage($fields_hash)
{
// 1. get language from database
$sql = 'SELECT ' . $this->lang_object->IDField . '
FROM ' . $this->lang_object->TableName . '
WHERE PackName = ' . $this->Conn->qstr($fields_hash['PackName']);
$language_id = $this->Conn->GetOne($sql);
if ($language_id) {
// 2. language found -> update, when allowed
$this->lang_object->Load($language_id);
if ($this->import_mode == LANG_OVERWRITE_EXISTING) {
// update live language record based on data from xml
$this->lang_object->SetFieldsFromHash($fields_hash);
$this->lang_object->Update();
}
}
else {
// 3. language not found -> create
$this->lang_object->SetFieldsFromHash($fields_hash);
$this->lang_object->SetDBField('Enabled', STATUS_ACTIVE);
if ($this->lang_object->Create()) {
$language_id = $this->lang_object->GetID();
if (defined('IS_INSTALL') && IS_INSTALL) {
// language created during install becomes admin interface language
$this->lang_object->setPrimary(true, true);
}
}
}
// 4. collect ID of every processed language
if (!in_array($language_id, $this->_languages)) {
$this->_languages[kUtil::crc32($fields_hash['PackName'])] = $language_id;
}
return $language_id;
}
/**
* Returns event id based on it's name and type
*
* @param string $event_name
* @param string $event_type
* @return int
*/
function _getEventId($event_name, $event_type)
{
$cache_key = $event_name . '_' . $event_type;
return array_key_exists($cache_key, $this->events_hash) ? $this->events_hash[$cache_key] : 0;
}
/**
* Returns country id based on it's 3letter ISO code
*
* @param string $iso
* @return int
*/
function _getCountryId($iso)
{
static $cache = null;
if (!isset($cache)) {
$sql = 'SELECT CountryStateId, IsoCode
FROM ' . TABLE_PREFIX . 'CountryStates
WHERE Type = ' . DESTINATION_TYPE_COUNTRY;
$cache = $this->Conn->GetCol($sql, 'IsoCode');
}
return array_key_exists($iso, $cache) ? $cache[$iso] : false;
}
/**
* Returns state id based on 3letter country ISO code and 2letter state ISO code
*
* @param string $country_iso
* @param string $state_iso
* @return int
*/
function _getStateId($country_iso, $state_iso)
{
static $cache = null;
if (!isset($cache)) {
$sql = 'SELECT CountryStateId, CONCAT(StateCountryId, "-", IsoCode) AS IsoCode
FROM ' . TABLE_PREFIX . 'CountryStates
WHERE Type = ' . DESTINATION_TYPE_STATE;
$cache = $this->Conn->GetCol($sql, 'IsoCode');
}
$country_id = $this->_getCountryId($country_iso);
return array_key_exists($country_id . '-' . $state_iso, $cache) ? $cache[$country_id . '-' . $state_iso] : false;
}
/**
* Returns comma-separated list of IDs, that will be exported
*
* @param string $prefix
* @return string
* @access public
*/
public function getExportIDs($prefix)
{
$ids = $this->Application->RecallVar($prefix . '_selected_ids');
if ( $ids ) {
// some records were selected in grid
return $ids;
}
$tag_params = Array (
'grid' => $prefix == 'phrases' ? 'Phrases' : 'Emails',
'skip_counting' => 1,
'per_page' => -1
);
$list = $this->Application->recallObject($prefix, $prefix . '_List', $tag_params);
/* @var $list kDBList */
$sql = $list->getCountSQL($list->GetSelectSQL());
$sql = str_replace('COUNT(*) AS count', $list->TableName . '.' . $list->IDField, $sql);
$ids = '';
$rows = $this->Conn->GetIterator($sql);
if ( count($rows) ) {
foreach ($rows as $row) {
$ids .= ',' . $row[$list->IDField];
}
$ids = substr($ids, 1);
}
return $ids;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/helpers/deployment_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/deployment_helper.php (revision 15568)
+++ branches/5.2.x/core/units/helpers/deployment_helper.php (revision 15569)
@@ -1,620 +1,620 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class DeploymentHelper extends kHelper {
/**
* How many symbols from sql should be shown
*/
const SQL_TRIM_LENGTH = 120;
/**
* Name of module, that is processed right now
*
* @var string
* @access private
*/
private $moduleName = '';
/**
* List of sqls, associated with each revision (from project_upgrades.sql file)
*
* @var Array
* @access private
*/
private $revisionSqls = Array ();
/**
* List of revision titles as user typed them (from project_upgrades.sql file)
* @var Array
*/
private $revisionTitles = Array ();
/**
* Revision dependencies
*
* @var Array
* @access private
*/
private $revisionDependencies = Array ();
/**
* Numbers of revisions, that were already applied
*
* @var Array
* @access private
*/
private $appliedRevisions = Array ();
/**
* Don't change database, but only check syntax of project_upgrades.sql file and mark all revisions discovered as applied
*
* @var bool
* @access private
*/
private $dryRun = false;
/**
* Remembers script invocation method
*
* @var bool
* @access public
*/
public $isCommandLine = false;
/**
* IP Address of script invoker
*
* @var string
*/
public $ip = '';
public function __construct()
{
parent::__construct();
set_time_limit(0);
ini_set('memory_limit', -1);
$this->isCommandLine = isset($GLOBALS['argv']) && count($GLOBALS['argv']);
if ( !$this->isCommandLine ) {
- $this->ip = $_SERVER['REMOTE_ADDR'];
+ $this->ip = $this->Application->getClientIp();
}
elseif ( isset($GLOBALS['argv'][3]) ) {
$this->ip = $GLOBALS['argv'][3];
}
}
/**
* Adds message to script execution log
*
* @param string $message
* @param bool $new_line
* @return void
* @access private
*/
private function toLog($message, $new_line = true)
{
$log_file = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE) . '/project_upgrades.log';
$fp = fopen($log_file, 'a');
fwrite($fp, $message . ($new_line ? "\n" : ''));
fclose($fp);
chmod($log_file, 0666);
}
/**
* Loads already applied revisions list of current module
*
* @return void
* @access private
*/
private function loadAppliedRevisions()
{
$sql = 'SELECT AppliedDBRevisions
FROM ' . TABLE_PREFIX . 'Modules
WHERE Name = ' . $this->Conn->qstr($this->moduleName);
$revisions = $this->Conn->GetOne($sql);
$this->appliedRevisions = $revisions ? explode(',', $revisions) : Array ();
}
/**
* Saves applied revision numbers to current module record
*
* @return void
* @access private
*/
private function saveAppliedRevisions()
{
// maybe optimize
sort($this->appliedRevisions);
$fields_hash = Array (
'AppliedDBRevisions' => implode(',', $this->appliedRevisions),
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', '`Name` = ' . $this->Conn->qstr($this->moduleName));
}
/**
* Deploys changes from all installed modules
*
* @param bool $dry_run
* @return bool
* @access public
*/
public function deployAll($dry_run = false)
{
if ( !$this->isCommandLine ) {
echo '<pre style="font-size: 10pt; color: #BBB; background-color: black; border: 2px solid darkgreen; padding: 8px;">' . PHP_EOL;
}
$ret = true;
$this->dryRun = $dry_run;
$this->toLog(PHP_EOL . '[' . adodb_date('Y-m-d H:i:s') . '] === ' . $this->ip . ' ===');
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$this->moduleName = $module_name;
if ( !file_exists($this->getModuleFile('project_upgrades.sql')) ) {
continue;
}
$ret = $ret && $this->deploy($module_name);
}
if ( $ret && !$this->dryRun ) {
$this->resetCaches();
$this->refreshThemes();
}
if ( !$this->isCommandLine ) {
echo '</pre>' . PHP_EOL;
}
return $ret;
}
/**
* Deploys pending changes to a site
*
* @param string $module_name
* @return bool
* @access private
*/
private function deploy($module_name)
{
echo $this->colorText('Deploying Module "' . $module_name . '":', 'cyan', true) . PHP_EOL;
if ( !$this->upgradeDatabase() ) {
return false;
}
if ( !$this->dryRun ) {
$this->importLanguagePack();
}
echo $this->colorText('Done with Module "' . $module_name . '".', 'green', true) . PHP_EOL . PHP_EOL;
return true;
}
/**
* Import latest languagepack (without overwrite)
*
* @return void
* @access private
*/
private function importLanguagePack()
{
$language_import_helper = $this->Application->recallObject('LanguageImportHelper');
/* @var $language_import_helper LanguageImportHelper */
$this->out('Importing LanguagePack ... ');
$filename = $this->getModuleFile('english.lang');
$language_import_helper->performImport($filename, '|0|1|2|', $this->moduleName, LANG_SKIP_EXISTING);
$this->displayStatus('OK');
}
/**
* Resets unit and section cache
*
* @return void
* @access private
*/
private function resetCaches()
{
// 2. reset unit config cache (so new classes get auto-registered)
$this->out('Resetting Unit Config Cache ... ');
$this->Application->HandleEvent(new kEvent('adm:OnResetConfigsCache'));
$this->displayStatus('OK');
// 3. reset sections cache
$this->out('Resetting Sections Cache ... ');
$this->Application->HandleEvent(new kEvent('adm:OnResetSections'));
$this->displayStatus('OK');
}
/**
* Rebuild theme files
*
* @return void
* @access private
*/
private function refreshThemes()
{
$this->out('Rebuilding Theme Files ... ');
$this->Application->HandleEvent(new kEvent('adm:OnRebuildThemes'));
$this->displayStatus('OK');
}
/**
* Runs database upgrade script
*
* @return bool
* @access private
*/
private function upgradeDatabase()
{
$this->loadAppliedRevisions();
$this->Conn->errorHandler = Array (&$this, 'handleSqlError');
$this->out('Verifying Database Revisions ... ');
if ( !$this->collectDatabaseRevisions() || !$this->checkRevisionDependencies() ) {
return false;
}
$this->displayStatus('OK');
$applied = $this->applyRevisions();
$this->saveAppliedRevisions();
return $applied;
}
/**
* Collects database revisions from "project_upgrades.sql" file.
*
* @return bool
* @access private
*/
private function collectDatabaseRevisions()
{
$filename = $this->getModuleFile('project_upgrades.sql');
if ( !file_exists($filename) ) {
return true;
}
$sqls = file_get_contents($filename);
preg_match_all("/# r([\d]+)([^\:]*):.*?(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
if ( !$matches ) {
$this->displayStatus('FAILED' . PHP_EOL . 'No Database Revisions Found');
return false;
}
foreach ($matches as $index => $match) {
$revision = $match[1][0];
if ( $this->revisionApplied($revision) ) {
// skip applied revisions
continue;
}
if ( isset($this->revisionSqls[$revision]) ) {
// duplicate revision among non-applied ones
$this->displayStatus('FAILED' . PHP_EOL . 'Duplicate revision #' . $revision . ' found');
return false;
}
// get revision sqls
$start_pos = $match[0][1] + strlen($match[0][0]);
$end_pos = isset($matches[$index + 1]) ? $matches[$index + 1][0][1] : strlen($sqls);
$revision_sqls = substr($sqls, $start_pos, $end_pos - $start_pos);
if ( !$revision_sqls ) {
// resision without sqls
continue;
}
$this->revisionTitles[$revision] = trim($match[0][0]);
$this->revisionSqls[$revision] = $revision_sqls;
$revision_lependencies = $this->parseRevisionDependencies($match[2][0]);
if ( $revision_lependencies ) {
$this->revisionDependencies[$revision] = $revision_lependencies;
}
}
ksort($this->revisionSqls);
ksort($this->revisionDependencies);
return true;
}
/**
* Checks that all dependent revisions are either present now OR were applied before
*
* @return bool
* @access private
*/
private function checkRevisionDependencies()
{
foreach ($this->revisionDependencies as $revision => $revision_dependencies) {
foreach ($revision_dependencies as $revision_dependency) {
if ( $this->revisionApplied($revision_dependency) ) {
// revision dependend upon already applied -> depencency fulfilled
continue;
}
if ( $revision_dependency >= $revision ) {
$this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' has incorrect dependency to revision #' . $revision_dependency . '. Only dependencies to older revisions are allowed!');
return false;
}
if ( !isset($this->revisionSqls[$revision_dependency]) ) {
$this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' depends on missing revision #' . $revision_dependency . '!');
return false;
}
}
}
return true;
}
/**
* Runs all pending sqls
*
* @return bool
* @access private
*/
private function applyRevisions()
{
if ( !$this->revisionSqls ) {
return true;
}
if ( $this->dryRun ) {
$this->appliedRevisions = array_merge($this->appliedRevisions, array_keys($this->revisionSqls));
return true;
}
$this->out('Upgrading Database ... ', true);
foreach ($this->revisionSqls as $revision => $sqls) {
echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... ';
$sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings
$no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", "# \\1;\n", $sqls); // add ";" to each comment end to ensure correct split
$sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it
$sqls = array_map('trim', $sqls);
foreach ($sqls as $sql) {
if ( substr($sql, 0, 1) == '#' ) {
// output comment as is
$this->toLog($sql);
echo $this->colorText($sql, 'purple') . PHP_EOL;
continue;
}
elseif ( $sql ) {
$this->toLog($sql . ' ... ', false);
echo mb_substr(trim(preg_replace('/(\n|\t| )+/is', ' ', ($this->isCommandLine ? $sql : htmlspecialchars($sql)))), 0, self::SQL_TRIM_LENGTH) . ' ... ';
$this->Conn->Query($sql);
if ( $this->Conn->hasError() ) {
// consider revisions with errors applied
$this->appliedRevisions[] = $revision;
return false;
}
else {
$this->toLog('OK (' . $this->Conn->getAffectedRows() . ')');
$this->displayStatus('OK (' . $this->Conn->getAffectedRows() . ')');
}
}
}
$this->appliedRevisions[] = $revision;
}
echo PHP_EOL;
return true;
}
/**
* Error handler for sql errors
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
*/
public function handleSqlError($code, $msg, $sql)
{
$this->toLog('FAILED' . PHP_EOL . 'SQL Error #' . $code . ': ' . $msg);
$this->displayStatus('FAILED' . PHP_EOL . 'SQL Error #' . $code . ': ' . $msg);
$this->out('Please execute rest of SQLs in this Revision by hand and run deployment script again.', true);
return true;
}
/**
* Checks if given revision was already applied
*
* @param int $revision
* @return bool
* @access private
*/
private function revisionApplied($revision)
{
foreach ($this->appliedRevisions as $applied_revision) {
// revision range
$applied_revision = explode('-', $applied_revision, 2);
if ( !isset($applied_revision[1]) ) {
// convert single revision to revision range
$applied_revision[1] = $applied_revision[0];
}
if ( $revision >= $applied_revision[0] && $revision <= $applied_revision[1] ) {
return true;
}
}
return false;
}
/**
* Returns path to given file in current module install folder
*
* @param string $filename
* @return string
* @access private
*/
private function getModuleFile($filename)
{
$module_folder = $this->Application->findModule('Name', $this->moduleName, 'Path');
return FULL_PATH . DIRECTORY_SEPARATOR . $module_folder . 'install/' . $filename;
}
/**
* Extracts revisions from string in format "(1,3,5464,23342,3243)"
*
* @param string $string
* @return Array
* @access private
*/
private function parseRevisionDependencies($string)
{
if ( !$string ) {
return Array ();
}
$string = explode(',', substr($string, 1, -1));
return array_map('trim', $string);
}
/**
* Applies requested color and bold attributes to given text string
*
* @param string $text
* @param string $color
* @param bool $bold
* @return string
* @access private
*/
private function colorText($text, $color, $bold = false)
{
if ( $this->isCommandLine ) {
$color_map = Array (
'black' => 30, // dark gray (in bold)
'blue' => 34, // light blue (in bold)
'green' => 32, // light green (in bold)
'cyan' => 36, // light cyan (in bold)
'red' => 31, // light red (in bold)
'purple' => 35, // light purple (in bold)
'brown' => 33, // yellow (in bold)
'gray' => 37, // white (in bold)
);
return "\033[" . ($bold ? 1 : 0) . ";" . $color_map[$color] . "m" . $text . "\033[0m";
}
$html_color_map = Array (
'black' => Array ('normal' => '#000000', 'bold' => '#666666'),
'blue' => Array ('normal' => '#00009C', 'bold' => '#3C3CFF'),
'green' => Array ('normal' => '#009000', 'bold' => '#00FF00'),
'cyan' => Array ('normal' => '#009C9C', 'bold' => '#00FFFF'),
'red' => Array ('normal' => '#9C0000', 'bold' => '#FF0000'),
'purple' => Array ('normal' => '#900090', 'bold' => '#F99CF9'),
'brown' => Array ('normal' => '#C9C909', 'bold' => '#FFFF00'),
'gray' => Array ('normal' => '#909090', 'bold' => '#FFFFFF'),
);
$html_color = $html_color_map[$color][$bold ? 'bold' : 'normal'];
return '<span style="color: ' . $html_color . '">' . htmlspecialchars($text) . '</span>';
}
/**
* Makes given text bold
*
* @param string $text
* @return string
* @access private
*/
private function boldText($text)
{
if ( $this->isCommandLine ) {
return "\033[1m" . $text . "\033[0m";
}
return '<strong>' . htmlspecialchars($text) . '</strong>';
}
/**
* Displays last command execution status
*
* @param string $status_text
* @param bool $new_line
* @return void
* @access private
*/
private function displayStatus($status_text, $new_line = true)
{
$color = substr($status_text, 0, 2) == 'OK' ? 'green' : 'red';
echo $this->colorText($status_text, $color, false);
if ( $new_line ) {
echo PHP_EOL;
}
}
/**
* Outputs a text and escapes it if necessary
*
* @param string $text
* @param bool $new_line
* @return void
*/
private function out($text, $new_line = false)
{
if ( !$this->isCommandLine ) {
$text = htmlspecialchars($text);
}
echo $text . ($new_line ? PHP_EOL : '');
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/helpers/rating_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/rating_helper.php (revision 15568)
+++ branches/5.2.x/core/units/helpers/rating_helper.php (revision 15569)
@@ -1,266 +1,266 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class RatingHelper extends kHelper {
/**
* One star width/height in pixels
*
* @var int
*/
var $ratingUnitWidth = 25;
var $ratingSmallUnitWidth = 10; //20;
/**
* Maximal star count
*
* @var int
*/
var $ratingMaximal = 5;
var $_phrases = Array (
'current_rating' => 'lu_CurrentRating',
'vote_title' => 'lu_VoteTitle',
'vote_count' => 'lu_VoteCount',
'invalid_rating' => 'lu_InvalidRating',
'already_voted' => 'lu_AlreadyVoted',
'thanks_for_voting' => 'lu_ThanksForVoting',
);
/**
* Draws rating bar for a given category item
*
* @param kDBItem $object
* @param bool $show_div
* @param string $additional_msg
* @param string $additional_style
* @return string
* @access public
*/
public function ratingBar(&$object, $show_div = true, $additional_msg = '', $additional_style = '')
{
// 1. user is allowed to vote by permissions
$perm_prefix = $this->Application->getUnitOption($object->Prefix, 'PermItemPrefix');
$static = !$this->Application->CheckPermission($perm_prefix . '.RATE', 0, $object->GetDBField('CategoryId'));
// 2. user isn't voting too frequently
$spam_helper =& $this->_getSpamHelper($object);
$user_voted = $spam_helper->InSpamControl();
if ( !$static && !$user_voted ) {
// allow to set rating when not static and user not voted before
$voting_js = $this->getVotingControl($object, $additional_style);
}
else {
$voting_js = '';
}
$msg_info = Array ('text' => $additional_msg, 'class' => Array ());
if ( $static ) {
$msg_info['class'][] = 'static';
}
if ( $user_voted ) {
$msg_info['class'][] = 'voted';
}
$rater = $this->ratingBarSimple($this->getAverageRating($object), $voting_js, $msg_info, $additional_style);
if ( $show_div ) {
// adds div around rating stars (when drawing rating first time)
$rater = '<div class="inline-rating" id="page_rating_' . $object->GetID() . '">' . $rater . '</div>';
}
return $rater;
}
/**
* Returns average rating
*
* @param kDBItem $object
* @return float|int
*/
function getAverageRating(&$object)
{
$total_votes = $object->GetDBField('CachedVotesQty');
$total_rating = $object->GetDBField('CachedRating') * $total_votes;
return $total_votes ? $total_rating / $total_votes : 0;
}
/**
* Draws rating bar for a given category item
*
* @param float $average_rating
* @param string $voting_js
* @param Array $msg_info
* @param string $additional_style
* @return string
* @access public
*/
public function ratingBarSimple($average_rating, $voting_js = '', $msg_info = null, $additional_style = '')
{
if ( !isset($msg_info) || !is_array($msg_info) ) {
$msg_info = Array ('text' => '', 'class' => Array ());
}
$unit_selected_width = $additional_style ? $this->ratingSmallUnitWidth : $this->ratingUnitWidth;
$rating_width = $average_rating ? @number_format($average_rating, 2) * $unit_selected_width : 0;
$rating2 = $average_rating ? @number_format($average_rating, 2) : 0;
$current_rating_text = $this->_replaceInPhrase('current_rating', Array ('<strong>' . $rating2 . '</strong>', $this->ratingMaximal));
$rater = ' <span class="inline-rating">
<ul class="star-rating ' . $additional_style . '" style="width: ' . $unit_selected_width * $this->ratingMaximal . 'px;">
<li class="current-rating" style="width: ' . $rating_width . 'px;">' . $current_rating_text . '</li>' . "\n" .
$voting_js . '
</ul>
</span>';
// this part is disabled for now, will be addressed once properly review
/*$rating1 = $average_rating ? @number_format($average_rating, 1) : 0;
$rater .= ' <p class="' . implode(' ', $msg_info['class']) . '">' . $this->_replaceInPhrase('vote_title', Array ('<strong>' . $rating1 . '</strong>', $this->ratingMaximal)) . ' (' . $this->_replaceInPhrase('vote_count', Array ($total_votes)) . ') </p>';*/
if ( $voting_js ) {
$rater .= '&nbsp;<span class="' . implode(' ', $msg_info['class']) . '">' . $msg_info['text'] . '</span>';
}
else {
// adds div around rating stars (when drawing rating first time)
$rater = '<div class="inline-rating">' . $rater . '</div>';
}
return $rater;
}
/**
* Returns control, used to vote on a given $object
*
* @param kDBItem $object
* @param string $additional_style
* @return string
*/
function getVotingControl(&$object, $additional_style = '')
{
$ret = '';
for ($i = 1; $i <= $this->ratingMaximal; $i++) {
$ret .= '<li><a href="#vote-' . $i . '" onclick="aRatingManager.makeVote(' . $i . ', \'' . $object->Prefix . '\', ' . $object->GetID() . ', \'' . $additional_style . '\'); return false;" title="' . $this->_replaceInPhrase('vote_title', Array ($i, $this->ratingMaximal)) . '" class="r' . $i . '-unit rater" rel="nofollow">' . $i . '</a></li>' . "\n";
}
return $ret;
}
/**
* Saves user's vote, when allowed
*
* @param kDBItem $object
* @return string
*/
function makeVote(&$object)
{
$spam_helper =& $this->_getSpamHelper($object);
if (!$object->isLoaded() || $spam_helper->InSpamControl()) {
return '@err:' . $this->_replaceInPhrase('already_voted');
}
$perm_prefix = $this->Application->getUnitOption($object->Prefix, 'PermItemPrefix');
$can_rate = $this->Application->CheckPermission($perm_prefix . '.RATE', 0, $object->GetDBField('CategoryId'));
$rating = (int)$this->Application->GetVar('rating'); // not numeric rating is from GoogleBot :(
$additional_style = $this->Application->GetVar('size');
if (($rating <= 0) || ($rating > $this->ratingMaximal) || !$can_rate) {
return '@err:' . $this->_replaceInPhrase('invalid_rating');
}
// save current rating
$fields_hash = Array (
'ItemId' => $object->GetID(),
'RatingValue' => $rating,
- 'IPAddress' => $_SERVER['REMOTE_ADDR'],
+ 'IPAddress' => $this->Application->getClientIp(),
'CreatedOn' => adodb_mktime(),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'CatalogRatings');
// recalculate average rating
$votes_count = $object->GetDBField('CachedVotesQty');
$avg_rating = $object->GetDBField('CachedRating');
$avg_rating = round((($votes_count * $avg_rating) + $rating) / ($votes_count + 1), 2);
$object->SetDBField('CachedRating', "$avg_rating");
$object->Update();
$sql = 'UPDATE '.$object->TableName.'
SET CachedVotesQty = CachedVotesQty + 1
WHERE '.$object->IDField.' = '.$object->GetID();
$this->Conn->Query($sql);
$object->SetDBField('CachedVotesQty', $object->GetDBField('CachedVotesQty') + 1); // for using in template
// prevent user from voting too quickly
$spam_helper->AddToSpamControl();
return $this->ratingBar($object, false, '<span class="thanks">' . $this->_replaceInPhrase('thanks_for_voting') . '</span>', $additional_style);
}
/*function purgeVotes()
{
$expired = adodb_mktime() - 86400 * $this->Application->ConfigValue('Timeout_Rating'); // 3600
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'CatalogRatings
WHERE CreatedOn < ' . $expired;
$this->Conn->Query($sql);
}*/
/**
* Performs sprintf on phrase translation using given variables
*
* @param string $phrase
* @param Array $arguments
* @return string
*/
function _replaceInPhrase($phrase, $arguments = Array ())
{
$value = $this->Application->Phrase($this->_phrases[$phrase], false);
if ($arguments) {
return vsprintf($value, $arguments);
}
return $value;
}
/**
* Returns SpamHelper object linked to given object
*
* @param kDBItem $object
* @return SpamHelper
* @access protected
*/
protected function &_getSpamHelper(&$object)
{
$spam_helper = $this->Application->recallObject('SpamHelper');
/* @var $spam_helper SpamHelper */
// 2. user isn't voting too frequently
$config_mapping = $this->Application->getUnitOption($object->Prefix, 'ConfigMapping');
$review_settings = $config_mapping['RatingDelayValue'] . ':' . $config_mapping['RatingDelayInterval'];
$spam_helper->InitHelper($object->GetDBField('ResourceId'), 'Rating', $review_settings, $object->GetCol('ResourceId'));
return $spam_helper;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/logs/session_logs/session_log_eh.php
===================================================================
--- branches/5.2.x/core/units/logs/session_logs/session_log_eh.php (revision 15568)
+++ branches/5.2.x/core/units/logs/session_logs/session_log_eh.php (revision 15569)
@@ -1,130 +1,130 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class SessionLogEventHandler extends kDBEventHandler {
/**
* Opens log for new session
*
* @param kEvent $event
*/
function OnStartSession($event)
{
if (!$this->Application->ConfigValue('UseChangeLog')) {
// don't use session log when change log is disabled
return ;
}
$object = $this->Application->recallObject($event->Prefix, null, Array ('skip_autoload' => 1));
/* @var $object kDBItem */
$fields_hash = Array (
'SessionStart' => adodb_mktime(),
- 'IP' => $_SERVER['REMOTE_ADDR'],
+ 'IP' => $this->Application->getClientIp(),
'PortalUserId' => $this->Application->RecallVar('user_id'),
'SessionId' => $this->Application->GetSID(),
'Status' => SESSION_LOG_ACTIVE,
);
$object->SetDBFieldsFromHash($fields_hash);
$object->UpdateFormattersSubFields();
if ($object->Create()) {
$this->Application->StoreVar('_SessionLogId_', $object->GetID());
}
}
/**
* Closes log for current session
*
* @param kEvent $event
*/
function OnEndSession($event)
{
$object = $this->Application->recallObject($event->Prefix, null, Array ('skip_autoload' => 1));
/* @var $object kDBItem */
$object->Load($this->Application->RecallVar('_SessionLogId_'));
if (!$object->isLoaded()) {
return ;
}
$fields_hash = Array (
'SessionEnd' => adodb_mktime(),
'Status' => SESSION_LOG_LOGGED_OUT,
);
$object->SetDBFieldsFromHash($fields_hash);
$object->UpdateFormattersSubFields();
$object->Update();
}
/**
* Apply custom processing to item
*
* @param kEvent $event
* @param string $type
* @return void
* @access protected
*/
protected function customProcessing(kEvent $event, $type)
{
if ( $event->Name == 'OnMassDelete' && $type == 'before' ) {
$ids = $event->getEventParam('ids');
if ( $ids ) {
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
$sql = 'SELECT ' . $id_field . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ') AND Status <> ' . SESSION_LOG_ACTIVE;
$allowed_ids = $this->Conn->GetCol($sql);
$event->setEventParam('ids', $allowed_ids);
}
}
}
/**
* Delete changes, related to deleted session
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent $event)
{
parent::OnAfterItemDelete($event);
$object = $event->getObject();
/* @var $object kDBItem */
$sql = 'SELECT ' . $this->Application->getUnitOption('change-log', 'IDField') . '
FROM ' . $this->Application->getUnitOption('change-log', 'TableName') . '
WHERE SessionLogId = ' . $object->GetID();
$related_ids = $this->Conn->GetCol($sql);
if ( $related_ids ) {
$temp_handler = $this->Application->recallObject('change-log_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems('change-log', '', $related_ids);
}
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/reviews/reviews_event_handler.php
===================================================================
--- branches/5.2.x/core/units/reviews/reviews_event_handler.php (revision 15568)
+++ branches/5.2.x/core/units/reviews/reviews_event_handler.php (revision 15569)
@@ -1,634 +1,634 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class ReviewsEventHandler extends kDBEventHandler
{
/**
* Returns special of main item for linking with sub-item
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function getMainSpecial(kEvent $event)
{
if ( $event->Special == 'product' && !$this->Application->isAdmin ) {
// rev.product should auto-link
return '';
}
return parent::getMainSpecial($event);
}
/**
* Checks REVIEW/REVIEW.PENDING permission by main object primary category (not current category)
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( $event->Name == 'OnAddReview' || $event->Name == 'OnCreate' ) {
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$main_object = $this->Application->recallObject($parent_prefix);
/* @var $main_object kCatDBItem */
$perm_name = $this->getPermPrefix($event).'.REVIEW';
$res = $this->Application->CheckPermission($perm_name, 0, $main_object->GetDBField('CategoryId')) ||
$this->Application->CheckPermission($perm_name.'.PENDING', 0, $main_object->GetDBField('CategoryId'));
if ( !$res ) {
$event->status = kEvent::erPERM_FAIL;
}
return $res;
}
$check_events = Array (
'OnItemBuild', 'OnUpdate', /*'OnMassApprove', 'OnMassDecline'*/
);
$perm_category = $this->_getReviewCategory($event);
if ( in_array($event->Name, $check_events) ) {
// check for PRODUCT.VIEW permission
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
$perm_prefix = $this->getPermPrefix($event);
if ( $perm_category === false ) {
// no item id present -> allow
return true;
}
switch ($event->Name) {
case 'OnItemBuild':
$res = $this->Application->CheckPermission($perm_prefix . '.VIEW', 0, $perm_category);
break;
case 'OnUpdate':
case 'OnMassApprove':
case 'OnMassDecline':
$res = $this->Application->CheckPermission($perm_prefix . '.ADD', 0, $perm_category) ||
$this->Application->CheckPermission($perm_prefix . '.MODIFY', 0, $perm_category);
break;
default:
$res = false;
break;
}
if ( !$res ) {
$event->status = kEvent::erPERM_FAIL;
}
return $res;
}
return parent::CheckPermission($event);
}
/**
* Returns primary category of review's main item
*
* @param kEvent $event
* @return int
*/
function _getReviewCategory($event)
{
$items_info = $this->Application->GetVar($event->getPrefixSpecial());
if ($items_info) {
// rev:PresetFormFields is used to initialize new review creation
list ($review_id, ) = each($items_info);
}
else {
// when adding new review in admin
$review_id = false;
}
if (!$review_id) {
return false;
}
$object = $event->getObject();
/* @var $object kDBItem */
// 1. get main item resource id (use object, because of temp tables in admin)
$sql = 'SELECT ItemId
FROM ' . $object->TableName . '
WHERE ' . $object->IDField . ' = ' . $review_id;
$resource_id = $this->Conn->GetOne($sql);
// 2. set main item id (for permission checks)
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$sql = 'SELECT ' . $this->Application->getUnitOption($parent_prefix, 'IDField') .'
FROM ' . $this->Application->getUnitOption($parent_prefix, 'TableName') .'
WHERE ResourceId = ' . $resource_id;
$this->Application->SetVar($parent_prefix . '_id', $this->Conn->GetOne($sql));
// 3. get main item category
$sql = 'SELECT CategoryId
FROM ' . $this->Application->getUnitOption('ci', 'TableName') .'
WHERE ItemResourceId = ' . $resource_id .' AND PrimaryCat = 1';
return $this->Conn->GetOne($sql);
}
/**
* Returns prefix for permissions
*
* @param kEvent $event
*/
function getPermPrefix($event)
{
$main_prefix = $this->Application->GetTopmostPrefix($event->Prefix, true);
// this will return LINK for l, ARTICLE for n, TOPIC for bb, PRODUCT for p
return $this->Application->getUnitOption($main_prefix, 'PermItemPrefix');
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @access protected
* @see OnListBuild
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
if ( !$this->Application->isAdminUser ) {
$object->addFilter('active', '%1$s.Status = ' . STATUS_ACTIVE);
}
switch ($event->Special) {
case 'showall':
$object->clearFilters();
break;
case 'item': // used ?
$object->clearFilters();
$parent_info = $object->getLinkedInfo();
$parent = $this->Application->recallObject($parent_info['ParentPrefix']);
/* @var $parent kDBItem */
$object->addFilter('item_reviews', '%1$s.ItemId = ' . $parent->GetDBField('ResourceId'));
break;
case 'products': // used in In-Portal (Structure & Data -> Reviews section)
$object->removeFilter('parent_filter'); // this is important
$object->addFilter('product_reviews', 'pr.ResourceId IS NOT NULL');
break;
}
if ( preg_match('/(.*)-rev/', $event->Prefix, $regs) ) {
// "Structure & Data" -> "Reviews" (section in K4)
$item_type = $this->Application->getUnitOption($regs[1], 'ItemType');
$object->addFilter('itemtype_filter', '%1$s.ItemType = ' . $item_type);
if ( $this->Application->isAdmin ) {
// temporarily solution so we can see sub-items on separate grid in Admin
$object->removeFilter('parent_filter');
}
}
if ( $event->getEventParam('type') == 'current_user' ) {
$object->addFilter('current_user', '%1$s.CreatedById = ' . $this->Application->RecallVar('user_id'));
- $object->addFilter('current_ip', '%1$s.IPAddress = "' . $_SERVER['REMOTE_ADDR'] . '"');
+ $object->addFilter('current_ip', '%1$s.IPAddress = "' . $this->Application->getClientIp() . '"');
}
}
/**
* Adds review from front in case if user is logged in
*
* @param kEvent $event
*/
function OnAddReview($event)
{
$event->CallSubEvent('OnCreate');
}
/**
* Get new review status on user review permission
*
* @param kEvent $event
* @return int
*/
function getReviewStatus($event)
{
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$main_object = $this->Application->recallObject($parent_prefix);
/* @var $main_object kCatDBItem */
$ret = STATUS_DISABLED;
$perm_name = $this->getPermPrefix($event).'.REVIEW';
if ($this->Application->CheckPermission($perm_name, 0, $main_object->GetDBField('CategoryId'))) {
$ret = STATUS_ACTIVE;
}
else if ($this->Application->CheckPermission($perm_name.'.PENDING', 0, $main_object->GetDBField('CategoryId'))) {
$ret = STATUS_PENDING;
}
return $ret;
}
/**
* Prefills all fields on front-end
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$parent_info = $object->getLinkedInfo();
$item_type = $this->Application->getUnitOption($parent_info['ParentPrefix'], 'ItemType');
- $object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('IPAddress', $this->Application->getClientIp());
$object->SetDBField('ItemType', $item_type);
$object->SetDBField('Module', $this->Application->findModule('Var', $parent_info['ParentPrefix'], 'Name'));
if ( $this->Application->isAdminUser ) {
// don't perform spam control on admin
return ;
}
$spam_helper = $this->Application->recallObject('SpamHelper');
/* @var $spam_helper SpamHelper */
$spam_helper->InitHelper($parent_info['ParentId'], 'Review', 0);
if ( $spam_helper->InSpamControl() ) {
$event->status = kEvent::erFAIL;
$object->SetError('ReviewText', 'too_frequent', 'lu_ferror_review_duplicate');
return;
}
$rating = $object->GetDBField('Rating');
if ( $rating < 1 || $rating > 5 ) {
$object->SetDBField('Rating', null);
}
$object->SetDBField('ItemId', $parent_info['ParentId']); // ResourceId
$object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
$object->SetDBField('Status', $this->getReviewStatus($event));
$object->SetDBField('TextFormat', 0); // set plain text format directly
}
/**
* Sets correct rating value
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$rating = $object->GetDBField('Rating');
if ( !$rating ) {
$object->SetDBField('Rating', null);
}
}
/**
* Updates item review counter
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->updateSubitemCounters($event);
if ( !$this->Application->isAdminUser ) {
$spam_helper = $this->Application->recallObject('SpamHelper');
/* @var $spam_helper SpamHelper */
$object = $event->getObject();
/* @var $object kDBItem */
$parent_info = $object->getLinkedInfo();
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
$review_settings = $config_mapping['ReviewDelayValue'] . ':' . $config_mapping['ReviewDelayInterval'];
$spam_helper->InitHelper($parent_info['ParentId'], 'Review', $review_settings);
$spam_helper->AddToSpamControl();
$review_status = $object->GetDBField('Status');
if ( $review_status == STATUS_ACTIVE || $review_status == STATUS_PENDING ) {
$email_event = $this->getPermPrefix($event) . '.REVIEW.' . ($review_status == STATUS_ACTIVE ? 'ADD' : 'ADD.PENDING');
$this->Application->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
$this->Application->EmailEventAdmin($email_event);
}
}
}
/**
* Updates item review counter
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$this->updateSubitemCounters($event);
$object = $event->getObject();
/* @var $object kDBItem */
if ( $this->Application->isAdminUser && !$object->IsTempTable() ) {
// send email on review status change from reviews grid in admin
$review_status = $object->GetDBField('Status');
$process_status = Array (STATUS_ACTIVE, STATUS_DISABLED);
if ( ($review_status != $object->GetOriginalField('Status')) && in_array($review_status, $process_status) ) {
$this->_loadMainObject($event);
$email_event = $this->getPermPrefix($event) . '.REVIEW.' . ($review_status == STATUS_ACTIVE ? 'APPROVE' : 'DENY');
$this->Application->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
}
}
}
/**
* Loads main object of review (link, article, etc.)
*
* @param kEvent $event
* @return kCatDBItem
*/
function _loadMainObject($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$parent_table_key = $this->Application->getUnitOption($event->Prefix, 'ParentTableKey');
$foreign_key = $this->Application->getUnitOption($event->Prefix, 'ForeignKey');
$main_object = $this->Application->recallObject($parent_prefix, null, Array ('skip_autoload' => true));
/* @var $main_object kDBItem */
$main_object->Load($object->GetDBField($foreign_key), $parent_table_key);
}
/**
* Updates total review counter, cached rating, votes count
*
* @param kEvent $event
*/
function updateSubitemCounters($event)
{
if ( $event->Special == '-item' ) {
// ignore Main Item Copy/Pasting and Deleting
return;
}
$object = $event->getObject();
/* @var $object kDBItem */
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$parent_table = $this->Application->getUnitOption($parent_prefix, 'TableName');
if ( $object->IsTempTable() ) {
$parent_table = $this->Application->GetTempName($parent_table, 'prefix:' . $object->Prefix);
}
$fields_hash = Array ('CachedReviewsQty' => 0, 'CachedRating' => 0, 'CachedVotesQty' => 0);
// 1. update review counter
$sql = 'SELECT COUNT(ReviewId)
FROM ' . $object->TableName . '
WHERE ItemId = ' . $object->GetDBField('ItemId');
$fields_hash['CachedReviewsQty'] = $this->Conn->GetOne($sql);
// 2. update votes counter + rating
$rating = $object->GetDBField('Rating');
$sql = 'SELECT CachedRating, CachedVotesQty
FROM ' . $parent_table . '
WHERE ResourceId = ' . $object->GetDBField('ItemId');
$parent_data = $this->Conn->GetRow($sql);
$avg_rating = $parent_data['CachedRating'];
$votes_count = $parent_data['CachedVotesQty'];
switch ($event->Name) {
case 'OnAfterItemCreate': // adding new review with rating
$this->changeRating($avg_rating, $votes_count, $rating, '+');
break;
case 'OnAfterItemDelete':
$this->changeRating($avg_rating, $votes_count, $rating, '-');
break;
case 'OnAfterItemUpdate':
$this->changeRating($avg_rating, $votes_count, $object->GetOriginalField('Rating'), '-');
$this->changeRating($avg_rating, $votes_count, $rating, '+');
break;
}
$fields_hash['CachedRating'] = "$avg_rating";
$fields_hash['CachedVotesQty'] = $votes_count;
$this->Conn->doUpdate($fields_hash, $parent_table, 'ResourceId = ' . $object->GetDBField('ItemId'));
}
/**
* Changes average rating and votes count based on requested operation
*
* @param float $avg_rating average rating before new vote
* @param int $votes_count votes count before new vote
* @param int $rating new vote (from 1 to 5)
* @param string $operation requested operation (+ / -)
*/
function changeRating(&$avg_rating, &$votes_count, $rating, $operation)
{
if ( $rating < 1 || $rating > 5 ) {
return;
}
if ( $operation == '+' ) {
$avg_rating = (($avg_rating * $votes_count) + $rating) / ($votes_count + 1);
++$votes_count;
}
else {
if ( $votes_count > 1 ) { // escape division by 0
$avg_rating = (($avg_rating * $votes_count) - $rating) / ($votes_count - 1);
}
else {
$avg_rating = (($avg_rating * $votes_count) - $rating) / 1;
}
--$votes_count;
}
}
/**
* Updates main item cached review counter
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent $event)
{
parent::OnAfterItemDelete($event);
$this->updateSubitemCounters($event);
}
/**
* Creates review & redirect to confirmation template
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
parent::OnCreate($event);
if ( $event->status != kEvent::erSUCCESS || $this->Application->isAdmin ) {
return;
}
$object = $event->getObject();
/* @var $object kDBItem */
if ( $this->Application->GetVar('ajax') == 'yes' ) {
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$params = Array ('status' => 'OK');
if ( $event->status != kEvent::erSUCCESS ) {
$ajax_form_helper->prepareJSONErrors($event, $params);
}
// let FormManager decide what template to show
$params['review_status'] = $object->GetDBField('Status');
$ajax_form_helper->sendResponse($event, $params);
}
else {
$event->SetRedirectParam('opener', 's');
$next_template = $object->GetDBField('Status') == STATUS_ACTIVE ? 'success_template' : 'success_pending_template';
$event->redirect = $this->Application->GetVar($next_template);
$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$event->SetRedirectParam('pass', 'm,'.$parent_prefix);
}
}
/**
* Makes left join to item's table, when in separate grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
if (preg_match('/(.*)-rev/', $event->Prefix, $regs) && $this->Application->prefixRegistred($regs[1])) {
// "Structure & Data" -> "Reviews" (section in K4)
// 1. add join to items table (for "Structure & Data" -> "Reviews" section)
$item_table = $this->Application->getUnitOption($regs[1], 'TableName');
$ci_table = $this->Application->getUnitOption('ci', 'TableName');
$list_sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs');
$list_sqls[''] .= ' LEFT JOIN '.$item_table.' item_table ON item_table.ResourceId = %1$s.ItemId';
$list_sqls[''] .= ' LEFT JOIN '.$ci_table.' ci ON item_table.ResourceId = ci.ItemResourceId AND ci.PrimaryCat = 1';
$this->Application->setUnitOption($event->Prefix, 'ListSQLs', $list_sqls);
// 2. add calculated field
$calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
$calculated_fields['']['CatalogItemName'] = 'item_table.' . $this->getTitleField($regs[1]);
$calculated_fields['']['CatalogItemId'] = 'item_table.' . $this->Application->getUnitOption($regs[1], 'IDField');
$calculated_fields['']['CatalogItemCategory'] = 'ci.CategoryId';
$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields);
}
}
/**
* Convert TitleField field of kMultiLanguage formatter used for it
*
* @param string $prefix
* @return string
*/
function getTitleField($prefix)
{
$lang_prefix = 'l'.$this->Application->GetVar('m_lang').'_';
$title_field = $this->Application->getUnitOption($prefix, 'TitleField');
$field_options = $this->Application->getUnitOption($prefix.'.'.$title_field, 'Fields');
$formatter_class = isset($field_options['formatter']) ? $field_options['formatter'] : '';
if ($formatter_class == 'kMultiLanguage' && !isset($field_options['master_field'])) {
$title_field = $lang_prefix.$title_field;
}
return $title_field;
}
/**
* Set's new perpage for Category Item Reviews (used on Front-end)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPerPage(kEvent $event)
{
parent::OnSetPerPage($event);
$parent_prefix = $event->Application->getUnitOption($event->Prefix, 'ParentPrefix');
$event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial() . ',' . $parent_prefix);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/forms/form_submissions/form_submissions_eh.php
===================================================================
--- branches/5.2.x/core/units/forms/form_submissions/form_submissions_eh.php (revision 15568)
+++ branches/5.2.x/core/units/forms/form_submissions/form_submissions_eh.php (revision 15569)
@@ -1,552 +1,552 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class FormSubmissionsEventHandler extends kDBEventHandler {
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( !$this->Application->isAdmin ) {
if ( $event->Name == 'OnCreate' ) {
// anybody can submit forms on front
return true;
}
}
$section = $event->getSection();
$form_id = $this->Application->GetVar('form_id');
$event->setEventParam('PermSection', $section . ':' . $form_id);
return parent::CheckPermission($event);
}
/**
* Always allow to view feedback form
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnItemBuild' => Array ('self' => true),
'OnEdit' => Array ('self' => 'view', 'subitem' => 'view'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Returns filter block based on field element type
*
* @param string $element_type
* @return string
*/
function _getFilterBlock($element_type)
{
$mapping = Array (
'text' => 'grid_like_filter',
'select' => 'grid_options_filter',
'radio' => 'grid_options_filter',
'checkbox' => 'grid_options_filter',
'password' => 'grid_like_filter',
'textarea' => 'grid_like_filter',
'label' => 'grid_like_filter',
'upload' => 'grid_empty_filter',
);
return $mapping[$element_type];
}
function OnBuildFormFields($event)
{
$form_id = $this->Application->GetVar('form_id');
if (!$form_id) return ;
$conf_fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$conf_grids = $this->Application->getUnitOption($event->Prefix, 'Grids');
$helper = $this->Application->recallObject('InpCustomFieldsHelper');
/* @var $helper InpCustomFieldsHelper */
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'FormFields
WHERE FormId = ' . (int)$form_id . '
ORDER BY Priority DESC';
$fields = $this->Conn->Query($sql, 'FormFieldId');
$use_options = Array ('radio', 'select', 'checkbox');
$check_visibility = $this->Application->LoggedIn() && !$this->Application->isAdminUser;
foreach ($fields as $field_id => $options) {
$field_visible = $check_visibility ? $options['Visibility'] == SubmissionFormField::VISIBILITY_EVERYONE : true;
$field_options = Array('type' => 'string', 'default' => $options['DefaultValue']);
if ($options['Required'] && $field_visible) {
$field_options['required'] = 1;
}
if ($options['Validation'] == 1) {
$field_options['formatter'] = 'kFormatter';
$field_options['regexp'] = '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i';
}
if ($options['DisplayInGrid']) {
$title = $options['Prompt'];
if (substr($title, 0, 1) == '+') {
$this->Application->Phrases->AddCachedPhrase('form_col_title' . $field_id, substr($title, 1));
$title = 'form_col_title' . $field_id;
}
$conf_grids['Default']['Fields']['fld_' . $field_id] = Array (
'title' => $title, 'no_special' => 1, 'nl2br' => 1, 'first_chars' => 200,
'filter_block' => $this->_getFilterBlock($options['ElementType'])
);
if ($options['ElementType'] == 'upload') {
$conf_grids['Default']['Fields']['fld_' . $field_id]['data_block'] = 'grid_upload_td';
}
if ($options['Validation'] == 1) {
$conf_grids['Default']['Fields']['fld_' . $field_id]['data_block'] = 'grid_email_td';
}
}
if ($options['ElementType'] == 'checkbox' && !$options['ValueList']) {
// fix case, when user haven't defined any options for checkbox
$options['ValueList'] = '1=la_Yes||0=la_No';
}
if (in_array($options['ElementType'], $use_options) && $options['ValueList']) {
// field type can have options and user have defined them too
$field_options['options'] = $helper->GetValuesHash( $options['ValueList'] );
$field_options['formatter'] = 'kOptionsFormatter';
}
if ($options['ElementType'] == 'password') {
$field_options['formatter'] = 'kPasswordFormatter';
$field_options['encryption_method'] = 'plain';
$field_options['verify_field'] = 'fld_' . $field_id . '_verify';
}
if ($options['ElementType'] == 'upload') {
$field_options['formatter'] = 'kUploadFormatter';
$field_options['upload_dir'] = WRITEBALE_BASE . DIRECTORY_SEPARATOR . 'user_files' . DIRECTORY_SEPARATOR . 'form_submissions';
if ( $options['UploadMaxSize'] ) {
$field_options['max_size'] = $options['UploadMaxSize'] * 1024; // convert Kbytes to bytes
}
if ( $options['UploadExtensions'] ) {
$field_options['file_types'] = '*.' . implode(';*.', explode(',', $options['UploadExtensions']));
}
}
$conf_fields['fld_' . $field_id] = $field_options;
}
$this->Application->setUnitOption($event->Prefix, 'Fields', $conf_fields);
$this->Application->setUnitOption($event->Prefix, 'Grids', $conf_grids);
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
$object->addFilter('form_filter', '%1$s.FormId = ' . (int)$this->Application->GetVar('form_id'));
}
/**
* Allows user to see it's last feedback form data
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent $event)
{
if ( $event->Special == 'last' ) {
// allow user to see his last submitted form
return $this->Application->RecallVar('last_submission_id');
}
if ( $this->Application->isAdminUser ) {
// don't check ids in admin
return parent::getPassedID($event);
}
// no way to see other user's form submission by giving it's ID directly in url
return 0;
}
/**
* Creates new form submission from Front-End
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
parent::OnCreate($event);
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$object = $event->getObject();
/* @var $object kDBItem */
// allows user to view only it's last submission
$this->Application->StoreVar('last_submission_id', $object->GetID());
$form_submission_helper = $this->Application->recallObject('FormSubmissionHelper');
/* @var $form_submission_helper FormSubmissionHelper */
$form =& $form_submission_helper->getForm($object);
$notify_email = $form->GetDBField('SubmitNotifyEmail');
if ( $notify_email ) {
$send_params = Array (
'to_name' => $notify_email,
'to_email' => $notify_email,
);
$this->Application->EmailEventAdmin('FORM.SUBMITTED', null, $send_params);
}
else {
$this->Application->EmailEventAdmin('FORM.SUBMITTED');
}
// $this->Application->EmailEventUser('FORM.SUBMITTED', null, 'to_email' => '');
$event->SetRedirectParam('opener', 's');
$event->SetRedirectParam('m_cat_id', 0);
$theme = $this->Application->recallObject('theme.current');
/* @var $theme kDBItem */
$template = htmlspecialchars_decode($this->Application->GetVar('success_template')); // kHTTPQuery do htmlspecialchars on everything
$alias_template = $theme->GetField('TemplateAliases', $template);
$event->redirect = $alias_template ? $alias_template : $template;
}
/**
* Processes Captcha code
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
- $object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('IPAddress', $this->Application->getClientIp());
if ( !$object->GetDBField('ReferrerURL') ) {
$referrer = $this->Application->GetVar('original_referrer');
if ( !$referrer ) {
$base_url = preg_quote($this->Application->BaseURL(), '/');
$referrer = preg_replace('/^' . $base_url . '/', '/', $_SERVER['HTTP_REFERER'], 1);
}
$object->SetDBField('ReferrerURL', $referrer);
}
$form_submission_helper = $this->Application->recallObject('FormSubmissionHelper');
/* @var $form_submission_helper FormSubmissionHelper */
$form =& $form_submission_helper->getForm($object);
// validate captcha code
if ( $form->GetDBField('UseSecurityImage') && !$this->Application->LoggedIn() ) {
$captcha_helper = $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
$captcha_helper->validateCode($event, false);
}
}
/**
* Checks, that target submission was selected for merging
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$object->setRequired('MergeToSubmission', $object->GetDBField('IsMergeToSubmission'));
}
/**
* Passes form_id, when using "Prev"/"Next" toolbar buttons
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndGo(kEvent $event)
{
parent::OnPreSaveAndGo($event);
if ( $event->status == kEvent::erSUCCESS ) {
$event->SetRedirectParam('pass', 'm,form,formsubs');
}
}
/**
* Saves edited item in temp table and goes
* to passed tabs, by redirecting to it with OnPreSave event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndGoToTab(kEvent $event)
{
parent::OnPreSaveAndGoToTab($event);
if ( $event->status == kEvent::erSUCCESS ) {
$event->SetRedirectParam('pass', 'm,form,formsubs');
}
}
/**
* Set's new per-page for grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPerPage(kEvent $event)
{
parent::OnSetPerPage($event);
$event->SetRedirectParam('pass', 'm,form,' . $event->getPrefixSpecial());
}
/**
* Occurs when page is changed (only for hooking)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPage(kEvent $event)
{
parent::OnSetPage($event);
$event->SetRedirectParam('pass', 'm,form,' . $event->getPrefixSpecial());
}
/**
* Fills merge-to dropdown
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
if ($event->Special == 'merge-to') {
return ;
}
$object = $event->getObject();
/* @var $object kDBItem */
$form_id = $object->GetDBField('FormId');
$email_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL);
if (!$email_field) {
return ;
}
$merge_to = $this->Application->recallObject($event->Prefix . '.merge-to', null, Array ('skip_autoload' => true));
/* @var $merge_to kDBItem */
$sql = $merge_to->GetSelectSQL() . ' WHERE (FormId = ' . $form_id . ') AND (' . $email_field . ' = ' . $this->Conn->qstr( $object->GetDBField($email_field) ) . ')';
$submissions = $this->Conn->Query($sql, $object->IDField);
// remove this submission
unset($submissions[ $object->GetID() ]);
if (!$submissions) {
return ;
}
$options = Array ();
$name_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_NAME);
$subject_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT);
$language = $this->Application->recallObject('lang.current');
/* @var $language kDBItem */
$date_format = $language->GetDBField('DateFormat');
foreach ($submissions as $submission_id => $submission_data) {
$option_title = ''; // SenderName (email@address.com) - Subject (06/29/2010)
$merge_to->LoadFromHash($submission_data);
if ($name_field) {
$option_title = $merge_to->GetDBField($name_field) . ' (' . $merge_to->GetDBField($email_field) . ') - ';
}
else {
$option_title = $merge_to->GetDBField($email_field) . ' - ';
}
if ($subject_field) {
$option_title .= $merge_to->GetField($subject_field) . ' (' . $merge_to->GetField('SubmissionTime', $date_format) . ')';
}
else {
$option_title .= $merge_to->GetField('SubmissionTime', $date_format);
}
$options[$submission_id] = $option_title;
}
$object->SetFieldOption('MergeToSubmission', 'options', $options);
}
/**
* Returns submission field name based on given role
*
* @param int $form_id
* @param string $role
* @return string
*/
function getFieldByRole($form_id, $role)
{
static $cache = Array ();
if (!array_key_exists($form_id, $cache)) {
$id_field = $this->Application->getUnitOption('formflds', 'IDField');
$table_name = $this->Application->getUnitOption('formflds', 'TableName');
$sql = 'SELECT ' . $id_field . ', EmailCommunicationRole
FROM ' . $table_name . '
WHERE FormId = ' . $form_id . ' AND EmailCommunicationRole <> 0';
$cache[$form_id] = $this->Conn->GetCol($sql, 'EmailCommunicationRole');
}
// get field name by role
return array_key_exists($role, $cache[$form_id]) ? 'fld_' . $cache[$form_id][$role] : false;
}
/**
* Performs submission merge
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
parent::OnUpdate($event);
if ($event->status == kEvent::erSUCCESS) {
$object = $event->getObject();
/* @var $object kDBItem */
$merge_to = $object->GetDBField('MergeToSubmission');
if (!$merge_to) {
return ;
}
$form_id = $object->GetDBField('FormId');
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Forms
WHERE FormId = ' . $form_id;
$form_info = $this->Conn->GetRow($sql);
$reply = $this->Application->recallObject('submission-log.merge', null, Array ('skip_autoload' => true));
/* @var $reply kDBItem */
$email_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL);
$subject_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT);
$body_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_BODY);
$reply->SetDBField('FormSubmissionId', $merge_to);
if ($email_field) {
$reply->SetDBField('FromEmail', $object->GetDBField($email_field));
}
$reply->SetDBField('ToEmail', $form_info['ReplyFromEmail']);
if ($subject_field) {
$reply->SetDBField('Subject', $object->GetDBField($subject_field));
}
if ($body_field) {
$reply->SetDBField('Message', $object->GetDBField($body_field));
}
$reply->SetDBField('SentOn_date', $object->GetDBField('SubmissionTime'));
$reply->SetDBField('SentOn_time', $object->GetDBField('SubmissionTime'));
$reply->SetDBField('MessageId', $object->GetDBField('MessageId'));
$reply->SetDBField('SentStatus', SUBMISSION_LOG_SENT);
// as if emails was really received via mailbox
$this->Application->SetVar('client_mode', 1);
if ($reply->Create()) {
// delete submission, since it was merged
$object->Delete();
}
}
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/phrases/phrases_event_handler.php
===================================================================
--- branches/5.2.x/core/units/phrases/phrases_event_handler.php (revision 15568)
+++ branches/5.2.x/core/units/phrases/phrases_event_handler.php (revision 15569)
@@ -1,520 +1,520 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class PhrasesEventHandler extends kDBEventHandler
{
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnItemBuild' => Array ('self' => true, 'subitem' => true),
'OnPreparePhrase' => Array ('self' => true, 'subitem' => true),
'OnExportPhrases' => Array ('self' => 'view'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Hides phrases from disabled modules
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
$object->addFilter('module_filter', '%1$s.Module IN (SELECT Name FROM ' . TABLE_PREFIX . 'Modules WHERE Loaded = 1)');
}
/**
* Apply some special processing to object being
* recalled before using it in other events that
* call prepareObject
*
* @param kDBItem|kDBList $object
* @param kEvent $event
* @return void
* @access protected
*/
protected function prepareObject(&$object, kEvent $event)
{
// don't call parent
if ( $event->Special == 'import' || $event->Special == 'export' ) {
$this->RemoveRequiredFields($object);
$object->setRequired(Array ('ExportDataTypes', 'LangFile', 'PhraseType', 'Module'));
// allow multiple phrase types to be selected during import/export
$object->SetFieldOption('PhraseType', 'type', 'string');
}
}
/**
* Allow to create phrases from front end in debug mode with DBG_PHRASES constant set
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( !$this->Application->isAdmin && $this->Application->isDebugMode(false) && kUtil::constOn('DBG_PHRASES') ) {
$allow_events = Array ('OnCreate', 'OnUpdate');
if ( in_array($event->Name, $allow_events) ) {
return true;
}
}
return parent::CheckPermission($event);
}
/**
* Prepares phrase for translation
*
* @param kEvent $event
*/
function OnPreparePhrase($event)
{
$label = $this->Application->GetVar($event->getPrefixSpecial() . '_label');
if (!$label) {
return ;
}
// we got label, try to get it's ID then if any
$phrase_id = $this->_getPhraseId($label);
if ($phrase_id) {
$event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $phrase_id);
$event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial());
$next_template = $this->Application->GetVar('next_template');
if ($next_template) {
$event->SetRedirectParam('next_template', $next_template);
}
}
else {
$event->CallSubEvent('OnNew');
}
if ($this->Application->GetVar('simple_mode')) {
$event->SetRedirectParam('simple_mode', 1);
}
}
function _getPhraseId($phrase)
{
$sql = 'SELECT ' . $this->Application->getUnitOption($this->Prefix, 'IDField') . '
FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName') . '
WHERE PhraseKey = ' . $this->Conn->qstr( mb_strtoupper($phrase) );
return $this->Conn->GetOne($sql);
}
/**
* Sets phrase type based on place where it's created (to display on form only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent $event)
{
parent::OnPreCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$this->_setPhraseModule($object);
}
/**
* Forces new label in case if issued from get link
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnNew(kEvent $event)
{
parent::OnNew($event);
$object = $event->getObject();
/* @var $object kDBItem */
$label = $this->Application->GetVar($event->getPrefixSpecial() . '_label');
if ( $label ) {
// phrase is created in language, used to display phrases
$object->SetDBField('Phrase', $label);
$object->SetDBField('PhraseType', $this->_getPhraseType($label)); // to show on form
$object->SetDBField('PrimaryTranslation', $this->_getPrimaryTranslation($label));
}
$this->_setPhraseModule($object);
if ( $event->Special == 'export' || $event->Special == 'import' ) {
$object->SetDBField('PhraseType', '|0|1|2|');
$object->SetDBField('Module', '|' . implode('|', array_keys($this->Application->ModuleInfo)) . '|');
$export_mode = $this->Application->GetVar('export_mode');
if ( $export_mode != 'lang' ) {
$object->SetDBField('ExportDataTypes', '|' . $export_mode . '|');
}
}
}
/**
* Returns given phrase translation on primary language
*
* @param string $phrase
* @return string
* @access protected
*/
protected function _getPrimaryTranslation($phrase)
{
$sql = 'SELECT l' . $this->Application->GetDefaultLanguageId() . '_Translation
FROM ' . $this->Application->getUnitOption($this->Prefix, 'TableName') . '
WHERE PhraseKey = ' . $this->Conn->qstr( mb_strtoupper($phrase) );
return $this->Conn->GetOne($sql);
}
/**
* Sets new phrase module
*
* @param kDBItem $object
* @return void
* @access protected
*/
protected function _setPhraseModule(&$object)
{
$last_module = $this->Application->GetVar('last_module');
if ( $last_module ) {
$object->SetDBField('Module', $last_module);
}
}
/**
* Forces create to use live table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
if ( $this->Application->GetVar($event->Prefix . '_label') ) {
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
if ( $this->Application->GetVar('m_lang') != $this->Application->GetVar('lang_id') ) {
$object->SwitchToLive();
}
$this->returnToOriginalTemplate($event);
}
parent::OnCreate($event);
}
/**
* Redirects to original template after phrase is being update
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
if ( $this->Application->GetVar($event->Prefix . '_label') ) {
$this->returnToOriginalTemplate($event);
}
parent::OnUpdate($event);
}
/**
* Returns to original template after phrase adding/editing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function returnToOriginalTemplate(kEvent $event)
{
$next_template = $this->Application->GetVar('next_template');
if ( $next_template ) {
$event->redirect = $next_template;
$event->SetRedirectParam('opener', 's');
}
}
/**
* Set last change info, when phrase is created
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$primary_language_id = $this->Application->GetDefaultLanguageId();
if ( !$object->GetDBField('l' . $primary_language_id . '_Translation') ) {
// no translation on primary language -> try to copy from other language
$src_languages = Array ('lang_id', 'm_lang'); // editable language, theme language
foreach ($src_languages as $src_language) {
$src_language = $this->Application->GetVar($src_language);
$src_value = $src_language ? $object->GetDBField('l' . $src_language . '_Translation') : false;
if ( $src_value ) {
$object->SetDBField('l' . $primary_language_id . '_Translation', $src_value);
break;
}
}
}
$this->_phraseChanged($event);
}
/**
* Update last change info, when phrase is updated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->_phraseChanged($event);
}
/**
* Set's phrase key and last change info, used for phrase updating and loading
*
* @param kEvent $event
*/
function _phraseChanged($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$label = $object->GetDBField('Phrase');
$object->SetDBField('PhraseKey', mb_strtoupper($label));
$object->SetDBField('PhraseType', $this->_getPhraseType($label));
if ( $this->translationChanged($object) ) {
$object->SetDBField('LastChanged_date', adodb_mktime() );
$object->SetDBField('LastChanged_time', adodb_mktime() );
- $object->SetDBField('LastChangeIP', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('LastChangeIP', $this->Application->getClientIp());
}
$this->Application->Session->SetCookie('last_module', $object->GetDBField('Module'));
}
/**
* Returns phrase type, that corresponds given phrase label
*
* @param string $label
* @return int
* @access protected
*/
protected function _getPhraseType($label)
{
$phrase_type_map = Array (
'LU' => Language::PHRASE_TYPE_FRONT,
'LA' => Language::PHRASE_TYPE_ADMIN,
'LC' => Language::PHRASE_TYPE_COMMON
);
$label = mb_strtoupper($label);
$label_prefix = substr($label, 0, 2);
return isset($phrase_type_map[$label_prefix]) ? $phrase_type_map[$label_prefix] : Language::PHRASE_TYPE_COMMON;
}
/**
* Checks, that at least one of phrase's translations was changed
*
* @param kDBItem $object
* @return bool
*/
function translationChanged(&$object)
{
$changed_fields = array_keys( $object->GetChangedFields() );
$translation_fields = Array ('Translation', 'HintTranslation', 'ColumnTranslation');
foreach ($changed_fields as $changed_field) {
$changed_field = preg_replace('/^l[\d]+_/', '', $changed_field);
if ( in_array($changed_field, $translation_fields) ) {
return true;
}
}
return false;
}
/**
* Changes default module to custom (when available)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
if ($this->Application->findModule('Name', 'Custom')) {
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$fields['Module']['default'] = 'Custom';
$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
}
// make sure, that PrimaryTranslation column always refrers to primary language column
$language_id = $this->Application->GetVar('lang_id');
if (!$language_id) {
$language_id = $this->Application->GetVar('m_lang');
}
$primary_language_id = $this->Application->GetDefaultLanguageId();
$calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
foreach ($calculated_fields[''] as $field_name => $field_expression) {
$field_expression = str_replace('%5$s', $language_id, $field_expression);
$field_expression = str_replace('%4$s', $primary_language_id, $field_expression);
$calculated_fields[''][$field_name] = $field_expression;
}
$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields);
if ($this->Application->GetVar('regional')) {
$this->Application->setUnitOption($event->Prefix, 'PopulateMlFields', true);
}
}
/**
* Saves changes & changes language
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndChangeLanguage(kEvent $event)
{
$label = $this->Application->GetVar($event->getPrefixSpecial() . '_label');
if ( $label && !$this->UseTempTables($event) ) {
$phrase_id = $this->_getPhraseId($label);
if ( $phrase_id ) {
$event->CallSubEvent('OnUpdate');
$event->SetRedirectParam('opener', 's');
}
else {
$event->CallSubEvent('OnCreate');
$event->SetRedirectParam('opener', 's');
}
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$event->SetRedirectParam($event->getPrefixSpecial() . '_event', 'OnPreparePhrase');
$event->SetRedirectParam('pass_events', true);
}
if ( $this->Application->GetVar('simple_mode') ) {
$event->SetRedirectParam('simple_mode', 1);
}
parent::OnPreSaveAndChangeLanguage($event);
}
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnEdit(kEvent $event)
{
parent::OnEdit($event);
// use language from grid, instead of primary language used by default
$event->SetRedirectParam('m_lang', $this->Application->GetVar('m_lang'));
}
/**
* Stores ids of selected phrases and redirects to export language step 1
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExportPhrases(kEvent $event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$this->Application->setUnitOption('phrases', 'AutoLoad', false);
$this->StoreSelectedIDs($event);
$this->Application->StoreVar('export_language_ids', $this->Application->GetVar('m_lang'));
$event->setRedirectParams(
Array (
'phrases.export_event' => 'OnNew',
'pass' => 'all,phrases.export',
'export_mode' => $event->Prefix,
)
);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/install/step_templates/sys_config.tpl
===================================================================
--- branches/5.2.x/core/install/step_templates/sys_config.tpl (revision 15568)
+++ branches/5.2.x/core/install/step_templates/sys_config.tpl (revision 15569)
@@ -1,75 +1,77 @@
<?php
$settings = Array (
'WebsitePath' => Array ('type' => 'text', 'title' => 'Web Path to Installation', 'section' => 'Misc', 'required' => 1, 'default' => ''),
'WriteablePath' => Array ('type' => 'text', 'title' => 'Path to Writable folder', 'section' => 'Misc', 'required' => 1, 'default' => '/system'),
'RestrictedPath' => Array ('type' => 'text', 'title' => 'Path to Restricted folder', 'section' => 'Misc', 'required' => 1, 'default' => '/system/.restricted'),
'AdminDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin folder', 'section' => 'Misc', 'default' => '/admin'),
'AdminPresetsDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin Interface Presets folder', 'section' => 'Misc', 'default' => '/admin'),
'ApplicationClass' => Array ('type' => 'text', 'title' => 'Name of Base Application Class', 'section' => 'Misc', 'default' => 'kApplication'),
'ApplicationPath' => Array ('type' => 'text', 'title' => 'Path to Base Application Class file', 'section' => 'Misc', 'default' => '/core/kernel/application.php'),
'CacheHandler' => Array ('type' => 'select', 'title' => 'Output Caching Engine', 'section' => 'Misc', 'default' => 'Fake'),
'MemcacheServers' => Array ('type' => 'text', 'title' => 'Location of Memcache Servers', 'section' => 'Misc', 'default' => 'localhost:11211'),
'CompressionEngine' => Array ('type' => 'select', 'title' => 'CSS/JS Compression Engine', 'section' => 'Misc', 'default' => ''),
'WebsiteCharset' => Array ('type' => 'text', 'title' => 'Website Charset', 'section' => 'Misc', 'required' => 1, 'default' => 'utf-8'),
'EnableSystemLog' => Array ('type' => 'radio', 'title' => 'Enable "System Log"', 'section' => 'Misc', 'required' => 1, 'default' => '0'),
'SystemLogMaxLevel' => Array ('type' => 'select', 'title' => 'Highest "Log Level", that will be saved in "System Log"', 'section' => 'Misc', 'required' => 1, 'default' => '5'),
+ 'TrustProxy' => Array ('type' => 'radio', 'title' => 'Trust Proxy', 'section' => 'Misc', 'required' => 1, 'default' => '0'),
);
$settings['CacheHandler']['options'] = $this->toolkit->getWorkingCacheHandlers();
$settings['CompressionEngine']['options'] = $this->toolkit->getWorkingCompressionEngines();
$settings['EnableSystemLog']['options'] = Array (1 => 'Enabled', 2 => 'User-only', 0 => 'Disabled');
$settings['SystemLogMaxLevel']['options'] = Array (
0 => 'emergency', 1 => 'alert', 2 => 'critical', 3 => 'error',
4 => 'warning', 5 => 'notice', 6 => 'info', 7 => 'debug'
);
+ $settings['TrustProxy']['options'] = Array (1 => 'Yes', 0 => 'No');
$row_class = 'table-color2';
foreach ($settings as $config_var => $output_params) {
$row_class = $row_class == 'table-color1' ? 'table-color2' : 'table-color1';
?>
<tr class="<?php echo $row_class; ?>">
<td class="text">
<b><?php echo ($output_params['title'] ? $output_params['title'] : $config_var) . (isset($output_params['required']) ? ' <span class="error">*</span>' : ''); ?>:</b>
</td>
<td>
<?php
$config_value = $this->toolkit->getSystemConfig($output_params['section'], $config_var, $output_params['default']);
switch ( $output_params['type'] ) {
case 'text':
echo '<input type="text" name="system_config[' . $output_params['section'] . '][' . $config_var . ']" value="' . $config_value . '" class="text" style="width: 200px;"/>';
break;
case 'select':
echo '<select name="system_config[' . $output_params['section'] . '][' . $config_var . ']">';
if ( $output_params['options'][$config_value] == 'None' ) {
$tmp_values = array_keys($output_params['options']);
if ( count($tmp_values) > 1 ) {
$config_value = $tmp_values[1];
}
}
foreach($output_params['options'] as $option_key => $option_value) {
$selected = $option_key == $config_value ? ' selected' : '';
echo '<option value="' . $option_key . '"' . $selected . '>' . $option_value . '</option>';
}
echo '</select>';
break;
case 'radio':
foreach($output_params['options'] as $option_key => $option_value) {
$selected = $option_key == $config_value ? ' checked' : '';
echo '<input type="radio" name="system_config[' . $output_params['section'] . '][' . $config_var . ']" value="' . $option_key . '"' . $selected . '/>' . $option_value;
}
break;
}
?>
</td>
</tr>
<?php
}
?>
Index: branches/5.2.x/core/install/steps_db.xml
===================================================================
--- branches/5.2.x/core/install/steps_db.xml (revision 15568)
+++ branches/5.2.x/core/install/steps_db.xml (revision 15569)
@@ -1,221 +1,224 @@
<steps>
<step name="clean_db" title="Clean Database">
<![CDATA[missing step description]]>
</step>
<step name="db_config" title="Database Configuration">
<![CDATA[<p><b><i>Database Hostname</i></b> - IP or hostname of your database server (normally <i>"localhost"</i>).</p>
<p><b><i>Database Name</i></b> - name of the database where In-Portal will be installed.</p>
<p><b><i>Database User Name</i></b> - name of the user for selected database.</p>
<p><b><i>Database User Password</i></b> - password for selected username.</p>
<p><b><i>Database Collation</i></b> - character set used to store data in text fields (normally <i>"utf8_general_ci"</i>).</p>
<p><b><i>Prefix for Table Names</i></b> - specified when multiple scripts will be run in the same database.
Prefix can be any text string allowed in table naming by your database engine (normally <i>"inp_"</i>).</p>
<p><b><i>Use existing In-Portal installation setup in this Database</i></b> - select <i>"Yes"</i>
if you already have In-Portal installed in this database and want to use it. Select <i>"No"</i> in all other cases.</p>
]]>
</step>
<step name="select_license" title="Select License" help_title="License Configuration">
<![CDATA[<p><b>In-Portal is an Open Source</b> object-oriented framework that is developed in PHP
and provides a quick and easy way to build websites and web applications.</p>
<p>In-Portal is copyrighted and distributed under <a href="http://www.in-portal.org/license" target="_blank">GPLv2 license</a>.</p>
<p><b><i>GPL / Open Source License</i></b> - by downloading and installing In-Portal
under GPLv2 license you understand and agree to all terms of the
<a href="http://www.in-portal.org/license" target="_blank">GPLv2 license</a>.</p>
<p><b><i>Upload License File</i></b> - if you have obtained Commercial
<a href="http://www.in-portal.com/features-modules.html" target="_blank">Modules</a>
or <a href="http://www.in-portal.com/support-downloads/customer-support.html" target="_blank">Support</a>
from Intechnic you will be provided with a license file, upload it here.</p>
<!--
<p><b><i>Download from Intechnic Servers</i></b> - .</p>
-->
<p><b><i>Use Existing License</i></b> - if a valid license has been detected on your server,
you can choose this option and continue the installation process.</p>]]>
</step>
<step name="download_license" title="Download License" help_title="Download License from Intechnic">
<![CDATA[<p><b>In-Portal is an Open Source</b> object-oriented framework that is developed in PHP
and provides a quick and easy way to build websites and web applications.</p>
<p>In-Portal is copyrighted and distributed under <a href="http://www.in-portal.org/license" target="_blank">GPLv2 license</a>.</p>
<p><b><i>GPL / Open Source License</i></b> - by downloading and installing In-Portal
under GPLv2 license you understand and agree to all terms of
<a href="http://www.in-portal.org/license" target="_blank">GPLv2 license</a>.</p>
<p><b><i>Download from Intechnic Servers</i></b> - if you have obtained Commercial
<a href="http://www.in-portal.com/features-modules.html" target="_blank">Modules</a>
or <a href="http://www.in-portal.com/support-downloads/customer-support.html" target="_blank">Support</a>
from Intechnic you will be provided with a license, specify your Username and Password in order to download the license.</p>]]>
</step>
<step name="select_domain" title="Select Domain" help_title="Select Licensed Domain">
<![CDATA[<p>Select the domain you wish to install In-Portal on.</p>
<p>The <i>Other</i> option can be used to install In-Portal other custom domains. Note that your web server should match entered domain name.</p>]]>
</step>
<step name="root_password" title="Set Root Password" help_title="Set Admin Root Password">
<![CDATA[<p>The <b>Root Password</b> is initially required to access the Admin Console of In-Portal.
The root user can <b>NOT</b> be used to access the Front-end of your In-Portal website.</p>
<p>Once installation is completed it's highly recommented to create additional users with admin privlidges.</p>]]>
</step>
<step name="choose_modules" title="Modules to Install">
<![CDATA[<p>Current step lists all <b>In-Portal Modules</b> that were found on your server and can be installed now.</p>
<p>Additional <a href="http://www.in-portal.com/features-modules.html" target="_blank">In-Portal Modules</a> can be found and downloaded <a href="http://www.in-portal.com/features-modules.html" target="_blank">here</a>.</p>
<p>While <a href="http://www.in-portal.com/features-modules.html" target="_blank">In-Portal Community</a> constantly works on improving In-Portal by creating new functionality and releasing new modules we are always looking for new ideas and Your help so we can make In-Portal even better software!</p>]]>
</step>
<step name="check_paths" title="Filesystem Check">
<![CDATA[<p><b>In-Portal Installer checks through the system folders and files</b> that require write permissions (777) to be set in order to run successfully In-Portal on your website.</p>
<p>In case if you see a <strong>Failure notice</strong> saying that In-Portal Installation cannot continue until all permissions are set correctly please continue reading below.</p>
<p><strong>Permissions can be set</strong> by using <a href="http://www.google.com/search?q=free+ftp+program" target="_blank">FTP program</a> or directly in <i>shell</i> running "chmod" command. Please refer to the following <a href="http://www.stadtaus.com/en/tutorials/chmod-ftp-file-permissions.php" target="_blank">guide</a> to learn how to set permissions using your FTP program. In case if you have access to <i>shell</i> in your account you can simply run fix_perms.sh files located in /tools folder or do <br/><br/>
&nbsp;&nbsp;&nbsp;# chmod -R 777 ../system ../themes ../system/config.php</p>
<p><strong>Security reasons</strong> you will be asked to change permissions back to 755 on /system/config.php file and root / folder of In-Portal on the last step of this installation process!</p>
]]>
</step>
<step name="post_config" title="Basic Configuration">
<![CDATA[<p>Adjust <b>Basic Configuration</b> settings on this step.</p>
<p>Once your In-Portal is installed you can login into <b>Admin Console</b> to change these and other settings. The Configuration section is located in main navigation menu.</p>
<br/>
<p><b>Additional Recommendations:</b></p>
<p><b><i>1. Use Cron</i></b> (<a href="http://en.wikipedia.org/wiki/Cron" target="_blank">UNIX/BSD/Linux</a>) or <b>Task Scheduler</b> (<a href="http://en.wikipedia.org/wiki/Task_Scheduler" target="_blank">Windows</a>) to run Regular Events in your In-Portal.<br />
It's highly recommended to setup your cron to run <b>every minute</b> so all system events that are enabled will run in the background based on their schedule. These events can be managed in Admin Console via <b><i>Configuration -> Website -> Scheduled Tasks</i></b> section.
<p>In-Portal <b>cron</b> file is located in <b>/tools/cron.php</b> folder and can be setup using hosting Control Panel or <a href="http://en.wikipedia.org/wiki/Cron" target="_blank">manually</a>. In Plesk or CPanel interfaces use dialog to add a new cron job and specify the following (use correct paths)<br />
&nbsp;&nbsp;&nbsp;<b>/absolute/path/to/bin/php -f /absolute/path/to/in-portal/tools/cron.php</b></p>
<p><b><i>2. Adjust Scheduled Tasks</i></b><br />
As was explained in the previous recommendation there is a <b><i>Configuration -> Website -> Scheduled Tasks</i></b> section where you can control Events triggered by the system. These events do their job to cleanup the data, old image files, check the data integrity, RSS feeds and other processes required for your In-Portal to run efficiently. We do recommend to review and enable/disable these events based on your website needs.</p>
<p><b><i>3. Set Mail Server</i></b><br />
It's recommended to review and adjust your mail server settings once your In-Portal is up and running. This can be done in Admin Console under <b><i>Configuration -> Website -> Advanced</i></b> section.</p>
<p><b>We strongly recommend carefully reviewing and adjusting all settings under Configuration -> Website section!</b></p>
]]>
</step>
<step name="sys_config" title="System Configuration">
<![CDATA[<p>These are system advanced settings and must be changed with caution. It's <strong><i>not recommended</i></strong> to
change these settings unless you exactly know what you are doing. These settings will be stored in <strong>system/config.php</strong>
file and can be changed manually if needed.</p>
<br/>
<p><b><i>Web Path to Installation</i></b> - web path to the root of your In-Portal installation. For example,
if your In-Portal will be running at http://www.your-website.com, then Web Path to Installation should be left empty
since In-Portal is setup in the root of the domain. In case if your In-Portal will be running under
http://www.your-website.com/in-portal/, then it should be set to <b>/in-portal</b> (no trailing slash). This setting is auto-detected during the
initial installation step, but can be adjusted at Installation Maintenance step.</p>
<p><b><i>Path to Writable folder</i></b> - path to a folder inside your In-Portal installation which can be accessed from the Web
and has writable permissions for the web server. This folder will be used to store dynamic content such as
uploaded and resized images, cached templates and other types of user files. The default value is <b>/system</b>.</p>
<p><b><i>Path to Restricted folder</i></b> - path to a folder inside or outside your In-Portal installation which will
used to store debug files, system logs and other non-public information. This folder must be writable by the web-server
and can be located outside of your In-Portal installation if needed. The default value is <b>/system/.restricted</b> .</p>
<p><b><i>Path to Admin folder</i></b> - web path to your In-Portal Admin Console folder. The default value is set to
<b>/admin</b> and your Admin Console will be accessible at http://www.your-website.com/admin.
In case if you want your Admin Console to be under http://www.your-website.com/secure-admin (or anything else)
you'll need to rename original <b>admin</b> folder to <b>secure-admin</b> on your filesystem and then set this path to <b>/secure-admin</b> .</p>
<p><b><i>Path to Admin Interface Presets folder</i></b> - path to a folder inside your In-Portal installation
contains Admin Interface Presets. The default value is <b>/admin</b> .</p>
<p><b><i>Name of Base Application Class</i></b> - default value is <b>kApplication</b> and can change very rarely. </p>
<p><b><i>Path to Base Application Class file</i></b> - default value is <b>/core/kernel/application.php</b> and can change very rarely.</p>
<p><b><i>Output Caching Engine</i></b> - provides ability to cache HTML output or other data using various caching engines to
lower the database load. The default value is set to <b>None</b> if no available engines detected. Available options are:
None (Fake), Memcached (Memcache), XCache (XCache) and Alternative PHP Cache (Apc).
Note that only auto-detected caching engines will be available for selection.</p>
<p><b><i>Location of Memcache Servers</i></b> - host or IP address with port where Memcached Server is running.
Multiple locations of can be listed separated by semi-colon (;). For example, 192.168.1.1:1121;192.168.1.2:1121;192.168.1.3:1121 .</p>
<p><b><i>CSS/JS Compression Engine</i></b> - provides <a href="http://en.wikipedia.org/wiki/Minification_(programming)">minification</a>
functionality for CSS / Javascript files. The default value is set to <b>PHP-based</b> if no Java is auto-detected on server-side.
Available options are: None (empty), YUICompressor (Java) (yui) and PHP-based (php) .</p>
<p><b><i>Website Charset</i></b> - character encoding that will be used across the website. By default this should be set to UTF-8,
but can set to other encoding also (see <a href="http://en.wikipedia.org/wiki/Character_encoding">wikipedia.org</a> options).
It's highly recommended to have Website Encoding match the Database Encoding (specified on DB step).</p>
<p><b><i>Enable "System Log"</i></b> - "System Log" has capability to record PHP Exceptions, Errors, Warnings, Notices, Database/SQL
Errors and Warnings, and User defined messages that happened on your website. It has 3 modes - Enabled (logs everything, including user
defined messages), User-only (user defined messages only), and Disabled (don't log anything at all - default setting).</p>
+ <p><b><i>Trust Proxy</i></b> - whatever to trust information provided by provided by proxy server (if any) located between web server
+ and client browser.</p>
+
<br/>
]]>
</step>
<step name="select_theme" title="Select Default Theme">
<![CDATA[<p>Selected theme will be used as a default in your In-Portal website.</p>
<p>You can manage your themes in Admin Console under Configuration -> Website -> Themes section.</p>
<p>Additional themes are available on <a href="http://www.in-portal.com/support-downloads.html" target="_blank">Support & Downloads</a> section on <a href="http://www.in-portal.com" target="_blank">In-Portal.com</a> website</p>]]>
</step>
<step name="security" title="Security Check">
<![CDATA[<p><strong>In-Portal Installer</strong> performs final security checks on this step.</p>
<p><b>1. Write Permissions Check</b> - checks whether critical In-Portal files are open for the outside world and can be used by hackers to attack your websites. <b>You won't be able to continue until you correctly set these permissions!</b></p>
<p><b>2. Ability to Execute PHP in Writable Folders</b> - checks if hackers can save and execute PHP files in your /system
folder used for the uploads.While it's recommended to adjust these settings you can continue In-Portal Installation without changing them.</p>
<p><b>3. Webserver PHP Configuration</b> - additional suggestions how to make your website even more secure. While it's recommended to adjust these settings you can continue In-Portal Installation without changing them.</p>
]]>
</step>
<step name="finish" title="Installation Complete" help_title="Thank You!">
<![CDATA[<p>Thank you for downloading and installing In-Portal Content Management System!</p>
<p>Feel free to visit <a target="_new" href="http://www.in-portal.com">www.in-portal.com</a> for support, latest news and module updates.</p>
<p><strong>Please make sure to clean your Browser's Cache if you were performing the upgrade.</strong></p>]]>
</step>
<step name="install_setup" title="Installation Maintenance">
<![CDATA[<p>A Configuration file has been detected on your system and it appears In-Portal is correctly installed.
In order to work with the maintenance functions provided to the left you must enter your admin Root password.
<b><i>(Use Username 'root' if using your root password)</i></b></p>
<p><b><i>Upgrade In-Portal</i></b> - available when you upload files from new In-Portal release into
your current installation. Upgrade scripts will run and upgrade your current In-Portal database to the uploaded version.</p>
<p><b><i>Reinstall In-Portal</i></b> - cleans out your existing In-Portal database and starts with a fresh installation.
<i>Note</i> that this operation cannot be undone and no backups are made! Use at your own risk.</p>
<p><b><i>Install In-Portal to a New Database</i></b> - keeps the existing installation and installs In-Portal to a new database.
If this option is selected you will be prompted for new database configuration information.</p>
<p><b><i>Update License Information</i></b> - used to update your In-Portal license data. Select this option if you have
modified your licensing status with Intechnic (obtained commercial support or module), or you have received new license data via email.</p>
<p><b><i>Update Database Configuration</i></b> - allows you to update your current database configuration variables such as
database server host, username, password and others.</p>
<p><b><i>Update Installation Paths</i></b> - should be used when the location of your In-Portal files has changed.
For example, if you moved them from one folder to another. It will update all settings and ensure In-Portal
is operational at the new location.</p>]]>
</step>
<step name="upgrade_modules" title="Select Modules to Upgrade">
<![CDATA[<p>Select modules from the list, you need to update to the last downloaded version of In-Portal</p>]]>
</step>
<step name="skin_upgrade" title="Admin Skin Upgrade">
<![CDATA[<p>Review Administrative Console skin upgrade log.</p>]]>
</step>
<step name="db_reconfig" title="Update Database Configuration">
<![CDATA[<p>In-Portal needs to connect to your Database Server. Please provide the database server type*,
host name (<i>normally "localhost"</i>), Database user name, and database Password. These fields are required
to connect to the database.</p><p>If you would like In-Portal to use a table prefix, enter it in the field
provided. This prefix can be any text which can be used in the names of tables on your system.
The characters entered in this field are placed <i>before</i> the names of the tables used by In-Portal.
For example, if you enter "inp_" into the prefix field, the table named Categories will be named inp_Categories.</p>]]>
</step>
<step name="sys_requirements" title="System Requirements Check">
<![CDATA[The <i>System Requirements Check</i> option should be used to ensure proper system behavior in the current environment.]]>
</step>
</steps>
\ No newline at end of file

Event Timeline