Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Wed, Feb 5, 7:32 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_event_handler.php (revision 15072)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php (revision 15073)
@@ -1,3429 +1,3419 @@
<?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!');
define('EH_CUSTOM_PROCESSING_BEFORE',1);
define('EH_CUSTOM_PROCESSING_AFTER',2);
/**
* Note:
* 1. When addressing variables from submit containing
* Prefix_Special as part of their name use
* $event->getPrefixSpecial(true) instead of
* $event->getPrefixSpecial() as usual. This is due PHP
* is converting "." symbols in variable names during
* submit info "_". $event->getPrefixSpecial optional
* 1st parameter returns correct current Prefix_Special
* for variables being submitted such way (e.g. variable
* name that will be converted by PHP: "users.read_only_id"
* will be submitted as "users_read_only_id".
*
* 2. When using $this->Application-LinkVar on variables submitted
* from form which contain $Prefix_Special then note 1st item. Example:
* LinkVar($event->getPrefixSpecial(true).'_varname',$event->getPrefixSpecial().'_varname')
*
*/
/**
* EventHandler that is used to process
* any database related events
*
*/
class kDBEventHandler extends kEventHandler {
/**
* Checks permissions of user
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent &$event)
{
$section = $event->getSection();
if ( !$this->Application->isAdmin ) {
$allow_events = Array ('OnSearch', 'OnSearchReset', 'OnNew');
if ( in_array($event->Name, $allow_events) ) {
// allow search on front
return true;
}
}
elseif ( ($event->Name == 'OnPreSaveAndChangeLanguage') && !$this->UseTempTables($event) ) {
// allow changing language in grids, when not in editing mode
return $this->Application->CheckPermission($section . '.view', 1);
}
if ( !preg_match('/^CATEGORY:(.*)/', $section) ) {
// only if not category item events
if ( (substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave') ) {
if ( $this->isNewItemCreate($event) ) {
return $this->Application->CheckPermission($section . '.add', 1);
}
else {
return $this->Application->CheckPermission($section . '.add', 1) || $this->Application->CheckPermission($section . '.edit', 1);
}
}
}
if ( $event->Name == 'OnPreCreate' ) {
// save category_id before item create (for item category selector not to destroy permission checking category)
$this->Application->LinkVar('m_cat_id');
}
if ( $event->Name == 'OnSaveWidths' ) {
return $this->Application->isAdminUser;
}
return parent::CheckPermission($event);
}
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnLoad' => Array ('self' => 'view', 'subitem' => 'view'),
'OnItemBuild' => Array ('self' => 'view', 'subitem' => 'view'),
'OnSuggestValues' => Array ('self' => 'view', 'subitem' => 'view'),
'OnBuild' => Array ('self' => true),
'OnNew' => Array ('self' => 'add', 'subitem' => 'add|edit'),
'OnCreate' => Array ('self' => 'add', 'subitem' => 'add|edit'),
'OnUpdate' => Array ('self' => 'edit', 'subitem' => 'add|edit'),
'OnSetPrimary' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnDelete' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
'OnDeleteAll' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
'OnMassDelete' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
'OnMassClone' => Array ('self' => 'add', 'subitem' => 'add|edit'),
'OnCut' => Array ('self'=>'edit', 'subitem' => 'edit'),
'OnCopy' => Array ('self'=>'edit', 'subitem' => 'edit'),
'OnPaste' => Array ('self'=>'edit', 'subitem' => 'edit'),
'OnSelectItems' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnProcessSelected' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnStoreSelected' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnSelectUser' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
'OnMassApprove' => Array ('self' => 'advanced:approve|edit', 'subitem' => 'advanced:approve|add|edit'),
'OnMassDecline' => Array ('self' => 'advanced:decline|edit', 'subitem' => 'advanced:decline|add|edit'),
'OnMassMoveUp' => Array ('self' => 'advanced:move_up|edit', 'subitem' => 'advanced:move_up|add|edit'),
'OnMassMoveDown' => Array ('self' => 'advanced:move_down|edit', 'subitem' => 'advanced:move_down|add|edit'),
'OnPreCreate' => Array ('self' => 'add|add.pending', 'subitem' => 'edit|edit.pending'),
'OnEdit' => Array ('self' => 'edit|edit.pending', 'subitem' => 'edit|edit.pending'),
'OnExport' => Array ('self' => 'view|advanced:export'),
'OnExportBegin' => Array ('self' => 'view|advanced:export'),
'OnExportProgress' => Array ('self' => 'view|advanced:export'),
'OnSetAutoRefreshInterval' => Array ('self' => true, 'subitem' => true),
'OnAutoRefreshToggle' => Array ('self' => true, 'subitem' => true),
// theese event do not harm, but just in case check them too :)
'OnCancelEdit' => Array ('self' => true, 'subitem' => true),
'OnCancel' => Array ('self' => true, 'subitem' => true),
'OnReset' => Array ('self' => true, 'subitem' => true),
'OnSetSorting' => Array ('self' => true, 'subitem' => true),
'OnSetSortingDirect' => Array ('self' => true, 'subitem' => true),
'OnResetSorting' => Array ('self' => true, 'subitem' => true),
'OnSetFilter' => Array ('self' => true, 'subitem' => true),
'OnApplyFilters' => Array ('self' => true, 'subitem' => true),
'OnRemoveFilters' => Array ('self' => true, 'subitem' => true),
'OnSetFilterPattern' => Array ('self' => true, 'subitem' => true),
'OnSetPerPage' => Array ('self' => true, 'subitem' => true),
'OnSetPage' => Array ('self' => true, 'subitem' => true),
'OnSearch' => Array ('self' => true, 'subitem' => true),
'OnSearchReset' => Array ('self' => true, 'subitem' => true),
'OnGoBack' => Array ('self' => true, 'subitem' => true),
// it checks permission itself since flash uploader does not send cookies
'OnUploadFile' => Array ('self' => true, 'subitem' => true),
'OnDeleteFile' => Array ('self' => true, 'subitem' => true),
'OnViewFile' => Array ('self' => true, 'subitem' => true),
'OnSaveWidths' => Array ('self' => true, 'subitem' => true),
'OnValidateMInputFields' => Array ('self' => 'view'),
'OnValidateField' => Array ('self' => true, 'subitem' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Define alternative event processing method names
*
* @return void
* @see kEventHandler::$eventMethods
* @access protected
*/
protected function mapEvents()
{
$events_map = Array (
'OnRemoveFilters' => 'FilterAction',
'OnApplyFilters' => 'FilterAction',
'OnMassApprove' => 'iterateItems',
'OnMassDecline' => 'iterateItems',
'OnMassMoveUp' => 'iterateItems',
'OnMassMoveDown' => 'iterateItems',
);
$this->eventMethods = array_merge($this->eventMethods, $events_map);
}
/**
* Returns ID of current item to be edited
* by checking ID passed in get/post as prefix_id
* or by looking at first from selected ids, stored.
* Returned id is also stored in Session in case
* it was explicitly passed as get/post
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent &$event)
{
if ( $event->getEventParam('raise_warnings') === false ) {
$event->setEventParam('raise_warnings', 1);
}
if ( $event->Special == 'previous' || $event->Special == 'next' ) {
$object =& $this->Application->recallObject($event->getEventParam('item'));
/* @var $object kDBItem */
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
$select_clause = $this->Application->getUnitOption($object->Prefix, 'NavigationSelectClause', null);
return $list_helper->getNavigationResource($object, $event->getEventParam('list'), $event->Special == 'next', $select_clause);
}
elseif ( $event->Special == 'filter' ) {
// temporary object, used to print filter options only
return 0;
}
if ( preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1]) ) {
// <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object)
$main_object =& $this->Application->recallObject($regs[1]);
/* @var $main_object kDBItem */
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
return $main_object->GetDBField($id_field);
}
// 1. get id from post (used in admin)
$ret = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id');
if ( ($ret !== false) && ($ret != '') ) {
return $ret;
}
// 2. get id from env (used in front)
$ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
if ( ($ret !== false) && ($ret != '') ) {
return $ret;
}
// recall selected ids array and use the first one
$ids = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids');
if ( $ids != '' ) {
$ids = explode(',', $ids);
if ( $ids ) {
$ret = array_shift($ids);
}
}
else { // if selected ids are not yet stored
$this->StoreSelectedIDs($event);
return $this->Application->GetVar($event->getPrefixSpecial() . '_id'); // StoreSelectedIDs sets this variable
}
return $ret;
}
/**
* Prepares and stores selected_ids string
* in Session and Application Variables
* by getting all checked ids from grid plus
* id passed in get/post as prefix_id
*
* @param kEvent $event
* @param Array $direct_ids
* @return Array
* @access protected
*/
protected function StoreSelectedIDs(kEvent &$event, $direct_ids = null)
{
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_');
$ids = $event->getEventParam('ids');
if ( isset($direct_ids) || ($ids !== false) ) {
// save ids directly if they given + reset array indexes
$resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false);
if ( $resulting_ids ) {
$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids));
$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', true);
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $resulting_ids[0]);
return $resulting_ids;
}
return Array ();
}
$ret = Array ();
// May be we don't need this part: ?
$passed = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id');
if ( $passed !== false && $passed != '' ) {
array_push($ret, $passed);
}
$ids = Array ();
// get selected ids from post & save them to session
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( $items_info ) {
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
foreach ($items_info as $id => $field_values) {
if ( getArrayValue($field_values, $id_field) ) {
array_push($ids, $id);
}
}
//$ids = array_keys($items_info);
}
$ret = array_unique(array_merge($ret, $ids));
$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $ret));
$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', !$ret); // optional when IDs are missing
// This is critical - otherwise getPassedID will return last ID stored in session! (not exactly true)
// this smells... needs to be refactored
$first_id = getArrayValue($ret, 0);
if ( ($first_id === false) && ($event->getEventParam('raise_warnings') == 1) ) {
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendTrace();
}
trigger_error('Requested ID for prefix <strong>' . $event->getPrefixSpecial() . '</strong> <span class="debug_error">not passed</span>', E_USER_NOTICE);
}
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id);
return $ret;
}
/**
* Returns stored selected ids as an array
*
* @param kEvent $event
* @param bool $from_session return ids from session (written, when editing was started)
* @return Array
* @access protected
*/
protected function getSelectedIDs(kEvent &$event, $from_session = false)
{
if ( $from_session ) {
$wid = $this->Application->GetTopmostWid($event->Prefix);
$var_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_');
$ret = $this->Application->RecallVar($var_name);
}
else {
$ret = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids');
}
return explode(',', $ret);
}
/**
* Stores IDs, selected in grid in session
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnStoreSelected(kEvent &$event)
{
$this->StoreSelectedIDs($event);
$id = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
if ( $id !== false ) {
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
}
}
/**
* Returns associative array of submitted fields for current item
* Could be used while creating/editing single item -
* meaning on any edit form, except grid edit
*
* @param kEvent $event
* @return Array
* @access protected
*/
protected function getSubmittedFields(kEvent &$event)
{
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
$field_values = $items_info ? array_shift($items_info) : Array ();
return $field_values;
}
/**
* Returns fields, that are not allowed to be changed from request
*
* @param Array $hash
* @return Array
* @access protected
*/
protected function getRequestProtectedFields($hash)
{
// by default don't allow changing ID or foreign key from request
$fields = Array ();
$fields[] = $this->Application->getUnitOption($this->Prefix, 'IDField');
$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');
if ( $parent_prefix ) {
$foreign_key = $this->Application->getUnitOption($this->Prefix, 'ForeignKey');
$fields[] = is_array($foreign_key) ? $foreign_key[$parent_prefix] : $foreign_key;
}
return $fields;
}
/**
* Removes any information about current/selected ids
* from Application variables and Session
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function clearSelectedIDs(kEvent &$event)
{
$prefix_special = $event->getPrefixSpecial();
$ids = implode(',', $this->getSelectedIDs($event, true));
$event->setEventParam('ids', $ids);
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($prefix_special . '_selected_ids_' . $wid, '_');
$this->Application->RemoveVar($session_name);
$this->Application->SetVar($prefix_special . '_selected_ids', '');
$this->Application->SetVar($prefix_special . '_id', ''); // $event->getPrefixSpecial(true) . '_id' too may be
}
/**
* Common builder part for Item & List
*
* @param kDBBase|kDBItem|kDBList $object
* @param kEvent $event
* @return void
* @access protected
*/
protected function dbBuild(&$object, kEvent &$event)
{
// for permission checking inside item/list build events
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
if ( $event->getEventParam('form_name') !== false ) {
$form_name = $event->getEventParam('form_name');
}
else {
$request_forms = $this->Application->GetVar('forms', Array ());
$form_name = (string)getArrayValue($request_forms, $object->getPrefixSpecial());
}
$object->Configure($event->getEventParam('populate_ml_fields') || $this->Application->getUnitOption($event->Prefix, 'PopulateMlFields'), $form_name);
$this->PrepareObject($object, $event);
$parent_event = $event->getEventParam('parent_event');
if ( is_object($parent_event) ) {
$object->setParentEvent($parent_event);
}
// force live table if specified or is original item
$live_table = $event->getEventParam('live_table') || $event->Special == 'original';
if ( $this->UseTempTables($event) && !$live_table ) {
$object->SwitchToTemp();
}
$this->Application->setEvent($event->getPrefixSpecial(), '');
$save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate';
$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', $save_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)
{
$status_fields = $this->Application->getUnitOption($event->Prefix, 'StatusField');
if ( !$status_fields ) {
return true;
}
$status_field = array_shift($status_fields);
if ( $status_field == 'Status' || $status_field == 'Enabled' ) {
$object =& $event->getObject();
/* @var $object kDBItem */
if ( !$object->isLoaded() ) {
return true;
}
return $object->GetDBField($status_field) == STATUS_ACTIVE;
}
return true;
}
/**
* Shows not found template content
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function _errorNotFound(kEvent &$event)
{
if ( $event->getEventParam('raise_warnings') === 0 ) {
// when it's possible, that autoload fails do nothing
return;
}
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_NOTICE);
- header('HTTP/1.0 404 Not Found');
+ $vars = $this->Application->UrlManager->prepare404();
- while (ob_get_level()) {
- ob_end_clean();
+ foreach ($vars as $var_name => $var_value) {
+ $this->Application->SetVar($var_name, $var_value);
}
- // object is used inside template parsing, so break out any parsing and return error document
- $error_template = $this->Application->ConfigValue('ErrorTemplate');
-
- $themes_helper =& $this->Application->recallObject('ThemesHelper');
- /* @var $themes_helper kThemesHelper */
-
- $this->Application->SetVar('t', $error_template);
- $this->Application->SetVar('m_cat_id', $themes_helper->getPageByTemplate($error_template));
-
// in case if missing item is recalled first from event (not from template)
- $this->Application->InitParser();
- $this->Application->HTML = $this->Application->ParseBlock(Array ('name' => $error_template));
+ $this->Application->QuickRun();
$this->Application->Done();
exit;
}
/**
* Builds item (loads if needed)
*
* Pattern: Prototype Manager
*
* @param kEvent $event
* @access protected
*/
protected function OnItemBuild(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$this->dbBuild($object, $event);
$sql = $this->ItemPrepareQuery($event);
$sql = $this->Application->ReplaceLanguageTags($sql);
$object->setSelectSQL($sql);
// 2. loads if allowed
$auto_load = $this->Application->getUnitOption($event->Prefix,'AutoLoad');
$skip_autoload = $event->getEventParam('skip_autoload');
if ( $auto_load && !$skip_autoload ) {
$perm_status = true;
$user_id = $this->Application->InitDone ? $this->Application->RecallVar('user_id') : USER_ROOT;
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
$status_checked = false;
if ( $user_id == USER_ROOT || $this->CheckPermission($event) ) {
// don't autoload item, when user doesn't have view permission
$this->LoadItem($event);
$status_checked = true;
$editing_mode = defined('EDITING_MODE') ? EDITING_MODE : false;
if ( $user_id != USER_ROOT && !$this->Application->isAdmin && !($editing_mode || $this->checkItemStatus($event)) ) {
// non-root user AND on front-end AND (not editing mode || incorrect status)
$perm_status = false;
}
}
else {
$perm_status = false;
}
if ( !$perm_status ) {
// when no permission to view item -> redirect to no permission template
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>' . ($status_checked ? 'checkItemStatus' : 'CheckPermission') . '</strong>', E_USER_NOTICE);
$template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
if ( $this->Application->GetVar('t') != $template ) {
// don't perform "no_permission" redirect if already on a "no_permission" template
if ( MOD_REWRITE ) {
$redirect_params = Array (
'm_cat_id' => 0,
'next_template' => urlencode('external:' . $_SERVER['REQUEST_URI']),
);
}
else {
$redirect_params = Array (
'next_template' => $this->Application->GetVar('t'),
);
}
$this->Application->Redirect($template, $redirect_params);
}
}
}
$actions =& $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set($event->getPrefixSpecial() . '_GoTab', '');
$actions->Set($event->getPrefixSpecial() . '_GoId', '');
$actions->Set('forms[' . $event->getPrefixSpecial() . ']', $object->getFormName());
}
/**
* Build sub-tables array from configs
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnTempHandlerBuild(kEvent &$event)
{
$object =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $object kTempTablesHandler */
$parent_event = $event->getEventParam('parent_event');
if ( is_object($parent_event) ) {
$object->setParentEvent($parent_event);
}
$object->BuildTables($event->Prefix, $this->getSelectedIDs($event));
}
/**
* Checks, that object used in event should use temp tables
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function UseTempTables(kEvent &$event)
{
$top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual
$special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event);
return $this->Application->IsTempMode($event->Prefix, $special);
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$id = $this->getPassedID($event);
if ( $object->isLoaded() && !is_array($id) && ($object->GetID() == $id) ) {
// object is already loaded by same id
return ;
}
if ( $object->Load($id) ) {
$actions =& $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set($event->getPrefixSpecial() . '_id', $object->GetID());
}
else {
$object->setID( is_array($id) ? false : $id );
}
}
/**
* Builds list
*
* Pattern: Prototype Manager
*
* @param kEvent $event
* @access protected
*/
protected function OnListBuild(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
/*if ( $this->Application->isDebugMode() ) {
$event_params = http_build_query($event->getEventParams());
$this->Application->Debugger->appendHTML('InitList "<strong>' . $event->getPrefixSpecial() . '</strong>" (' . $event_params . ')');
}*/
$this->dbBuild($object, $event);
if ( !$object->isMainList() && $event->getEventParam('main_list') ) {
// once list is set to main, then even "requery" parameter can't remove that
/*$passed = $this->Application->GetVar('passed');
$this->Application->SetVar('passed', $passed . ',' . $event->Prefix);*/
$object->becameMain();
}
$object->setGridName($event->getEventParam('grid'));
$sql = $this->ListPrepareQuery($event);
$sql = $this->Application->ReplaceLanguageTags($sql);
$object->setSelectSQL($sql);
$object->reset();
if ( $event->getEventParam('skip_parent_filter') === false ) {
$object->linkToParent($this->getMainSpecial($event));
}
$this->AddFilters($event);
$this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex.
$this->SetPagination($event);
$this->SetSorting($event);
$actions =& $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set('remove_specials[' . $event->getPrefixSpecial() . ']', '0');
$actions->Set($event->getPrefixSpecial() . '_GoTab', '');
}
/**
* Returns special of main item for linking with sub-item
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function getMainSpecial(kEvent &$event)
{
$main_special = $event->getEventParam('main_special');
if ( $main_special === false ) {
// main item's special not passed
if ( substr($event->Special, -5) == '-item' ) {
// temp handler added "-item" to given special -> process that here
return substr($event->Special, 0, -5);
}
// by default subitem's special is used for main item searching
return $event->Special;
}
return $main_special;
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent &$event)
{
}
/**
* Set's new per-page for grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPerPage(kEvent &$event)
{
$per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage');
$event->SetRedirectParam($event->getPrefixSpecial() . '_PerPage', $per_page);
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
if ( !$this->Application->isAdminUser ) {
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
$this->_passListParams($event, 'per_page');
}
}
/**
* Occurs when page is changed (only for hooking)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPage(kEvent &$event)
{
$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
$event->SetRedirectParam($event->getPrefixSpecial() . '_Page', $page);
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
if ( !$this->Application->isAdminUser ) {
$this->_passListParams($event, 'page');
}
}
/**
* Passes through main list pagination and sorting
*
* @param kEvent $event
* @param string $skip_var
* @return void
* @access protected
*/
protected function _passListParams(&$event, $skip_var)
{
$param_names = array_diff(Array ('page', 'per_page', 'sort_by'), Array ($skip_var));
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
foreach ($param_names as $param_name) {
$value = $this->Application->GetVar($param_name);
switch ($param_name) {
case 'page':
if ( $value > 1 ) {
$event->SetRedirectParam('page', $value);
}
break;
case 'per_page':
if ( $value > 0 ) {
if ( $value != $list_helper->getDefaultPerPage($event->Prefix) ) {
$event->SetRedirectParam('per_page', $value);
}
}
break;
case 'sort_by':
$event->setPseudoClass('_List');
$object =& $event->getObject(Array ('main_list' => 1));
/* @var $object kDBList */
if ( $list_helper->hasUserSorting($object) ) {
$event->SetRedirectParam('sort_by', $value);
}
break;
}
}
}
/**
* Set's correct page for list based on data provided with event
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetPagination(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
// get PerPage (forced -> session -> config -> 10)
$object->SetPerPage($this->getPerPage($event));
// main lists on Front-End have special get parameter for page
$page = $object->isMainList() ? $this->Application->GetVar('page') : false;
if ( !$page ) {
// page is given in "env" variable for given prefix
$page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page');
}
if ( !$page && $event->Special ) {
// when not part of env, then variables like "prefix.special_Page" are
// replaced (by PHP) with "prefix_special_Page", so check for that too
$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
}
if ( !$object->isMainList() ) {
// main lists doesn't use session for page storing
$this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional
if ( $page ) {
// page found in request -> store in session
$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional
}
else {
// page not found in request -> get from session
$page = $this->Application->RecallVar($event->getPrefixSpecial() . '_Page');
}
if ( !$event->getEventParam('skip_counting') ) {
// when stored page is larger, then maximal list page number
// (such case is also processed in kDBList::Query method)
$pages = $object->GetTotalPages();
if ( $page > $pages ) {
$page = 1;
$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', 1, true);
}
}
}
$object->SetPage($page);
}
/**
* Returns current per-page setting for list
*
* @param kEvent $event
* @return int
* @access protected
*/
protected function getPerPage(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
$per_page = $event->getEventParam('per_page');
if ( $per_page ) {
// per-page is passed as tag parameter to PrintList, InitList, etc.
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
// 2. per-page setting is stored in configuration variable
if ( $config_mapping ) {
// such pseudo per-pages are only defined in templates directly
switch ($per_page) {
case 'short_list':
$per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']);
break;
case 'default':
$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
break;
}
}
return $per_page;
}
if ( !$per_page && $object->isMainList() ) {
// main lists on Front-End have special get parameter for per-page
$per_page = $this->Application->GetVar('per_page');
}
if ( !$per_page ) {
// per-page is given in "env" variable for given prefix
$per_page = $this->Application->GetVar($event->getPrefixSpecial() . '_PerPage');
}
if ( !$per_page && $event->Special ) {
// when not part of env, then variables like "prefix.special_PerPage" are
// replaced (by PHP) with "prefix_special_PerPage", so check for that too
$per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage');
}
if ( !$object->isMainList() ) {
// per-page given in env and not in main list
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
if ( $per_page ) {
// per-page found in request -> store in session and persistent session
$this->setListSetting($event, 'PerPage', $per_page);
}
else {
// per-page not found in request -> get from pesistent session (or session)
$per_page = $this->getListSetting($event, 'PerPage');
}
}
if ( !$per_page ) {
// per page wan't found in request/session/persistent session
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
// allow to override default per-page value from tag
$default_per_page = $event->getEventParam('default_per_page');
if ( !is_numeric($default_per_page) ) {
$default_per_page = 10;
}
$per_page = $list_helper->getDefaultPerPage($event->Prefix, $default_per_page);
}
return $per_page;
}
/**
* Set's correct sorting for list based on data provided with event
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetSorting(kEvent &$event)
{
$event->setPseudoClass('_List');
$object =& $event->getObject();
/* @var $object kDBList */
if ( $object->isMainList() ) {
$sort_by = $this->Application->GetVar('sort_by');
$cur_sort1 = $cur_sort1_dir = $cur_sort2 = $cur_sort2_dir = false;
if ( $sort_by ) {
list ($cur_sort1, $cur_sort1_dir) = explode(',', $sort_by);
}
}
else {
$sorting_settings = $this->getListSetting($event, 'Sortings');
$cur_sort1 = getArrayValue($sorting_settings, 'Sort1');
$cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir');
$cur_sort2 = getArrayValue($sorting_settings, 'Sort2');
$cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir');
}
$tag_sort_by = $event->getEventParam('sort_by');
if ( $tag_sort_by ) {
if ( $tag_sort_by == 'random' ) {
$object->AddOrderField('RAND()', '');
}
else {
// multiple sortings could be specified at once
$tag_sort_by = explode('|', $tag_sort_by);
foreach ($tag_sort_by as $sorting_element) {
list ($by, $dir) = explode(',', $sorting_element);
$object->AddOrderField($by, $dir);
}
}
}
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
$sorting_prefix = array_key_exists($event->Special, $list_sortings) ? $event->Special : '';
$sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
if ( $sorting_configs && array_key_exists('DefaultSorting1Field', $sorting_configs) ) {
// sorting defined in configuration variables overrides one from unit config
$list_sortings[$sorting_prefix]['Sorting'] = Array (
$this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']),
$this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']),
);
// TODO: lowercase configuration variable values in db, instead of here
$list_sortings[$sorting_prefix]['Sorting'] = array_map('strtolower', $list_sortings[$sorting_prefix]['Sorting']);
}
// use default if not specified in session
if ( !$cur_sort1 || !$cur_sort1_dir ) {
$sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting');
if ( $sorting ) {
reset($sorting);
$cur_sort1 = key($sorting);
$cur_sort1_dir = current($sorting);
if ( next($sorting) ) {
$cur_sort2 = key($sorting);
$cur_sort2_dir = current($sorting);
}
}
}
// always add forced sorting before any user sorting fields
$forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting');
/* @var $forced_sorting Array */
if ( $forced_sorting ) {
foreach ($forced_sorting as $field => $dir) {
$object->AddOrderField($field, $dir);
}
}
// add user sorting fields
if ( $cur_sort1 != '' && $cur_sort1_dir != '' ) {
$object->AddOrderField($cur_sort1, $cur_sort1_dir);
}
if ( $cur_sort2 != '' && $cur_sort2_dir != '' ) {
$object->AddOrderField($cur_sort2, $cur_sort2_dir);
}
}
/**
* Gets list setting by name (persistent or real session)
*
* @param kEvent $event
* @param string $variable_name
* @return string|Array
* @access protected
*/
protected function getListSetting(kEvent &$event, $variable_name)
{
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->getPrefixSpecial();
// get sorting from persistent session
$variable_value = $this->Application->RecallPersistentVar($storage_prefix . '_' . $variable_name . '.' . $view_name, ALLOW_DEFAULT_SETTINGS);
/*if ( !$variable_value ) {
// get sorting from session
$variable_value = $this->Application->RecallVar($storage_prefix . '_' . $variable_name);
}*/
if ( kUtil::IsSerialized($variable_value) ) {
$variable_value = unserialize($variable_value);
}
return $variable_value;
}
/**
* Sets list setting by name (persistent and real session)
*
* @param kEvent $event
* @param string $variable_name
* @param string|Array $variable_value
* @return void
* @access protected
*/
protected function setListSetting(kEvent &$event, $variable_name, $variable_value = null)
{
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
// $this->Application->StoreVar($event->getPrefixSpecial() . '_' . $variable_name, $variable_value, true); //true for optional
if ( isset($variable_value) ) {
if ( is_array($variable_value) ) {
$variable_value = serialize($variable_value);
}
$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name, $variable_value, true); //true for optional
}
else {
$this->Application->RemovePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name);
}
}
/**
* Add filters found in session
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function AddFilters(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
$edit_mark = rtrim($this->Application->GetSID() . '_' . $this->Application->GetTopmostWid($event->Prefix), '_');
// add search filter
$filter_data = $this->Application->RecallVar($event->getPrefixSpecial() . '_search_filter');
if ( $filter_data ) {
$filter_data = unserialize($filter_data);
foreach ($filter_data as $filter_field => $filter_params) {
$filter_type = ($filter_params['type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER;
$filter_value = str_replace(EDIT_MARK, $edit_mark, $filter_params['value']);
$object->addFilter($filter_field, $filter_value, $filter_type, kDBList::FLT_SEARCH);
}
}
// add custom filter
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
$custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_custom_filter.' . $view_name);
if ( $custom_filters ) {
$grid_name = $event->getEventParam('grid');
$custom_filters = unserialize($custom_filters);
if ( isset($custom_filters[$grid_name]) ) {
foreach ($custom_filters[$grid_name] as $field_name => $field_options) {
list ($filter_type, $field_options) = each($field_options);
if ( isset($field_options['value']) && $field_options['value'] ) {
$filter_type = ($field_options['sql_filter_type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER;
$filter_value = str_replace(EDIT_MARK, $edit_mark, $field_options['value']);
$object->addFilter($field_name, $filter_value, $filter_type, kDBList::FLT_CUSTOM);
}
}
}
}
// add view filter
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
if ( $view_filter ) {
$view_filter = unserialize($view_filter);
$temp_filter =& $this->Application->makeClass('kMultipleFilter');
/* @var $temp_filter kMultipleFilter */
$filter_menu = $this->Application->getUnitOption($event->Prefix, 'FilterMenu');
$group_key = 0;
$group_count = count($filter_menu['Groups']);
while ($group_key < $group_count) {
$group_info = $filter_menu['Groups'][$group_key];
$temp_filter->setType(constant('kDBList::FLT_TYPE_' . $group_info['mode']));
$temp_filter->clearFilters();
foreach ($group_info['filters'] as $flt_id) {
$sql_key = getArrayValue($view_filter, $flt_id) ? 'on_sql' : 'off_sql';
if ( $filter_menu['Filters'][$flt_id][$sql_key] != '' ) {
$temp_filter->addFilter('view_filter_' . $flt_id, $filter_menu['Filters'][$flt_id][$sql_key]);
}
}
$object->addFilter('view_group_' . $group_key, $temp_filter, $group_info['type'], kDBList::FLT_VIEW);
$group_key++;
}
}
// add item filter
if ( $object->isMainList() ) {
$this->applyItemFilters($event);
}
}
/**
* Applies item filters
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function applyItemFilters(&$event)
{
$filter_values = $this->Application->GetVar('filters', Array ());
if ( !$filter_values ) {
return;
}
$object =& $event->getObject();
/* @var $object kDBList */
$where_clause = Array (
'ItemPrefix = ' . $this->Conn->qstr($object->Prefix),
'FilterField IN (' . implode(',', $this->Conn->qstrArray(array_keys($filter_values))) . ')',
'Enabled = 1',
);
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('item-filter', 'TableName') . '
WHERE (' . implode(') AND (', $where_clause) . ')';
$filters = $this->Conn->Query($sql, 'FilterField');
foreach ($filters as $filter_field => $filter_data) {
$filter_value = $filter_values[$filter_field];
if ( "$filter_value" === '' ) {
// ListManager don't pass empty values, but check here just in case
continue;
}
$table_name = $object->isVirtualField($filter_field) ? '' : '%1$s.';
switch ($filter_data['FilterType']) {
case 'radio':
$filter_value = $table_name . '`' . $filter_field . '` = ' . $this->Conn->qstr($filter_value);
break;
case 'checkbox':
$filter_value = explode('|', substr($filter_value, 1, -1));
$filter_value = $this->Conn->qstrArray($filter_value, 'escape');
if ( $object->GetFieldOption($filter_field, 'multiple') ) {
$filter_value = $table_name . '`' . $filter_field . '` LIKE "%|' . implode('|%" OR ' . $table_name . '`' . $filter_field . '` LIKE "%|', $filter_value) . '|%"';
}
else {
$filter_value = $table_name . '`' . $filter_field . '` IN (' . implode(',', $filter_value) . ')';
}
break;
case 'range':
$filter_value = $this->Conn->qstrArray(explode('-', $filter_value));
$filter_value = $table_name . '`' . $filter_field . '` BETWEEN ' . $filter_value[0] . ' AND ' . $filter_value[1];
break;
}
$object->addFilter('item_filter_' . $filter_field, $filter_value, $object->isVirtualField($filter_field) ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER);
}
}
/**
* Set's new sorting for list
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetSorting(kEvent &$event)
{
$sorting_settings = $this->getListSetting($event, 'Sortings');
$cur_sort1 = getArrayValue($sorting_settings, 'Sort1');
$cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir');
$use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting');
if ( $use_double_sorting ) {
$cur_sort2 = getArrayValue($sorting_settings, 'Sort2');
$cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir');
}
$passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Sort1');
if ( $cur_sort1 == $passed_sort1 ) {
$cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc';
}
else {
if ( $use_double_sorting ) {
$cur_sort2 = $cur_sort1;
$cur_sort2_dir = $cur_sort1_dir;
}
$cur_sort1 = $passed_sort1;
$cur_sort1_dir = 'asc';
}
$sorting_settings = Array ('Sort1' => $cur_sort1, 'Sort1_Dir' => $cur_sort1_dir);
if ( $use_double_sorting ) {
$sorting_settings['Sort2'] = $cur_sort2;
$sorting_settings['Sort2_Dir'] = $cur_sort2_dir;
}
$this->setListSetting($event, 'Sortings', $sorting_settings);
}
/**
* Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetSortingDirect(kEvent &$event)
{
// used on Front-End in category item lists
$prefix_special = $event->getPrefixSpecial();
$combined = $this->Application->GetVar($event->getPrefixSpecial(true) . '_CombinedSorting');
if ( $combined ) {
list ($field, $dir) = explode('|', $combined);
if ( $this->Application->isAdmin || !$this->Application->GetVar('main_list') ) {
$this->setListSetting($event, 'Sortings', Array ('Sort1' => $field, 'Sort1_Dir' => $dir));
}
else {
$event->setPseudoClass('_List');
$this->Application->SetVar('sort_by', $field . ',' . $dir);
$object =& $event->getObject(Array ('main_list' => 1));
/* @var $object kDBList */
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
$this->_passListParams($event, 'sort_by');
if ( $list_helper->hasUserSorting($object) ) {
$event->SetRedirectParam('sort_by', $field . ',' . strtolower($dir));
}
$event->SetRedirectParam('pass', 'm');
}
return;
}
// used in "View Menu -> Sort" menu in administrative console
$field_pos = $this->Application->GetVar($event->getPrefixSpecial(true) . '_SortPos');
$this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos, $prefix_special . '_Sort' . $field_pos);
$this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos . '_Dir', $prefix_special . '_Sort' . $field_pos . '_Dir');
}
/**
* Reset grid sorting to default (from config)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnResetSorting(kEvent &$event)
{
$this->setListSetting($event, 'Sortings');
}
/**
* Sets grid refresh interval
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetAutoRefreshInterval(kEvent &$event)
{
$refresh_interval = $this->Application->GetVar('refresh_interval');
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_interval);
}
/**
* Changes auto-refresh state for grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAutoRefreshToggle(kEvent &$event)
{
$refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals');
if ( !$refresh_intervals ) {
return;
}
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
$auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name);
if ( $auto_refresh === false ) {
$refresh_intervals = explode(',', $refresh_intervals);
$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_intervals[0]);
}
$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name, $auto_refresh ? 0 : 1);
}
/**
* Creates needed sql query to load item,
* if no query is defined in config for
* special requested, then use list query
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function ItemPrepareQuery(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$sqls = $object->getFormOption('ItemSQLs', Array ());
$special = isset($sqls[$event->Special]) ? $event->Special : '';
// preferred special not found in ItemSQLs -> use analog from ListSQLs
return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event);
}
/**
* Creates needed sql query to load list,
* if no query is defined in config for
* special requested, then use default
* query
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function ListPrepareQuery(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$sqls = $object->getFormOption('ListSQLs', Array ());
return $sqls[array_key_exists($event->Special, $sqls) ? $event->Special : ''];
}
/**
* Apply custom processing to item
*
* @param kEvent $event
* @param string $type
* @return void
* @access protected
*/
protected function customProcessing(kEvent &$event, $type)
{
}
/* Edit Events mostly used in Admin */
/**
* Creates new kDBItem
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent &$event)
{
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( !$items_info ) {
return;
}
list($id, $field_values) = each($items_info);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$this->customProcessing($event, 'before');
// look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default
if ( $object->Create($event->getEventParam('ForceCreateId')) ) {
$this->customProcessing($event, 'after');
$event->SetRedirectParam('opener', 'u');
return;
}
$event->redirect = false;
$event->status = kEvent::erFAIL;
$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate');
$object->setID($id);
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent &$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$this->_update($event);
$event->SetRedirectParam('opener', 'u');
}
/**
* Updates data in database based on request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function _update(kEvent &$event)
{
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ( $items_info ) {
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$this->customProcessing($event, 'before');
if ( $object->Update($id) ) {
$this->customProcessing($event, 'after');
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
}
/**
* Delete's kDBItem object
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDelete(kEvent &$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($this->getPassedID($event)));
}
/**
* Deletes all records from table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDeleteAll(kEvent &$event)
{
$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName');
$ids = $this->Conn->GetCol($sql);
if ( $ids ) {
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
}
}
/**
* Prepares new kDBItem object
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnNew(kEvent &$event)
{
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$object->Clear(0);
$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate');
if ( $event->getEventParam('top_prefix') != $event->Prefix ) {
// this is subitem prefix, so use main item special
$table_info = $object->getLinkedInfo($this->getMainSpecial($event));
}
else {
$table_info = $object->getLinkedInfo();
}
$object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']);
$event->redirect = false;
}
/**
* Cancels kDBItem Editing/Creation
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCancel(kEvent &$event)
{
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( $items_info ) {
$delete_ids = Array ();
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
foreach ($items_info as $id => $field_values) {
$object->Load($id);
// record created for using with selector (e.g. Reviews->Select User), and not validated => Delete it
if ( $object->isLoaded() && !$object->Validate() && ($id <= 0) ) {
$delete_ids[] = $id;
}
}
if ( $delete_ids ) {
$temp_handler->DeleteItems($event->Prefix, $event->Special, $delete_ids);
}
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Deletes all selected items.
* Automatically recurse into sub-items using temp handler, and deletes sub-items
* by calling its Delete method if sub-item has AutoDelete set to true in its config file
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnMassDelete(kEvent &$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return ;
}
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$ids = $this->StoreSelectedIDs($event);
$event->setEventParam('ids', $ids);
$this->customProcessing($event, 'before');
$ids = $event->getEventParam('ids');
if ( $ids ) {
$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
/**
* Sets window id (of first opened edit window) to temp mark in uls
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function setTempWindowID(kEvent &$event)
{
$prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true));
foreach ($prefixes as $prefix) {
$mode = $this->Application->GetVar($prefix . '_mode');
if ($mode == 't') {
$wid = $this->Application->GetVar('m_wid');
$this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid);
break;
}
}
}
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnEdit(kEvent &$event)
{
$this->setTempWindowID($event);
$ids = $this->StoreSelectedIDs($event);
$this->Application->RemoveVar($this->_getPendingActionVariableName($event));
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->PrepareEdit();
$event->SetRedirectParam('m_lang', $this->Application->GetDefaultLanguageId());
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', array_shift($ids));
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
}
/**
* Saves content of temp table into live and
* redirects to event' default redirect (normally grid template)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSave(kEvent &$event)
{
$event->CallSubEvent('OnPreSave');
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$skip_master = false;
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
if ( !$this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$live_ids = $temp_handler->SaveEdit($event->getEventParam('master_ids') ? $event->getEventParam('master_ids') : Array ());
if ( $live_ids === false ) {
// coping from table failed, because we have another coping process to same table, that wasn't finished
$event->status = kEvent::erFAIL;
return;
}
if ( $live_ids ) {
// ensure, that newly created item ids are available as if they were selected from grid
// NOTE: only works if main item has sub-items !!!
$this->StoreSelectedIDs($event, $live_ids);
}
$object =& $event->getObject();
/* @var $object kDBItem */
$this->SaveLoggedChanges($changes_var_name, $object->ShouldLogChanges());
}
else {
$event->status = kEvent::erFAIL;
}
$this->clearSelectedIDs($event);
$event->SetRedirectParam('opener', 'u');
$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
// all temp tables are deleted here => all after hooks should think, that it's live mode now
$this->Application->SetVar($event->Prefix . '_mode', '');
}
/**
* Saves changes made in temporary table to log
*
* @param string $changes_var_name
* @param bool $save
* @return void
* @access public
*/
public function SaveLoggedChanges($changes_var_name, $save = true)
{
// 1. get changes, that were made
$changes = $this->Application->RecallVar($changes_var_name);
$changes = $changes ? unserialize($changes) : Array ();
$this->Application->RemoveVar($changes_var_name);
if (!$changes) {
// no changes, skip processing
return ;
}
// TODO: 2. optimize change log records (replace multiple changes to same record with one change record)
$to_increment = Array ();
// 3. collect serials to reset based on foreign keys
foreach ($changes as $index => $rec) {
if (array_key_exists('DependentFields', $rec)) {
foreach ($rec['DependentFields'] as $field_name => $field_value) {
// will be "ci|ItemResourceId:345"
$to_increment[] = $rec['Prefix'] . '|' . $field_name . ':' . $field_value;
// also reset sub-item prefix general serial
$to_increment[] = $rec['Prefix'];
}
unset($changes[$index]['DependentFields']);
}
unset($changes[$index]['ParentId'], $changes[$index]['ParentPrefix']);
}
// 4. collect serials to reset based on changed ids
foreach ($changes as $change) {
$to_increment[] = $change['MasterPrefix'] . '|' . $change['MasterId'];
if ($change['MasterPrefix'] != $change['Prefix']) {
// also reset sub-item prefix general serial
$to_increment[] = $change['Prefix'];
// will be "ci|ItemResourceId"
$to_increment[] = $change['Prefix'] . '|' . $change['ItemId'];
}
}
// 5. reset serials collected before
$to_increment = array_unique($to_increment);
$this->Application->incrementCacheSerial($this->Prefix);
foreach ($to_increment as $to_increment_mixed) {
if (strpos($to_increment_mixed, '|') !== false) {
list ($to_increment_prefix, $to_increment_id) = explode('|', $to_increment_mixed, 2);
$this->Application->incrementCacheSerial($to_increment_prefix, $to_increment_id);
}
else {
$this->Application->incrementCacheSerial($to_increment_mixed);
}
}
// save changes to database
$sesion_log_id = $this->Application->RecallVar('_SessionLogId_');
if (!$save || !$sesion_log_id) {
// saving changes to database disabled OR related session log missing
return ;
}
$add_fields = Array (
'PortalUserId' => $this->Application->RecallVar('user_id'),
'SessionLogId' => $sesion_log_id,
);
$change_log_table = $this->Application->getUnitOption('change-log', 'TableName');
foreach ($changes as $rec) {
$this->Conn->doInsert(array_merge($rec, $add_fields), $change_log_table);
}
$this->Application->incrementCacheSerial('change-log');
$sql = 'UPDATE ' . $this->Application->getUnitOption('session-log', 'TableName') . '
SET AffectedItems = AffectedItems + ' . count($changes) . '
WHERE SessionLogId = ' . $sesion_log_id;
$this->Conn->Query($sql);
$this->Application->incrementCacheSerial('session-log');
}
/**
* Cancels edit
* Removes all temp tables and clears selected ids
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCancelEdit(kEvent &$event)
{
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->CancelEdit();
$this->clearSelectedIDs($event);
$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
$event->SetRedirectParam('opener', 'u');
}
/**
* Allows to determine if we are creating new item or editing already created item
*
* @param kEvent $event
* @return bool
* @access public
*/
public function isNewItemCreate(kEvent &$event)
{
$object =& $event->getObject( Array ('raise_warnings' => 0) );
/* @var $object kDBItem */
return !$object->isLoaded();
}
/**
* Saves edited item into temp table
* If there is no id, new item is created in temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSave(kEvent &$event)
{
// if there is no id - it means we need to create an item
if ( is_object($event->MasterEvent) ) {
$event->MasterEvent->setEventParam('IsNew', false);
}
if ( $this->isNewItemCreate($event) ) {
$event->CallSubEvent('OnPreSaveCreated');
if ( is_object($event->MasterEvent) ) {
$event->MasterEvent->setEventParam('IsNew', true);
}
return ;
}
// don't just call OnUpdate event here, since it maybe overwritten to Front-End specific behavior
$this->_update($event);
}
/**
* [HOOK] Saves sub-item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveSubItem(kEvent &$event)
{
$not_created = $this->isNewItemCreate($event);
$event->CallSubEvent($not_created ? 'OnCreate' : 'OnUpdate');
if ( $event->status == kEvent::erSUCCESS ) {
$object =& $event->getObject();
/* @var $object kDBItem */
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID());
}
else {
$event->MasterEvent->status = $event->status;
}
$event->SetRedirectParam('opener', 's');
}
/**
* Saves edited item in temp table and loads
* item with passed id in current template
* Used in Prev/Next buttons
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndGo(kEvent &$event)
{
$event->CallSubEvent('OnPreSave');
if ( $event->status == kEvent::erSUCCESS ) {
$id = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId');
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
}
}
/**
* Saves edited item in temp table and goes
* to passed tabs, by redirecting to it with OnPreSave event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndGoToTab(kEvent &$event)
{
$event->CallSubEvent('OnPreSave');
if ( $event->status == kEvent::erSUCCESS ) {
$event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab');
}
}
/**
* Saves editable list and goes to passed tab,
* by redirecting to it with empty event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdateAndGoToTab(kEvent &$event)
{
$event->setPseudoClass('_List');
$event->CallSubEvent('OnUpdate');
if ( $event->status == kEvent::erSUCCESS ) {
$event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab');
}
}
/**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent &$event)
{
$this->setTempWindowID($event);
$this->clearSelectedIDs($event);
$this->Application->SetVar('m_lang', $this->Application->GetDefaultLanguageId());
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$temp_handler =& $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->PrepareEdit();
$object->setID(0);
$this->Application->SetVar($event->getPrefixSpecial() . '_id', 0);
$this->Application->SetVar($event->getPrefixSpecial() . '_PreCreate', 1);
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
$event->redirect = false;
}
/**
* Creates a new item in temp table and
* stores item id in App vars and Session on success
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveCreated(kEvent &$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$this->customProcessing($event, 'before');
if ( $object->Create() ) {
$this->customProcessing($event, 'after');
$event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID());
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
$object->setID(0);
}
}
/**
* Reloads form to loose all changes made during item editing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnReset(kEvent &$event)
{
//do nothing - should reset :)
if ( $this->isNewItemCreate($event) ) {
// just reset id to 0 in case it was create
$object =& $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$object->setID(0);
$this->Application->SetVar($event->getPrefixSpecial() . '_id', 0);
}
}
/**
* Apply same processing to each item being selected in grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function iterateItems(kEvent &$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return ;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$ids = $this->StoreSelectedIDs($event);
if ( $ids ) {
$status_field = array_shift( $this->Application->getUnitOption($event->Prefix, 'StatusField') );
$order_field = $this->Application->getUnitOption($event->Prefix, 'OrderField');
if ( !$order_field ) {
$order_field = 'Priority';
}
foreach ($ids as $id) {
$object->Load($id);
switch ( $event->Name ) {
case 'OnMassApprove':
$object->SetDBField($status_field, 1);
break;
case 'OnMassDecline':
$object->SetDBField($status_field, 0);
break;
case 'OnMassMoveUp':
$object->SetDBField($order_field, $object->GetDBField($order_field) + 1);
break;
case 'OnMassMoveDown':
$object->SetDBField($order_field, $object->GetDBField($order_field) - 1);
break;
}
if ( $object->Update() ) {
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
$this->clearSelectedIDs($event);
}
/**
* Clones selected items in list
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnMassClone(kEvent &$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => &$event));
/* @var $temp_handler kTempTablesHandler */
$ids = $this->StoreSelectedIDs($event);
if ( $ids ) {
$temp_handler->CloneItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
/**
* Checks if given value is present in given array
*
* @param Array $records
* @param string $field
* @param mixed $value
* @return bool
* @access protected
*/
protected function check_array($records, $field, $value)
{
foreach ($records as $record) {
if ($record[$field] == $value) {
return true;
}
}
return false;
}
/**
* Saves data from editing form to database without checking required fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSavePopup(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$this->RemoveRequiredFields($object);
$event->CallSubEvent('OnPreSave');
$event->SetRedirectParam('opener', 'u');
}
/* End of Edit events */
// III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item
/**
* Occurs before loading item, 'id' parameter
* allows to get id of item being loaded
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemLoad(kEvent &$event)
{
}
/**
* Occurs after loading item, 'id' parameter
* allows to get id of item that was loaded
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent &$event)
{
}
/**
* Occurs before creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent &$event)
{
}
/**
* Occurs after creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
if ( !$object->IsTempTable() ) {
$this->_proccessPendingActions($event);
}
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent &$event)
{
}
/**
* Occurs after updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
if ( !$object->IsTempTable() ) {
$this->_proccessPendingActions($event);
}
}
/**
* Occurs before deleting item, id of item being
* deleted is stored as 'id' event param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemDelete(kEvent &$event)
{
}
/**
* Occurs after deleting item, id of deleted item
* is stored as 'id' param of event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent &$event)
{
}
/**
* Occurs before validation attempt
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemValidate(kEvent &$event)
{
}
/**
* Occurs after successful item validation
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemValidate(kEvent &$event)
{
}
/**
* Occurs after an item has been copied to temp
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterCopyToTemp(kEvent &$event)
{
}
/**
* Occurs before an item is deleted from live table when copying from temp
* (temp handler deleted all items from live and then copy over all items from temp)
* Id of item being deleted is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteFromLive(kEvent &$event)
{
}
/**
* Occurs before an item is copied to live table (after all foreign keys have been updated)
* Id of item being copied is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeCopyToLive(kEvent &$event)
{
}
/**
* Occurs after an item has been copied to live table
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterCopyToLive(kEvent &$event)
{
$this->_proccessPendingActions($event);
}
/**
* Processing file pending actions (e.g. delete scheduled files)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function _proccessPendingActions(kEvent &$event)
{
$var_name = $this->_getPendingActionVariableName($event);
$schedule = $this->Application->RecallVar($var_name);
if ( $schedule ) {
$schedule = unserialize($schedule);
foreach ($schedule as $data) {
if ( $data['action'] == 'delete' ) {
unlink($data['file']);
}
}
$this->Application->RemoveVar($var_name);
}
}
/**
* Returns variable name, used to store pending file actions
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function _getPendingActionVariableName(kEvent &$event)
{
$window_id = $this->Application->GetTopmostWid($event->Prefix);
return $event->Prefix . '_file_pending_actions' . $window_id;
}
/**
* Occurs before an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(kEvent &$event)
{
}
/**
* Occurs after an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterClone(kEvent &$event)
{
}
/**
* Occurs after list is queried
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterListQuery(kEvent &$event)
{
}
/**
* Ensures that popup will be closed automatically
* and parent window will be refreshed with template
* passed
*
* @param kEvent $event
* @return void
* @access protected
* @deprecated
*/
protected function finalizePopup(kEvent &$event)
{
$event->SetRedirectParam('opener', 'u');
}
/**
* Create search filters based on search query
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSearch(kEvent &$event)
{
$event->setPseudoClass('_List');
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->performSearch($event);
}
/**
* Clear search keywords
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSearchReset(kEvent &$event)
{
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->resetSearch($event);
}
/**
* Set's new filter value (filter_id meaning from config)
*
* @param kEvent $event
* @return void
* @access protected
* @deprecated
*/
protected function OnSetFilter(kEvent &$event)
{
$filter_id = $this->Application->GetVar('filter_id');
$filter_value = $this->Application->GetVar('filter_value');
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array ();
$view_filter[$filter_id] = $filter_value;
$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
}
/**
* Sets view filter based on request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetFilterPattern(kEvent &$event)
{
$filters = $this->Application->GetVar($event->getPrefixSpecial(true) . '_filters');
if ( !$filters ) {
return;
}
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array ();
$filters = explode(',', $filters);
foreach ($filters as $a_filter) {
list($id, $value) = explode('=', $a_filter);
$view_filter[$id] = $value;
}
$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
$event->redirect = false;
}
/**
* Add/Remove all filters applied to list from "View" menu
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function FilterAction(kEvent &$event)
{
$view_filter = Array ();
$filter_menu = $this->Application->getUnitOption($event->Prefix, 'FilterMenu');
switch ($event->Name) {
case 'OnRemoveFilters':
$filter_value = 1;
break;
case 'OnApplyFilters':
$filter_value = 0;
break;
default:
$filter_value = 0;
break;
}
foreach ($filter_menu['Filters'] as $filter_key => $filter_params) {
if ( !$filter_params ) {
continue;
}
$view_filter[$filter_key] = $filter_value;
}
$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
}
/**
* Enter description here...
*
* @param kEvent $event
* @access protected
*/
protected function OnPreSaveAndOpenTranslator(kEvent &$event)
{
$this->Application->SetVar('allow_translation', true);
$object =& $event->getObject();
/* @var $object kDBItem */
$this->RemoveRequiredFields($object);
$event->CallSubEvent('OnPreSave');
if ( $event->status == kEvent::erSUCCESS ) {
$resource_id = $this->Application->GetVar('translator_resource_id');
if ( $resource_id ) {
$t_prefixes = explode(',', $this->Application->GetVar('translator_prefixes'));
$cdata =& $this->Application->recallObject($t_prefixes[1], null, Array ('skip_autoload' => true));
/* @var $cdata kDBItem */
$cdata->Load($resource_id, 'ResourceId');
if ( !$cdata->isLoaded() ) {
$cdata->SetDBField('ResourceId', $resource_id);
$cdata->Create();
}
$this->Application->SetVar($cdata->getPrefixSpecial() . '_id', $cdata->GetID());
}
$event->redirect = $this->Application->GetVar('translator_t');
$redirect_params = Array (
'pass' => 'all,trans,' . $this->Application->GetVar('translator_prefixes'),
'opener' => 's',
$event->getPrefixSpecial(true) . '_id' => $object->GetID(),
'trans_event' => 'OnLoad',
'trans_prefix' => $this->Application->GetVar('translator_prefixes'),
'trans_field' => $this->Application->GetVar('translator_field'),
'trans_multi_line' => $this->Application->GetVar('translator_multi_line'),
);
$event->setRedirectParams($redirect_params);
// 1. SAVE LAST TEMPLATE TO SESSION (really needed here, because of tweaky redirect)
$last_template = $this->Application->RecallVar('last_template');
preg_match('/index4\.php\|' . $this->Application->GetSID() . '-(.*):/U', $last_template, $rets);
$this->Application->StoreVar('return_template', $this->Application->GetVar('t'));
}
}
/**
* Makes all fields non-required
*
* @param kDBItem $object
* @return void
* @access protected
*/
protected function RemoveRequiredFields(&$object)
{
// making all field non-required to achieve successful presave
$fields = array_keys( $object->getFields() );
foreach ($fields as $field) {
if ( $object->isRequired($field) ) {
$object->setRequired($field, false);
}
}
}
/**
* Saves selected user in needed field
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSelectUser(kEvent &$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$items_info = $this->Application->GetVar('u');
if ( $items_info ) {
$user_id = array_shift(array_keys($items_info));
$this->RemoveRequiredFields($object);
$is_new = !$object->isLoaded();
$is_main = substr($this->Application->GetVar($event->Prefix . '_mode'), 0, 1) == 't';
if ( $is_new ) {
$new_event = $is_main ? 'OnPreCreate' : 'OnNew';
$event->CallSubEvent($new_event);
$event->redirect = true;
}
$object->SetDBField($this->Application->RecallVar('dst_field'), $user_id);
if ( $is_new ) {
$object->Create();
}
else {
$object->Update();
}
}
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $object->GetID());
$event->SetRedirectParam('opener', 'u');
}
/** EXPORT RELATED **/
/**
* Shows export dialog
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExport(kEvent &$event)
{
$selected_ids = $this->StoreSelectedIDs($event);
if ( implode(',', $selected_ids) == '' ) {
// K4 fix when no ids found bad selected ids array is formed
$selected_ids = false;
}
$this->Application->StoreVar($event->Prefix . '_export_ids', $selected_ids ? implode(',', $selected_ids) : '');
$this->Application->LinkVar('export_finish_t');
$this->Application->LinkVar('export_progress_t');
$this->Application->StoreVar('export_oroginal_special', $event->Special);
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/*list ($index_file, $env) = explode('|', $this->Application->RecallVar('last_template'));
$finish_url = $this->Application->BaseURL('/admin') . $index_file . '?' . ENV_VAR_NAME . '=' . $env;
$this->Application->StoreVar('export_finish_url', $finish_url);*/
$redirect_params = Array (
$this->Prefix . '.export_event' => 'OnNew',
'pass' => 'all,' . $this->Prefix . '.export'
);
$event->setRedirectParams($redirect_params);
}
/**
* Apply some special processing to object 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)
{
if ( $event->Special == 'export' || $event->Special == 'import' ) {
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->prepareExportColumns($event);
}
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
* @access public
*/
public function getCustomExportColumns(kEvent &$event)
{
return Array();
}
/**
* Export form validation & processing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExportBegin(kEvent &$event)
{
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->OnExportBegin($event);
}
/**
* Enter description here...
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExportCancel(kEvent &$event)
{
$this->OnGoBack($event);
}
/**
* Allows configuring export options
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeExportBegin(kEvent &$event)
{
}
/**
* Deletes export preset
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDeleteExportPreset(kEvent &$event)
{
$field_values = $this->getSubmittedFields($event);
if ( !$field_values ) {
return ;
}
$preset_key = $field_values['ExportPresets'];
$export_settings = $this->Application->RecallPersistentVar('export_settings');
if ( !$export_settings ) {
return ;
}
$export_settings = unserialize($export_settings);
if ( !isset($export_settings[$event->Prefix]) ) {
return ;
}
$to_delete = '';
foreach ($export_settings[$event->Prefix] as $key => $val) {
if ( implode('|', $val['ExportColumns']) == $preset_key ) {
$to_delete = $key;
break;
}
}
if ( $to_delete ) {
unset($export_settings[$event->Prefix][$to_delete]);
$this->Application->StorePersistentVar('export_settings', serialize($export_settings));
}
}
/**
* Saves changes & changes language
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndChangeLanguage(kEvent &$event)
{
if ( $this->UseTempTables($event) ) {
$event->CallSubEvent('OnPreSave');
}
if ( $event->status == kEvent::erSUCCESS ) {
$this->Application->SetVar('m_lang', $this->Application->GetVar('language'));
$data = $this->Application->GetVar('st_id');
if ( $data ) {
$event->SetRedirectParam('st_id', $data);
}
}
}
/**
* Used to save files uploaded via swfuploader
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUploadFile(kEvent &$event)
{
$event->status = kEvent::erSTOP;
// define('DBG_SKIP_REPORTING', 0);
$default_msg = "Flash requires that we output something or it won't fire the uploadSuccess event";
if ( !$this->Application->HttpQuery->Post ) {
// Variables {field, id, flashsid} are always submitted through POST!
// When file size is larger, then "upload_max_filesize" (in php.ini),
// then these variables also are not submitted -> handle such case.
header('HTTP/1.0 413 File size exceeds allowed limit');
echo $default_msg;
return;
}
if ( !$this->_checkFlashUploaderPermission($event) ) {
// 403 Forbidden
header('HTTP/1.0 403 You don\'t have permissions to upload');
echo $default_msg;
return;
}
$value = $this->Application->GetVar('Filedata');
if ( !$value || ($value['error'] != UPLOAD_ERR_OK) ) {
// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
// to large for web server to accept, see "upload_max_filesize" in php.ini)
header('HTTP/1.0 413 File size exceeds allowed limit');
echo $default_msg;
return;
}
$tmp_path = WRITEABLE . '/tmp/';
$fname = $value['name'];
$id = $this->Application->GetVar('id');
if ( $id ) {
$fname = $id . '_' . $fname;
}
$field_name = $this->Application->GetVar('field');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
$field_options = array_key_exists($field_name, $fields) ? $fields[$field_name] : $virtual_fields[$field_name];
$upload_dir = $field_options['upload_dir'];
$storage_format = array_key_exists('storage_format', $field_options) ? $field_options['storage_format'] : false;
if ( !is_writable($tmp_path) ) {
// 500 Internal Server Error
// check both temp and live upload directory
header('HTTP/1.0 500 Write permissions not set on the server');
echo $default_msg;
return;
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$fname = $file_helper->ensureUniqueFilename($tmp_path, $fname);
if ( $storage_format ) {
$image_helper =& $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
move_uploaded_file($value['tmp_name'], $value['tmp_name'] . '.jpg'); // add extension, so ResizeImage can work
$url = $image_helper->ResizeImage($value['tmp_name'] . '.jpg', $storage_format);
$tmp_name = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', '/', $url);
rename($tmp_name, $tmp_path . $fname);
}
else {
move_uploaded_file($value['tmp_name'], $tmp_path . $fname);
}
echo preg_replace('/^' . preg_quote($id, '/') . '_/', '', $fname);
$this->deleteTempFiles($tmp_path);
}
/**
* Delete temporary files, that won't be used for sure
*
* @param string $path
* @return void
* @access protected
*/
protected function deleteTempFiles($path)
{
$files = glob($path . '*.*');
$max_file_date = strtotime('-1 day');
foreach ($files as $file) {
if (filemtime($file) < $max_file_date) {
unlink($file);
}
}
}
/**
* Checks, that flash uploader is allowed to perform upload
*
* @param kEvent $event
* @return bool
*/
protected function _checkFlashUploaderPermission(kEvent &$event)
{
// Flash uploader does NOT send correct cookies, so we need to make our own check
$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
$admin_ses =& $this->Application->recallObject('Session.admin');
/* @var $admin_ses Session */
if ( $admin_ses->RecallVar('user_id') == USER_ROOT ) {
return true;
}
// copy some data from given session to current session
$backup_user_id = $this->Application->RecallVar('user_id');
$this->Application->StoreVar('user_id', $admin_ses->RecallVar('user_id'));
$backup_user_groups = $this->Application->RecallVar('UserGroups');
$this->Application->StoreVar('UserGroups', $admin_ses->RecallVar('UserGroups'));
// check permissions using event, that have "add|edit" rule
$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected');
$check_event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
$allowed_to_upload = $this->CheckPermission($check_event);
// restore changed data, so nothing gets saved to database
$this->Application->StoreVar('user_id', $backup_user_id);
$this->Application->StoreVar('UserGroups', $backup_user_groups);
return $allowed_to_upload;
}
/**
* Remembers, that file should be deleted on item's save from temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDeleteFile(kEvent &$event)
{
$event->status = kEvent::erSTOP;
if ( strpos($this->Application->GetVar('file'), '../') !== false ) {
return;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
$options = $object->GetFieldOptions($this->Application->GetVar('field'));
$var_name = $this->_getPendingActionVariableName($event);
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : Array ();
$schedule[] = Array ('action' => 'delete', 'file' => FULL_PATH . $options['upload_dir'] . $this->Application->GetVar('file'));
$this->Application->StoreVar($var_name, serialize($schedule));
}
/**
* Returns url for viewing uploaded file
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnViewFile(kEvent &$event)
{
$event->status = kEvent::erSTOP;
$file = $this->Application->GetVar('file');
if ( (strpos($file, '../') !== false) || (trim($file) !== $file) ) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
return;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$field = $this->Application->GetVar('field');
$options = $object->GetFieldOptions($field);
// set current uploaded file
if ( $this->Application->GetVar('tmp') ) {
$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
unset($options['include_path']);
$object->SetFieldOptions($field, $options);
$object->SetDBField($field, $this->Application->GetVar('id') . '_' . $file);
}
else {
$object->SetDBField($field, $file);
}
// get url to uploaded file
if ( $this->Application->GetVar('thumb') ) {
$url = $object->GetField($field, $options['thumb_format']);
}
else {
$url = $object->GetField($field, 'full_url'); // don't use "file_urls" format to prevent recursion
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$path = $file_helper->urlToPath($url);
if ( !file_exists($path) ) {
exit;
}
header('Content-Length: ' . filesize($path));
header('Content-Type: ' . kUtil::mimeContentType($path));
header('Content-Disposition: inline; filename="' . $file . '"');
readfile($path);
}
/**
* Validates MInput control fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnValidateMInputFields(kEvent &$event)
{
$minput_helper =& $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
$minput_helper->OnValidateMInputFields($event);
}
/**
* Validates individual object field and returns the result
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnValidateField(kEvent &$event)
{
$event->status = kEvent::erSTOP;
$field = $this->Application->GetVar('field');
if ( ($this->Application->GetVar('ajax') != 'yes') || !$field ) {
return;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( !$items_info ) {
return;
}
list ($id, $field_values) = each($items_info);
$object->Load($id);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
$object->setID($id);
$response = Array ('status' => 'OK');
$event->CallSubEvent($object->isLoaded() ? 'OnBeforeItemUpdate' : 'OnBeforeItemCreate');
// validate all fields, since "Password_plain" field sets error to "Password" field, which is passed here
$error_field = $object->GetFieldOption($field, 'error_field', false, $field);
if ( !$object->Validate() && $object->GetErrorPseudo($error_field) ) {
$response['status'] = $object->GetErrorMsg($error_field);
}
$ajax_form_helper =& $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$response['other_errors'] = $ajax_form_helper->getErrorMessages($object);
$event->status = kEvent::erSTOP; // since event's OnBefore... events can change this event status
echo json_encode($response);
}
/**
* Returns auto-complete values for ajax-dropdown
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSuggestValues(kEvent &$event)
{
if ( !$this->Application->isAdminUser ) {
// very careful here, because this event allows to
// view every object field -> limit only to logged-in admins
return;
}
$event->status = kEvent::erSTOP;
$field = $this->Application->GetVar('field');
$cur_value = $this->Application->GetVar('cur_value');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$object =& $event->getObject();
if ( !$field || !$cur_value || !$object->isField($field) ) {
return;
}
$limit = $this->Application->GetVar('limit');
if ( !$limit ) {
$limit = 20;
}
$sql = 'SELECT DISTINCT ' . $field . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
WHERE ' . $field . ' LIKE ' . $this->Conn->qstr($cur_value . '%') . '
ORDER BY ' . $field . '
LIMIT 0,' . $limit;
$data = $this->Conn->GetCol($sql);
$this->Application->XMLHeader();
echo '<suggestions>';
foreach ($data as $item) {
echo '<item>' . htmlspecialchars($item) . '</item>';
}
echo '</suggestions>';
}
/**
* Enter description here...
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSaveWidths(kEvent &$event)
{
$event->status = kEvent::erSTOP;
/*$lang =& $this->Application->recallObject('lang.current');
header('Content-type: text/xml; charset=' . $lang->GetDBField('Charset'));*/
$picker_helper =& $this->Application->RecallObject('ColumnPickerHelper');
/* @var $picker_helper kColumnPickerHelper */
$picker_helper->PreparePicker($event->getPrefixSpecial(), $this->Application->GetVar('grid_name'));
$picker_helper->SaveWidths($event->getPrefixSpecial(), $this->Application->GetVar('widths'));
echo 'OK';
}
/**
* Called from CSV import script after item fields
* are set and validated, but before actual item create/update.
* If event status is kEvent::erSUCCESS, line will be imported,
* else it will not be imported but added to skipped lines
* and displayed in the end of import.
* Event status is preset from import script.
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeCSVLineImport(kEvent &$event)
{
// abstract, for hooking
}
/**
* [HOOK] Allows to add cloned subitem to given prefix
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCloneSubItem(kEvent &$event)
{
$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
$subitem_prefix = $event->Prefix . '-' . preg_replace('/^#/', '', $event->MasterEvent->Prefix);
$clones[$subitem_prefix] = Array ('ParentPrefix' => $event->Prefix);
$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
}
/**
* Returns constrain for priority calculations
*
* @param kEvent $event
* @return void
* @see PriorityEventHandler
* @access protected
*/
protected function OnGetConstrainInfo(kEvent &$event)
{
$event->setEventParam('constrain_info', Array ('', ''));
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php (revision 15072)
+++ branches/5.2.x/core/kernel/application.php (revision 15073)
@@ -1,3007 +1,3143 @@
<?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 incapsulates the main run-cycle of the script, provide access to all other objects in the 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
*/
- var $InitDone = false;
+ public $InitDone = false;
/**
- * Holds internal NParser object
- * @access private
- * @var NParser
- */
- var $Parser;
-
- /**
- * Holds parser output buffer
- * @access private
- * @var string
- */
- var $HTML;
-
- /**
- * The main Factory used to create
- * almost any class of kernel and
- * modules
- *
- * @access private
- * @var kFactory
- */
- var $Factory;
+ * 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
*/
- var $ReplacementTemplates = Array ();
+ public $ReplacementTemplates = Array ();
/**
* Mod-Rewrite listeners used during url building and parsing
*
* @var Array
+ * @access public
*/
- var $RewriteListeners = Array ();
+ public $RewriteListeners = Array ();
/**
* Reference to debugger
*
* @var Debugger
+ * @access public
*/
- var $Debugger = null;
+ public $Debugger = null;
/**
* Holds all phrases used
* in code and template
*
* @var PhrasesCache
+ * @access public
*/
- var $Phrases;
+ public $Phrases;
/**
* Modules table content, key - module name
*
* @var Array
+ * @access public
*/
- var $ModuleInfo = Array();
+ public $ModuleInfo = Array ();
/**
* Holds DBConnection
*
* @var kDBConnection
+ * @access public
*/
- var $Conn = null;
+ public $Conn = null;
/**
* Maintains list of user-defined error handlers
*
* @var Array
+ * @access public
*/
- var $errorHandlers = Array();
+ public $errorHandlers = Array ();
/**
* Maintains list of user-defined exception handlers
*
* @var Array
+ * @access public
*/
- var $exceptionHandlers = Array();
+ public $exceptionHandlers = Array ();
// performance needs:
/**
* Holds a reference to httpquery
*
* @var kHttpQuery
+ * @access public
*/
- var $HttpQuery = null;
+ public $HttpQuery = null;
/**
* Holds a reference to UnitConfigReader
*
* @var kUnitConfigReader
+ * @access public
*/
- var $UnitConfigReader = null;
+ public $UnitConfigReader = null;
/**
* Holds a reference to Session
*
* @var Session
+ * @access public
*/
- var $Session = null;
+ public $Session = null;
/**
* Holds a ref to kEventManager
*
* @var kEventManager
+ * @access public
*/
- var $EventManager = null;
+ public $EventManager = null;
/**
* Holds a ref to kUrlManager
*
* @var kUrlManager
* @access public
*/
public $UrlManager = null;
/**
* Ref for TemplatesCache
*
* @var TemplatesCache
+ * @access public
*/
- var $TemplatesCache = null;
-
- var $CompilationCache = array(); //used when compiling templates
- var $CachedProcessors = array(); //used when running compiled templates
-
- var $LambdaElements = 1; // for autonumbering unnamed RenderElements [any better place for this prop? KT]
+ 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
*/
- var $CurrentNTag = null;
+ public $CurrentNTag = null;
/**
* Object of unit caching class
*
* @var kCacheManager
+ * @access public
*/
- var $cacheManager = null;
+ 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
*/
- var $isAdminUser = false;
+ 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
*/
- var $isAdmin = false;
+ public $isAdmin = false;
- /**
+ /**
* Instance of site domain object
*
* @var kDBItem
+ * @access public
+ * @todo move away into separate module
*/
- var $siteDomain = null;
+ public $siteDomain = null;
/**
* Prevent kApplication class to be created directly, only via Instance method
*
+ * @access protected
*/
protected function __construct()
{
}
/**
- * 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
- * @access public
- * @return kApplication
- */
+ * 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) {
+ if ( !$instance ) {
$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
$instance = new $class();
$instance->Application =& $instance;
}
return $instance;
}
/**
- * Initializes the Application
- *
- * @access public
- * @see kHTTPQuery
- * @see Session
- * @see TemplatesCache
- * @return bool Was Init actually made now or before
- */
+ * Initializes the Application
+ *
+ * @return bool Was Init actually made now or before
+ * @access public
+ * @see kHTTPQuery
+ * @see Session
+ * @see TemplatesCache
+ */
public function Init()
{
if ( $this->InitDone ) {
return false;
}
$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:');
}
if ( !$this->isDebugMode() && !kUtil::constOn('DBG_ZEND_PRESENT') ) {
error_reporting(0);
ini_set('display_errors', 0);
}
if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) {
$error_handler = set_error_handler(Array (&$this, 'handleError'));
if ( $error_handler ) {
// wrap around previous error handler, if any was set
$this->errorHandlers[] = $error_handler;
}
$exception_handler = set_exception_handler(Array (&$this, 'handleException'));
if ( $exception_handler ) {
// wrap around previous exception handler, if any was set
$this->exceptionHandlers[] = $exception_handler;
}
}
$this->Factory = new kFactory();
$this->registerDefaultClasses();
$vars = kUtil::parseConfig(true);
$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : 'kDBConnection';
$this->Conn =& $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array (&$this, '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->UrlManager->LoadStructureTemplateMapping();
$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);
}
$language =& $this->recallObject('lang.current', null, Array ('live_table' => true));
/* @var $language LanguagesItem */
if ( preg_match('/utf-8/', $language->GetDBField('Charset')) ) {
setlocale(LC_ALL, 'en_US.UTF-8');
mb_internal_encoding('UTF-8');
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->profileFinish('kernel4_startup');
}
$this->InitDone = true;
$this->HandleEvent(new kEvent('adm:OnStartup'));
return true;
}
- function InitManagers()
+ /**
+ * Performs initialization of manager classes, that can be overridden from unit configs
+ *
+ * @return void
+ * @access public
+ * @throws Exception
+ */
+ public function InitManagers()
{
- if ($this->InitDone) {
+ if ( $this->InitDone ) {
throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
- return ;
+ 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
*/
- function findModule($field, $value, $return_field = null)
+ 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)) {
+ if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) {
$this->registerModuleConstants();
- return ;
+ 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
*/
- function VerifyLanguageId()
+ public function VerifyLanguageId()
{
$language_id = $this->GetVar('m_lang');
- if (!$language_id) {
+ 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')) {
+ if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) {
+ if ( !defined('IS_INSTALL') ) {
$this->ApplicationDie('Unknown or disabled language');
}
}
- $this->SetVar('lang_mode',$lang_mode);
+ $this->SetVar('lang_mode', $lang_mode);
}
/**
* Checks if passed theme id if valid and sets it to primary otherwise
*
+ * @return void
+ * @access public
*/
- function VerifyThemeId()
+ public function VerifyThemeId()
{
- if ($this->isAdmin) {
+ if ( $this->isAdmin ) {
kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
return;
}
$path = $this->GetFrontThemePath();
- if ($path === false) {
+ 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', 'kiCacheable');
$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php', null, 'kiCacheable');
$this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php', null, 'kiCacheable');
$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_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', null, 'kUrlProcessor');
$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php', null, 'kUrlProcessor');
$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php', null, 'kiCacheable');
$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('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', 'Params');
// 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', 'kTagProcessor');
$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php', null, 'kTagProcessor');
$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php', null, 'kDBTagProcessor');
$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php', null, Array ('kHelper', 'kDBTagProcessor'));
// database
$this->registerClass('kDBConnection', 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', null, 'kDBItem');
$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php', null, 'kDBList');
$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php', null, 'kDBEventHandler');
// email sending
$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender', 'kHelper');
$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);
}
/**
* Adds new value to cache $cache_name and identified by key $key
*
* @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);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return void
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
$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 void
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
{
$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 && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Done:');
}
if ( $debug_mode ) {
$this->EventManager->runScheduledTasks();
$this->Session->SaveData();
if ( kUtil::constOn('DBG_CACHE') ) {
$this->cacheManager->printStatistics();
}
$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;
}
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 {
echo $this->HTML;
}
$this->cacheManager->UpdateApplicationCache();
flush();
if ( !$debug_mode ) {
$this->EventManager->runScheduledTasks();
$this->Session->SaveData();
}
if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) {
$this->_storeStatistics();
}
}
/**
* 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 = 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;
}
return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($_SERVER['HTTP_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
- * @access public
- * @return kDBConnection
- */
- function &GetADODBConnection()
+ * 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
*/
- function ParseBlock($params, $pass_params = false, $as_template = false)
+ public function ParseBlock($params, $pass_params = false, $as_template = false)
{
- if (substr($params['name'], 0, 5) == 'html:') {
+ 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
*/
- function ParserBlockFound($name)
+ 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
*/
- function IncludeTemplate($params)
+ 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 variables with values that should be passed through with this link + variable list
*
* @param Array $params
* @return Array
+ * @access public
*/
- function getPassThroughVariables(&$params)
+ 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);
}
- function BaseURL($prefix = '', $ssl = null, $add_port = true)
+ /**
+ * 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) {
+ if ( $ssl === null ) {
// stay on same encryption level
return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
- if ($ssl) {
+ if ( $ssl ) {
// going from http:// to https://
$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
- if (!$base_url) {
+ 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) {
+ if ( $domain === false ) {
$domain = DOMAIN;
}
return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
- function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
+ /**
+ * 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) {
+ if ( $t == '' || $t === true ) {
$t = $this->GetVar('t');
}
// pass prefixes and special from previous url
- if (array_key_exists('js_redirect', $params)) {
+ 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)) {
+ 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)) {
+ if ( !array_key_exists('pass', $params) ) {
$params['pass'] = 'all';
}
- if ($this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t')) {
+ 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))) {
+ 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) {
+ 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 true;
+ return;
}
else {
- if ($this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t')) {
+ 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 occured -> redirect using javascript
+ 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 */
- $this->HandleEvent( new kEvent('adm:OnBeforeShutdown') );
+ $this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
$session->SaveData();
ob_end_flush();
exit;
}
- function Phrase($label, $allow_editing = true, $use_admin = false)
+ /**
+ * 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
*/
- function ReplaceLanguageTags($text, $force_escape = null)
+ 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.
*
- * @access private
+ * @return void
+ * @access protected
*/
- function ValidateLogin()
+ 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
*/
- function LoadPersistentVars()
+ public function LoadPersistentVars()
{
$this->Session->LoadPersistentVars();
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
+ * @access public
*/
- function ConfigValue($name)
+ 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
* @access public
- * @author Alex
*/
- function HandleEvent(&$event, $params = null, $specific_params = null)
+ public function HandleEvent(&$event, $params = null, $specific_params = null)
{
if ( isset($params) ) {
$event = new kEvent($params, $specific_params);
}
$this->EventManager->HandleEvent($event);
}
/**
* Registers new class in the factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $file Filename in what $real_class is declared
* @param string $pseudo_class Name under this class object will be accessed using getObject method
- * @param Array $dependecies List of classes required for this class functioning
+ * @param Array $dependencies List of classes required for this class functioning
+ * @return void
* @access public
- * @author Alex
*/
- function registerClass($real_class, $file, $pseudo_class = null, $dependecies = Array() )
+ public function registerClass($real_class, $file, $pseudo_class = null, $dependencies = Array() )
{
- $this->Factory->registerClass($real_class, $file, $pseudo_class, $dependecies);
+ $this->Factory->registerClass($real_class, $file, $pseudo_class, $dependencies);
}
/**
* 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
*/
- function unregisterClass($real_class, $pseudo_class = null)
+ public function unregisterClass($real_class, $pseudo_class = null)
{
$this->Factory->unregisterClass($real_class, $pseudo_class);
}
/**
* Add $class_name to required classes list for $depended_class class.
* All required class files are included before $depended_class file is included
*
* @param string $depended_class
* @param string $class_name
- * @author Alex
+ * @return void
+ * @access public
*/
- function registerDependency($depended_class, $class_name)
+ public function registerDependency($depended_class, $class_name)
{
$this->Factory->registerDependency($depended_class, $class_name);
}
/**
* 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_interval run interval in seconds
* @param int $status
* @access public
*/
public function registerScheduledTask($short_name, $event_string, $run_interval, $status = STATUS_ACTIVE)
{
$this->EventManager->registerScheduledTask($short_name, $event_string, $run_interval, $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
- * @author Kostja
+ * @return void
+ * @access public
*/
- function registerAggregateTag($tag_info)
+ public function registerAggregateTag($tag_info)
{
$aggregator =& $this->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
- $tag_data = Array(
+ $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();
}*/
$result =& $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
return $result;
}
/**
* Returns tag processor for prefix specified
*
* @param string $prefix
* @return kDBTagProcessor
+ * @access public
*/
- function &recallTagProcessor($prefix)
+ public function &recallTagProcessor($prefix)
{
$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
$result =& $this->recallObject($prefix . '_TagProcessor');
return $result;
}
/**
* Checks if object with prefix passes was already created in factory
*
- * @param string $name object presudo_class, prefix
+ * @param string $name object pseudo_class, prefix
* @return bool
- * @author Kostja
+ * @access public
*/
- function hasObject($name)
+ public function hasObject($name)
{
return isset($this->Factory->Storage[$name]);
}
/**
* Removes object from storage by given name
*
* @param string $name Object's name in the Storage
- * @author Kostja
+ * @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 ())
{
$result =& $this->Factory->makeClass($pseudo_class, $arguments);
return $result;
}
/**
* Checks if application is in debug mode
*
* @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
* @return bool
* @author Alex
* @access public
*/
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
*/
- function RewriteURLs($ssl = false)
+ 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
*/
- function setEvent($prefix_special, $event_name)
+ public function setEvent($prefix_special, $event_name)
{
- $this->EventManager->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 private
- * @author Alex
+ * @access public
*/
- function handleSQLError($code, $msg, $sql)
+ public function handleSQLError($code, $msg, $sql)
{
if ( isset($this->Debugger) ) {
$long_error_msg = '<span class="debug_error">' . $msg . ' (' . $code . ')</span><br/><a href="javascript:$Debugger.SetClipboard(\'' . htmlspecialchars($sql) . '\');"><strong>SQL</strong></a>: ' . $this->Debugger->formatSQL($sql);
$long_id = $this->Debugger->mapLongError($long_error_msg);
$error_msg = mb_substr($msg . ' (' . $code . ') [' . $sql . ']', 0, 1000) . ' #' . $long_id;
if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) {
throw new Exception($error_msg);
}
else {
$this->Debugger->appendTrace();
}
}
else {
// when not debug mode, then fatal database query won't break anything
$error_msg = '<strong>SQL Error</strong> in sql: ' . $sql . ', code <strong>' . $code . '</strong> (' . $msg . ')';
}
trigger_error($error_msg, E_USER_WARNING);
return true;
}
/**
* Default error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param Array $errcontext
* @return bool
* @access public
*/
public function handleError($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
{
$this->errorLogSilent($errno, $errstr, $errfile, $errline);
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
if ( !$this->errorHandlers || ($debug_mode && $skip_reporting) ) {
// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
if ( $errno == E_USER_ERROR ) {
$this->errorDisplayFatal('<strong>Fatal Error: </strong>' . "{$errstr} in {$errfile} on line {$errline}");
}
if ( !$this->errorHandlers ) {
return true;
}
}
$res = false;
/* @var $handler Closure */
foreach ($this->errorHandlers 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;
}
/**
* Handles exception
*
* @param Exception $exception
* @return bool
* @access public
*/
public function handleException($exception)
{
// transform exception to regular error (no need to rewrite existing error handlers)
$errno = $exception->getCode();
$errstr = $exception->getMessage();
$errfile = $exception->getFile();
$errline = $exception->getLine();
$this->errorLogSilent($errno, $errstr, $errfile, $errline);
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
if ( !$this->exceptionHandlers || ($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)
$this->errorDisplayFatal('<strong>' . get_class($exception) . ': </strong>' . "{$errstr} in {$errfile} on line {$errline}");
if ( !$this->exceptionHandlers ) {
return true;
}
}
$res = false;
/* @var $handler Closure */
foreach ($this->exceptionHandlers as $handler) {
if ( is_array($handler) ) {
$object =& $handler[0];
$method = $handler[1];
$res = $object->$method($exception);
}
else {
$res = $handler($exception);
}
}
return $res;
}
/**
* Silently saves each given error message to "silent_log.txt" file, when silent log mode is enabled
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return void
* @access protected
*/
protected function errorLogSilent($errno, $errstr = '', $errfile = '', $errline = null)
{
if ( !defined('SILENT_LOG') || !SILENT_LOG ) {
return;
}
if ( !(defined('DBG_IGNORE_STRICT_ERRORS') && DBG_IGNORE_STRICT_ERRORS && defined('E_STRICT') && ($errno == E_STRICT)) ) {
$time = adodb_date('d/m/Y H:i:s');
$fp = fopen((defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/silent_log.txt', 'a');
fwrite($fp, '[' . $time . '] #' . $errno . ': ' . strip_tags($errstr) . ' in [' . $errfile . '] on line ' . $errline . "\n");
fclose($fp);
}
}
/**
* Displays div with given error message
*
* @param string $msg
* @return void
* @access protected
*/
protected function errorDisplayFatal($msg)
{
$margin = $this->isAdmin ? '8px' : 'auto';
echo '<div style="background-color: #FEFFBF; margin: ' . $margin . '; padding: 10px; border: 2px solid red; text-align: center">' . $msg . '</div>';
exit;
}
/**
* Prints trace, when debug mode is not available
*
* @param bool $return_result
* @param int $skip_levels
* @return string
* @access public
*/
public function printTrace($return_result = false, $skip_levels = 1)
{
$ret = Array ();
$trace = debug_backtrace(false);
for ($i = 0; $i < $skip_levels; $i++) {
array_shift($trace);
}
foreach ($trace as $level => $trace_info) {
if ( isset($trace_info['class']) ) {
$object = $trace_info['class'];
}
elseif ( isset($trace_info['object']) ) {
$object = get_class($trace_info['object']);
}
else {
$object = '';
}
$args = '';
$type = isset($trace_info['type']) ? $trace_info['type'] : '';
if ( isset($trace_info['args']) ) {
foreach ($trace_info['args'] as $argument) {
if ( is_object($argument) ) {
$args .= get_class($argument) . ' instance, ';
}
else {
$args .= is_array($argument) ? 'Array' : substr($argument, 0, 10) . ' ..., ';
}
}
$args = substr($args, 0, -2);
}
$ret[] = '#' . $level . ' ' . $object . $type . $trace_info['function'] . '(' . $args . ') called at [' . $trace_info['file'] . ':' . $trace_info['line'] . ']';
}
if ( $return_result ) {
return implode("\n", $ret);
}
echo implode("\n", $ret);
return '';
}
/**
* Returns & blocks next ResourceId available in system
*
* @return int
* @access public
- * @author Alex
*/
- function NextResourceId()
+ public function NextResourceId()
{
- $table_name = TABLE_PREFIX.'IdGenerator';
+ $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);
- $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)');
+ 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
*/
- function GetTopmostPrefix($current_prefix, $real_top = false)
+ 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)) {
+ 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) {
+ 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)) {
+ 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
*/
- function &EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
+ public function &EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
{
$event =& $this->EmailEvent($email_event_name, EmailEvent::EVENT_TYPE_ADMIN, $to_user_id, $send_params);
return $event;
}
/**
* Triggers email event of type User
*
* @param string $email_event_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
+ * @access public
*/
- function &EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
+ public function &EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
{
$event =& $this->EmailEvent($email_event_name, EmailEvent::EVENT_TYPE_FRONTEND, $to_user_id, $send_params);
return $event;
}
/**
* Triggers general email event
*
* @param string $email_event_name
* @param int $email_event_type (0 for User, 1 for Admin)
* @param int $to_user_id
* @param array $send_params associative array of direct send params,
* possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
+ * @access protected
*/
- function &EmailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
+ protected function &EmailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
{
$params = Array (
'EmailEventName' => $email_event_name,
'EmailEventToUserId' => $to_user_id,
'EmailEventType' => $email_event_type,
'DirectSendParams' => $send_params,
);
- if (array_key_exists('use_special', $send_params)) {
+ if ( array_key_exists('use_special', $send_params) ) {
$event_str = 'emailevents.' . $send_params['use_special'] . ':OnEmailEvent';
}
else {
$event_str = 'emailevents:OnEmailEvent';
}
$this->HandleEvent($event, $event_str, $params);
return $event;
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
+ * @access public
*/
- function LoggedIn()
+ 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
*/
- function CheckPermission($name, $type = 1, $cat_id = null)
+ 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
*/
- function CheckAdminPermission($name, $type = 1, $cat_id = null)
+ 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
*/
- function setVisitField($field, $value)
+ public function setVisitField($field, $value)
{
- if ($this->isAdmin || !$this->ConfigValue('UseVisitorTracking')) {
- // admin logins are not registred in visits list
- return ;
+ 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()) {
+ if ( $visit->isLoaded() ) {
$visit->SetDBField($field, $value);
$visit->Update();
}
}
/**
* Allows to check if in-portal is installed
*
* @return bool
+ * @access public
*/
- function isInstalled()
+ 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
*/
- function isModuleEnabled($module_name)
+ 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 mixed
+ * @return int
+ * @access public
*/
- function GetTopmostWid($prefix)
+ public function GetTopmostWid($prefix)
{
$top_prefix = $this->GetTopmostPrefix($prefix);
- $mode = $this->GetVar($top_prefix.'_mode');
+ $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
*/
- function GetTempName($table, $wid = '')
+ public function GetTempName($table, $wid = '')
{
return $this->GetTempTablePrefix($wid) . $table;
}
- function GetTempTablePrefix($wid = '')
+ /**
+ * 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)) {
+ if ( preg_match('/prefix:(.*)/', $wid, $regs) ) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
}
- function IsTempTable($table)
+ /**
+ * 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);
+ $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
*/
- function IsTempMode($prefix, $special = '')
+ 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')) {
+
+ if ( $value && (substr($value, 0, 1) == 't') ) {
$temp_mode = true;
break;
}
}
return $temp_mode;
}
/**
* Return live table name based on temp table name
*
* @param string $temp_table
* @return string
*/
- function GetLiveName($temp_table)
+ public function GetLiveName($temp_table)
{
- if( preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$temp_table, $rets) )
- {
+ 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
- {
+ else {
return $temp_table;
}
}
- function CheckProcessors($processors)
+ /**
+ * Stops processing of user request and displays given message
+ *
+ * @param string $message
+ * @access public
+ */
+ public function ApplicationDie($message = '')
{
- foreach ($processors as $a_processor)
- {
- if (!isset($this->CachedProcessors[$a_processor])) {
- $this->CachedProcessors[$a_processor] =& $this->recallObject($a_processor.'_TagProcessor');
- }
- }
- }
+ $message = ob_get_clean() . $message;
- function ApplicationDie($message = '')
- {
- $message = ob_get_clean().$message;
- if ($this->isDebugMode()) {
+ if ( $this->isDebugMode() ) {
$message .= $this->Debugger->printReport(true);
}
echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message;
exit;
}
-
- /* moved from MyApplication */
-
- function getUserGroups($user_id)
+ /**
+ * 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 ($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
*/
- function IsSpider()
+ /*public function IsSpider()
{
static $is_spider = null;
- if (!isset($is_spider)) {
+ if ( !isset($is_spider) ) {
$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
- $robots = file(FULL_PATH.'/core/robots_list.txt');
+ $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])) {
+ if ( $user_agent == trim($robot_info[2]) ) {
$is_spider = true;
break;
}
}
}
return $is_spider;
- }
+ }*/
/**
- * Allows to detect table's presense in database
+ * Allows to detect table's presence in database
*
* @param string $table_name
* @param bool $force
* @return bool
+ * @access public
*/
- function TableFound($table_name, $force = false)
+ 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 parmeters)
+ * @param string $query_name specify query name directly (don't generate from parameters)
* @param bool $multiple_results
* @return mixed
+ * @access public
*/
- function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
+ 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)
{
$lang =& $this->recallObject('lang.current');
/* @var $lang LanguagesItem */
header('Content-type: text/xml; charset=' . $lang->GetDBField('Charset'));
return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . $lang->GetDBField('Charset') . '"?>' : '';
}
/**
* Returns category tree
*
* @param int $category_id
* @return Array
+ * @access public
*/
- function getTreeIndex($category_id)
+ public function getTreeIndex($category_id)
{
$tree_index = $this->getCategoryCache($category_id, 'category_tree');
- if ($tree_index) {
+ 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
*/
- function getBaseCategory()
+ 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 (
- 'Application.ReplacementTemplates' => $this->ReplacementTemplates,
- 'Application.RewriteListeners' => $this->RewriteListeners,
- 'Application.ModuleInfo' => $this->ModuleInfo,
- );
+ 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;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/url_manager.php
===================================================================
--- branches/5.2.x/core/kernel/managers/url_manager.php (revision 15072)
+++ branches/5.2.x/core/kernel/managers/url_manager.php (revision 15073)
@@ -1,423 +1,448 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2010 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 kUrlManager extends kBase {
/**
* Processor of plain urls (initialized always)
*
* @var kPlainUrlProcessor
* @access public
*/
public $plain = null;
/**
* Processor of rewritten urls (initialized on demand only)
*
* @var kRewriteUrlProcessor
* @access public
*/
public $rewrite = null;
/**
* Tells, that rewrite system is ready
*
* @var bool
* @access protected
*/
protected $rewriteReady = false;
/**
* Physical template name mapping to their template names based on structure
*
* @var Array
*/
protected $structureTemplateMapping = Array ();
/**
* Creates instead of kUrlManager class
*/
public function __construct()
{
parent::__construct();
// don't use kApplication::recallObject, since it will call kApplication::EventManager, which isn't ready yet
$this->plain =& $this->Application->makeClass('kPlainUrlProcessor', Array (&$this));
}
/**
* Delay initialization of rewrite processor, since it uses site domains & http query
*
* @return void
* @access public
*/
public function initRewrite()
{
if ( $this->rewriteReady ) {
return;
}
$this->rewrite =& $this->Application->recallObject('kRewriteUrlProcessor', null, Array (), Array (&$this));
$this->rewriteReady = true;
}
/**
* 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)
{
static $theme_id = null;
if ( !isset($theme_id) ) {
$theme_id = $this->Application->GetVar('m_theme');
}
if ( !$t ) {
// when template not specified, use current
$t = $this->Application->GetVar('t');
}
$t = preg_replace('/^Content\//i', '', $t);
if ( substr($t, -4) == '.tpl' ) {
// cut template extension (deprecated link format)
$t = substr($t, 0, strlen($t) - 4);
}
if ( substr($t, 0, 3) == 'id:' ) {
// link to structure page using it's id
$params['m_cat_id'] = substr($t, 3);
$t = $this->structureTemplateMapping[$t];
}
if ( array_key_exists('use_section', $params) ) {
$use_section = $params['use_section'];
unset($params['use_section']);
}
if ( isset($use_section) && $use_section && array_key_exists($t . ':' . $theme_id, $this->structureTemplateMapping) ) {
// structure template corresponding to given physical template
$t = $this->structureTemplateMapping[$t . ':' . $theme_id];
unset($params['use_section']);
}
if ( preg_match('/external:(.*)/', $t, $regs) ) {
// external url
return $regs[1];
}
if ( $this->Application->isAdmin && $prefix == '' ) {
$prefix = ADMIN_DIRECTORY;
}
if ( $this->Application->isAdmin && $prefix == '_FRONT_END_' ) {
$prefix = '';
}
if ( isset($params['_auto_prefix_']) ) {
unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here
}
$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
if ( $ssl !== null ) {
$session =& $this->Application->recallObject('Session');
/* @var $session Session */
$target_url = rtrim($this->Application->BaseURL('', $ssl, false), '/');
$cookie_url = trim($session->CookieDomain . $session->CookiePath, '/.');
// set session to GET_ONLY, to pass sid only if sid is REAL AND session is set
if ( !preg_match('#' . preg_quote($cookie_url) . '#', $target_url) && $session->SessionSet ) {
// when SSL<->NON-SSL redirect to different domain pass SID in url
$session->SetMode(Session::smGET_ONLY);
}
}
if ( isset($params['opener']) && $params['opener'] == 'u' ) {
$ret = $this->processPopupClose($prefix, $params);
if ( $ret !== false ) {
return $ret;
}
else {
//define('DBG_REDIRECT', 1);
$t = $this->Application->GetVar('t');
}
}
$pass = isset($params['pass']) ? $params['pass'] : '';
// pass events with url
$pass_events = false;
if ( isset($params['pass_events']) ) {
$pass_events = $params['pass_events'];
unset($params['pass_events']);
}
$map_link = '';
if ( isset($params['anchor']) ) {
$map_link = '#' . $params['anchor'];
unset($params['anchor']);
}
if ( isset($params['no_amp']) ) {
$params['__URLENCODE__'] = $params['no_amp'];
unset($params['no_amp']);
}
$rewrite = true;
if ( isset($params['__NO_REWRITE__']) ) {
$rewrite = false;
unset($params['__NO_REWRITE__']);
}
$force_rewrite = false;
if ( isset($params['__MOD_REWRITE__']) ) {
$force_rewrite = true;
unset($params['__MOD_REWRITE__']);
}
$force_no_sid = false;
if ( isset($params['__NO_SID__']) ) {
$force_no_sid = true;
unset($params['__NO_SID__']);
}
// append pass through variables to each link to be build
$params = array_merge($this->getPassThroughVariables($params), $params);
if ( $force_rewrite || ($this->Application->RewriteURLs($ssl) && $rewrite) ) {
if ( !$this->rewriteReady ) {
$this->initRewrite();
}
$session =& $this->Application->recallObject('Session');
if ( $session->NeedQueryString() && !$force_no_sid ) {
$params['sid'] = $this->Application->GetSID();
}
$url = $this->rewrite->build($t, $params, $pass, $pass_events);
}
else {
unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off
$index_file = $this->getIndexFile($prefix, $index_file, $params);
$url = $index_file . '?' . $this->plain->build($t, $params, $pass, $pass_events);
}
return $this->Application->BaseURL($prefix, $ssl) . $url . $map_link;
}
/**
* Returns popup's parent window url and optionally removes it from opener stack
*
* @param string $prefix
* @param Array $params
* @return bool|string
* @access protected
*/
protected function processPopupClose($prefix = '', $params = Array ())
{
$opener_stack =& $this->Application->makeClass('kOpenerStack');
/* @var $opener_stack kOpenerStack */
if ( $opener_stack->isEmpty() ) {
return false;
}
$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
list($index_file, $env) = explode('|', $opener_stack->get(kOpenerStack::LAST_ELEMENT, true));
$ret = $this->Application->BaseURL($prefix, $ssl) . $index_file . '?' . ENV_VAR_NAME . '=' . $env;
if ( isset($params['escape']) && $params['escape'] ) {
$ret = addslashes($ret);
}
if ( isset($params['m_opener']) && $params['m_opener'] == 'u' ) {
$opener_stack->pop();
$opener_stack->save(true);
if ( $opener_stack->isEmpty() ) {
// remove popups last templates, because popup is closing now
$this->Application->RemoveVar('last_template_' . $opener_stack->getWindowID());
$this->Application->RemoveVar('last_template_popup_' . $opener_stack->getWindowID());
// don't save popups last templates again :)
$this->Application->SetVar('skip_last_template', 1);
}
// store window relations
/*$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
if (array_key_exists($wid, $window_relations)) {
unset($window_relations[$wid]);
$this->Application->StoreVar('window_relations', serialize($window_relations));
}*/
}
return $ret;
}
/**
* 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)
{
static $cached_pass_through = null;
if ( isset($params['no_pass_through']) && $params['no_pass_through'] ) {
unset($params['no_pass_through']);
return Array ();
}
// because pass through is not changed during script run, then we can cache it
if ( is_null($cached_pass_through) ) {
$cached_pass_through = Array ();
$pass_through = $this->Application->GetVar('pass_through');
if ( $pass_through ) {
// names of variables to pass to each link
$cached_pass_through['pass_through'] = $pass_through;
$pass_through = explode(',', $pass_through);
foreach ($pass_through as $pass_through_var) {
$cached_pass_through[$pass_through_var] = $this->Application->GetVar($pass_through_var);
}
}
}
return $cached_pass_through;
}
/**
* Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all
*
* @param string $prefix
* @param string $index_file
* @param Array $params
* @return string
* @access protected
*/
protected function getIndexFile($prefix, $index_file = null, &$params)
{
static $cache = Array ();
if ( isset($params['index_file']) ) {
$index_file = $params['index_file'];
unset($params['index_file']);
return $index_file;
}
if ( isset($index_file) ) {
return $index_file;
}
if ( defined('INDEX_FILE') ) {
return INDEX_FILE;
}
// detect index file only once for given script and $cut_prefix
$php_self = $_SERVER['PHP_SELF'];
$cut_prefix = BASE_PATH . '/' . trim($prefix, '/');
if ( isset($cache[$php_self . ':' . $cut_prefix]) ) {
return $cache[$php_self . ':' . $cut_prefix];
}
$cache[$php_self . ':' . $cut_prefix] = trim(preg_replace('/' . preg_quote($cut_prefix, '/') . '(.*)/', '\\1', $php_self), '/');
return $cache[$php_self . ':' . $cut_prefix];
}
/**
* 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 array_search($seo_template, $this->structureTemplateMapping);
}
/**
* 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 isset($this->structureTemplateMapping['id:' . $page_id]) ? $this->structureTemplateMapping['id:' . $page_id] : false;
}
/**
* Loads template mapping for Front-End
*
* @return void
* @access public
*/
public function LoadStructureTemplateMapping()
{
if (!$this->Application->isAdmin) {
$category_helper =& $this->Application->recallObject('CategoryHelper');
/* @var $category_helper CategoryHelper */
$this->structureTemplateMapping = $category_helper->getTemplateMapping();
}
}
/**
* Removes tpl part from template name + resolved template ID to name
*
* @param string $default_template
* @return string
* @access public
*/
public function getTemplateName($default_template = '')
{
if ( $this->Application->GetVarDirect('t', 'Get') !== false ) {
// template name is passed directly in url (GET method)
$t = $this->Application->GetVarDirect('t', 'Get');
}
elseif ( $this->Application->GetVar('env') && $this->Application->RewriteURLs() && $this->Application->GetVar('t') ) {
// if t was set through env, even in mod_rewrite mode!
$t = $this->Application->GetVar('t');
}
else {
$t = trim($default_template ? $default_template : 'index', '/');
}
return trim(preg_replace('/\.tpl$/', '', $t), '/');
}
+
+ /**
+ * Prepares case, when requested page wasn't found
+ *
+ * @param int $theme_id
+ * @return array
+ */
+ public function prepare404($theme_id = null)
+ {
+ if ( !isset($theme_id) ) {
+ $theme_id = $this->Application->GetVar('m_theme');
+ }
+
+ $not_found = $this->Application->ConfigValue('ErrorTemplate');
+ $vars['t'] = $not_found ? $not_found : 'error_notfound';
+
+ $themes_helper =& $this->Application->recallObject('ThemesHelper');
+ /* @var $themes_helper kThemesHelper */
+
+ $vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $theme_id);
+
+ header('HTTP/1.0 404 Not Found');
+
+ return $vars;
+ }
}
\ 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 15072)
+++ branches/5.2.x/core/kernel/managers/cache_manager.php (revision 15073)
@@ -1,805 +1,802 @@
<?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
*
* @param kApplication $application
* @access public
*/
public function __construct($application = null)
{
parent::__construct($application);
$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->Factory->setFromCache($cache);
- $this->Application->UnitConfigReader->setFromCache($cache);
- $this->Application->EventManager->setFromCache($cache);
+ $this->Application->setFromCache($cache);
$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
$aggregator->setFromCache($cache);
$this->setFromCache($cache);
- $this->Application->setFromCache($cache);
unset($cache);
return true;
}
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
}
else {
$this->Application->rebuildDBCache('configs_parsed', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
}
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', 'Factory.Dependencies',
- 'EventManager.buildEvents', 'EventManager.beforeHooks',
- 'EventManager.afterHooks', 'EventManager.scheduledTasks',
+ '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->Factory->setFromCache($empty_cache);
- $this->Application->EventManager->setFromCache($empty_cache);
+ $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->Factory->getToCache(),
- $this->Application->UnitConfigReader->getToCache(),
- $this->Application->EventManager->getToCache(),
+ $this->Application->getToCache(),
$aggregator->getToCache(),
- $this->getToCache(),
- $this->Application->getToCache()
+ $this->getToCache()
);
$cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . 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->Application->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->Application->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',
// 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;
}
/**
* Prints caching statistics
*
* @access public
*/
public function printStatistics()
{
$this->cacheHandler->printStatistics();
}
/**
* 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);
}
/**
* Adds new value to cache $cache_name and identified by key $key
*
* @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);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return void
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
$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)
{
if ( $this->_getDBCache($name . '_rebuild') ) {
// cache rebuild requested -> rebuild now
$this->deleteDBCache($name . '_rebuild');
return false;
}
// no serials in cache key OR cache is outdated
$wait_seconds = $max_rebuild_seconds;
while (true) {
$cache = $this->_getDBCache($name);
$rebuilding = $this->_getDBCache($name . '_rebuilding');
if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) {
// cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready
return false;
}
elseif ( $cache !== false ) {
// cache present -> return it
return $cache;
}
$wait_seconds -= kCache::WAIT_STEP;
sleep(kCache::WAIT_STEP);
}
return false;
}
/**
* Returns value from database cache
*
* @param string $name key name
* @return mixed
* @access protected
*/
protected function _getDBCache($name)
{
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT Data, Cached, LifeTime
FROM ' . TABLE_PREFIX . 'SystemCache
WHERE VarName = ' . $this->Conn->qstr($name);
$data = $this->Conn->GetRow($sql);
if ($data) {
$lifetime = (int)$data['LifeTime']; // in seconds
if (($lifetime > 0) && ($data['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);
return false;
}
return $data['Data'];
}
return false;
}
/**
* 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->deleteDBCache($name . '_rebuilding');
$this->_setDBCache($name, $value, $expiration);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @access protected
*/
protected function _setDBCache($name, $value, $expiration = false)
{
if ((int)$expiration <= 0) {
$expiration = -1;
}
$fields_hash = Array (
'VarName' => $name,
'Data' => &$value,
'Cached' => adodb_mktime(),
'LifeTime' => (int)$expiration,
);
$this->Conn->nextQueryCachable = true;
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SystemCache', 'REPLACE');
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return void
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
{
if ( !isset($mode) || $mode == kCache::REBUILD_NOW ) {
$this->_setDBCache($name . '_rebuilding', 1, $max_rebuilding_time);
$this->deleteDBCache($name . '_rebuild');
}
elseif ( $mode == kCache::REBUILD_LATER ) {
$this->_setDBCache($name . '_rebuild', 1, 0);
$this->deleteDBCache($name . '_rebuilding');
}
}
/**
* 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/managers/rewrite_url_processor.php
===================================================================
--- branches/5.2.x/core/kernel/managers/rewrite_url_processor.php (revision 15072)
+++ branches/5.2.x/core/kernel/managers/rewrite_url_processor.php (revision 15073)
@@ -1,981 +1,973 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kRewriteUrlProcessor extends kUrlProcessor {
/**
* Holds a reference to httpquery
*
* @var kHttpQuery
* @access protected
*/
protected $HTTPQuery = null;
/**
* Urls parts, that needs to be matched by rewrite listeners
*
* @var Array
* @access protected
*/
protected $_partsToParse = Array ();
/**
* Category item prefix, that was found
*
* @var string|bool
* @access public
*/
public $modulePrefix = false;
/**
* Template aliases for current theme
*
* @var Array
* @access protected
*/
protected $_templateAliases = null;
/**
* Domain-based primary language id
*
* @var int
* @access public
*/
public $primaryLanguageId = false;
/**
* Domain-based primary theme id
*
* @var int
* @access public
*/
public $primaryThemeId = false;
/**
* Possible url endings from ModRewriteUrlEnding configuration variable
*
* @var Array
* @access protected
*/
protected $_urlEndings = Array ('.html', '/', '');
/**
* Factory storage sub-set, containing mod-rewrite listeners, used during url building and parsing
*
* @var Array
* @access protected
*/
protected $rewriteListeners = Array ();
/**
* Constructor of kRewriteUrlProcessor class
*
* @param $manager
* @return kRewriteUrlProcessor
*/
public function __construct(&$manager)
{
parent::__construct($manager);
$this->HTTPQuery =& $this->Application->recallObject('HTTPQuery');
// domain based primary language
$this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId');
if (!$this->primaryLanguageId) {
// when domain-based language not found -> use site-wide language
$this->primaryLanguageId = $this->Application->GetDefaultLanguageId();
}
// domain based primary theme
$this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId');
if (!$this->primaryThemeId) {
// when domain-based theme not found -> use site-wide theme
$this->primaryThemeId = $this->Application->GetDefaultThemeId(true);
}
$this->_initRewriteListeners();
}
/**
* Parses url
*
* @return void
*/
public function parseRewriteURL()
{
$url = $this->Application->GetVar('_mod_rw_url_');
if ($url) {
foreach ($this->_urlEndings as $url_ending) {
if (substr($url, strlen($url) - strlen($url_ending)) == $url_ending) {
$url = substr($url, 0, strlen($url) - strlen($url_ending));
$default_ending = $this->Application->ConfigValue('ModRewriteUrlEnding');
// user manually typed url with different url ending -> redirect to same url with default url ending
if (($url_ending != $default_ending) && $this->Application->ConfigValue('ForceModRewriteUrlEnding')) {
$target_url = $this->Application->BaseURL() . $url . $default_ending;
$this->Application->Redirect('external:' . $target_url, Array ('response_code' => 301));
}
break;
}
}
}
$cached = $this->_getCachedUrl($url);
if ( $cached !== false ) {
$vars = $cached['vars'];
$passed = $cached['passed'];
}
else {
$vars = $this->parse($url);
$passed = $vars['pass']; // also used in bottom of this method
unset($vars['pass']);
if ( !$this->_partsToParse ) {
// don't cache 404 Not Found
$this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed));
}
if ( $this->Application->GetVarDirect('t', 'Post') ) {
// template from POST overrides template from URL.
$vars['t'] = $this->Application->GetVarDirect('t', 'Post');
if ( isset($vars['is_virtual']) && $vars['is_virtual'] ) {
$vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS)
}
}
unset($vars['is_virtual']);
}
foreach ($vars as $name => $value) {
$this->HTTPQuery->Set($name, $value);
}
$this->_initAll(); // also will use parsed language to load phrases from it
$this->HTTPQuery->finalizeParsing($passed);
}
/**
* Returns url parsing result from cache or false, when not yet parsed
*
* @param $url
* @return Array|bool
* @access protected
*/
protected function _getCachedUrl($url)
{
if (!$url) {
return false;
}
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'CachedUrls
WHERE Hash = ' . crc32($url) . ' AND DomainId = ' . (int)$this->Application->siteDomainField('DomainId');
$data = $this->Conn->GetRow($sql);
if ($data) {
$lifetime = (int)$data['LifeTime']; // in seconds
if (($lifetime > 0) && ($data['Cached'] + $lifetime < adodb_mktime())) {
// delete expired
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls
WHERE UrlId = ' . $data['UrlId'];
$this->Conn->Query($sql);
return false;
}
return unserialize($data['ParsedVars']);
}
return false;
}
/**
* Caches url
*
* @param string $url
* @param Array $data
* @return void
* @access protected
*/
protected function _setCachedUrl($url, $data)
{
if (!$url) {
return ;
}
$vars = $data['vars'];
$passed = $data['passed'];
sort($passed);
// get expiration
if ($vars['m_cat_id'] > 0) {
$sql = 'SELECT PageExpiration
FROM ' . TABLE_PREFIX . 'Categories
WHERE CategoryId = ' . $vars['m_cat_id'];
$expiration = $this->Conn->GetOne($sql);
}
// get prefixes
$prefixes = Array ();
$m_index = array_search('m', $passed);
if ($m_index !== false) {
unset($passed[$m_index]);
if ($vars['m_cat_id'] > 0) {
$prefixes[] = 'c:' . $vars['m_cat_id'];
}
$prefixes[] = 'lang:' . $vars['m_lang'];
$prefixes[] = 'theme:' . $vars['m_theme'];
}
foreach ($passed as $prefix) {
if (array_key_exists($prefix . '_id', $vars) && is_numeric($vars[$prefix . '_id'])) {
$prefixes[] = $prefix . ':' . $vars[$prefix . '_id'];
}
else {
$prefixes[] = $prefix;
}
}
$fields_hash = Array (
'Url' => $url,
'Hash' => crc32($url),
'DomainId' => (int)$this->Application->siteDomainField('DomainId'),
'Prefixes' => $prefixes ? '|' . implode('|', $prefixes) . '|' : '',
'ParsedVars' => serialize($data),
'Cached' => adodb_mktime(),
'LifeTime' => isset($expiration) && is_numeric($expiration) ? $expiration : -1
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CachedUrls');
}
/**
* Loads all registered rewrite listeners, so they could be quickly accessed later
*
* @access protected
*/
protected function _initRewriteListeners()
{
static $init_done = false;
if ($init_done || count($this->Application->RewriteListeners) == 0) {
// not initialized OR mod-rewrite url with missing config cache
return ;
}
foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
foreach ($listener_data['listener'] as $index => $rewrite_listener) {
list ($listener_prefix, $listener_method) = explode(':', $rewrite_listener);
// don't use temp variable, since it will swap objects in Factory in PHP5
$this->rewriteListeners[$prefix][$index] = Array ();
$this->rewriteListeners[$prefix][$index][0] =& $this->Application->recallObject($listener_prefix);
$this->rewriteListeners[$prefix][$index][1] = $listener_method;
}
}
define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding'));
$init_done = true;
}
/**
* Parses given string into a set of variables (url in this case)
*
* @param string $string
* @param string $pass_name
* @return Array
* @access public
*/
public function parse($string, $pass_name = 'pass')
{
$vars = Array ($pass_name => Array ('m'));
$url_parts = $string ? explode('/', trim(mb_strtolower($string, 'UTF-8'), '/')) : Array ();
$this->_partsToParse = $url_parts;
if ( ($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts ) {
$this->_setDefaultValues($vars);
}
if ( !$url_parts ) {
$this->_initAll();
$vars['t'] = $this->Application->UrlManager->getTemplateName();
return $vars;
}
$this->_parseLanguage($url_parts, $vars);
$this->_parseTheme($url_parts, $vars);
// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_page>
// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_page> (category-based section template)
// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_item>
// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_item> (category-based detail template)
// http://site-url/<language>/<theme>/<rl_injections>/<category>[_<category_page>]/<rl_part> (customized url)
if ( $this->_processRewriteListeners($url_parts, $vars) ) {
return $vars;
}
$this->_parsePhysicalTemplate($url_parts, $vars);
if ( ($this->modulePrefix === false) && $vars['m_cat_id'] && !$this->_partsToParse ) {
// no category item found, but category found and all url matched -> module index page
return $vars;
}
if ( $this->_partsToParse ) {
- $not_found = $this->Application->ConfigValue('ErrorTemplate');
- $vars['t'] = $not_found ? $not_found : 'error_notfound';
-
- $themes_helper =& $this->Application->recallObject('ThemesHelper');
- /* @var $themes_helper kThemesHelper */
-
- $vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $vars['m_theme']);
-
- header('HTTP/1.0 404 Not Found');
+ $vars = array_merge($vars, $this->manager->prepare404($vars['m_theme']));
}
return $vars;
}
/**
* Initializes theme & language based on parse results
*
* @return void
* @access protected
*/
protected function _initAll()
{
$this->Application->VerifyThemeId();
$this->Application->VerifyLanguageId();
// no need, since we don't have any cached phrase IDs + nobody will use PhrasesCache::LanguageId soon
// $this->Application->Phrases->Init('phrases');
}
/**
* Sets default parsed values before actual url parsing (only, for empty url)
*
* @param Array $vars
* @access protected
*/
protected function _setDefaultValues(&$vars)
{
$defaults = Array (
'm_cat_id' => 0, // no category
'm_cat_page' => 1, // first category page
'm_opener' => 's', // stay on same page
't' => 'index' // main site page
);
if ($this->primaryLanguageId) {
// domain-based primary language
$defaults['m_lang'] = $this->primaryLanguageId;
}
if ($this->primaryThemeId) {
// domain-based primary theme
$defaults['m_theme'] = $this->primaryThemeId;
}
foreach ($defaults as $default_key => $default_value) {
if ($this->HTTPQuery->Get($default_key) === false) {
$vars[$default_key] = $default_value;
}
}
}
/**
* Processes url using rewrite listeners
*
* Pattern: Chain of Command
*
* @param Array $url_parts
* @param Array $vars
* @return bool
* @access protected
*/
protected function _processRewriteListeners(&$url_parts, &$vars)
{
$this->_initRewriteListeners();
$page_number = $this->_parsePage($url_parts, $vars);
foreach ($this->rewriteListeners as $prefix => $listeners) {
// set default page
// $vars[$prefix . '_Page'] = 1; // will override page in session in case, when none is given in url
if ($page_number) {
// page given in url - use it
$vars[$prefix . '_id'] = 0;
$vars[$prefix . '_Page'] = $page_number;
}
// $listeners[1] - listener, used for parsing
$listener_result = $listeners[1][0]->$listeners[1][1](REWRITE_MODE_PARSE, $prefix, $vars, $url_parts);
if ($listener_result === false) {
// will not proceed to other methods
return true;
}
}
// will proceed to other methods
return false;
}
/**
* Set's page (when found) to all modules
*
* @param Array $url_parts
* @param Array $vars
* @return string
* @access protected
*
* @todo Should find a way, how to determine what rewrite listerner page is it
*/
protected function _parsePage(&$url_parts, &$vars)
{
if (!$url_parts) {
return false;
}
$page_number = end($url_parts);
if (!is_numeric($page_number)) {
return false;
}
array_pop($url_parts);
$this->partParsed($page_number, 'rtl');
return $page_number;
}
/**
* Gets language part from url
*
* @param Array $url_parts
* @param Array $vars
* @return bool
* @access protected
*/
protected function _parseLanguage(&$url_parts, &$vars)
{
if (!$url_parts) {
return false;
}
$url_part = reset($url_parts);
$sql = 'SELECT LanguageId, IF(LOWER(PackName) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryLang) AS SortKey
FROM ' . TABLE_PREFIX . 'Languages
WHERE Enabled = 1
ORDER BY SortKey DESC';
$language_info = $this->Conn->GetRow($sql);
if ($language_info && $language_info['LanguageId'] && $language_info['SortKey']) {
// primary language will be selected in case, when $url_part doesn't match to other's language pack name
// don't use next enabled language, when primary language is disabled
$vars['m_lang'] = $language_info['LanguageId'];
if ($language_info['SortKey'] == 2) {
// language was found by pack name
array_shift($url_parts);
$this->partParsed($url_part);
}
elseif ($this->primaryLanguageId) {
// use domain-based primary language instead of site-wide primary language
$vars['m_lang'] = $this->primaryLanguageId;
}
return true;
}
return false;
}
/**
* Gets theme part from url
*
* @param Array $url_parts
* @param Array $vars
* @return bool
*/
protected function _parseTheme(&$url_parts, &$vars)
{
if (!$url_parts) {
return false;
}
$url_part = reset($url_parts);
$sql = 'SELECT ThemeId, IF(LOWER(Name) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryTheme) AS SortKey, TemplateAliases
FROM ' . TABLE_PREFIX . 'Themes
WHERE Enabled = 1
ORDER BY SortKey DESC';
$theme_info = $this->Conn->GetRow($sql);
if ($theme_info && $theme_info['ThemeId'] && $theme_info['SortKey']) {
// primary theme will be selected in case, when $url_part doesn't match to other's theme name
// don't use next enabled theme, when primary theme is disabled
$vars['m_theme'] = $theme_info['ThemeId'];
if ($theme_info['TemplateAliases']) {
$this->_templateAliases = unserialize($theme_info['TemplateAliases']);
}
else {
$this->_templateAliases = Array ();
}
if ($theme_info['SortKey'] == 2) {
// theme was found by name
array_shift($url_parts);
$this->partParsed($url_part);
}
elseif ($this->primaryThemeId) {
// use domain-based primary theme instead of site-wide primary theme
$vars['m_theme'] = $this->primaryThemeId;
}
return true;
}
$vars['m_theme'] = 0; // required, because used later for category/template detection
return false;
}
/**
* Parses real template name from url
*
* @param Array $url_parts
* @param Array $vars
* @return bool
*/
protected function _parsePhysicalTemplate($url_parts, &$vars)
{
if ( !$url_parts ) {
return false;
}
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
do {
$index_added = false;
$template_path = implode('/', $url_parts);
$template_found = $themes_helper->getTemplateId($template_path, $vars['m_theme']);
if ( !$template_found ) {
$index_added = true;
$template_found = $themes_helper->getTemplateId($template_path . '/index', $vars['m_theme']);
}
if ( !$template_found ) {
array_shift($url_parts);
}
} while ( !$template_found && $url_parts );
if ( $template_found ) {
$template_parts = explode('/', $template_path);
$vars['t'] = $template_path . ($index_added ? '/index' : '');
while ( $template_parts ) {
$this->partParsed(array_pop($template_parts), 'rtl');
}
// 1. will damage actual category during category item review add process
// 2. will use "use_section" parameter of "m_Link" tag to gain same effect
// $vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']);
return true;
}
return false;
}
/**
* Returns environment variable values for given prefix (uses directly given params, when available)
*
* @param string $prefix_special
* @param Array $params
* @param bool $keep_events
* @return Array
* @access public
*/
public function getProcessedParams($prefix_special, &$params, $keep_events)
{
list ($prefix) = explode('.', $prefix_special);
$query_vars = $this->Application->getUnitOption($prefix, 'QueryString', Array ());
/* @var $query_vars Array */
if ( !$query_vars ) {
// given prefix doesn't use "env" variable to pass it's data
return false;
}
$event_key = array_search('event', $query_vars);
if ( $event_key ) {
// pass through event of this prefix
unset($query_vars[$event_key]);
}
if ( array_key_exists($prefix_special . '_event', $params) && !$params[$prefix_special . '_event'] ) {
// if empty event, then remove it from url
unset($params[$prefix_special . '_event']);
}
// if pass events is off and event is not implicity passed
if ( !$keep_events && !array_key_exists($prefix_special . '_event', $params) ) {
unset($params[$prefix_special . '_event']); // remove event from url if requested
//otherwise it will use value from get_var
}
$processed_params = Array ();
foreach ($query_vars as $var_name) {
// if value passed in params use it, otherwise use current from application
$var_name = $prefix_special . '_' . $var_name;
$processed_params[$var_name] = array_key_exists($var_name, $params) ? $params[$var_name] : $this->Application->GetVar($var_name);
if ( array_key_exists($var_name, $params) ) {
unset($params[$var_name]);
}
}
return $processed_params;
}
/**
* Returns module item details template specified in given category custom field for given module prefix
*
* @param int|Array $category
* @param string $module_prefix
* @return string
* @access public
* @todo Move to kPlainUrlProcessor
*/
public function GetItemTemplate($category, $module_prefix)
{
$category_id = is_array($category) ? $category['CategoryId'] : $category;
$cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CIDSerial:' . $category_id . '%]:' . $module_prefix;
$cached_value = $this->Application->getCache($cache_key);
if ( $cached_value !== false ) {
return $cached_value;
}
if ( !is_array($category) ) {
if ( $category == 0 ) {
$category = $this->Application->findModule('Var', $module_prefix, 'RootCat');
}
$sql = 'SELECT c.ParentPath, c.CategoryId
FROM ' . TABLE_PREFIX . 'Categories AS c
WHERE c.CategoryId = ' . $category;
$category = $this->Conn->GetRow($sql);
}
$parent_path = implode(',', explode('|', substr($category['ParentPath'], 1, -1)));
// item template is stored in module' system custom field - need to get that field Id
$primary_lang = $this->Application->GetDefaultLanguageId();
$item_template_field_id = $this->getItemTemplateCustomField($module_prefix);
// looking for item template through cats hierarchy sorted by parent path
$query = ' SELECT ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ',
FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') AS Ord1,
c.CategoryId, c.Name, ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . '
FROM ' . TABLE_PREFIX . 'Categories AS c
LEFT JOIN ' . TABLE_PREFIX . 'CategoryCustomData AS ccd
ON ccd.ResourceId = c.ResourceId
WHERE c.CategoryId IN (' . $parent_path . ') AND ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ' != \'\'
ORDER BY FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') DESC';
$item_template = $this->Conn->GetOne($query);
if ( !isset($this->_templateAliases) ) {
// when empty url OR mod-rewrite disabled
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$sql = 'SELECT TemplateAliases
FROM ' . TABLE_PREFIX . 'Themes
WHERE ThemeId = ' . (int)$themes_helper->getCurrentThemeId();
$template_aliases = $this->Conn->GetOne($sql);
$this->_templateAliases = $template_aliases ? unserialize($template_aliases) : Array ();
}
if ( substr($item_template, 0, 1) == '#' ) {
// it's template alias + "#" isn't allowed in filenames
$item_template = (string)getArrayValue($this->_templateAliases, $item_template);
}
$this->Application->setCache($cache_key, $item_template);
return $item_template;
}
/**
* Returns category custom field id, where given module prefix item template name is stored
*
* @param string $module_prefix
* @return int
* @access public
* @todo Move to kPlainUrlProcessor; decrease visibility, since used only during upgrade
*/
public function getItemTemplateCustomField($module_prefix)
{
$cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CfSerial%]:' . $module_prefix;
$cached_value = $this->Application->getCache($cache_key);
if ($cached_value !== false) {
return $cached_value;
}
$sql = 'SELECT CustomFieldId
FROM ' . TABLE_PREFIX . 'CustomFields
WHERE FieldName = ' . $this->Conn->qstr($module_prefix . '_ItemTemplate');
$item_template_field_id = $this->Conn->GetOne($sql);
$this->Application->setCache($cache_key, $item_template_field_id);
return $item_template_field_id;
}
/**
* Marks url part as parsed
*
* @param string $url_part
* @param string $parse_direction
* @access public
*/
public function partParsed($url_part, $parse_direction = 'ltr')
{
if ( !$this->_partsToParse ) {
return ;
}
if ( $parse_direction == 'ltr' ) {
$expected_url_part = reset($this->_partsToParse);
if ( $url_part == $expected_url_part ) {
array_shift($this->_partsToParse);
}
}
else {
$expected_url_part = end($this->_partsToParse);
if ( $url_part == $expected_url_part ) {
array_pop($this->_partsToParse);
}
}
if ( $url_part != $expected_url_part ) {
trigger_error('partParsed: expected URL part "<strong>' . $expected_url_part . '</strong>", received URL part "<strong>' . $url_part . '</strong>"', E_USER_NOTICE);
}
}
/**
* Builds url
*
* @param string $t
* @param Array $params
* @param string $pass
* @param bool $pass_events
* @param bool $env_var
* @return string
* @access public
*/
public function build($t, $params, $pass = 'all', $pass_events = false, $env_var = false)
{
if ( $this->Application->GetVar('admin') || (array_key_exists('admin', $params) && $params['admin']) ) {
$params['admin'] = 1;
if ( !array_key_exists('editing_mode', $params) ) {
$params['editing_mode'] = EDITING_MODE;
}
}
$ret = '';
$env = '';
$encode = false;
if ( isset($params['__URLENCODE__']) ) {
$encode = $params['__URLENCODE__'];
unset($params['__URLENCODE__']);
}
if ( isset($params['__SSL__']) ) {
unset($params['__SSL__']);
}
$catalog_item_found = false;
$pass_info = $this->getPassInfo($pass);
if ( $pass_info ) {
if ( $pass_info[0] == 'm' ) {
array_shift($pass_info);
}
$inject_parts = Array (); // url parts for beginning of url
$params['t'] = $t; // make template available for rewrite listeners
$params['pass_template'] = true; // by default we keep given template in resulting url
if ( !array_key_exists('pass_category', $params) ) {
$params['pass_category'] = false; // by default we don't keep categories in url
}
foreach ($pass_info as $pass_index => $pass_element) {
list ($prefix) = explode('.', $pass_element);
$catalog_item = $this->Application->findModule('Var', $prefix) && $this->Application->getUnitOption($prefix, 'CatalogItem');
if ( array_key_exists($prefix, $this->rewriteListeners) ) {
// if next prefix is same as current, but with special => exclude current prefix from url
$next_prefix = array_key_exists($pass_index + 1, $pass_info) ? $pass_info[$pass_index + 1] : false;
if ( $next_prefix ) {
$next_prefix = substr($next_prefix, 0, strlen($prefix) + 1);
if ( $prefix . '.' == $next_prefix ) {
continue;
}
}
// rewritten url part
$url_part = $this->BuildModuleEnv($pass_element, $params, $pass_events);
if ( is_string($url_part) && $url_part ) {
$ret .= $url_part . '/';
if ( $catalog_item ) {
// pass category later only for catalog items
$catalog_item_found = true;
}
}
elseif ( is_array($url_part) ) {
// rewrite listener want to insert something at the beginning of url too
if ( $url_part[0] ) {
$inject_parts[] = $url_part[0];
}
if ( $url_part[1] ) {
$ret .= $url_part[1] . '/';
}
if ( $catalog_item ) {
// pass category later only for catalog items
$catalog_item_found = true;
}
}
elseif ( $url_part === false ) {
// rewrite listener decided not to rewrite given $pass_element
$env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events);
}
}
else {
$env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events);
}
}
if ( $catalog_item_found || preg_match('/c\.[-\d]*/', implode(',', $pass_info)) ) {
// "c" prefix is present -> keep category
$params['pass_category'] = true;
}
$params['inject_parts'] = $inject_parts;
$ret = $this->BuildModuleEnv('m', $params, $pass_events) . '/' . $ret;
$cat_processed = array_key_exists('category_processed', $params) && $params['category_processed'];
// remove temporary parameters used by listeners
unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']);
$ret = trim($ret, '/');
if ( isset($params['url_ending']) ) {
if ( $ret ) {
$ret .= $params['url_ending'];
}
unset($params['url_ending']);
}
elseif ( $ret ) {
$ret .= MOD_REWRITE_URL_ENDING;
}
if ( $env ) {
$params[ENV_VAR_NAME] = ltrim($env, ':');
}
}
unset($params['pass'], $params['opener'], $params['m_event']);
if ( array_key_exists('escape', $params) && $params['escape'] ) {
$ret = addslashes($ret);
unset($params['escape']);
}
$ret = str_replace('%2F', '/', urlencode($ret));
if ( $params ) {
$params_str = '';
$join_string = $encode ? '&' : '&amp;';
foreach ($params as $param => $value) {
$params_str .= $join_string . $param . '=' . $value;
}
$ret .= '?' . substr($params_str, strlen($join_string));
}
if ( $encode ) {
$ret = str_replace('\\', '%5C', $ret);
}
return $ret;
}
/**
* Builds env part that corresponds prefix passed
*
* @param string $prefix_special item's prefix & [special]
* @param Array $params url params
* @param bool $pass_events
* @return string
* @access protected
*/
protected function BuildModuleEnv($prefix_special, &$params, $pass_events = false)
{
list ($prefix) = explode('.', $prefix_special);
$url_parts = Array ();
$listener = $this->rewriteListeners[$prefix][0];
$ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $pass_events);
return $ret;
}
}
\ 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 15072)
+++ branches/5.2.x/core/kernel/utility/debugger.php (revision 15073)
@@ -1,1939 +1,1939 @@
<?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 {
/**
* 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
return false;
}
$ip_match = false;
$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();
foreach ($ip_addresses as $ip_address) {
if (self::netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) {
$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;
}
}
/**
* 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;
/**
* 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;
/**
* Long errors are saved here, because trigger_error doesn't support error messages over 1KB in size
*
* @var Array
* @access private
*/
private $longErrors = Array ();
/**
* 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');
}
// 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);
// 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_IGNORE_STRICT_ERRORS' => 0, // ignore PHP5 errors about private/public view modified missing in class declarations
'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
);
// 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 = '@' . (/*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';
}
/**
* Allows to overcome short error message problem in tigger_error function
*
* @param string $msg
* @return int
* @access public
*/
public function mapLongError($msg)
{
$key = $this->generateID();
$this->longErrors[$key] = $msg;
return $key;
}
/**
* 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
*/
private 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');
}
/**
* 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 ( $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_USER_ERROR),
'Warning' => Array (E_WARNING, E_USER_WARNING),
'Notice' => Array (E_NOTICE, E_USER_NOTICE),
);
if ( defined('E_STRICT') ) {
// since PHP 5
$error_map['PHP5 Strict'] = Array (E_STRICT);
}
if ( defined('E_RECOVERABLE_ERROR') ) {
// since PHP 5.2
$error_map['Fatal Error (recoverable)'] = Array (E_RECOVERABLE_ERROR);
}
if ( defined('E_DEPRECATED') ) {
// since PHP 5.3
$error_map['PHP5 Depricated'] = Array (E_DEPRECATED, 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 '';
}
$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);
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>
<script type="text/javascript" src="<?php echo $this->baseURL; ?>/debugger.js?v2"></script>
<link rel="stylesheet" rev="stylesheet" href="<?php echo $this->baseURL; ?>/debugger.css" type="text/css" media="screen" />
<script type="text/javascript">
var $Debugger = new Debugger(<?php echo "'".$this->rowSeparator."', ".$this->getProfilerTotal('error_handling').', '.($this->IsFatalError ? 'true' : 'false').', '.$this->getProfilerTotal('sql'); ?>);
$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.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 ( !isset($this->ProfilerTotals['error_handling']) ) {
$memory_used = $debugger_start;
$this->ProfilerTotalCount['error_handling'] = 0;
}
else {
$memory_used = $debugger_start - $this->ProfilerTotals['error_handling'];
}
if ( $returnResult ) {
$ret = ob_get_contents();
if ( $clean_output_buffer ) {
ob_end_clean();
}
$ret .= $this->getShortReport($memory_used);
$this->reportDone = true;
return $ret;
}
else {
if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) {
$this->breakOutofBuffering();
}
elseif ( $clean_output_buffer ) {
ob_clean();
}
echo $this->getShortReport($memory_used);
$this->reportDone = true;
}
return '';
}
/**
* 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') ) {
// we have sql & error count in toolbar, don't duplicate here
$info = Array (
'Script Runtime' => 'PROFILE:script_runtime',
'SQL\'s Runtime' => 'PROFILE_T:sql',
);
}
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);
}
if ( DebuggerUtil::constOn('DBG_IGNORE_STRICT_ERRORS') && defined('E_STRICT') && ($errno == E_STRICT) ) {
return false;
}
$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;
}
/**
* User-defined exception handler
*
* @param Exception $exception
* @return void
* @access public
*/
public function saveException($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');
$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)
{
if ( preg_match('/(.*)#([\d]+)$/', $errstr, $rets) ) {
// replace short message with long one (due triger_error limitations on message size)
$long_id = $rets[2];
$errstr = $this->longErrors[$long_id];
unset($this->longErrors[$long_id]);
}
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') ) {
// replace application error/exception handler with own
restore_error_handler();
restore_exception_handler();
$this->Application =& kApplication::Instance();
$this->Application->Debugger =& $this;
$this->Application->errorHandlers[] = Array (&$this, 'saveError');
$this->Application->exceptionHandlers[] = Array (&$this, 'saveException');
}
else {
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/utility/unit_config_reader.php
===================================================================
--- branches/5.2.x/core/kernel/utility/unit_config_reader.php (revision 15072)
+++ branches/5.2.x/core/kernel/utility/unit_config_reader.php (revision 15073)
@@ -1,1065 +1,1068 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kUnitConfigReader extends kBase implements kiCacheable {
/**
* Configs reader
*
* @var Array
* @access private
*/
var $configData = Array();
var $configFiles = Array();
var $CacheExpired = false;
var $prefixFiles = array();
var $ProcessAllConfigs = false;
var $FinalStage = false;
var $StoreCache = false;
var $AfterConfigProcessed = array();
/**
* Escaped directory separator for using in regular expressions
*
* @var string
*/
var $_directorySeparator = '';
/**
* Regular expression for detecting module folder
*
* @var string
*/
var $_moduleFolderRegExp = '';
/**
* Folders to skip during unit config search
*
* @var Array
*/
var $_skipFolders = Array ('CVS', '.svn', 'admin_templates', 'libchart');
/**
* Creates instance of unit config reader
*
*/
public function __construct()
{
parent::__construct();
$this->_directorySeparator = preg_quote(DIRECTORY_SEPARATOR);
$editor_path = explode('/', trim(EDITOR_PATH, '/'));
$this->_skipFolders[] = array_pop($editor_path); // last of cmseditor folders
$this->_moduleFolderRegExp = '#' . $this->_directorySeparator . '(core|modules' . $this->_directorySeparator . '.*?)' . $this->_directorySeparator . '#';
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->prefixFiles = $data['ConfigReader.prefixFiles'];
}
/**
* Gets object data for caching
*
* @access public
* @return Array
*/
public function getToCache()
{
return Array (
'ConfigReader.prefixFiles' => $this->prefixFiles,
);
}
function scanModules($folderPath, $cache = true)
{
if (defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE')) {
// disable config caching during installation
$cache = false;
}
if ($cache) {
$restored = $this->Application->cacheManager->LoadUnitCache();
if ($restored) {
if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache');
}
return;
}
}
if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache');
}
$this->ProcessAllConfigs = true;
$this->includeConfigFiles($folderPath, $cache);
$this->ParseConfigs();
// tell AfterConfigRead to store cache if needed
// can't store it here because AfterConfigRead needs ability to change config data
$this->StoreCache = $cache;
if ( !$this->Application->InitDone ) {
// scanModules is called multiple times during installation process
$this->Application->InitManagers();
// get build-in rewrite listeners ONLY to be able to parse mod-rewrite url when unit config cache is missing
$this->retrieveCollections();
$this->_sortRewriteListeners();
}
$this->Application->cacheManager->applyDelayedUnitProcessing();
}
function findConfigFiles($folderPath, $level = 0)
{
// if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted
$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
$folderPath = preg_replace($reg_exp, '', $folderPath, 1); // this make sense, since $folderPath may NOT contain FULL_PATH
$base_folder = FULL_PATH . $folderPath . DIRECTORY_SEPARATOR;
$sub_folders = glob($base_folder . '*', GLOB_ONLYDIR);
if (!$sub_folders) {
return ;
}
if ($level == 0) {
// don't scan Front-End themes because of extensive directory structure
$sub_folders = array_diff($sub_folders, Array ($base_folder . 'themes', $base_folder . 'tools'));
}
foreach ($sub_folders as $full_path) {
$sub_folder = substr($full_path, strlen($base_folder));
if (in_array($sub_folder, $this->_skipFolders)) {
continue;
}
if (preg_match('/^\./', $sub_folder)) {
// don't scan ".folders"
continue;
}
$config_name = $this->getConfigName($folderPath . DIRECTORY_SEPARATOR . $sub_folder);
if (file_exists(FULL_PATH . $config_name)) {
$this->configFiles[] = $config_name;
}
$this->findConfigFiles($full_path, $level + 1);
}
}
function includeConfigFiles($folderPath, $cache = true)
{
$this->Application->refreshModuleInfo();
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$data = $this->Application->getCache('master:config_files', false, CacheSettings::$unitCacheRebuildTime);
}
else {
$data = $this->Application->getDBCache('config_files', CacheSettings::$unitCacheRebuildTime);
}
if ( $data ) {
$this->configFiles = unserialize($data);
if ( !defined('DBG_VALIDATE_CONFIGS') && !DBG_VALIDATE_CONFIGS ) {
shuffle($this->configFiles);
}
}
else {
if ( $cache ) {
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->rebuildCache('master:config_files', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
}
else {
$this->Application->rebuildDBCache('config_files', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
}
}
$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from core directory
$this->findConfigFiles($folderPath); // search from modules directory
if ( $cache ) {
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->setCache('master:config_files', serialize($this->configFiles));
}
else {
$this->Application->setDBCache('config_files', serialize($this->configFiles));
}
}
}
foreach ($this->configFiles as $filename) {
$prefix = $this->PreloadConfigFile($filename);
if (!$prefix) {
throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>');
}
}
if ($cache) {
unset($this->configFiles);
}
}
/**
* Process all read config files - called ONLY when there is no cache!
*
*/
function ParseConfigs()
{
// 1. process normal configs and their dependencies
$prioritized_configs = array();
foreach ($this->configData as $prefix => $config) {
if (isset($config['ConfigPriority'])) {
$prioritized_configs[$prefix] = $config['ConfigPriority'];
continue;
}
$this->parseConfig($prefix);
}
foreach ($this->configData as $prefix => $config) {
$this->ProcessDependencies($prefix);
$this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
}
// 2. process prioritized configs and their dependencies
asort($prioritized_configs);
foreach ($prioritized_configs as $prefix => $priority) {
$this->parseConfig($prefix);
}
foreach ($prioritized_configs as $prefix => $priority) {
$this->ProcessDependencies($prefix);
}
}
function AfterConfigRead($store_cache = null)
{
// if (!$this->ProcessAllConfigs) return ;
$this->FinalStage = true;
foreach ($this->configData as $prefix => $config) {
$this->runAfterConfigRead($prefix);
}
if ( !isset($store_cache) ) {
// $store_cache not overridden -> use global setting
$store_cache = $this->StoreCache;
}
if ($store_cache || (defined('IS_INSTALL') && IS_INSTALL)) {
// cache is not stored during install, but dynamic clones should be processed in any case
$this->processDynamicClones();
$this->retrieveCollections();
}
if ($store_cache) {
$this->_sortRewriteListeners();
$after_event = new kEvent('adm:OnAfterCacheRebuild');
$this->Application->HandleEvent($after_event);
$this->Application->cacheManager->UpdateUnitCache();
if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) {
// validate configs here to have changes from OnAfterConfigRead hooks to prefixes
foreach ($this->configData as $prefix => $config) {
if (!isset($config['TableName'])) continue;
$this->ValidateConfig($prefix);
}
}
}
}
/**
* Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first)
*
*/
function _sortRewriteListeners()
{
$listeners = Array ();
$prioritized_listeners = Array ();
// process non-prioritized listeners
foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
if ($listener_data['priority'] === false) {
$listeners[$prefix] = $listener_data;
}
else {
$prioritized_listeners[$prefix] = $listener_data['priority'];
}
}
// process prioritized listeners
asort($prioritized_listeners, SORT_NUMERIC);
foreach ($prioritized_listeners as $prefix => $priority) {
$listeners[$prefix] = $this->Application->RewriteListeners[$prefix];
}
$this->Application->RewriteListeners = $listeners;
}
/**
* Re-reads all configs
*
*/
function ReReadConfigs()
{
+ // don't reset prefix file, since file scanning could slow down the process
+ $prefix_files_backup = $this->prefixFiles;
$this->Application->cacheManager->EmptyUnitCache();
+ $this->prefixFiles = $prefix_files_backup;
// parse all configs
$this->ProcessAllConfigs = true;
$this->AfterConfigProcessed = Array ();
$this->includeConfigFiles(MODULES_PATH, false);
$this->ParseConfigs();
$this->AfterConfigRead(false);
$this->processDynamicClones();
// don't call kUnitConfigReader::retrieveCollections since it
// will overwrite what we already have in kApplication class instance
}
/**
* Process clones, that were defined via OnAfterConfigRead event
*
*/
function processDynamicClones()
{
$new_clones = Array();
foreach ($this->configData as $prefix => $config) {
$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
if ($clones) {
$new_clones = array_merge($new_clones, $clones);
}
}
// execute delayed methods for cloned unit configs
$this->Application->cacheManager->applyDelayedUnitProcessing();
// call OnAfterConfigRead for cloned configs
$new_clones = array_unique($new_clones);
foreach ($new_clones as $prefix) {
$this->runAfterConfigRead($prefix);
}
}
/**
* Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events
*
*/
function retrieveCollections()
{
foreach ($this->configData as $prefix => $config) {
// collect replacement templates
if (array_key_exists('ReplacementTemplates', $config) && $config['ReplacementTemplates']) {
$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config['ReplacementTemplates']);
}
// collect rewrite listeners
if (array_key_exists('RewriteListener', $config) && $config['RewriteListener']) {
$rewrite_listeners = $config['RewriteListener'];
if (!is_array($rewrite_listeners)) {
// when one method is used to build and parse url
$rewrite_listeners = Array ($rewrite_listeners, $rewrite_listeners);
}
foreach ($rewrite_listeners as $index => $rewrite_listener) {
if (strpos($rewrite_listener, ':') === false) {
$rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener;
}
}
$rewrite_priority = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false;
$this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listeners, 'priority' => $rewrite_priority);
}
}
}
/**
* Register nessasary classes
* This method should only process the data which is cached!
*
* @param string $prefix
* @access private
*/
function parseConfig($prefix)
{
$this->parseClasses($prefix);
$this->parseScheduledTasks($prefix);
$this->parseHooks($prefix);
$this->parseAggregatedTags($prefix);
}
protected function parseClasses($prefix)
{
$config =& $this->configData[$prefix];
$register_classes = $this->getClasses($prefix);
foreach ($register_classes as $class_info) {
// remember class dependencies
$class_name = $class_info['class'];
$require_classes = isset($class_info['require_classes']) ? $class_info['require_classes'] : Array ();
if ($require_classes) {
$require_classes = (array)$require_classes;
if ( !isset($config['_Dependencies'][$class_name]) ) {
$config['_Dependencies'][$class_name] = Array ();
}
$config['_Dependencies'][$class_name] = array_merge($config['_Dependencies'][$class_name], $require_classes);
}
// register class
$this->Application->registerClass(
$class_name,
$config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'],
$class_info['pseudo']
);
if ( isset($class_info['build_event']) && $class_info['build_event'] ) {
$this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event']));
}
}
}
protected function parseScheduledTasks($prefix)
{
$config =& $this->configData[$prefix];
if ( !isset($config['ScheduledTasks']) || !$config['ScheduledTasks'] ) {
return ;
}
$scheduled_tasks = $config['ScheduledTasks'];
foreach ($scheduled_tasks as $short_name => $scheduled_task_info) {
$event_status = array_key_exists('Status', $scheduled_task_info) ? $scheduled_task_info['Status'] : STATUS_ACTIVE;
$this->Application->delayUnitProcessing('registerScheduledTask', Array ( $short_name, $config['Prefix'] . ':' . $scheduled_task_info['EventName'], $scheduled_task_info['RunInterval'], $event_status ));
}
}
protected function parseHooks($prefix)
{
$config =& $this->configData[$prefix];
if ( !isset($config['Hooks']) || !$config['Hooks'] ) {
return ;
}
$hooks = $config['Hooks'];
foreach ($hooks as $hook) {
if ( isset($config['ParentPrefix']) && ($hook['HookToPrefix'] == $config['ParentPrefix']) ) {
trigger_error('Depricated Hook Usage [prefix: <strong>' . $config['Prefix'] . '</strong>; do_prefix: <strong>' . $hook['DoPrefix'] . '</strong>] use <strong>#PARENT#</strong> as <strong>HookToPrefix</strong> value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
}
if ($hook['HookToPrefix'] == '') {
// new: set hooktoprefix to current prefix if not set
$hook['HookToPrefix'] = $config['Prefix'];
}
if ( isset($config['ParentPrefix']) ) {
// new: allow to set hook to parent prefix what ever it is
if ($hook['HookToPrefix'] == '#PARENT#') {
$hook['HookToPrefix'] = $config['ParentPrefix'];
}
if ($hook['DoPrefix'] == '#PARENT#') {
$hook['DoPrefix'] = $config['ParentPrefix'];
}
}
elseif ($hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#') {
// we need parent prefix but it's not set !
continue;
}
$hook_events = (array)$hook['HookToEvent'];
$do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix'];
foreach ($hook_events as $hook_event) {
$hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event;
$do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'];
$this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional']));
}
}
}
protected function parseAggregatedTags($prefix)
{
$config =& $this->configData[$prefix];
$aggregated_tags = isset($config['AggregateTags']) ? $config['AggregateTags'] : Array ();
foreach ($aggregated_tags as $aggregate_tag) {
if ( isset($config['ParentPrefix']) ) {
if ($aggregate_tag['AggregateTo'] == $config['ParentPrefix']) {
trigger_error('Depricated Aggregate Tag Usage [prefix: <b>'.$config['Prefix'].'</b>; AggregateTo: <b>'.$aggregate_tag['AggregateTo'].'</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
}
if ($aggregate_tag['AggregateTo'] == '#PARENT#') {
$aggregate_tag['AggregateTo'] = $config['ParentPrefix'];
}
}
$aggregate_tag['LocalPrefix'] = $config['Prefix'];
$this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag));
}
}
function ValidateConfig($prefix)
{
global $debugger;
$config =& $this->configData[$prefix];
$tablename = $config['TableName'];
$float_types = Array ('float', 'double', 'numeric');
$table_found = $this->Conn->Query('SHOW TABLES LIKE "'.$tablename.'"');
if (!$table_found) {
// config present, but table missing, strange
kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$tablename</strong> missing, but prefix <b>".$config['Prefix']."</b> requires it!");
$debugger->WarningCount++;
return ;
}
$res = $this->Conn->Query('DESCRIBE '.$tablename);
$config_link = $debugger->getFileLink(FULL_PATH.$this->prefixFiles[$config['Prefix']], 1, $config['Prefix']);
$error_messages = Array (
'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config',
'default_missing' => 'Default value for field <strong>%s</strong> not set in config',
'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required',
'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>',
'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>',
'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)',
'date_column_not_null_error' => 'Field <strong>%s</strong> must be NULL in config and database, since it contains date',
'user_column_default_error' => 'Field <strong>%s</strong> must be have NULL as default value, since it holds user id',
'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
'virtual_type_missing' => '<strong>Type definition</strong> for virtual field <strong>%s</strong> missing in config',
'virtual_default_missing' => 'Default value for virtual field <strong>%s</strong> not set in config',
'virtual_not_null_error' => 'Virtual field <strong>%s</strong> cannot be not null, since it doesn\'t exist in database',
'invalid_calculated_field' => 'Calculated field <strong>%s</strong> is missing corresponding virtual field',
);
$config_errors = Array ();
$tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix
// validate unit config field declaration in relation to database table structure
foreach ($res as $field) {
$f_name = $field['Field'];
if (getArrayValue($config, 'Fields')) {
if (preg_match('/l[\d]+_[\w]/', $f_name)) {
// skip multilingual fields
continue;
}
if (!array_key_exists ($f_name, $config['Fields'])) {
$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
}
else {
$db_default = $field['Default'];
if (is_numeric($db_default)) {
$db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default;
}
$default_missing = false;
$options = $config['Fields'][$f_name];
$not_null = isset($options['not_null']) && $options['not_null'];
$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
if (!array_key_exists('default', $options)) {
$config_errors[] = sprintf($error_messages['default_missing'], $f_name);
$default_missing = true;
}
if ($field['Null'] != 'YES') {
// field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "")
if ( $f_name != $config['IDField'] && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
}
if ($not_null && !isset($options['default']) ) {
$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
}
}
elseif ($not_null) {
$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
}
if (($formatter == 'kDateFormatter') && $not_null) {
$config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name);
}
// columns, holding userid should have NULL as default value
if (array_key_exists('type', $options) && !$default_missing) {
// both type and default value set
if (preg_match('/ById$/', $f_name) && $options['default'] !== null) {
$config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name);
}
}
if (!array_key_exists('type', $options)) {
$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
}
if (!$default_missing && ($field['Type'] != 'text')) {
if ( is_null($db_default) && $not_null ) {
$db_default = $options['type'] == 'string' ? '' : 0;
}
if ($f_name == $config['IDField'] && $options['type'] != 'string' && $options['default'] !== 0) {
$config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
}
else if (((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types)) {
$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($db_default));
}
}
}
}
}
// validate virtual fields
if ( array_key_exists('VirtualFields', $config) ) {
foreach ($config['VirtualFields'] as $f_name => $options) {
if (!array_key_exists('type', $options)) {
$config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name);
}
if (array_key_exists('not_null', $options)) {
$config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name);
}
if (!array_key_exists('default', $options)) {
$config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name);
}
}
}
// validate calculated fields
if ( array_key_exists('CalculatedFields', $config) ) {
foreach ($config['CalculatedFields'] as $special => $calculated_fields) {
foreach ($calculated_fields as $calculated_field => $calculated_field_expr) {
if ( !isset($config['VirtualFields'][$calculated_field]) ) {
$config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field);
}
}
}
$config_errors = array_unique($config_errors);
}
if ($config_errors) {
$error_prefix = '<strong class="debug_error">Config Error'.(count($config_errors) > 1 ? 's' : '').': </strong> for prefix <strong>'.$config_link.'</strong> ('.$tablename.') in unit config:<br />';
$config_errors = $error_prefix.'&nbsp;&nbsp;&nbsp;'.implode('<br />&nbsp;&nbsp;&nbsp;', $config_errors);
kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
$debugger->appendHTML($config_errors);
$debugger->WarningCount++;
}
}
function varDump($value)
{
return '<strong>'.var_export($value, true).'</strong> of '.gettype($value);
}
protected function ProcessDependencies($prefix)
{
$config =& $this->configData[$prefix];
$dependencies = getArrayValue($config, '_Dependencies');
/* @var $dependencies Array */
if ( !$dependencies ) {
return ;
}
foreach ($dependencies as $real_class => $requires) {
foreach ($requires as $class) {
$this->Application->registerDependency($real_class, $class);
}
}
unset($config['_Dependencies']);
}
function postProcessConfig($prefix, $config_key, $dst_prefix_var)
{
$main_config =& $this->configData[$prefix];
$sub_configs = isset($main_config[$config_key]) && $main_config[$config_key] ? $main_config[$config_key] : Array ();
if ( !$sub_configs ) {
return Array ();
}
unset($main_config[$config_key]);
$processed = array();
foreach ($sub_configs as $sub_prefix => $sub_config) {
if ($config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix])) {
$this->loadConfig($sub_prefix);
}
$sub_config['Prefix'] = $sub_prefix;
$this->configData[$sub_prefix] = kUtil::array_merge_recursive($this->configData[$$dst_prefix_var], $sub_config);
// when merging empty array to non-empty results non-empty array, but empty is required
foreach ($sub_config as $sub_key => $sub_value) {
if (!$sub_value) {
unset($this->configData[$sub_prefix][$sub_key]);
}
}
if ($config_key == 'Clones') {
$this->prefixFiles[$sub_prefix] = $this->prefixFiles[$prefix];
}
$this->postProcessConfig($sub_prefix, $config_key, $dst_prefix_var);
if ($config_key == 'AggregateConfigs') {
$processed = array_merge($this->postProcessConfig($sub_prefix, 'Clones', 'prefix'), $processed);
}
elseif ($this->ProcessAllConfigs) {
$this->parseConfig($sub_prefix);
}
array_push($processed, $sub_prefix);
}
if (!$prefix) {
// configs, that used only for cloning & not used ifself
unset($this->configData[$prefix]);
}
return array_unique($processed);
}
function PreloadConfigFile($filename)
{
$config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename);
if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES) {
if ( in_array($filename, get_included_files()) ) {
return '';
}
global $debugger;
if ($config_found) {
$file = FULL_PATH . $filename;
$file_crc = crc32($file);
$debugger->ProfileStart('inc_' . $file_crc, $file);
include_once($file);
$debugger->ProfileFinish('inc_' . $file_crc);
$debugger->profilerAddTotal('includes', 'inc_' . $file_crc);
}
}
elseif ($config_found) {
include_once(FULL_PATH . $filename);
}
if ($config_found) {
if (isset($config) && $config) {
// config file is included for 1st time -> save it's content for future processing
$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
preg_match($this->_moduleFolderRegExp, $filename, $rets);
$config['ModuleFolder'] = str_replace(DIRECTORY_SEPARATOR, '/', $rets[1]);
$config['BasePath'] = dirname(FULL_PATH . $filename);
if (array_key_exists('AdminTemplatePath', $config)) {
// append template base folder for admin templates path of this prefix
$module_templates = $rets[1] == 'core' ? '' : substr($rets[1], 8) . '/';
$config['AdminTemplatePath'] = $module_templates . $config['AdminTemplatePath'];
}
if (array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename)) {
trigger_error(
'Single unit config prefix "<strong>' . $prefix . '</strong>" ' .
'is used in multiple unit config files: ' .
'"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"',
E_USER_WARNING
);
}
$this->configData[$prefix] = $config;
$this->prefixFiles[$prefix] = $filename;
return $prefix;
}
else {
$prefix = array_search($filename, $this->prefixFiles);
if ( $prefix ) {
// attempt is made to include config file twice or more, but include_once prevents that,
// but file exists on hdd, then it is already saved to all required arrays, just return it's prefix
return $prefix;
}
}
}
return 'dummy';
}
function loadConfig($prefix)
{
if ( !isset($this->prefixFiles[$prefix]) ) {
throw new Exception('Configuration file for prefix <strong>' . $prefix . '</strong> is unknown');
return ;
}
$file = $this->prefixFiles[$prefix];
$prefix = $this->PreloadConfigFile($file);
if ($this->FinalStage) {
// run prefix OnAfterConfigRead so all
// hooks to it can define their clonses
$this->runAfterConfigRead($prefix);
}
$clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
$clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones);
if ($this->FinalStage) {
$clones = array_unique($clones);
foreach ($clones as $a_prefix) {
$this->runAfterConfigRead($a_prefix);
}
}
}
function runAfterConfigRead($prefix)
{
if (in_array($prefix, $this->AfterConfigProcessed)) {
return ;
}
$this->Application->HandleEvent( new kEvent($prefix . ':OnAfterConfigRead') );
if (!(defined('IS_INSTALL') && IS_INSTALL)) {
// allow to call OnAfterConfigRead multiple times during install
array_push($this->AfterConfigProcessed, $prefix);
}
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $name
* @param mixed $default
* @return string
* @access public
*/
function getUnitOption($prefix, $name, $default = false)
{
if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) {
if (!isset($this->configData[$rets[1]])) {
$this->loadConfig($rets[1]);
}
$ret = isset($this->configData[$rets[1]][$name][$rets[2]]) ? $this->configData[$rets[1]][$name][$rets[2]] : false;
// $ret = getArrayValue($this->configData, $rets[1], $name, $rets[2]);
}
else {
if (!isset($this->configData[$prefix])) {
$this->loadConfig($prefix);
}
$ret = isset($this->configData[$prefix][$name]) ? $this->configData[$prefix][$name] : false;
// $ret = getArrayValue($this->configData, $prefix, $name);
}
return $ret === false ? $default : $ret;
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
*/
function getUnitOptions($prefix)
{
if (!isset($this->configData[$prefix])) {
$this->loadConfig($prefix);
}
return $this->configData[$prefix];
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $name
* @param string $value
* @access public
*/
function setUnitOption($prefix, $name, $value)
{
if ( preg_match('/(.*)\.(.*)/', $prefix, $rets) ) {
if ( !isset($this->configData[$rets[1]]) ) {
$this->loadConfig($rets[1]);
}
$this->configData[$rets[1]][$name][$rets[2]] = $value;
}
else {
if ( !isset($this->configData[$prefix]) ) {
$this->loadConfig($prefix);
}
$this->configData[$prefix][$name] = $value;
}
}
protected function getClasses($prefix)
{
$config =& $this->configData[$prefix];
$class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass');
$register_classes = isset($config['RegisterClasses']) ? $config['RegisterClasses'] : Array ();
foreach ($class_params as $param_name) {
if ( !isset($config[$param_name]) ) {
continue;
}
$config[$param_name]['pseudo'] = $this->getPseudoByOptionName($param_name, $prefix);
$register_classes[] = $config[$param_name];
}
return $register_classes;
}
protected function getPseudoByOptionName($option_name, $prefix)
{
$pseudo_class_map = Array (
'ItemClass' => '%s',
'ListClass' => '%s_List',
'EventHandlerClass' => '%s_EventHandler',
'TagProcessorClass' => '%s_TagProcessor'
);
return sprintf($pseudo_class_map[$option_name], $prefix);
}
/**
* Get's config file name based
* on folder name supplied
*
* @param string $folderPath
* @return string
* @access private
*/
function getConfigName($folderPath)
{
return $folderPath . DIRECTORY_SEPARATOR . basename($folderPath) . '_config.php';
}
/**
* Checks if config file is allowed for includion (if module of config is installed)
*
* @param string $config_path relative path from in-portal directory
*/
function configAllowed($config_path)
{
static $module_paths = null;
if (defined('IS_INSTALL') && IS_INSTALL) {
// at installation start no modules in db and kernel configs could not be read
return true;
}
if (preg_match('#^' . $this->_directorySeparator . 'core#', $config_path)) {
// always allow to include configs from "core" module's folder
return true;
}
if (!$this->Application->ModuleInfo) {
return false;
}
if (!isset($module_paths)) {
$module_paths = Array ();
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/'));
}
$module_paths = array_unique($module_paths);
}
preg_match($this->_moduleFolderRegExp, $config_path, $rets);
// config file path starts with module folder path
return in_array($rets[1], $module_paths);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
function prefixRegistred($prefix)
{
return isset($this->prefixFiles[$prefix]) ? true : false;
}
/**
* Returns config file for given prefix
*
* @param string $prefix
* @return string
*/
function getPrefixFile($prefix)
{
return array_key_exists($prefix, $this->prefixFiles) ? $this->prefixFiles[$prefix] : false;
}
function iterateConfigs($callback_function, $params)
{
$this->includeConfigFiles(MODULES_PATH); //make sure to re-read all configs
$this->AfterConfigRead();
foreach ($this->configData as $prefix => $config_data) {
$callback_function[0]->$callback_function[1]($prefix, $config_data, $params);
}
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/languages/phrases_cache.php
===================================================================
--- branches/5.2.x/core/kernel/languages/phrases_cache.php (revision 15072)
+++ branches/5.2.x/core/kernel/languages/phrases_cache.php (revision 15073)
@@ -1,354 +1,363 @@
<?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 PhrasesCache extends kBase {
var $Phrases = Array();
var $Ids = Array();
var $OriginalIds = Array(); //for comparing cache
var $LanguageId = null;
/**
* Administrator's language, when visiting site (from frame)
*
* @var int
*/
var $AdminLanguageId = null;
var $fromTag = false;
/**
* Allows to edit existing phrases
*
* @var bool
*/
var $_editExisting = false;
/**
* Allows to edit missing phrases
*
* @var bool
*/
var $_editMissing = false;
/**
* Template, used for phrase adding/editing
*
* @var string
*/
var $_phraseEditTemplate = 'languages/phrase_edit';
/**
* Use simplified form for phrase editing
*
* @var bool
*/
var $_simpleEditingMode = false;
/**
* HTML tag used to translate phrases
*
* @var string
*/
var $_translateHtmlTag = 'a';
/**
* Phrases, that are in cache, but are not in database
*
* @var Array
*/
var $_missingPhrases = Array ();
/**
* Mask for editing link
*
* @var string
*/
var $_editLinkMask = '';
/**
* Escape phrase name, before placing it in javascript translation link
* @var bool
*/
var $_escapePhraseName = true;
/**
* Sets phrase editing mode, that corresponds current editing mode
*
*/
function setPhraseEditing()
{
if (!$this->Application->isAdmin && (EDITING_MODE == EDITING_MODE_CONTENT)) {
// front-end viewed in content mode
$this->_editExisting = true;
$this->_editMissing = true;
$this->_simpleEditingMode = !$this->Application->isDebugMode();
$this->_translateHtmlTag = 'span';
}
$this->_editLinkMask = 'javascript:translate_phrase(\'#LABEL#\', \'' . $this->_phraseEditTemplate . '\', {event: \'OnPreparePhrase\', simple_mode: ' . ($this->_simpleEditingMode ? 'true' : 'false') . '});';
if (defined('DEBUG_MODE') && DEBUG_MODE && !$this->Application->GetVar('admin')) {
// admin and front-end while not viewed using content mode (via admin)
$this->_editMissing = defined('DBG_PHRASES') && DBG_PHRASES;
if (!$this->Application->isAdmin) {
$this->_phraseEditTemplate = 'phrases_edit';
$url_params = Array (
'm_opener' => 'd',
'phrases_label' => '#LABEL#',
'phrases_event' => 'OnPreparePhrase',
'next_template' => urlencode('external:' . $_SERVER['REQUEST_URI']),
'pass' => 'm,phrases'
);
$this->_escapePhraseName = false;
$this->_editLinkMask = $this->Application->HREF($this->_phraseEditTemplate, '', $url_params);
}
}
}
/**
* Loads phrases from current language
* Method is called manually (not from kFactory class) too
*
* @param string $prefix
* @param string $special
* @param int $language_id
* @param Array $phrase_ids
*/
public function Init($prefix, $special = '', $language_id = null, $phrase_ids = null)
{
parent::Init($prefix, $special);
if (kUtil::constOn('IS_INSTALL')) {
$this->LanguageId = 1;
}
else {
if ( !isset($language_id) ) {
if ($this->Application->isAdmin) {
$language_id = $this->Application->Session->GetField('Language');
$this->AdminLanguageId = $language_id; // same languages, when used from Admin Console
}
else {
$language_id = $this->Application->GetVar('m_lang');
}
}
$this->LanguageId = $language_id;
if (!$this->Application->isAdmin && $this->Application->GetVar('admin')) {
$admin_session =& $this->Application->recallObject('Session.admin');
/* @var $admin_session Session */
$this->AdminLanguageId = $admin_session->GetField('Language');
}
}
$this->LoadPhrases($phrase_ids);
}
function LoadPhrases($ids)
{
if ( !is_array($ids) || !implode('', $ids) ) {
return ;
}
$sql = 'SELECT l' . $this->LanguageId . '_Translation AS Translation, l' . $this->LanguageId . '_HintTranslation AS HintTranslation, l' . $this->LanguageId . '_ColumnTranslation AS ColumnTranslation, PhraseKey
FROM ' . TABLE_PREFIX . 'LanguageLabels
WHERE PhraseId IN (' . implode(',', $ids) . ') AND l' . $this->LanguageId . '_Translation IS NOT NULL';
$this->Phrases = $this->Conn->Query($sql, 'PhraseKey');
$this->Ids = $this->OriginalIds = $ids;
}
function AddCachedPhrase($label, $value, $allow_editing = true)
{
// uppercase phrase name for cases, when this method is called outside this class
$cache_key = ($allow_editing ? '' : 'NE:') . mb_strtoupper($label);
$this->Phrases[$cache_key] = Array ('Translation' => $value, 'HintTranslation' => $value, 'ColumnTranslation' => $value);
}
function NeedsCacheUpdate()
{
return is_array($this->Ids) && count($this->Ids) > 0 && $this->Ids != $this->OriginalIds;
}
- function GetPhrase($label, $allow_editing = true, $use_admin = false)
+ /**
+ * 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 GetPhrase($label, $allow_editing = true, $use_admin = false)
{
if ( !isset($this->LanguageId) ) {
//actually possible when custom field contains references to language labels and its being rebuilt in OnAfterConfigRead
//which is triggered by Sections rebuild, which in turn read all the configs and all of that happens BEFORE seeting the language...
return 'impossible case';
}
// cut exclamation marks - depricated form of passing phrase name from templates
$label = preg_replace('/^!(.*)!$/', '\\1', $label);
if ( strlen($label) == 0 ) {
return '';
}
$original_label = $this->_escapePhraseName ? addslashes($label) : $label;
$label = mb_strtoupper($label);
if ( substr($label, 0, 5) == 'HINT:' || substr($label, 0, 7) == 'COLUMN:' ) {
// don't just check for ":" since phrases could have ":" in their names (e.g. advanced permission labels)
list ($field_prefix, $label) = explode(':', $label, 2);
$translation_field = mb_convert_case($field_prefix, MB_CASE_TITLE) . 'Translation';
}
else {
$translation_field = 'Translation';
}
$cache_key = ($allow_editing ? '' : 'NE:') . $label;
if ( isset($this->Phrases[$cache_key]) ) {
$translated_label = $this->Phrases[$cache_key][$translation_field];
if ($this->_editExisting && $allow_editing && !array_key_exists($label, $this->_missingPhrases)) {
// option to change translation for Labels
$original_label = explode(':', $original_label, 2);
$edit_url = 'javascript:translate_phrase(\'' . end($original_label) . '\', \'' . $this->_phraseEditTemplate . '\', {event: \'OnPreparePhrase\', simple_mode: ' . ($this->_simpleEditingMode ? 'true' : 'false') . '});';
$translated_label = '<' . $this->_translateHtmlTag . ' href="' . $edit_url . '" name="cms-translate-phrase" title="Edit translation">' . $translated_label . '</' . $this->_translateHtmlTag . '>';
if ($this->fromTag) {
$translated_label = $this->escapeTagReserved($translated_label);
}
}
return $translated_label;
}
$this->LoadPhraseByLabel($label, $original_label, $allow_editing, $use_admin);
return $this->GetPhrase($original_label, $allow_editing);
}
function LoadPhraseByLabel($label, $original_label, $allow_editing = true, $use_admin = false)
{
if ( !$allow_editing && !$use_admin && !isset($this->_missingPhrases[$label]) && isset($this->Phrases[$label]) ) {
// label is aready translated, but it's version without on the fly translation code is requested
$this->Phrases['NE:' . $label] = $this->Phrases[$label];
return true;
}
$language_id = $use_admin ? $this->AdminLanguageId : $this->LanguageId;
$sql = 'SELECT PhraseId, l' . $language_id . '_Translation AS Translation, l' . $language_id . '_HintTranslation AS HintTranslation, l' . $language_id . '_ColumnTranslation AS ColumnTranslation
FROM ' . TABLE_PREFIX . 'LanguageLabels
WHERE (PhraseKey = ' . $this->Conn->qstr($label) . ') AND (l' . $language_id . '_Translation IS NOT NULL)';
$res = $this->Conn->GetRow($sql);
if ($res === false || count($res) == 0) {
$translation = '!' . $label . '!';
if ($this->_editMissing && $allow_editing) {
$original_label = explode(':', $original_label, 2);
$edit_url = str_replace('#LABEL#', end($original_label), $this->_editLinkMask);
$translation = '<' . $this->_translateHtmlTag . ' href="' . $edit_url . '" name="cms-translate-phrase" title="Translate">!' . $label . '!</' . $this->_translateHtmlTag . '>';
if ($this->fromTag) {
$translation = $this->escapeTagReserved($translation);
}
$this->_missingPhrases[$label] = true; // add as key for faster accessing
}
// add it as already cached, as long as we dont need to cache not found phrase
$this->AddCachedPhrase($label, $translation, $allow_editing);
return false;
}
$cache_key = ($allow_editing ? '' : 'NE:') . $label;
$this->Phrases[$cache_key] = $res;
array_push($this->Ids, $res['PhraseId']);
$this->Ids = array_unique($this->Ids); // just to make sure
return true;
}
/**
* Sort params by name and then by length
*
* @param string $a
* @param string $b
* @return int
* @access private
*/
function CmpParams($a, $b)
{
$a_len = mb_strlen($a);
$b_len = mb_strlen($b);
if ($a_len == $b_len) return 0;
return $a_len > $b_len ? -1 : 1;
}
/**
* Replace language tags in exclamation marks found in text
*
* @param string $text
* @param bool|null $force_escaping force escaping, not escaping of resulting string
* @return mixed
* @access public
*/
public function ReplaceLanguageTags($text, $force_escaping = null)
{
$this->fromTag = true;
if( isset($force_escaping) ) {
$this->fromTag = $force_escaping;
}
preg_match_all("(!(la|lu)[^!]+!)", $text, $res, PREG_PATTERN_ORDER);
$language_tags = $res[0];
uasort($language_tags, Array(&$this, 'CmpParams'));
$i = 0;
$values = Array();
foreach ($language_tags as $label) {
array_push($values, $this->GetPhrase($label) );
//array_push($values, $this->Application->Phrase($label) );
$language_tags[$i] = '/' . $language_tags[$i] . '/';
$i++;
}
$this->fromTag = false;
return preg_replace($language_tags, $values, $text);
}
/**
* Escape chars in phrase translation, that could harm parser to process tag
*
* @param string $text
* @return string
* @access private
*/
function escapeTagReserved($text)
{
$reserved = Array('"',"'"); // =
$replacement = Array('\"',"\'"); // \=
return str_replace($reserved,$replacement,$text);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/languages/languages_event_handler.php
===================================================================
--- branches/5.2.x/core/units/languages/languages_event_handler.php (revision 15072)
+++ branches/5.2.x/core/units/languages/languages_event_handler.php (revision 15073)
@@ -1,675 +1,675 @@
<?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 LanguagesEventHandler extends kDBEventHandler
{
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnChangeLanguage' => Array ('self' => true),
'OnSetPrimary' => Array ('self' => 'advanced:set_primary|add|edit'),
'OnImportLanguage' => Array ('self' => 'advanced:import'),
'OnExportLanguage' => Array ('self' => 'advanced:export'),
'OnExportProgress' => Array ('self' => 'advanced:export'),
'OnReflectMultiLingualFields' => Array ('self' => 'view'),
'OnSynchronizeLanguages' => Array ('self' => 'edit'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent &$event)
{
if ( $event->Name == 'OnItemBuild' ) {
// check permission without using $event->getSection(),
// so first cache rebuild won't lead to "ldefault_Name" field being used
return true;
}
return parent::CheckPermission($event);
}
/**
* Allows to get primary language object
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent &$event)
{
if ( $event->Special == 'primary' ) {
return $this->Application->GetDefaultLanguageId();
}
return parent::getPassedID($event);
}
/**
* [HOOK] Updates table structure on new language adding/removing language
*
* @param kEvent $event
*/
function OnReflectMultiLingualFields(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = kEvent::erSTOP;
}
if (is_object($event->MasterEvent)) {
if ($event->MasterEvent->status != kEvent::erSUCCESS) {
// only rebuild when all fields are validated
return ;
}
if (($event->MasterEvent->Name == 'OnSave') && !$this->Application->GetVar('new_language')) {
// only rebuild during new language adding
return ;
}
}
$ml_helper =& $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$this->Application->UnitConfigReader->ReReadConfigs();
foreach ($this->Application->UnitConfigReader->configData as $prefix => $config_data) {
$ml_helper->createFields($prefix);
}
$event->SetRedirectParam('action_completed', 1);
}
/**
* Allows to set selected language as primary
*
* @param kEvent $event
*/
function OnSetPrimary(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$this->StoreSelectedIDs($event);
$ids = $this->getSelectedIDs($event);
if ($ids) {
$id = array_shift($ids);
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object LanguagesItem */
$object->Load($id);
$object->copyMissingData( $object->setPrimary() );
}
}
/**
* [HOOK] Reset primary status of other languages if we are saving primary language
*
* @param kEvent $event
*/
function OnUpdatePrimary(&$event)
{
if ($event->MasterEvent->status != kEvent::erSUCCESS) {
return ;
}
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object LanguagesItem */
$object->SwitchToLive();
// set primary for each languages, that have this checkbox checked
$ids = explode(',', $event->MasterEvent->getEventParam('ids'));
foreach ($ids as $id) {
$object->Load($id);
if ($object->GetDBField('PrimaryLang')) {
$object->copyMissingData( $object->setPrimary(true, false) );
}
if ($object->GetDBField('AdminInterfaceLang')) {
$object->setPrimary(true, true);
}
}
// if no primary language left, then set primary last language (not to load again) from edited list
$sql = 'SELECT '.$object->IDField.'
FROM '.$object->TableName.'
WHERE PrimaryLang = 1';
$primary_language = $this->Conn->GetOne($sql);
if (!$primary_language) {
$object->setPrimary(false, false); // set primary language
}
$sql = 'SELECT '.$object->IDField.'
FROM '.$object->TableName.'
WHERE AdminInterfaceLang = 1';
$primary_language = $this->Conn->GetOne($sql);
if (!$primary_language) {
$object->setPrimary(false, true); // set admin interface language
}
}
/**
* Prefills options with dynamic values
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent &$event)
{
parent::OnAfterConfigRead($event);
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
// set dynamic hints for options in date format fields
$options = $fields['InputDateFormat']['options'];
if ($options) {
foreach ($options as $i => $v) {
$options[$i] = $v . ' (' . adodb_date($i) . ')';
}
$fields['InputDateFormat']['options'] = $options;
}
$options = $fields['InputTimeFormat']['options'];
if ($options) {
foreach ($options as $i => $v) {
$options[$i] = $v . ' (' . adodb_date($i) . ')';
}
$fields['InputTimeFormat']['options'] = $options;
}
$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent &$event)
{
parent::OnBeforeItemUpdate($event);
-
+
$object =& $event->getObject();
/* @var $object kDBItem */
$status_fields = $this->Application->getUnitOption($event->Prefix, 'StatusField');
$status_field = array_shift($status_fields);
if ( $object->GetDBField('PrimaryLang') == 1 && $object->GetDBField($status_field) == 0 ) {
$object->SetDBField($status_field, 1);
}
}
/**
* Shows only enabled languages on front
*
* @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 ( in_array($event->Special, Array ('enabled', 'selected', 'available')) ) {
$object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE);
}
// site domain language picker
if ( $event->Special == 'selected' || $event->Special == 'available' ) {
$edit_picker_helper =& $this->Application->recallObject('EditPickerHelper');
/* @var $edit_picker_helper EditPickerHelper */
$edit_picker_helper->applyFilter($event, 'Languages');
}
// apply domain-based language filtering
$languages = $this->Application->siteDomainField('Languages');
if ( strlen($languages) ) {
$languages = explode('|', substr($languages, 1, -1));
$object->addFilter('domain_filter', '%1$s.LanguageId IN (' . implode(',', $languages) . ')');
}
}
/**
* Copy labels from another language
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent &$event)
{
parent::OnAfterItemCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$src_language = $object->GetDBField('CopyFromLanguage');
if ( $object->GetDBField('CopyLabels') && $src_language ) {
$dst_language = $object->GetID();
// 1. schedule data copy after OnSave event is executed
$var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid');
$pending_actions = $this->Application->RecallVar($var_name, Array ());
if ( $pending_actions ) {
$pending_actions = unserialize($pending_actions);
}
$pending_actions[$src_language] = $dst_language;
$this->Application->StoreVar($var_name, serialize($pending_actions));
$object->SetDBField('CopyLabels', 0);
}
}
/**
* Saves language from temp table to live
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSave(kEvent &$event)
{
parent::OnSave($event);
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid');
$pending_actions = $this->Application->RecallVar($var_name, Array ());
if ( $pending_actions ) {
$pending_actions = unserialize($pending_actions);
}
// create multilingual columns for phrases & email events table first (actual for 6+ language)
$ml_helper =& $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$ml_helper->createFields('phrases');
$ml_helper->createFields('emailevents');
foreach ($pending_actions as $src_language => $dst_language) {
// phrases import
$sql = 'UPDATE ' . $this->Application->getUnitOption('phrases', 'TableName') . '
SET l' . $dst_language . '_Translation = l' . $src_language . '_Translation';
$this->Conn->Query($sql);
// events import
$sql = 'UPDATE ' . $this->Application->getUnitOption('emailevents', 'TableName') . '
SET
l' . $dst_language . '_Subject = l' . $src_language . '_Subject,
l' . $dst_language . '_Body = l' . $src_language . '_Body';
$this->Conn->Query($sql);
}
$this->Application->RemoveVar($var_name);
$event->CallSubEvent('OnReflectMultiLingualFields');
$event->CallSubEvent('OnUpdatePrimary');
}
/**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent &$event)
{
parent::OnPreCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('CopyLabels', 1);
$sql = 'SELECT ' . $object->IDField . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
WHERE PrimaryLang = 1';
$primary_lang_id = $this->Conn->GetOne($sql);
$object->SetDBField('CopyFromLanguage', $primary_lang_id);
$object->SetDBField('SynchronizationModes', Language::SYNCHRONIZE_DEFAULT);
}
/**
* Sets new language mark
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteFromLive(kEvent &$event)
{
parent::OnBeforeDeleteFromLive($event);
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$sql = 'SELECT ' . $id_field . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
WHERE ' . $id_field . ' = ' . $event->getEventParam('id');
$id = $this->Conn->GetOne($sql);
if ( !$id ) {
$this->Application->SetVar('new_language', 1);
}
}
function OnChangeLanguage(&$event)
{
$language_id = $this->Application->GetVar('language');
$language_field = $this->Application->isAdmin ? 'AdminLanguage' : 'FrontLanguage';
$this->Application->SetVar('m_lang', $language_id);
// set new language for this session
$this->Application->Session->SetField('Language', $language_id);
// remember last user language
if ($this->Application->RecallVar('user_id') == USER_ROOT) {
$this->Application->StorePersistentVar($language_field, $language_id);
}
else {
$object =& $this->Application->recallObject('u.current');
/* @var $object kDBItem */
$object->SetDBField($language_field, $language_id);
$object->Update();
}
// without this language change in admin will cause erase of last remembered tree section
$this->Application->SetVar('skip_last_template', 1);
}
/**
* Parse language XML file into temp tables and redirect to progress bar screen
*
* @param kEvent $event
*/
function OnImportLanguage(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$items_info = $this->Application->GetVar('phrases_import');
if ($items_info) {
list ($id, $field_values) = each($items_info);
$object =& $this->Application->recallObject('phrases.import', 'phrases', Array('skip_autoload' => true));
/* @var $object kDBItem */
$object->setID($id);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if (!$object->Validate()) {
$event->status = kEvent::erFAIL;
return ;
}
$filename = $object->GetField('LangFile', 'full_path');
if (!filesize($filename)) {
$object->SetError('LangFile', 'la_empty_file', 'la_EmptyFile');
$event->status = kEvent::erFAIL;
}
$language_import_helper =& $this->Application->recallObject('LanguageImportHelper');
/* @var $language_import_helper LanguageImportHelper */
$language_import_helper->performImport(
$filename,
$object->GetDBField('PhraseType'),
$object->GetDBField('Module'),
$object->GetDBField('ImportOverwrite') ? LANG_OVERWRITE_EXISTING : LANG_SKIP_EXISTING
);
// delete uploaded language pack after import is finished
unlink($filename);
$event->SetRedirectParam('opener', 'u');
}
}
/**
* Stores ids of selected languages and redirects to export language step 1
*
* @param kEvent $event
*/
function OnExportLanguage(&$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', implode(',', $this->getSelectedIDs($event)) );
$event->setRedirectParams( Array('phrases.export_event' => 'OnNew', 'pass' => 'all,phrases.export') );
}
/**
* Saves selected languages to xml file passed
*
* @param kEvent $event
*/
function OnExportProgress(&$event)
{
$items_info = $this->Application->GetVar('phrases_export');
if ($items_info) {
list($id, $field_values) = each($items_info);
$object =& $this->Application->recallObject('phrases.export', null, Array('skip_autoload' => true));
/* @var $object kDBItem */
$object->setID($id);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if (!$object->Validate()) {
$event->status = kEvent::erFAIL;
return ;
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->CheckFolder(EXPORT_PATH);
if (!is_writable(EXPORT_PATH)) {
$event->status = kEvent::erFAIL;
$object->SetError('LangFile', 'write_error', 'la_ExportFolderNotWritable');
return ;
}
if ( substr($field_values['LangFile'], -5) != '.lang') {
$field_values['LangFile'] .= '.lang';
}
$filename = EXPORT_PATH . '/' . $field_values['LangFile'];
$language_import_helper =& $this->Application->recallObject('LanguageImportHelper');
/* @var $language_import_helper LanguageImportHelper */
if ($object->GetDBField('DoNotEncode')) {
$language_import_helper->setExportEncoding('plain');
}
$language_import_helper->setExportLimits($field_values['ExportPhrases'], $field_values['ExportEmailEvents']);
$lang_ids = explode(',', $this->Application->RecallVar('export_language_ids') );
$language_import_helper->performExport($filename, $field_values['PhraseType'], $lang_ids, $field_values['Module']);
}
$event->redirect = 'regional/languages_export_step2';
$event->SetRedirectParam('export_file', $field_values['LangFile']);
}
/**
* Returns to previous template in opener stack
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnGoBack(kEvent &$event)
{
$event->SetRedirectParam('opener', 'u');
}
function OnScheduleTopFrameReload(&$event)
{
$this->Application->StoreVar('RefreshTopFrame',1);
}
/**
* Do now allow deleting current language
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemDelete(kEvent &$event)
{
parent::OnBeforeItemDelete($event);
$object =& $event->getObject();
/* @var $object kDBItem */
if ( $object->GetDBField('PrimaryLang') || $object->GetDBField('AdminInterfaceLang') || $object->GetID() == $this->Application->GetVar('m_lang') ) {
$event->status = kEvent::erFAIL;
}
}
/**
* Deletes phrases and email events on given language
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent &$event)
{
parent::OnAfterItemDelete($event);
$object =& $event->getObject();
/* @var $object kDBItem */
// clean EmailEvents table
$fields_hash = Array (
'l' . $object->GetID() . '_Subject' => NULL,
'l' . $object->GetID() . '_Body' => NULL,
);
$this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('emailevents', 'TableName'), 1);
// clean Phrases table
$fields_hash = Array (
'l' . $object->GetID() . '_Translation' => NULL,
'l' . $object->GetID() . '_HintTranslation' => NULL,
'l' . $object->GetID() . '_ColumnTranslation' => NULL,
);
$this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('phrases', 'TableName'), 1);
}
/**
* Copy missing phrases across all system languages (starting from primary)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSynchronizeLanguages(&$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$source_languages = $target_languages = Array ();
// get language list with primary language first
$sql = 'SELECT SynchronizationModes, LanguageId
FROM ' . TABLE_PREFIX . 'Languages
WHERE SynchronizationModes <> ""
ORDER BY PrimaryLang DESC';
$languages = $this->Conn->GetCol($sql, 'LanguageId');
foreach ($languages as $language_id => $synchronization_modes) {
$synchronization_modes = explode('|', substr($synchronization_modes, 1, -1));
if ( in_array(Language::SYNCHRONIZE_TO_OTHERS, $synchronization_modes) ) {
$source_languages[] = $language_id;
}
if ( in_array(Language::SYNCHRONIZE_FROM_OTHERS, $synchronization_modes) ) {
$target_languages[] = $language_id;
}
}
foreach ($source_languages as $source_id) {
foreach ($target_languages as $target_id) {
if ( $source_id == $target_id ) {
continue;
}
$sql = 'UPDATE ' . TABLE_PREFIX . 'LanguageLabels
SET l' . $target_id . '_Translation = l' . $source_id . '_Translation
WHERE COALESCE(l' . $target_id . '_Translation, "") = "" AND COALESCE(l' . $source_id . '_Translation, "") <> ""';
$this->Conn->Query($sql);
}
}
}
}
\ No newline at end of file

Event Timeline