Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, May 3, 5:32 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/admin/system_presets/simple/email_events_emailevents.php
===================================================================
--- branches/5.2.x/admin/system_presets/simple/email_events_emailevents.php (revision 15251)
+++ branches/5.2.x/admin/system_presets/simple/email_events_emailevents.php (revision 15252)
@@ -1,64 +1,64 @@
<?php
defined('FULL_PATH') or die('restricted access!');
// section removal
$remove_sections = Array (
// 'in-portal:configemail',
);
// sections shown with debug on
$debug_only_sections = Array (
'in-portal:configemail',
);
// toolbar buttons
$remove_buttons = Array (
// list of all email templates
'email_message_list' => Array (/*'new_item', 'edit', 'delete', 'approve', 'decline', 'frontend_mail', 'view',*/ 'export', /*'dbl-click'*/),
// edit email edit - general tab
// 'email_message_edit' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'),
// edit email edit - settings tab
// 'email_message_edit_settings' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'),
);
// fields to hide
$hidden_fields = Array (
/*'EventId', 'Event', 'ReplacementTags', 'AllowChangingSender', 'CustomSender',
'SenderName', 'SenderAddressType', 'SenderAddress', 'AllowChangingRecipient',
'CustomRecipient', 'Recipients', 'Subject', 'HtmlBody', 'PlainTextBody', 'Headers',
- 'Enabled', 'FrontEndOnly', 'Module', 'Description', 'Type'*/
+ 'Enabled', 'FrontEndOnly', 'Module', 'BindToSystemEvent', 'Description', 'Type'*/
);
// virtual fields to hide
$virtual_hidden_fields = Array (
/*'RecipientType', 'RecipientName', 'RecipientAddressType',
'RecipientAddress', 'Tag', 'Replacement', 'ReplacementTagsXML',*/
);
// fields to make required
$required_fields = Array (
/*'EventId',*/ 'Event', /*'ReplacementTags', 'AllowChangingSender', 'CustomSender',
'SenderName', 'SenderAddressType', 'SenderAddress', 'AllowChangingRecipient',
'CustomRecipient', 'Recipients',*/ 'Subject', /*'HtmlBody', 'PlainTextBody', 'Headers',*/
- /*'Enabled', 'FrontEndOnly',*/ 'Module', /*'Description',*/ 'Type'
+ /*'Enabled', 'FrontEndOnly',*/ 'Module', /*'BindToSystemEvent', 'Description',*/ 'Type'
);
// virtual fields to make required
$virtual_required_fields = Array (
/*'RecipientType', 'RecipientName', 'RecipientAddressType',
'RecipientAddress', 'Tag', 'Replacement', 'ReplacementTagsXML',*/
);
// tabs during editing
$hide_edit_tabs = Array (
'Default' => Array (/*'general',*/ 'settings'),
);
// hide columns in grids
$hide_columns = Array (
// 'Default' => Array ('EventId', 'Description', 'Event', 'Module', 'Type', 'Enabled', 'LastChanged'),
// 'Emails' => Array ('EventId', 'Event', 'Subject', 'Description', 'Type', 'Enabled', 'Module', 'FrontEndOnly', 'LastChanged'),
);
\ No newline at end of file
Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_event_handler.php (revision 15251)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php (revision 15252)
@@ -1,3418 +1,3456 @@
<?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 && !$this->Application->isAdmin ) {
$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);
$vars = $this->Application->UrlManager->prepare404();
foreach ($vars as $var_name => $var_value) {
$this->Application->SetVar($var_name, $var_value);
}
// in case if missing item is recalled first from event (not from 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);
- }
+ $this->_processItemLoadingError($event, $status_checked);
}
}
$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());
}
/**
+ * Processes case, when item wasn't loaded because of lack of permissions
+ *
+ * @param kEvent $event
+ * @param bool $status_checked
+ * @throws kNoPermissionException
+ * @return void
+ * @access protected
+ */
+ protected function _processItemLoadingError($event, $status_checked)
+ {
+ $current_template = $this->Application->GetVar('t');
+ $redirect_template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
+ $error_msg = 'ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>' . ($status_checked ? 'checkItemStatus' : 'CheckPermission') . '</strong>';
+
+ if ( $current_template == $redirect_template ) {
+ // don't perform "no_permission" redirect if already on a "no_permission" template
+ if ( $this->Application->isDebugMode() ) {
+ $this->Application->Debugger->appendTrace();
+ }
+
+ trigger_error($error_msg, E_USER_NOTICE);
+
+ return;
+ }
+
+ if ( MOD_REWRITE ) {
+ $redirect_params = Array (
+ 'm_cat_id' => 0,
+ 'next_template' => urlencode('external:' . $_SERVER['REQUEST_URI']),
+ );
+ }
+ else {
+ $redirect_params = Array (
+ 'next_template' => $current_template,
+ );
+ }
+
+ $exception = new kNoPermissionException($error_msg);
+ $exception->setup($redirect_template, $redirect_params);
+
+ throw $exception;
+ }
+
+ /**
* 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 = $this->Application->ConfigValue('DefaultGridPerPage');
}
$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
*
+ * Also deletes subscriptions to that particual item once it's deleted
+ *
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent $event)
{
+ $object = $event->getObject();
+ /* @var $object kDBItem */
+ $sql = 'SELECT EventId
+ FROM ' . TABLE_PREFIX . 'EmailEvents
+ WHERE BindToSystemEvent REGEXP "' . $this->Conn->escape($event->Prefix) . '[.]{0,1}([^:]*):(.*)"';
+ $email_event_ids = $this->Conn->GetCol($sql);
+
+ if ( $email_event_ids ) {
+ // e-mail events, connected to that unit prefix are found
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailEventSubscribers
+ WHERE ItemId = ' . $object->GetID() . ' AND EmailEventId IN (' . implode(',', $email_event_ids) . ')';
+ $this->Conn->Query($sql);
+ }
}
/**
* 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));
$this->Application->setContentType(kUtil::mimeContentType($path), false);
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;
// $this->Application->setContentType('text/xml');
$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/db/dbitem.php
===================================================================
--- branches/5.2.x/core/kernel/db/dbitem.php (revision 15251)
+++ branches/5.2.x/core/kernel/db/dbitem.php (revision 15252)
@@ -1,1435 +1,1436 @@
<?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!');
/**
* DBItem
*
*/
class kDBItem extends kDBBase {
/**
* Description
*
* @var array Associative array of current item' field values
* @access protected
*/
protected $FieldValues = Array ();
/**
* Unformatted field values, before parse
*
* @var Array
* @access protected
*/
protected $DirtyFieldValues = Array ();
/**
* Holds item values after loading (not affected by submit)
*
* @var Array
* @access protected
*/
protected $OriginalFieldValues = Array ();
/**
* If set to true, Update will skip Validation before running
*
* @var array Associative array of current item' field values
* @access public
*/
public $IgnoreValidation = false;
/**
* Remembers if object was loaded
*
* @var bool
* @access protected
*/
protected $Loaded = false;
/**
* Holds item' primary key value
*
* @var int Value of primary key field for current item
* @access protected
*/
protected $ID;
/**
* This object is used in cloning operations
*
* @var bool
* @access public
*/
public $inCloning = false;
/**
* Validator object reference
*
* @var kValidator
*/
protected $validator = null;
/**
* Creates validator object, only when required
*
*/
public function initValidator()
{
if ( !is_object($this->validator) ) {
$validator_class = $this->Application->getUnitOption($this->Prefix, 'ValidatorClass', 'kValidator');
$this->validator = $this->Application->makeClass($validator_class);
}
$this->validator->setDataSource($this);
}
public function SetDirtyField($field_name, $field_value)
{
$this->DirtyFieldValues[$field_name] = $field_value;
}
public function GetDirtyField($field_name)
{
return $this->DirtyFieldValues[$field_name];
}
public function GetOriginalField($field_name, $formatted = false, $format=null)
{
if (array_key_exists($field_name, $this->OriginalFieldValues)) {
// item was loaded before
$value = $this->OriginalFieldValues[$field_name];
}
else {
// no original fields -> use default field value
$value = $this->Fields[$field_name]['default'];
}
if (!$formatted) {
return $value;
}
$res = $value;
$formatter = $this->GetFieldOption($field_name, 'formatter');
if ( $formatter ) {
$formatter = $this->Application->recallObject($formatter);
/* @var $formatter kFormatter */
$res = $formatter->Format($value, $field_name, $this, $format);
}
return $res;
}
/**
* Sets original field value (useful for custom virtual fields)
*
* @param string $field_name
* @param string $field_value
*/
public function SetOriginalField($field_name, $field_value)
{
$this->OriginalFieldValues[$field_name] = $field_value;
}
/**
* Set's default values for all fields
*
* @access public
*/
public function SetDefaultValues()
{
parent::SetDefaultValues();
if ($this->populateMultiLangFields) {
$this->PopulateMultiLangFields();
}
foreach ($this->Fields as $field => $field_options) {
$default_value = isset($field_options['default']) ? $field_options['default'] : NULL;
$this->SetDBField($field, $default_value);
}
}
/**
* Sets current item field value
* (applies formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetField($name,$value)
{
$options = $this->GetFieldOptions($name);
$parsed = $value;
if ($value == '') {
$parsed = NULL;
}
// kFormatter is always used, to make sure, that numeric value is converted to normal representation
// according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why)
$formatter = $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter');
/* @var $formatter kFormatter */
$parsed = $formatter->Parse($value, $name, $this);
$this->SetDBField($name,$parsed);
}
/**
* Sets current item field value
* (doesn't apply formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetDBField($name,$value)
{
$this->FieldValues[$name] = $value;
}
/**
* Set's field error, if pseudo passed not found then create it with message text supplied.
* Don't overwrite existing pseudo translation.
*
* @param string $field
* @param string $pseudo
* @param string $error_label
* @param Array $error_params
*
* @return bool
* @access public
*/
public function SetError($field, $pseudo, $error_label = null, $error_params = null)
{
$this->initValidator();
return $this->validator->SetError($field, $pseudo, $error_label, $error_params);
}
/**
* Removes error on field
*
* @param string $field
* @access public
*/
public function RemoveError($field)
{
if ( !is_object($this->validator) ) {
return ;
}
$this->validator->RemoveError($field);
}
/**
* Returns error pseudo
*
* @param string $field
* @return string
*/
public function GetErrorPseudo($field)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorPseudo($field);
}
/**
* Return current item' field value by field name
* (doesn't apply formatter)
*
* @param string $name field name to return
* @return mixed
* @access public
*/
public function GetDBField($name)
{
/*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) {
$this->Application->Debugger->appendTrace();
}*/
return $this->FieldValues[$name];
}
public function HasField($name)
{
return array_key_exists($name, $this->FieldValues);
}
public function GetFieldValues()
{
return $this->FieldValues;
}
/**
* Sets item' fields corresponding to elements in passed $hash values.
*
* The function sets current item fields to values passed in $hash, by matching $hash keys with field names
* of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before actually setting the fields
*
* @param Array $hash
* @param Array $skip_fields Optional param, field names in target object not to set, other fields will be set
* @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
* @return void
* @access public
*/
public function SetFieldsFromHash($hash, $skip_fields = Array (), $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
if ( $skip_fields ) {
$set_fields = array_diff($set_fields, $skip_fields);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
// used in formatter which work with multiple fields together
foreach ($set_fields as $field_name) {
$this->SetDirtyField($field_name, $hash[$field_name]);
}
// formats all fields using associated formatters
foreach ($set_fields as $field_name) {
$this->SetField($field_name, $hash[$field_name]);
}
}
/**
* Sets object fields from $hash array
* @param Array $hash
* @param Array|null $skip_fields
* @param Array|null $set_fields
* @return void
* @access public
*/
public function SetDBFieldsFromHash($hash, $skip_fields = Array (), $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
if ( $skip_fields ) {
$set_fields = array_diff($set_fields, $skip_fields);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
foreach ($set_fields as $field_name) {
$this->SetDBField($field_name, $hash[$field_name]);
}
}
/**
* Returns part of SQL WHERE clause identifying the record, ex. id = 25
*
* @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method
* @param Array $keys_hash alternative, then item id, keys hash to load item by
* @see kDBItem::Load()
* @see kDBItem::Update()
* @see kDBItem::Delete()
* @return string
* @access protected
*/
protected function GetKeyClause($method = null, $keys_hash = null)
{
if ( !isset($keys_hash) ) {
$keys_hash = Array ($this->IDField => $this->ID);
}
$ret = '';
foreach ($keys_hash as $field => $value) {
$value_part = is_null($value) ? ' IS NULL' : ' = ' . $this->Conn->qstr($value);
$ret .= '(' . (strpos($field, '.') === false ? '`' . $this->TableName . '`.' : '') . $field . $value_part . ') AND ';
}
return substr($ret, 0, -5);
}
/**
- * Loads item from the database by given id
- *
- * @access public
- * @param mixed $id item id of keys->values hash to load item by
- * @param string $id_field_name Optional parameter to load item by given Id field
- * @param bool $cachable cache this query result based on it's prefix serial
- * @return bool True if item has been loaded, false otherwise
- */
+ * Loads item from the database by given id
+ *
+ * @access public
+ * @param mixed $id item id of keys->values hash to load item by
+ * @param string $id_field_name Optional parameter to load item by given Id field
+ * @param bool $cachable cache this query result based on it's prefix serial
+ * @return bool True if item has been loaded, false otherwise
+ */
public function Load($id, $id_field_name = null, $cachable = false)
{
if ( isset($id_field_name) ) {
$this->IDField = $id_field_name; // set new IDField
}
$keys_sql = '';
if (is_array($id)) {
$keys_sql = $this->GetKeyClause('load', $id);
}
else {
$this->setID($id);
$keys_sql = $this->GetKeyClause('load');
}
if ( isset($id_field_name) ) {
// restore original IDField from unit config
$this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField');
}
if (($id === false) || !$keys_sql) {
return $this->Clear();
}
if (!$this->raiseEvent('OnBeforeItemLoad', $id)) {
return false;
}
$q = $this->GetSelectSQL() . ' WHERE ' . $keys_sql;
if ($cachable && $this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
$serial_name = $this->Application->incrementCacheSerial($this->Prefix == 'st' ? 'c' : $this->Prefix, isset($id_field_name) ? null : $id, false);
$cache_key = 'kDBItem::Load_' . crc32(serialize($id) . '-' . $this->IDField) . '[%' . $serial_name . '%]';
$field_values = $this->Application->getCache($cache_key, false);
if ($field_values === false) {
$field_values = $this->Conn->GetRow($q);
if ($field_values !== false) {
// only cache, when data was retrieved
$this->Application->setCache($cache_key, $field_values);
}
}
}
else {
$field_values = $this->Conn->GetRow($q);
}
if ($field_values) {
$this->FieldValues = array_merge($this->FieldValues, $field_values);
$this->OriginalFieldValues = $this->FieldValues;
}
else {
return $this->Clear();
}
if (is_array($id) || isset($id_field_name)) {
$this->setID($this->FieldValues[$this->IDField]);
}
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $this->GetID());
$this->Loaded = true;
return true;
}
/**
* Loads object from hash (not db)
*
* @param Array $fields_hash
* @param string $id_field
*/
public function LoadFromHash($fields_hash, $id_field = null)
{
if (!isset($id_field)) {
$id_field = $this->IDField;
}
$this->Clear();
if (!$fields_hash || !array_key_exists($id_field, $fields_hash)) {
// no data OR id field missing
return false;
}
$id = $fields_hash[$id_field];
if ( !$this->raiseEvent('OnBeforeItemLoad', $id) ) {
return false;
}
$this->FieldValues = array_merge($this->FieldValues, $fields_hash);
$this->OriginalFieldValues = $this->FieldValues;
$this->setID($id);
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $id);
$this->Loaded = true;
return true;
}
/**
* Builds select sql, SELECT ... FROM parts only
*
* @access public
* @return string
*/
/**
* Returns SELECT part of list' query
*
* @param string $base_query
* @param bool $replace_table
* @return string
* @access public
*/
public function GetSelectSQL($base_query = null, $replace_table = true)
{
if (!isset($base_query)) {
$base_query = $this->SelectClause;
}
$base_query = $this->addCalculatedFields($base_query);
return parent::GetSelectSQL($base_query, $replace_table);
}
public function UpdateFormattersMasterFields()
{
$this->initValidator(); // used, when called not from kValidator::Validate method
foreach ($this->Fields as $field => $options) {
if ( isset($options['formatter']) ) {
$formatter = $this->Application->recallObject($options['formatter']);
/* @var $formatter kFormatter */
$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
}
}
}
/**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
* @param mixed $force_id
* @return bool
*/
public function skipField($field_name, $force_id = false)
{
$skip = false;
// 1. skipping 'virtual' field
$skip = $skip || array_key_exists($field_name, $this->VirtualFields);
// 2. don't write empty field value to db, when "skip_empty" option is set
$field_value = array_key_exists($field_name, $this->FieldValues) ? $this->FieldValues[$field_name] : false;
if (array_key_exists($field_name, $this->Fields)) {
$skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['skip_empty'] : false;
}
else {
// field found in database, but not declared in unit config
$skip_empty = false;
}
$skip = $skip || (!$field_value && $skip_empty);
// 3. skipping field not in Fields (nor virtual, nor real)
$skip = $skip || !array_key_exists($field_name, $this->Fields);
return $skip;
}
/**
* Updates previously loaded record with current item' values
*
* @access public
* @param int $id Primary Key Id to update
* @param bool $system_update
* @return bool
* @access public
*/
public function Update($id = null, $system_update = false)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemUpdate') ) {
return false;
}
if ( !isset($this->ID) ) {
// ID could be set inside OnBeforeItemUpdate event, so don't combine this check with previous one
return false;
}
// validate before updating
if ( !$this->Validate() ) {
return false;
}
if ( !$this->FieldValues ) {
// nothing to update
return true;
}
$sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ( $this->skipField($field_name) ) {
continue;
}
if ( is_null($field_value) ) {
if ( array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null'] ) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$field_value = $this->Fields[$field_name]['default'];
}
}
$sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', ';
}
$sql = 'UPDATE ' . $this->TableName . '
SET ' . substr($sql, 0, -2) . '
WHERE ' . $this->GetKeyClause('update');
if ( $this->Conn->ChangeQuery($sql) === false ) {
// there was and sql error
$this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg());
return false;
}
$affected_rows = $this->Conn->getAffectedRows();
if ( !$system_update && ($affected_rows > 0) ) {
$this->setModifiedFlag(ChangeLog::UPDATE);
}
$this->saveCustomFields();
$this->raiseEvent('OnAfterItemUpdate');
$this->OriginalFieldValues = $this->FieldValues;
$this->Loaded = true;
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return true;
}
/**
* Validates given field
*
* @param string $field
* @return bool
* @access public
*/
public function ValidateField($field)
{
$this->initValidator();
return $this->validator->ValidateField($field);
}
/**
* Validate all item fields based on
* constraints set in each field options
* in config
*
* @return bool
* @access private
*/
public function Validate()
{
if ( $this->IgnoreValidation ) {
return true;
}
$this->initValidator();
// will apply any custom validation to the item
$this->raiseEvent('OnBeforeItemValidate');
if ( $this->validator->Validate() ) {
// no validation errors
$this->raiseEvent('OnAfterItemValidate');
return true;
}
return false;
}
/**
* Check if item has errors
*
* @param Array $skip_fields fields to skip during error checking
* @return bool
*/
public function HasErrors($skip_fields = Array ())
{
if ( !is_object($this->validator) ) {
return false;
}
return $this->validator->HasErrors($skip_fields);
}
/**
* Check if value is set for required field
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access public
* @todo Find a way to get rid of direct call from kMultiLanguage::UpdateMasterFields method
*/
public function ValidateRequired($field, $params)
{
return $this->validator->ValidateRequired($field, $params);
}
/**
* Return error message for field
*
* @param string $field
* @param bool $force_escape
* @return string
* @access public
*/
public function GetErrorMsg($field, $force_escape = null)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorMsg($field, $force_escape);
}
/**
* Returns field errors
*
* @return Array
* @access public
*/
public function GetFieldErrors()
{
if ( !is_object($this->validator) ) {
return Array ();
}
return $this->validator->GetFieldErrors();
}
/**
* Creates a record in the database table with current item' values
*
* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
* @param bool $system_create
* @return bool
* @access public
*/
public function Create($force_id = false, $system_create = false)
{
if (!$this->raiseEvent('OnBeforeItemCreate')) {
return false;
}
// Validating fields before attempting to create record
if (!$this->Validate()) {
return false;
}
if (is_int($force_id)) {
$this->FieldValues[$this->IDField] = $force_id;
}
elseif (!$force_id || !is_bool($force_id)) {
$this->FieldValues[$this->IDField] = $this->generateID();
}
$fields_sql = '';
$values_sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ($this->skipField($field_name, $force_id)) {
continue;
}
if (is_null($field_value)) {
if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']);
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
else {
if (($field_name == $this->IDField) && ($field_value == 0) && !is_int($force_id)) {
// don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value
$values_sql .= 'DEFAULT';
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
$fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement
$values_sql .= ', ';
}
$sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ')
VALUES (' . substr($values_sql, 0, -2) . ')';
//Executing the query and checking the result
if ($this->Conn->ChangeQuery($sql) === false) {
$this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg());
return false;
}
$insert_id = $this->Conn->getInsertID();
if ($insert_id == 0) {
// insert into temp table (id is not auto-increment field)
$insert_id = $this->FieldValues[$this->IDField];
}
$this->setID($insert_id);
$this->OriginalFieldValues = $this->FieldValues;
if (!$system_create){
$this->setModifiedFlag(ChangeLog::CREATE);
}
$this->saveCustomFields();
if (!$this->IsTempTable()) {
$this->Application->resetCounters($this->TableName);
}
if ($this->IsTempTable() && ($this->Application->GetTopmostPrefix($this->Prefix) != $this->Prefix) && !is_int($force_id)) {
// temp table + subitem = set negative id
$this->setTempID();
}
$this->raiseEvent('OnAfterItemCreate');
$this->Loaded = true;
return true;
}
/**
* Deletes the record from database
*
* @param int $id
* @return bool
* @access public
*/
public function Delete($id = null)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemDelete') ) {
return false;
}
$sql = 'DELETE FROM ' . $this->TableName . '
WHERE ' . $this->GetKeyClause('Delete');
$ret = $this->Conn->ChangeQuery($sql);
$affected_rows = $this->Conn->getAffectedRows();
if ( $affected_rows > 0 ) {
$this->setModifiedFlag(ChangeLog::DELETE); // will change affected rows, so get it before this line
// something was actually deleted
$this->raiseEvent('OnAfterItemDelete');
}
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return $ret;
}
public function PopulateMultiLangFields()
{
foreach ($this->Fields as $field => $options) {
// master field is set only for CURRENT language
$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
if ( ($formatter == 'kMultiLanguage') && isset($options['master_field']) && isset($options['error_field']) ) {
// MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode,
// we display ML fields directly so we set it back to itself, otherwise error won't be displayed
unset( $this->Fields[$field]['error_field'] );
}
}
}
/**
* Sets new name for item in case if it is being copied in same table
*
* @param array $master Table data from TempHandler
* @param int $foreign_key ForeignKey value to filter name check query by
* @param string $title_field FieldName to alter, by default - TitleField of the prefix
* @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name
* @access public
*/
public function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s')
{
if (!isset($title_field)) {
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return;
}
$new_name = $this->GetDBField($title_field);
$original_checked = false;
do {
if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) {
$new_name = sprintf($format, ($regs[1]+1), $regs[2]);
}
elseif ($original_checked) {
$new_name = sprintf($format, '', $new_name);
}
// if we are cloning in temp table this will look for names in temp table,
// since object' TableName contains correct TableName (for temp also!)
// if we are cloning live - look in live
$query = 'SELECT '.$title_field.' FROM '.$this->TableName.'
WHERE '.$title_field.' = '.$this->Conn->qstr($new_name);
$foreign_key_field = getArrayValue($master, 'ForeignKey');
$foreign_key_field = is_array($foreign_key_field) ? $foreign_key_field[ $master['ParentPrefix'] ] : $foreign_key_field;
if ($foreign_key_field && isset($foreign_key)) {
$query .= ' AND '.$foreign_key_field.' = '.$foreign_key;
}
$res = $this->Conn->GetOne($query);
/*// if not found in live table, check in temp table if applicable
if ($res === false && $object->Special == 'temp') {
$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
$res = $this->Conn->GetOne($query);
}*/
$original_checked = true;
} while ($res !== false);
$this->SetDBField($title_field, $new_name);
}
protected function raiseEvent($name, $id = null, $additional_params = Array())
{
$additional_params['id'] = isset($id) ? $id : $this->GetID();
$event = new kEvent($this->getPrefixSpecial() . ':' . $name, $additional_params);
if ( is_object($this->parentEvent) ) {
$event->MasterEvent = $this->parentEvent;
}
$this->Application->HandleEvent($event);
return $event->status == kEvent::erSUCCESS;
}
/**
* Set's new ID for item
*
* @param int $new_id
* @access public
*/
public function setID($new_id)
{
$this->ID = $new_id;
$this->SetDBField($this->IDField, $new_id);
}
/**
* Generate and set new temporary id
*
* @access private
*/
public function setTempID()
{
$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$this->IDField.') FROM '.$this->TableName);
if($new_id > 0) $new_id = 0;
--$new_id;
$this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID());
if ($this->ShouldLogChanges(true)) {
// Updating TempId in ChangesLog, if changes are disabled
$ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix) . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
if ($changes) {
foreach ($changes as $key => $rec) {
if ($rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID()) {
// change log for record, that's ID was just updated -> update in change log record too
$changes[$key]['ItemId'] = $new_id;
}
if ($rec['MasterPrefix'] == $this->Prefix && $rec['MasterId'] == $this->GetID()) {
// master item id was changed
$changes[$key]['MasterId'] = $new_id;
}
if (in_array($this->Prefix, $rec['ParentPrefix']) && $rec['ParentId'][$this->Prefix] == $this->GetID()) {
// change log record of given item's sub item -> update changed id's in dependent fields
$changes[$key]['ParentId'][$this->Prefix] = $new_id;
if (array_key_exists('DependentFields', $rec)) {
// these are fields from table of $rec['Prefix'] table!
// when one of dependent fields goes into idfield of it's parent item, that was changed
$parent_table_key = $this->Application->getUnitOption($rec['Prefix'], 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$this->Prefix] : $parent_table_key;
if ($parent_table_key == $this->IDField) {
$foreign_key = $this->Application->getUnitOption($rec['Prefix'], 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$this->Prefix] : $foreign_key;
$changes[$key]['DependentFields'][$foreign_key] = $new_id;
}
}
}
}
}
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
$this->SetID($new_id);
}
/**
* Set's modification flag for main prefix of current prefix to true
*
* @param int $mode
* @access private
*/
public function setModifiedFlag($mode = null)
{
$main_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
$this->Application->StoreVar($main_prefix . '_modified', '1', true); // true for optional
if ($this->ShouldLogChanges(true)) {
$this->LogChanges($main_prefix, $mode);
if (!$this->IsTempTable()) {
$handler = $this->Application->recallObject($this->Prefix . '_EventHandler');
/* @var $handler kDBEventHandler */
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$handler->SaveLoggedChanges($ses_var_name, $this->ShouldLogChanges());
}
}
}
/**
* Determines, that changes made to this item should be written to change log
*
* @param bool $log_changes
* @return bool
*/
public function ShouldLogChanges($log_changes = null)
{
if (!isset($log_changes)) {
// specific logging mode no forced -> use global logging settings
$log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog');
}
return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges');
}
protected function LogChanges($main_prefix, $mode)
{
if ( !$mode ) {
return ;
}
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
$fields_hash = Array (
'Prefix' => $this->Prefix,
'ItemId' => $this->GetID(),
'OccuredOn' => adodb_mktime(),
'MasterPrefix' => $main_prefix,
'Action' => $mode,
);
if ( $this->Prefix == $main_prefix ) {
// main item
$fields_hash['MasterId'] = $this->GetID();
$fields_hash['ParentPrefix'] = Array ($main_prefix);
$fields_hash['ParentId'] = Array ($main_prefix => $this->GetID());
}
else {
// sub item
// collect foreign key values (for serial reset)
$foreign_keys = $this->Application->getUnitOption($this->Prefix, 'ForeignKey', Array ());
$dependent_fields = $fields_hash['ParentId'] = $fields_hash['ParentPrefix'] = Array ();
/* @var $foreign_keys Array */
if ( is_array($foreign_keys) ) {
foreach ($foreign_keys as $prefix => $field_name) {
$dependent_fields[$field_name] = $this->GetDBField($field_name);
$fields_hash['ParentPrefix'][] = $prefix;
$fields_hash['ParentId'][$prefix] = $this->getParentId($prefix);
}
}
else {
$dependent_fields[$foreign_keys] = $this->GetDBField($foreign_keys);
$fields_hash['ParentPrefix'] = Array ( $this->Application->getUnitOption($this->Prefix, 'ParentPrefix') );
$fields_hash['ParentId'][ $fields_hash['ParentPrefix'][0] ] = $this->getParentId('auto');
}
$fields_hash['DependentFields'] = $dependent_fields;
// works only, when main item is present in url, when sub-item is changed
$master_id = $this->Application->GetVar($main_prefix . '_id');
if ( $master_id === false ) {
// works in case of we are not editing topmost item, when sub-item is created/updated/deleted
$master_id = $this->getParentId('auto', true);
}
$fields_hash['MasterId'] = $master_id;
}
switch ( $mode ) {
case ChangeLog::UPDATE:
$to_save = array_merge($this->GetTitleField(), $this->GetChangedFields());
break;
case ChangeLog::CREATE:
$to_save = $this->GetTitleField();
break;
case ChangeLog::DELETE:
$to_save = array_merge($this->GetTitleField(), $this->GetRealFields());
break;
default:
$to_save = Array ();
break;
}
$fields_hash['Changes'] = serialize($to_save);
$changes[] = $fields_hash;
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
/**
* Returns current item parent's ID
*
* @param string $parent_prefix
* @param bool $top_most return topmost parent, when used
* @return int
+ * @access public
*/
- protected function getParentId($parent_prefix, $top_most = false)
+ public function getParentId($parent_prefix, $top_most = false)
{
$current_id = $this->GetID();
$current_prefix = $this->Prefix;
if ($parent_prefix == 'auto') {
$parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix');
}
if (!$parent_prefix) {
return $current_id;
}
do {
// field in this table
$foreign_key = $this->Application->getUnitOption($current_prefix, 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$parent_prefix] : $foreign_key;
// get foreign key value for $current_prefix
if ($current_prefix == $this->Prefix) {
$foreign_key_value = $this->GetDBField($foreign_key);
}
else {
$id_field = $this->Application->getUnitOption($current_prefix, 'IDField');
$table_name = $this->Application->getUnitOption($current_prefix, 'TableName');
if ($this->IsTempTable()) {
$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $current_prefix);
}
$sql = 'SELECT ' . $foreign_key . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' = ' . $current_id;
$foreign_key_value = $this->Conn->GetOne($sql);
}
// field in parent table
$parent_table_key = $this->Application->getUnitOption($current_prefix, 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$parent_prefix] : $parent_table_key;
$parent_id_field = $this->Application->getUnitOption($parent_prefix, 'IDField');
$parent_table_name = $this->Application->getUnitOption($parent_prefix, 'TableName');
if ($this->IsTempTable()) {
$parent_table_name = $this->Application->GetTempName($parent_table_name, 'prefix:' . $current_prefix);
}
if ($parent_id_field == $parent_table_key) {
// sub-item is related by parent item idfield
$current_id = $foreign_key_value;
}
else {
// sub-item is related by other parent item field
$sql = 'SELECT ' . $parent_id_field . '
FROM ' . $parent_table_name . '
WHERE ' . $parent_table_key . ' = ' . $foreign_key_value;
$current_id = $this->Conn->GetOne($sql);
}
$current_prefix = $parent_prefix;
if (!$top_most) {
break;
}
} while ( $parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix') );
return $current_id;
}
/**
* Returns title field (if any)
*
* @return Array
*/
public function GetTitleField()
{
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if ($title_field) {
$value = $this->GetField($title_field);
return $value ? Array ($title_field => $value) : Array ();
}
return Array ();
}
/**
* Returns only fields, that are present in database (no virtual and no calculated fields)
*
* @return Array
*/
public function GetRealFields()
{
return array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields);
}
/**
* Returns only changed database field
*
* @param bool $include_virtual_fields
* @return Array
*/
public function GetChangedFields($include_virtual_fields = false)
{
$changes = Array ();
$fields = $include_virtual_fields ? $this->FieldValues : $this->GetRealFields();
$diff = array_diff_assoc($fields, $this->OriginalFieldValues);
foreach ($diff as $field => $new_value) {
$old_value = $this->GetOriginalField($field, true);
$new_value = $this->GetField($field);
if ($old_value != $new_value) {
// "0.00" and "0.0000" are stored as strings and will differ. Double check to prevent that.
$changes[$field] = Array ('old' => $old_value, 'new' => $new_value);
}
}
return $changes;
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
public function GetID()
{
return $this->ID;
}
/**
* Generates ID for new items before inserting into database
*
* @return int
* @access private
*/
protected function generateID()
{
return 0;
}
/**
* Returns true if item was loaded successfully by Load method
*
* @return bool
*/
public function isLoaded()
{
return $this->Loaded;
}
/**
* Checks if field is required
*
* @param string $field
* @return bool
*/
public function isRequired($field)
{
return isset($this->Fields[$field]['required']) && $this->Fields[$field]['required'];
}
/**
* Sets new required flag to field
*
* @param mixed $fields
* @param bool $is_required
*/
public function setRequired($fields, $is_required = true)
{
if ( !is_array($fields) ) {
$fields = explode(',', $fields);
}
foreach ($fields as $field) {
$this->Fields[$field]['required'] = $is_required;
}
}
/**
* Removes all data from an object
*
* @param int $new_id
* @return bool
* @access public
*/
public function Clear($new_id = null)
{
$this->Loaded = false;
$this->FieldValues = $this->OriginalFieldValues = Array ();
$this->SetDefaultValues(); // will wear off kDBItem::setID effect, so set it later
if ( is_object($this->validator) ) {
$this->validator->reset();
}
$this->setID($new_id);
return $this->Loaded;
}
public function Query($force = false)
{
throw new Exception('<b>Query</b> method is called in class <strong>' . get_class($this) . '</strong> for prefix <strong>' . $this->getPrefixSpecial() . '</strong>');
}
protected function saveCustomFields()
{
if ( !$this->customFields || $this->inCloning ) {
return true;
}
$cdata_key = rtrim($this->Prefix . '-cdata.' . $this->Special, '.');
$cdata = $this->Application->recallObject($cdata_key, null, Array ('skip_autoload' => true));
/* @var $cdata kDBItem */
$resource_id = $this->GetDBField('ResourceId');
$cdata->Load($resource_id, 'ResourceId');
$cdata->SetDBField('ResourceId', $resource_id);
$ml_formatter = $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$languages = $ml_helper->getLanguages();
foreach ($this->customFields as $custom_id => $custom_name) {
$force_primary = $cdata->GetFieldOption('cust_' . $custom_id, 'force_primary');
if ( $force_primary ) {
$cdata->SetDBField($ml_formatter->LangFieldName('cust_' . $custom_id, true), $this->GetDBField('cust_' . $custom_name));
}
else {
foreach ($languages as $language_id) {
$cdata->SetDBField('l' . $language_id . '_cust_' . $custom_id, $this->GetDBField('l' . $language_id . '_cust_' . $custom_name));
}
}
}
return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create();
}
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return Array
*/
public function GetCol($field, $formatted = false, $format = null)
{
if ($formatted) {
return Array (0 => $this->GetField($field, $format));
}
return Array (0 => $this->GetDBField($field));
}
/**
* Set's loaded status of object
*
* @param bool $is_loaded
* @access public
* @todo remove this method, since item can't be marked as loaded externally
*/
public function setLoaded($is_loaded = true)
{
$this->Loaded = $is_loaded;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php (revision 15251)
+++ branches/5.2.x/core/kernel/application.php (revision 15252)
@@ -1,3176 +1,3194 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Basic class for Kernel4-based Application
*
* This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
* The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
* <br>
* The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
* This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method,
* which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br>
* See singleton pattern by GOF.
*/
class kApplication implements kiCacheable {
/**
* Location of module helper class (used in installator too)
*/
const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php';
/**
* Is true, when Init method was called already, prevents double initialization
*
* @var bool
*/
public $InitDone = false;
/**
* Holds internal NParser object
*
* @var NParser
* @access public
*/
public $Parser;
/**
* Holds parser output buffer
*
* @var string
* @access protected
*/
protected $HTML = '';
/**
* The main Factory used to create
* almost any class of kernel and
* modules
*
* @var kFactory
* @access protected
*/
protected $Factory;
/**
* Template names, that will be used instead of regular templates
*
* @var Array
* @access public
*/
public $ReplacementTemplates = Array ();
/**
* Mod-Rewrite listeners used during url building and parsing
*
* @var Array
* @access public
*/
public $RewriteListeners = Array ();
/**
* Reference to debugger
*
* @var Debugger
* @access public
*/
public $Debugger = null;
/**
* Holds all phrases used
* in code and template
*
* @var PhrasesCache
* @access public
*/
public $Phrases;
/**
* Modules table content, key - module name
*
* @var Array
* @access public
*/
public $ModuleInfo = Array ();
/**
* Holds DBConnection
*
* @var kDBConnection
* @access public
*/
public $Conn = null;
/**
* Maintains list of user-defined error handlers
*
* @var Array
* @access public
*/
public $errorHandlers = Array ();
/**
* Maintains list of user-defined exception handlers
*
* @var Array
* @access public
*/
public $exceptionHandlers = Array ();
// performance needs:
/**
* Holds a reference to httpquery
*
* @var kHttpQuery
* @access public
*/
public $HttpQuery = null;
/**
* Holds a reference to UnitConfigReader
*
* @var kUnitConfigReader
* @access public
*/
public $UnitConfigReader = null;
/**
* Holds a reference to Session
*
* @var Session
* @access public
*/
public $Session = null;
/**
* Holds a ref to kEventManager
*
* @var kEventManager
* @access public
*/
public $EventManager = null;
/**
* Holds a ref to kUrlManager
*
* @var kUrlManager
* @access public
*/
public $UrlManager = null;
/**
* Ref for TemplatesCache
*
* @var TemplatesCache
* @access public
*/
public $TemplatesCache = null;
/**
* Holds current NParser tag while parsing, can be used in error messages to display template file and line
*
* @var _BlockTag
* @access public
*/
public $CurrentNTag = null;
/**
* Object of unit caching class
*
* @var kCacheManager
* @access public
*/
public $cacheManager = null;
/**
* Tells, that administrator has authenticated in administrative console
* Should be used to manipulate data change OR data restrictions!
*
* @var bool
* @access public
*/
public $isAdminUser = false;
/**
* Tells, that admin version of "index.php" was used, nothing more!
* Should be used to manipulate data display!
*
* @var bool
* @access public
*/
public $isAdmin = false;
/**
* Instance of site domain object
*
* @var kDBItem
* @access public
* @todo move away into separate module
*/
public $siteDomain = null;
/**
* Prevent kApplication class to be created directly, only via Instance method
*
* @access private
*/
private function __construct()
{
}
final private function __clone() {}
/**
* Returns kApplication instance anywhere in the script.
*
* This method should be used to get single kApplication object instance anywhere in the
* Kernel-based application. The method is guaranteed to return the SAME instance of kApplication.
* Anywhere in the script you could write:
* <code>
* $application =& kApplication::Instance();
* </code>
* or in an object:
* <code>
* $this->Application =& kApplication::Instance();
* </code>
* to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
* To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
* BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
* create and return default KernelApplication instance.
*
* Pattern: Singleton
*
* @static
* @return kApplication
* @access public
*/
public static function &Instance()
{
static $instance = false;
if ( !$instance ) {
$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
$instance = new $class();
}
return $instance;
}
/**
* Initializes the Application
*
* @param string $factory_class
* @return bool Was Init actually made now or before
* @access public
* @see kHTTPQuery
* @see Session
* @see TemplatesCache
*/
public function Init($factory_class = 'kFactory')
{
if ( $this->InitDone ) {
return false;
}
$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 $factory_class();
$this->registerDefaultClasses();
$vars = kUtil::parseConfig(true);
$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection');
$this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array (&$this, 'handleSQLError')));
$this->Conn->setup($vars);
$this->cacheManager = $this->makeClass('kCacheManager');
$this->cacheManager->InitCache();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Before UnitConfigReader');
}
// init config reader and all managers
$this->UnitConfigReader = $this->makeClass('kUnitConfigReader');
$this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read
$this->registerModuleConstants();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('After UnitConfigReader');
}
define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
// start processing request
$this->HttpQuery = $this->recallObject('HTTPQuery');
$this->HttpQuery->process();
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
}
$this->Session = $this->recallObject('Session');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed Session');
}
$this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
}
$this->cacheManager->LoadApplicationCache();
$site_timezone = $this->ConfigValue('Config_Site_Time');
if ( $site_timezone ) {
putenv('TZ=' . $site_timezone);
}
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Loaded cache and phrases');
}
$this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
$this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendTimestamp('Processed AfterConfigRead');
}
if ( $this->GetVar('m_cat_id') === false ) {
$this->SetVar('m_cat_id', 0);
}
if ( !$this->RecallVar('curr_iso') ) {
$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
}
$visit_id = $this->RecallVar('visit_id');
if ( $visit_id !== false ) {
$this->SetVar('visits_id', $visit_id);
}
$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;
}
/**
* Performs initialization of manager classes, that can be overridden from unit configs
*
* @return void
* @access public
* @throws Exception
*/
public function InitManagers()
{
if ( $this->InitDone ) {
throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
return;
}
$this->UrlManager = $this->makeClass('kUrlManager');
$this->EventManager = $this->makeClass('EventManager');
$this->Phrases = $this->makeClass('kPhraseCache');
$this->RegisterDefaultBuildEvents();
}
/**
* Returns module information. Searches module by requested field
*
* @param string $field
* @param mixed $value
* @param string $return_field field value to returns, if not specified, then return all fields
* @return Array
*/
public function findModule($field, $value, $return_field = null)
{
$found = $module_info = false;
foreach ($this->ModuleInfo as $module_info) {
if ( strtolower($module_info[$field]) == strtolower($value) ) {
$found = true;
break;
}
}
if ( $found ) {
return isset($return_field) ? $module_info[$return_field] : $module_info;
}
return false;
}
/**
* Refreshes information about loaded modules
*
* @return void
* @access public
*/
public function refreshModuleInfo()
{
if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) {
$this->registerModuleConstants();
return;
}
// use makeClass over recallObject, since used before kApplication initialization during installation
$modules_helper = $this->makeClass('ModulesHelper');
/* @var $modules_helper kModulesHelper */
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Modules
WHERE ' . $modules_helper->getWhereClause() . '
ORDER BY LoadOrder';
$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
$this->registerModuleConstants();
}
/**
* Checks if passed language id if valid and sets it to primary otherwise
*
* @return void
* @access public
*/
public function VerifyLanguageId()
{
$language_id = $this->GetVar('m_lang');
if ( !$language_id ) {
$language_id = 'default';
}
$this->SetVar('lang.current_id', $language_id);
$this->SetVar('m_lang', $language_id);
$lang_mode = $this->GetVar('lang_mode');
$this->SetVar('lang_mode', '');
$lang = $this->recallObject('lang.current');
/* @var $lang kDBItem */
if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) {
if ( !defined('IS_INSTALL') ) {
$this->ApplicationDie('Unknown or disabled language');
}
}
$this->SetVar('lang_mode', $lang_mode);
}
/**
* Checks if passed theme id if valid and sets it to primary otherwise
*
* @return void
* @access public
*/
public function VerifyThemeId()
{
if ( $this->isAdmin ) {
kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
return;
}
$path = $this->GetFrontThemePath();
if ( $path === false ) {
$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
}
kUtil::safeDefine('THEMES_PATH', $path);
}
/**
* Returns relative path to current front-end theme
*
* @param bool $force
* @return string
* @access public
*/
public function GetFrontThemePath($force = false)
{
static $path = null;
if ( !$force && isset($path) ) {
return $path;
}
$theme_id = $this->GetVar('m_theme');
if ( !$theme_id ) {
$theme_id = 'default'; // $this->GetDefaultThemeId(1); // 1 to force front-end mode!
}
$this->SetVar('m_theme', $theme_id);
$this->SetVar('theme.current_id', $theme_id); // KOSTJA: this is to fool theme' getPassedID
$theme = $this->recallObject('theme.current');
/* @var $theme ThemeItem */
if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) {
return false;
}
// assign & then return, since it's static variable
$path = '/themes/' . $theme->GetDBField('Name');
return $path;
}
/**
* Returns primary front/admin language id
*
* @param bool $init
* @return int
* @access public
*/
public function GetDefaultLanguageId($init = false)
{
$cache_key = 'primary_language_info[%LangSerial%]';
$language_info = $this->getCache($cache_key);
if ( $language_info === false ) {
// cache primary language info first
$table = $this->getUnitOption('lang', 'TableName');
$id_field = $this->getUnitOption('lang', 'IDField');
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
FROM ' . $table . '
WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
if ( $language_info !== false ) {
$this->setCache($cache_key, $language_info);
}
}
$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) {
// get from cache
return $language_info[$language_key];
}
$language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false;
if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) {
$language_id = 1;
}
return $language_id;
}
/**
* Returns front-end primary theme id (even, when called from admin console)
*
* @param bool $force_front
* @return int
* @access public
*/
public function GetDefaultThemeId($force_front = false)
{
static $theme_id = 0;
if ( $theme_id > 0 ) {
return $theme_id;
}
if ( kUtil::constOn('DBG_FORCE_THEME') ) {
$theme_id = DBG_FORCE_THEME;
}
elseif ( !$force_front && $this->isAdmin ) {
$theme_id = 999;
}
else {
$cache_key = 'primary_theme[%ThemeSerial%]';
$theme_id = $this->getCache($cache_key);
if ( $theme_id === false ) {
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
FROM ' . $this->getUnitOption('theme', 'TableName') . '
WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
$theme_id = $this->Conn->GetOne($sql);
if ( $theme_id !== false ) {
$this->setCache($cache_key, $theme_id);
}
}
}
return $theme_id;
}
/**
* Returns site primary currency ISO code
*
* @return string
* @access public
* @todo Move into In-Commerce
*/
public function GetPrimaryCurrency()
{
$cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId');
$currency_iso = $this->getCache($cache_key);
if ( $currency_iso === false ) {
if ( $this->isModuleEnabled('In-Commerce') ) {
$this->Conn->nextQueryCachable = true;
$currency_id = $this->siteDomainField('PrimaryCurrencyId');
$sql = 'SELECT ISO
FROM ' . $this->getUnitOption('curr', 'TableName') . '
WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1');
$currency_iso = $this->Conn->GetOne($sql);
}
else {
$currency_iso = 'USD';
}
$this->setCache($cache_key, $currency_iso);
}
return $currency_iso;
}
/**
* Returns site domain field. When none of site domains are found false is returned.
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return mixed
* @todo Move into separate module
*/
public function siteDomainField($field, $formatted = false, $format = null)
{
if ( $this->isAdmin ) {
// don't apply any filtering in administrative console
return false;
}
if ( !$this->siteDomain ) {
$this->siteDomain = $this->recallObject('site-domain.current');
/* @var $site_domain kDBItem */
}
if ( $this->siteDomain->isLoaded() ) {
return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field);
}
return false;
}
/**
* Registers default classes such as kDBEventHandler, kUrlManager
*
* Called automatically while initializing kApplication
*
* @return void
* @access public
*/
public function RegisterDefaultClasses()
{
$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
$this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php');
$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php');
$this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php');
$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php');
$this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php');
$this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php');
$this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php');
$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php');
$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php');
$this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache');
$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
$this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php');
$this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php');
$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
// Params class descendants
$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery');
// session
$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage');
// template parser
$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor');
$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php');
$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php');
// database
$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php');
$this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php');
$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
// email sending
$this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php');
$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender');
$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
// do not move to config - this helper is used before configs are read
$this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper');
}
/**
* Registers default build events
*
* @return void
* @access protected
*/
protected function RegisterDefaultBuildEvents()
{
$this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
}
/**
* Returns cached category information by given cache name. All given category
* information is recached, when at least one of 4 caches is missing.
*
* @param int $category_id
* @param string $name cache name = {filenames, category_designs, category_tree}
* @return string
* @access public
*/
public function getCategoryCache($category_id, $name)
{
return $this->cacheManager->getCategoryCache($category_id, $name);
}
/**
* Returns caching type (none, memory, temporary)
*
* @param int $caching_type
* @return bool
* @access public
*/
public function isCachingType($caching_type)
{
return $this->cacheManager->isCachingType($caching_type);
}
/**
* Increments serial based on prefix and it's ID (optional)
*
* @param string $prefix
* @param int $id ID (value of IDField) or ForeignKeyField:ID
* @param bool $increment
* @return string
* @access public
*/
public function incrementCacheSerial($prefix, $id = null, $increment = true)
{
return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
}
/**
* Returns cached $key value from cache named $cache_name
*
* @param int $key key name from cache
* @param bool $store_locally store data locally after retrieved
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds);
}
/**
* Stores new $value in cache with $key name
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function setCache($key, $value, $expiration = 0)
{
return $this->cacheManager->setCache($key, $value, $expiration);
}
/**
* Stores new $value in cache with $key name (only if it's not there)
*
* @param int $key key name to add to cache
* @param mixed $value value of cached record
* @param int $expiration when value expires (0 - doesn't expire)
* @return bool
* @access public
*/
public function addCache($key, $value, $expiration = 0)
{
return $this->cacheManager->addCache($key, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from cache
*
* @param string $key
* @return void
* @access public
*/
public function deleteCache($key)
{
$this->cacheManager->deleteCache($key);
}
/**
* Reset's all memory cache at once
*
* @return void
* @access public
*/
public function resetCache()
{
$this->cacheManager->resetCache();
}
/**
* Returns value from database cache
*
* @param string $name key name
* @param int $max_rebuild_seconds
* @return mixed
* @access public
*/
public function getDBCache($name, $max_rebuild_seconds = 0)
{
return $this->cacheManager->getDBCache($name, $max_rebuild_seconds);
}
/**
* Sets value to database cache
*
* @param string $name
* @param mixed $value
* @param int|bool $expiration
* @return void
* @access public
*/
public function setDBCache($name, $value, $expiration = false)
{
$this->cacheManager->setDBCache($name, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
* @return bool
* @access public
*/
public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
{
return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time);
}
/**
* Deletes key from database cache
*
* @param string $name
* @return void
* @access public
*/
public function deleteDBCache($name)
{
$this->cacheManager->deleteDBCache($name);
}
/**
* Registers each module specific constants if any found
*
* @return bool
* @access protected
*/
protected function registerModuleConstants()
{
if ( file_exists(KERNEL_PATH . '/constants.php') ) {
kUtil::includeOnce(KERNEL_PATH . '/constants.php');
}
if ( !$this->ModuleInfo ) {
return false;
}
foreach ($this->ModuleInfo as $module_info) {
$constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
if ( file_exists($constants_file) ) {
kUtil::includeOnce($constants_file);
}
}
return true;
}
/**
* Performs redirect to hard maintenance template
*
* @return void
* @access public
*/
public function redirectToMaintenance()
{
$maintenance_page = WRITEBALE_BASE . '/maintenance.html';
$query_string = ''; // $this->isAdmin ? '' : '?next_template=' . urlencode($_SERVER['REQUEST_URI']);
if ( file_exists(FULL_PATH . $maintenance_page) ) {
header('Location: ' . BASE_PATH . $maintenance_page . $query_string);
exit;
}
}
/**
* Actually runs the parser against current template and stores parsing result
*
* This method gets 't' variable passed to the script, loads the template given in 't' variable and
* parses it. The result is store in {@link $this->HTML} property.
*
* @return void
* @access public
*/
public function Run()
{
// process maintenance mode redirect: begin
$maintenance_mode = $this->getMaintenanceMode();
if ( $maintenance_mode == MaintenanceMode::HARD ) {
$this->redirectToMaintenance();
}
elseif ( $maintenance_mode == MaintenanceMode::SOFT ) {
$maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate');
if ( $this->GetVar('t') != $maintenance_template ) {
$redirect_params = Array ();
if ( !$this->isAdmin ) {
$redirect_params['next_template'] = urlencode($_SERVER['REQUEST_URI']);
}
$this->Redirect($maintenance_template, $redirect_params);
}
}
// process maintenance mode redirect: end
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Run:');
}
if ( $this->isAdminUser ) {
// for permission checking in events & templates
$this->LinkVar('module'); // for common configuration templates
$this->LinkVar('module_key'); // for common search templates
$this->LinkVar('section'); // for common configuration templates
if ( $this->GetVar('m_opener') == 'p' ) {
$this->LinkVar('main_prefix'); // window prefix, that opened selector
$this->LinkVar('dst_field'); // field to set value choosed in selector
}
if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) {
// hide debug output from ajax requests automatically
kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
}
}
elseif ( $this->GetVar('admin') ) {
$admin_session = $this->recallObject('Session.admin');
/* @var $admin_session Session */
// store Admin Console User's ID to Front-End's session for cross-session permission checks
$this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
// user can edit cms blocks (when viewing front-end through admin's frame)
$editing_mode = $this->GetVar('editing_mode');
define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
}
}
kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything
$this->Phrases->setPhraseEditing();
$this->EventManager->ProcessRequest();
$this->InitParser();
$t = $this->GetVar('render_template', $this->GetVar('t'));
if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) {
$cms_handler = $this->recallObject('st_EventHandler');
/* @var $cms_handler CategoriesEventHandler */
$t = ltrim($cms_handler->GetDesignTemplate(), '/');
if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
}
}
/*else {
$cms_handler->SetCatByTemplate();
}*/
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Parsing:');
}
$this->HTML = $this->Parser->Run($t);
if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application after Parsing:');
}
}
/**
* Only renders template
*
* @see kDBEventHandler::_errorNotFound()
*/
public function QuickRun()
{
$this->InitParser();
$this->HTML = $this->ParseBlock(Array ('name' => $this->GetVar('t')));
}
/**
* Performs template parser/cache initialization
*
* @param bool|string $theme_name
* @return void
* @access public
*/
public function InitParser($theme_name = false)
{
if ( !is_object($this->Parser) ) {
$this->Parser = $this->recallObject('NParser');
$this->TemplatesCache = $this->recallObject('TemplatesCache');
}
$this->TemplatesCache->forceThemeName = $theme_name;
}
/**
* Send the parser results to browser
*
* Actually send everything stored in {@link $this->HTML}, to the browser by echoing it.
*
* @return void
* @access public
*/
public function Done()
{
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
$debug_mode = defined('DEBUG_MODE') && $this->isDebugMode();
if ( $debug_mode && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
$this->Debugger->appendMemoryUsage('Application before Done:');
}
if ( $debug_mode ) {
$this->EventManager->runScheduledTasks();
$this->Session->SaveData();
$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
}
else {
// send "Set-Cookie" header before any output is made
$this->Session->SetSession();
$this->HTML = ob_get_clean() . $this->HTML;
}
$this->setContentType();
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 = kUtil::crc32($slow_sql);
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'SlowSqlCapture
WHERE QueryCrc = ' . $query_crc;
$data = $this->Conn->Query($sql, null, true);
if ( $data ) {
$this->_updateAverageStatistics($data, 'Time', $time);
$template_names = explode(',', $data['TemplateNames']);
array_push($template_names, $this->GetVar('t'));
$data['TemplateNames'] = implode(',', array_unique($template_names));
$data['Hits']++;
$data['LastHit'] = adodb_mktime();
$this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']);
}
else {
$data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time;
$data['SqlQuery'] = $slow_sql;
$data['QueryCrc'] = $query_crc;
$data['TemplateNames'] = $this->GetVar('t');
$data['Hits'] = 1;
$data['LastHit'] = adodb_mktime();
$this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture');
}
}
/**
* Checks if output compression options is available
*
* @return bool
* @access protected
*/
protected function UseOutputCompression()
{
if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
return false;
}
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
*
* @return kDBConnection
* @access public
*/
public function &GetADODBConnection()
{
return $this->Conn;
}
/**
* Allows to parse given block name or include template
*
* @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
* @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter.
* @param bool $as_template
* @return string
* @access public
*/
public function ParseBlock($params, $pass_params = false, $as_template = false)
{
if ( substr($params['name'], 0, 5) == 'html:' ) {
return substr($params['name'], 5);
}
return $this->Parser->ParseBlock($params, $pass_params, $as_template);
}
/**
* Checks, that we have given block defined
*
* @param string $name
* @return bool
* @access public
*/
public function ParserBlockFound($name)
{
return $this->Parser->blockFound($name);
}
/**
* Allows to include template with a given name and given parameters
*
* @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
* @return string
* @access public
*/
public function IncludeTemplate($params)
{
return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0);
}
/**
* Return href for template
*
* @param string $t Template path
* @param string $prefix index.php prefix - could be blank, 'admin'
* @param Array $params
* @param string $index_file
* @return string
*/
public function HREF($t, $prefix = '', $params = Array (), $index_file = null)
{
return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
}
/**
* Returns theme template filename and it's corresponding page_id based on given seo template
*
* @param string $seo_template
* @return string
* @access public
*/
public function getPhysicalTemplate($seo_template)
{
return $this->UrlManager->getPhysicalTemplate($seo_template);
}
/**
* Returns template name, that corresponds with given virtual (not physical) page id
*
* @param int $page_id
* @return string|bool
* @access public
*/
public function getVirtualPageTemplate($page_id)
{
return $this->UrlManager->getVirtualPageTemplate($page_id);
}
/**
* Returns variables with values that should be passed through with this link + variable list
*
* @param Array $params
* @return Array
* @access public
*/
public function getPassThroughVariables(&$params)
{
return $this->UrlManager->getPassThroughVariables($params);
}
/**
* Builds url
*
* @param string $t
* @param Array $params
* @param string $pass
* @param bool $pass_events
* @param bool $env_var
* @return string
* @access public
*/
public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true)
{
return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var);
}
/**
* Process QueryString only, create
* events, ids, based on config
* set template name and sid in
* desired application variables.
*
* @param string $env_var environment string value
* @param string $pass_name
* @return Array
* @access public
*/
public function processQueryString($env_var, $pass_name = 'passed')
{
return $this->UrlManager->plain->parse($env_var, $pass_name);
}
/**
* Returns base part of all urls, build on website
*
* @param string $prefix
* @param bool $ssl
* @param bool $add_port
* @return string
* @access public
*/
public function BaseURL($prefix = '', $ssl = null, $add_port = true)
{
if ( $ssl === null ) {
// stay on same encryption level
return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
if ( $ssl ) {
// going from http:// to https://
$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
if ( !$base_url ) {
$ssl_url = $this->siteDomainField('SSLUrl');
$base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL');
}
return rtrim($base_url, '/') . $prefix . '/';
}
// going from https:// to http://
$domain = $this->siteDomainField('DomainName');
if ( $domain === false ) {
$domain = DOMAIN;
}
return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
}
/**
* Redirects user to url, that's build based on given parameters
*
* @param string $t
* @param Array $params
* @param string $prefix
* @param string $index_file
* @return void
* @access public
*/
public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
{
$js_redirect = getArrayValue($params, 'js_redirect');
if ( $t == '' || $t === true ) {
$t = $this->GetVar('t');
}
// pass prefixes and special from previous url
if ( array_key_exists('js_redirect', $params) ) {
unset($params['js_redirect']);
}
// allows to send custom responce code along with redirect header
if ( array_key_exists('response_code', $params) ) {
$response_code = (int)$params['response_code'];
unset($params['response_code']);
}
else {
$response_code = 302; // Found
}
if ( !array_key_exists('pass', $params) ) {
$params['pass'] = 'all';
}
if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) {
// redirects to the same template as current
$params['ajax'] = 'yes';
}
$params['__URLENCODE__'] = 1;
$location = $this->HREF($t, $prefix, $params, $index_file);
if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) {
$this->Debugger->appendTrace();
echo '<strong>Debug output above !!!</strong><br/>' . "\n";
if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
echo 'Referer: <strong>' . $_SERVER['HTTP_REFERER'] . '</strong><br/>' . "\n";
}
echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n";
}
else {
if ( $js_redirect ) {
// show "redirect" template instead of redirecting,
// because "Set-Cookie" header won't work, when "Location"
// header is used later
$this->SetVar('t', 'redirect');
$this->SetVar('redirect_to', $location);
// make all additional parameters available on "redirect" template too
foreach ($params as $name => $value) {
$this->SetVar($name, $value);
}
return;
}
else {
if ( $this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t') ) {
// redirection to other then current template during ajax request
kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
echo '#redirect#' . $location;
}
elseif ( headers_sent() != '' ) {
// some output occurred -> redirect using javascript
echo '<script type="text/javascript">window.location.href = \'' . $location . '\';</script>';
}
else {
// no output before -> redirect using HTTP header
// header('HTTP/1.1 302 Found');
header('Location: ' . $location, true, $response_code);
}
}
}
// session expiration is called from session initialization,
// that's why $this->Session may be not defined here
$session = $this->recallObject('Session');
/* @var $session Session */
if ( $this->InitDone ) {
// if redirect happened in the middle of application initialization don't call event,
// that presumes that application was successfully initialized
$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
}
$session->SaveData();
ob_end_flush();
exit;
}
/**
* Returns translation of given label
*
* @param string $label
* @param bool $allow_editing return translation link, when translation is missing on current language
* @param bool $use_admin use current Admin Console language to translate phrase
* @return string
* @access public
*/
public function Phrase($label, $allow_editing = true, $use_admin = false)
{
return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin);
}
/**
* Replace language tags in exclamation marks found in text
*
* @param string $text
* @param bool $force_escape force escaping, not escaping of resulting string
* @return string
* @access public
*/
public function ReplaceLanguageTags($text, $force_escape = null)
{
return $this->Phrases->ReplaceLanguageTags($text, $force_escape);
}
/**
* Checks if user is logged in, and creates
* user object if so. User object can be recalled
* later using "u.current" prefix_special. Also you may
* get user id by getting "u.current_id" variable.
*
* @return void
* @access protected
*/
protected function ValidateLogin()
{
$session = $this->recallObject('Session');
/* @var $session Session */
$user_id = $session->GetField('PortalUserId');
if ( !$user_id && $user_id != USER_ROOT ) {
$user_id = USER_GUEST;
}
$this->SetVar('u.current_id', $user_id);
if ( !$this->isAdmin ) {
// needed for "profile edit", "registration" forms ON FRONT ONLY
$this->SetVar('u_id', $user_id);
}
$this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional
$this->isAdminUser = $this->isAdmin && $this->LoggedIn();
if ( $this->GetVar('expired') == 1 ) {
// this parameter is set only from admin
$user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login'));
/* @var $user UsersItem */
$user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired');
}
if ( ($user_id != USER_GUEST) && defined('DBG_REQUREST_LOG') && DBG_REQUREST_LOG ) {
$this->HttpQuery->writeRequestLog(DBG_REQUREST_LOG);
}
if ( $user_id != USER_GUEST ) {
// normal users + root
$this->LoadPersistentVars();
}
$user_timezone = $this->Session->GetField('TimeZone');
if ( $user_timezone ) {
putenv('TZ=' . $user_timezone);
}
}
/**
* Loads current user persistent session data
*
* @return void
* @access public
*/
public function LoadPersistentVars()
{
$this->Session->LoadPersistentVars();
}
/**
* Returns configuration option value by name
*
* @param string $name
* @return string
* @access public
*/
public function ConfigValue($name)
{
return $this->cacheManager->ConfigValue($name);
}
/**
* Changes value of individual configuration variable (+resets cache, when needed)
*
* @param string $name
* @param string $value
* @param bool $local_cache_only
* @return string
* @access public
*/
public function SetConfigValue($name, $value, $local_cache_only = false)
{
return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
* @param Array $params
* @param Array $specific_params
+ * @return void
* @access public
*/
public function HandleEvent($event, $params = null, $specific_params = null)
{
if ( isset($params) ) {
$event = new kEvent($params, $specific_params);
}
$this->EventManager->HandleEvent($event);
}
/**
+ * Notifies event subscribers, that event has occured
+ *
+ * @param kEvent $event
+ * @return void
+ */
+ public function notifyEventSubscribers(kEvent $event)
+ {
+ $this->EventManager->notifySubscribers($event);
+ }
+
+ /**
* Allows to process any type of event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function eventImplemented(kEvent $event)
{
return $this->EventManager->eventImplemented($event);
}
/**
* Registers new class in the factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $file Filename in what $real_class is declared
* @param string $pseudo_class Name under this class object will be accessed using getObject method
* @return void
* @access public
*/
public function registerClass($real_class, $file, $pseudo_class = null)
{
$this->Factory->registerClass($real_class, $file, $pseudo_class);
}
/**
* Unregisters existing class from factory
*
* @param string $real_class Real name of class as in class declaration
* @param string $pseudo_class Name under this class object is accessed using getObject method
* @return void
* @access public
*/
public function unregisterClass($real_class, $pseudo_class = null)
{
$this->Factory->unregisterClass($real_class, $pseudo_class);
}
/**
* Add new scheduled task
*
* @param string $short_name name to be used to store last maintenance run info
* @param string $event_string
* @param int $run_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
* @return void
* @access public
*/
public function registerAggregateTag($tag_info)
{
$aggregator = $this->recallObject('TagsAggregator', 'kArray');
/* @var $aggregator kArray */
$tag_data = Array (
$tag_info['LocalPrefix'],
$tag_info['LocalTagName'],
getArrayValue($tag_info, 'LocalSpecial')
);
$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data);
}
/**
* Returns object using params specified, creates it if is required
*
* @param string $name
* @param string $pseudo_class
* @param Array $event_params
* @param Array $arguments
* @return kBase
*/
public function recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
{
/*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) {
// first time, when object with "_prefix_here_" prefix is accessed
$this->Debugger->appendTrace();
}*/
return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
}
/**
* Returns tag processor for prefix specified
*
* @param string $prefix
* @return kDBTagProcessor
* @access public
*/
public function recallTagProcessor($prefix)
{
$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
return $this->recallObject($prefix . '_TagProcessor');
}
/**
* Checks if object with prefix passes was already created in factory
*
* @param string $name object pseudo_class, prefix
* @return bool
* @access public
*/
public function hasObject($name)
{
return $this->Factory->hasObject($name);
}
/**
* Removes object from storage by given name
*
* @param string $name Object's name in the Storage
* @return void
* @access public
*/
public function removeObject($name)
{
$this->Factory->DestroyObject($name);
}
/**
* Get's real class name for pseudo class, includes class file and creates class instance
*
* Pattern: Factory Method
*
* @param string $pseudo_class
* @param Array $arguments
* @return kBase
* @access public
*/
public function makeClass($pseudo_class, $arguments = Array ())
{
return $this->Factory->makeClass($pseudo_class, $arguments);
}
/**
* Checks if application is in debug mode
*
* @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
* @return bool
* @author Alex
* @access public
*/
public function isDebugMode($check_debugger = true)
{
$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
if ($check_debugger) {
$debug_mode = $debug_mode && is_object($this->Debugger);
}
return $debug_mode;
}
/**
* Apply url rewriting used by mod_rewrite or not
*
* @param bool|null $ssl Force ssl link to be build
* @return bool
* @access public
*/
public function RewriteURLs($ssl = false)
{
// case #1,#4:
// we want to create https link from http mode
// we want to create https link from https mode
// conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
// case #2,#3:
// we want to create http link from https mode
// we want to create http link from http mode
// conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
$allow_rewriting =
(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
|| // or allow rewriting for redirect TO httpS or when already in httpS
(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
return kUtil::constOn('MOD_REWRITE') && $allow_rewriting;
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $option
* @param mixed $default
* @return string
* @access public
*/
public function getUnitOption($prefix, $option, $default = false)
{
return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $option
* @param string $value
* @access public
*/
public function setUnitOption($prefix, $option, $value)
{
$this->UnitConfigReader->setUnitOption($prefix,$option,$value);
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
*/
public function getUnitOptions($prefix)
{
return $this->UnitConfigReader->getUnitOptions($prefix);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
public function prefixRegistred($prefix)
{
return $this->UnitConfigReader->prefixRegistred($prefix);
}
/**
* Splits any mixing of prefix and
* special into correct ones
*
* @param string $prefix_special
* @return Array
* @access public
*/
public function processPrefix($prefix_special)
{
return $this->Factory->processPrefix($prefix_special);
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @return void
* @access public
*/
public function setEvent($prefix_special, $event_name)
{
$this->EventManager->setEvent($prefix_special, $event_name);
}
/**
* SQL Error Handler
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
*/
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 ( $exception instanceof kRedirectException ) {
+ /* @var $exception kRedirectException */
+
+ $exception->run();
+ }
+
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
*/
public function NextResourceId()
{
$table_name = TABLE_PREFIX . 'IdGenerator';
$this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE');
$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
$id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
if ( $id === false ) {
$this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)');
$id = 2;
}
$this->Conn->Query('UNLOCK TABLES');
return $id - 1;
}
/**
* Returns genealogical main prefix for sub-table prefix passes
* OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix
*
* @param string $current_prefix
* @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
* @return string
* @access public
*/
public function GetTopmostPrefix($current_prefix, $real_top = false)
{
// 1. get genealogical tree of $current_prefix
$prefixes = Array ($current_prefix);
while ($parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix')) {
if ( !$this->prefixRegistred($parent_prefix) ) {
// stop searching, when parent prefix is not registered
break;
}
$current_prefix = $parent_prefix;
array_unshift($prefixes, $current_prefix);
}
if ( $real_top ) {
return $current_prefix;
}
// 2. find what if parent is passed
$passed = explode(',', $this->GetVar('all_passed'));
foreach ($prefixes as $a_prefix) {
if ( in_array($a_prefix, $passed) ) {
return $a_prefix;
}
}
return $current_prefix;
}
/**
* Triggers email event of type Admin
*
* @param string $email_event_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access public
*/
public function EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
{
return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_ADMIN, $to_user_id, $send_params);
}
/**
* Triggers email event of type User
*
* @param string $email_event_name
* @param int $to_user_id
* @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access public
*/
public function EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
{
return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_FRONTEND, $to_user_id, $send_params);
}
/**
* Triggers general email event
*
* @param string $email_event_name
* @param int $email_event_type (0 for User, 1 for Admin)
* @param int $to_user_id
* @param array $send_params associative array of direct send params,
* possible keys: to_email, to_name, from_email, from_name, message, message_text
* @return kEvent
* @access protected
*/
protected function _emailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
{
$email = $this->makeClass('kEmail');
/* @var $email kEmail */
if ( !$email->findEvent($email_event_name, $email_event_type) ) {
return false;
}
$email->setParams($send_params);
return $email->send($to_user_id);
}
/**
* Allows to check if user in this session is logged in or not
*
* @return bool
* @access public
*/
public function LoggedIn()
{
// no session during expiration process
return is_null($this->Session) ? false : $this->Session->LoggedIn();
}
/**
* Check current user permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckPermission($name, $type = 1, $cat_id = null)
{
$perm_helper = $this->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
return $perm_helper->CheckPermission($name, $type, $cat_id);
}
/**
* Check current admin permissions based on it's group permissions in specified category
*
* @param string $name permission name
* @param int $cat_id category id, current used if not specified
* @param int $type permission type {1 - system, 0 - per category}
* @return int
* @access public
*/
public function CheckAdminPermission($name, $type = 1, $cat_id = null)
{
$perm_helper = $this->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
return $perm_helper->CheckAdminPermission($name, $type, $cat_id);
}
/**
* Set's any field of current visit
*
* @param string $field
* @param mixed $value
* @return void
* @access public
* @todo move to separate module
*/
public function setVisitField($field, $value)
{
if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) {
// admin logins are not registered in visits list
return;
}
$visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0));
/* @var $visit kDBItem */
if ( $visit->isLoaded() ) {
$visit->SetDBField($field, $value);
$visit->Update();
}
}
/**
* Allows to check if in-portal is installed
*
* @return bool
* @access public
*/
public function isInstalled()
{
return $this->InitDone && (count($this->ModuleInfo) > 0);
}
/**
* Allows to determine if module is installed & enabled
*
* @param string $module_name
* @return bool
* @access public
*/
public function isModuleEnabled($module_name)
{
return $this->findModule('Name', $module_name) !== false;
}
/**
* Returns Window ID of passed prefix main prefix (in edit mode)
*
* @param string $prefix
* @return int
* @access public
*/
public function GetTopmostWid($prefix)
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$mode = $this->GetVar($top_prefix . '_mode');
return $mode != '' ? substr($mode, 1) : '';
}
/**
* Get temp table name
*
* @param string $table
* @param mixed $wid
* @return string
* @access public
*/
public function GetTempName($table, $wid = '')
{
return $this->GetTempTablePrefix($wid) . $table;
}
/**
* Builds temporary table prefix based on given window id
*
* @param string $wid
* @return string
* @access public
*/
public function GetTempTablePrefix($wid = '')
{
if ( preg_match('/prefix:(.*)/', $wid, $regs) ) {
$wid = $this->GetTopmostWid($regs[1]);
}
return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
}
/**
* Checks if given table is a temporary table
*
* @param string $table
* @return bool
* @access public
*/
public function IsTempTable($table)
{
static $cache = Array ();
if ( !array_key_exists($table, $cache) ) {
$cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table);
}
return (bool)$cache[$table];
}
/**
* Checks, that given prefix is in temp mode
*
* @param string $prefix
* @param string $special
* @return bool
* @access public
*/
public function IsTempMode($prefix, $special = '')
{
$top_prefix = $this->GetTopmostPrefix($prefix);
$var_names = Array (
$top_prefix,
rtrim($top_prefix . '_' . $special, '_'), // from post
rtrim($top_prefix . '.' . $special, '.'), // assembled locally
);
$var_names = array_unique($var_names);
$temp_mode = false;
foreach ($var_names as $var_name) {
$value = $this->GetVar($var_name . '_mode');
if ( $value && (substr($value, 0, 1) == 't') ) {
$temp_mode = true;
break;
}
}
return $temp_mode;
}
/**
* Return live table name based on temp table name
*
* @param string $temp_table
* @return string
*/
public function GetLiveName($temp_table)
{
if ( preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $temp_table, $rets) ) {
// cut wid from table end if any
return $rets[2];
}
else {
return $temp_table;
}
}
/**
* Stops processing of user request and displays given message
*
* @param string $message
* @access public
*/
public function ApplicationDie($message = '')
{
$message = ob_get_clean() . $message;
if ( $this->isDebugMode() ) {
$message .= $this->Debugger->printReport(true);
}
echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message;
exit;
}
/**
* Returns comma-separated list of groups from given user
*
* @param int $user_id
* @return string
*/
public function getUserGroups($user_id)
{
switch ($user_id) {
case USER_ROOT:
$user_groups = $this->ConfigValue('User_LoggedInGroup');
break;
case USER_GUEST:
$user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup');
break;
default:
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . (int)$user_id;
$res = $this->Conn->GetCol($sql);
$user_groups = Array ($this->ConfigValue('User_LoggedInGroup'));
if ( $res ) {
$user_groups = array_merge($user_groups, $res);
}
$user_groups = implode(',', $user_groups);
}
return $user_groups;
}
/**
* Allows to detect if page is browsed by spider (293 scheduled_tasks supported)
*
* @return bool
* @access public
*/
/*public function IsSpider()
{
static $is_spider = null;
if ( !isset($is_spider) ) {
$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
$robots = file(FULL_PATH . '/core/robots_list.txt');
foreach ($robots as $robot_info) {
$robot_info = explode("\t", $robot_info, 3);
if ( $user_agent == trim($robot_info[2]) ) {
$is_spider = true;
break;
}
}
}
return $is_spider;
}*/
/**
* Allows to detect table's presence in database
*
* @param string $table_name
* @param bool $force
* @return bool
* @access public
*/
public function TableFound($table_name, $force = false)
{
return $this->Conn->TableFound($table_name, $force);
}
/**
* Returns counter value
*
* @param string $name counter name
* @param Array $params counter parameters
* @param string $query_name specify query name directly (don't generate from parameters)
* @param bool $multiple_results
* @return mixed
* @access public
*/
public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
{
$count_helper = $this->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
}
/**
* Resets counter, which are affected by one of specified tables
*
* @param string $tables comma separated tables list used in counting sqls
* @return void
* @access public
*/
public function resetCounters($tables)
{
if ( kUtil::constOn('IS_INSTALL') ) {
return;
}
$count_helper = $this->recallObject('CountHelper');
/* @var $count_helper kCountHelper */
$count_helper->resetCounters($tables);
}
/**
* Sends XML header + optionally displays xml heading
*
* @param string|bool $xml_version
* @return string
* @access public
* @author Alex
*/
public function XMLHeader($xml_version = false)
{
$lang = $this->recallObject('lang.current');
/* @var $lang LanguagesItem */
$this->setContentType('text/xml');
return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . $lang->GetDBField('Charset') . '"?>' : '';
}
/**
* Returns category tree
*
* @param int $category_id
* @return Array
* @access public
*/
public function getTreeIndex($category_id)
{
$tree_index = $this->getCategoryCache($category_id, 'category_tree');
if ( $tree_index ) {
$ret = Array ();
list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
return $ret;
}
return false;
}
/**
* Base category of all categories
* Usually replaced category, with ID = 0 in category-related operations.
*
* @return int
* @access public
*/
public function getBaseCategory()
{
// same, what $this->findModule('Name', 'Core', 'RootCat') does
// don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade
return $this->ModuleInfo['Core']['RootCat'];
}
/**
* Deletes all data, that was cached during unit config parsing (excluding unit config locations)
*
* @param Array $config_variables
* @access public
*/
public function DeleteUnitCache($config_variables = null)
{
$this->cacheManager->DeleteUnitCache($config_variables);
}
/**
* Deletes cached section tree, used during permission checking and admin console tree display
*
* @return void
* @access public
*/
public function DeleteSectionCache()
{
$this->cacheManager->DeleteSectionCache();
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->Factory->setFromCache($data);
$this->UnitConfigReader->setFromCache($data);
$this->EventManager->setFromCache($data);
$this->ReplacementTemplates = $data['Application.ReplacementTemplates'];
$this->RewriteListeners = $data['Application.RewriteListeners'];
$this->ModuleInfo = $data['Application.ModuleInfo'];
}
/**
* Gets object data for caching
* The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
*
* @access public
* @return Array
*/
public function getToCache()
{
return array_merge(
$this->Factory->getToCache(),
$this->UnitConfigReader->getToCache(),
$this->EventManager->getToCache(),
Array (
'Application.ReplacementTemplates' => $this->ReplacementTemplates,
'Application.RewriteListeners' => $this->RewriteListeners,
'Application.ModuleInfo' => $this->ModuleInfo,
)
);
}
public function delayUnitProcessing($method, $params)
{
$this->cacheManager->delayUnitProcessing($method, $params);
}
/**
* Returns current maintenance mode state
*
* @param bool $check_ips
* @return int
* @access public
*/
public function getMaintenanceMode($check_ips = true)
{
$exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : '';
$setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT';
if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) {
$exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false;
if ( !$exception_ip ) {
return constant($setting_name);
}
}
return MaintenanceMode::NONE;
}
/**
* Sets content type of the page
*
* @param string $content_type
* @param bool $include_charset
* @return void
* @access public
*/
public function setContentType($content_type = 'text/html', $include_charset = null)
{
static $aleady_set = false;
if ( $aleady_set ) {
return;
}
$header = 'Content-type: ' . $content_type;
if ( !isset($include_charset) ) {
$include_charset = $content_type = 'text/html' || $content_type = 'text/xml';
}
if ( $include_charset ) {
$language = $this->recallObject('lang.current');
/* @var $language LanguagesItem */
$header .= '; charset=' . $language->GetDBField('Charset');
}
$aleady_set = true;
header($header);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/request_manager.php
===================================================================
--- branches/5.2.x/core/kernel/managers/request_manager.php (revision 15251)
+++ branches/5.2.x/core/kernel/managers/request_manager.php (revision 15252)
@@ -1,483 +1,484 @@
<?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 kRequestManager extends kBase {
/**
* Prevents request from being proceeded twice
*
* @var bool
* @access protected
*/
protected $processed = false;
/**
* Processes request
*
* @access public
*/
public function process()
{
if ( $this->processed ) {
return;
}
$this->dumpRequest();
$this->processOpener();
$events = $this->getEvents();
$all_passed = $this->getAllPassed($events);
// set "all_passed" before kApplication::GetTopmostPrefix method call !
$this->Application->SetVar('all_passed', implode(',', $all_passed));
foreach ($events as $prefix_special => $event_name) {
$event =& $this->runEvent($prefix_special, $event_name);
if ( $event->status == kEvent::erSTOP ) {
// event requested to stop processing at this point
kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
$this->Application->Session->SaveData();
exit;
}
if ( $event->status == kEvent::erPERM_FAIL ) {
$this->processPermissionError($event);
}
if ( ($event->status == kEvent::erSUCCESS || $event->status == kEvent::erPERM_FAIL) && $this->canRedirect($event) ) {
$this->performRedirect($event);
}
}
$this->Application->SetVar('events', $events);
$this->Application->SetVar('passed', implode(',', $all_passed));
$this->processed = true;
}
/**
* Dumps user request to debugger (only when enabled)
*
* @return void
* @access protected
*/
protected function dumpRequest()
{
if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() && kUtil::constOn('DBG_SHOW_HTTPQUERY') ) {
$this->Application->Debugger->appendHTML('HTTPQuery:');
$this->Application->Debugger->dumpVars($this->Application->HttpQuery->_Params);
}
}
/**
* Returns event names, given in request (post, get)
*
* @return Array
* @access protected
*/
protected function getEvents()
{
$post_events = $this->getEventsFromPost();
return $post_events ? $post_events : $this->getEventsFromGet();
}
/**
* Get all passed prefixes
*
* @param Array $events
* @return Array
* @access protected
*/
protected function getAllPassed($events)
{
$ret = explode(',', $this->Application->GetVar('passed'));
foreach ($events as $prefix_special => $event_name) {
if (!$event_name) {
continue;
}
if ($this->Application->isAdmin) {
array_push($ret, $prefix_special);
}
else {
// don't add special on Front-end because of category item list special is autogenerated
$prefix_special = explode('.', $prefix_special);
array_push($ret, $prefix_special[0]);
}
}
return $ret;
}
/**
* Creates and runs event. Returns false, when prefix of given event isn't registered
*
* @param string $prefix_special
* @param string $event_name
*
* @return kEvent|false
* @access protected
*/
protected function &runEvent($prefix_special, $event_name)
{
$event = new kEvent($prefix_special . ':' . $event_name);
if ( preg_match('/(.*?)-(.*)/', $event->Prefix, $regs) && $this->Application->prefixRegistred($regs[1]) ) {
// this is event from cloned config -> load parent config to create valid clone
$this->Application->UnitConfigReader->loadConfig($regs[1]);
$this->Application->UnitConfigReader->runAfterConfigRead($regs[1]);
}
if ( !$this->Application->EventManager->verifyEventPrefix($event, true) ) {
$false = false;
return $false;
}
$event->SetRedirectParam('opener', 's'); // stay on same page after event is called
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
/* @var $event_handler kEventHandler */
if ( ($this->Application->RecallVar('user_id') == USER_ROOT) || $event_handler->CheckPermission($event) ) {
$this->Application->HandleEvent($event);
+ $this->Application->notifyEventSubscribers($event);
}
return $event;
}
/**
* Processes case, when event finished with permission error
*
* @param kEvent $event
* @access protected
*/
protected function processPermissionError($event)
{
// should do redirect but to no_permissions template
$event->redirect = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
$event->SetRedirectParam('pass', 'm');
$themes_helper = $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$event->SetRedirectParam( 'm_cat_id', $themes_helper->getPageByTemplate($event->redirect) );
// restore stuff, that processOpener() changed
$wid = $this->Application->GetVar('m_wid');
$this->Application->RestoreVar( rtrim('opener_stack_' . $wid, '_') );
// don't save last_template, because no_permission template does js history.back and could cause invalid opener_stack content
$this->Application->SetVar('skip_last_template', 1);
}
/**
* Performs redirect after event execution
*
* @param kEvent $event
* @access protected
*/
protected function performRedirect($event)
{
// we need to pass category if the action was submitted to self-template, with the category passed
// and it has not explicitly set redirect template or pass_category param
if ( $this->samePageRedirect($event) && ($event->getEventParam('pass_category') === false) && $this->Application->GetVar('m_cat_id') ) {
$event->SetRedirectParam('pass_category', 1);
}
$wid = $this->Application->GetVar('m_wid');
$redirect_params = $event->getRedirectParams();
if ( $wid && $event->getRedirectParam('opener') == 'u' ) {
// update last element in current opener stack
unset($redirect_params['opener']);
$redirect_template = is_string($event->redirect) ? $event->redirect : null;
$this->openerStackChange($redirect_template, $redirect_params);
// reset opener, because kApplication::HREF will react differently when 'opener' => 'u'
$event->SetRedirectParam('opener', 's');
$event->redirect = defined('CLOSE_POPUP_TPL') ? CLOSE_POPUP_TPL : 'incs/close_popup';
}
if ( $event->getRedirectParam('pass') === false ) {
// pass all discovered units to redirected page unless developer decided otherwise
$event->SetRedirectParam('pass', 'all');
}
$this->Application->Redirect($event->redirect, $event->getRedirectParams(), '', $event->redirectScript);
}
/**
* Checks, if redirect can be made
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function canRedirect($event)
{
return $this->samePageRedirect($event) || strlen($event->redirect) > 0;
}
/**
* Checks, that current template will be displayed after redirect
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function samePageRedirect($event)
{
return $event->redirect === true || $event->redirect == $this->Application->GetVar('t');
}
/**
* Returns event names given in GET
*
* @return Array
* @access protected
*/
protected function getEventsFromGet()
{
$events = Array ();
$discovered_units = $this->Application->HttpQuery->getDiscoveredUnits(false);
foreach ($discovered_units as $prefix_special => $query_string) {
$query_string = array_flip($query_string);
if ( !isset($query_string['event']) ) {
continue;
}
$event_name = $this->Application->GetVar($prefix_special . '_event');
// we need to check for pre 5.1.0 url format, because of "PerPage"
// query string part (that was added in place of "event" query
// string part) is available only since 5.1.0 version
if ($event_name && !is_numeric($event_name)) {
$events[$prefix_special] = $event_name;
}
}
return $events;
}
/**
* Returns event names given in POST
*
* @return Array
* @access protected
*/
protected function getEventsFromPost()
{
$ret = Array ();
$events = $this->Application->GetVar('events');
if (!$events) {
return Array ();
}
foreach ($events as $prefix_special => $event_name) {
if (!$event_name) {
continue;
}
if ( is_array($event_name) ) {
// HTML-input names like "events[prefix.special][event_name]", input value don't matter
$event_name = key($event_name);
$this->Application->SetVar($prefix_special . '_event', $event_name);
}
// HTML-input names like "events[prefix.special]", input value is event name
$ret[$prefix_special] = $event_name;
}
return $ret;
}
/**
* Processes window opener stack
*
* @access protected
*/
protected function processOpener()
{
$opener_stack = $this->Application->makeClass('kOpenerStack');
/* @var $opener_stack kOpenerStack */
switch ( $this->Application->GetVar('m_opener') ) {
case 'r':
$opener_stack->reset();
break;
case 'd':
// "down/push" new template to opener stack, deeplevel++
if ( $this->Application->GetVar('front') ) {
$front_session = $this->Application->recallObject('Session.front');
/* @var $front_session Session */
$opener_stack->pushRaw( '../' . $front_session->RecallVar('last_template') );
}
else {
$opener_stack->pushRaw( $this->Application->RecallVar('last_template') );
}
break;
case 'u':
// "up/pop" last template from opener stack, deeplevel--
$opener_stack->pop();
break;
case 'p':
// pop-up - generate new wid
$parent_wid = $this->Application->GetVar('m_wid'); // window_id of popup's parent window
$popup_wid = (int)$this->Application->RecallVar('last_wid') + 1;
$this->Application->StoreVar('last_wid', $popup_wid);
$this->Application->SetVar('m_wid', $popup_wid);
$popup_opener_stack = $this->Application->makeClass('kOpenerStack', Array ($popup_wid));
/* @var $popup_opener_stack kOpenerStack */
$popup_opener_stack->pushRaw( $this->getLastTemplate($parent_wid) );
$popup_opener_stack->save();
$this->Application->SetVar('m_opener', 's');
/*// store window relations
$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
$window_relations[$popup_wid] = $parent_wid;
$this->Application->StoreVar('window_relations', serialize($window_relations));*/
return;
break;
default:
// "s/0," stay on same deep level
break;
}
$this->Application->SetVar('m_opener', 's');
$opener_stack->save();
}
/**
* Returns last template from window with given id
*
* @param int $window_id
* @return string
* @access protected
*/
protected function getLastTemplate($window_id)
{
if ( $this->Application->GetVar('front') ) {
$front_session = $this->Application->recallObject('Session.front');
/* @var $front_session Session */
return '../' . $front_session->RecallVar( rtrim('last_template_popup_' . $window_id, '_') );
}
if ( $this->Application->GetVar('merge_opener_stack') ) {
// get last template from parent (that was closed) window opener stack
$parent_opener_stack = $this->Application->makeClass('kOpenerStack', Array ($window_id));
/* @var $parent_opener_stack kOpenerStack */
$last_template = $parent_opener_stack->pop(true);
$parent_opener_stack->save(true);
}
else {
$last_template = $this->Application->RecallVar( rtrim('last_template_popup_' . $window_id, '_') );
}
return $last_template;
}
/**
* Allows to add new element to opener stack
*
* @param string $template
* @param Array $params
* @param int $wid
* @access public
*/
public function openerStackPush($template = '', $params = Array (), $wid = null)
{
if ( !isset($params['pass']) ) {
$params['pass'] = 'all';
}
/*// get parent window wid, when this was popup
$window_relations = $this->Application->RecallVar('window_relations');
$window_relations = $window_relations ? unserialize($window_relations) : Array ();
$wid = isset($window_relations[$wid]) ? $window_relations[$wid] : false;*/
$opener_stack = $this->Application->makeClass('kOpenerStack', Array ($wid));
/* @var $opener_stack kOpenerStack */
// change opener stack
$default_params = Array ('m_opener' => 'u', '__URLENCODE__' => 1);
if ( !$this->Application->ConfigValue('UsePopups') && $opener_stack->getWindowID() ) {
// remove wid to show combined header block in editing window
$default_params['m_wid'] = '';
list ($last_template, $last_params, ) = $opener_stack->get(kOpenerStack::LAST_ELEMENT);
// move last popup's opener stack element to main window's opener stack
if ( $last_params ) {
$last_params = array_merge($last_params, $default_params);
$this->openerStackPush($last_template, $last_params, '');
}
}
$params = array_merge($default_params, $params);
$opener_stack->push($template, $params, 'index.php');
$opener_stack->save();
}
/**
* Allows to change last element in opener stack
*
* @param string $new_template
* @param Array $new_params
* @access protected
*/
protected function openerStackChange($new_template = null, $new_params = null)
{
$opener_stack = $this->Application->makeClass('kOpenerStack');
/* @var $opener_stack kOpenerStack */
list ($template, $params, $index_file) = $opener_stack->pop();
if ( isset($new_template) ) {
$template = $new_template;
}
if ( isset($new_params) ) {
$params = array_merge($params, $new_params);
}
if ( !isset($params['pass_events']) ) {
// don't pass events, unless requested
$params['pass_events'] = false;
}
$opener_stack->push($template, $params, $index_file);
$opener_stack->save();
}
}
\ 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 15251)
+++ branches/5.2.x/core/kernel/managers/rewrite_url_processor.php (revision 15252)
@@ -1,973 +1,975 @@
<?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) {
+ if ( $url ) {
foreach ($this->_urlEndings as $url_ending) {
- if (substr($url, strlen($url) - strlen($url_ending)) == $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')) {
+ if ( ($url_ending != $default_ending) && $this->Application->ConfigValue('ForceModRewriteUrlEnding') ) {
$target_url = $this->Application->BaseURL() . $url . $default_ending;
+
+ trigger_error('Mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" without "<strong>' . $default_ending . '</strong>" line ending used', E_USER_NOTICE);
$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 = ' . kUtil::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' => kUtil::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 ) {
$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/event_manager.php
===================================================================
--- branches/5.2.x/core/kernel/event_manager.php (revision 15251)
+++ branches/5.2.x/core/kernel/event_manager.php (revision 15252)
@@ -1,373 +1,504 @@
<?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 kEventManager extends kBase implements kiCacheable {
/**
* Instance of hook manager
*
* @var kHookManager
* @access protected
*/
protected $Hooks = null;
/**
* Instance of scheduled task manager
*
* @var kScheduledTaskManager
* @access protected
*/
protected $ScheduledTasks = null;
/**
* Instance of request manager
*
* @var kRequestManager
* @access protected
*/
protected $Request = null;
/**
* Build events registred for pseudo classes.
* key - pseudo class, value - event name
*
* @var Array
* @access protected
*/
protected $buildEvents = Array ();
/**
* Event recursion tracking stack
*
* @var Array
* @access protected
*/
protected $recursionStack = Array ();
/**
* Creates new instance of kEventManager class
*
*/
public function __construct()
{
parent::__construct();
$this->Hooks = $this->Application->makeClass('kHookManager');
$this->ScheduledTasks = $this->Application->makeClass('kScheduledTaskManager');
$this->Request = $this->Application->makeClass('kRequestManager');
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->Hooks->setFromCache($data);
$this->ScheduledTasks->setFromCache($data);
$this->buildEvents = $data['EventManager.buildEvents'];
}
/**
* Gets object data for caching
*
* @return Array
* @access public
*/
public function getToCache()
{
return array_merge(
$this->Hooks->getToCache(),
$this->ScheduledTasks->getToCache(),
Array (
'EventManager.buildEvents' => $this->buildEvents,
)
);
}
/**
* Returns information about registered scheduled tasks
*
* @param bool $from_cache
* @return Array
* @access public
*/
public function getScheduledTasks($from_cache = false)
{
return $this->ScheduledTasks->getAll($from_cache);
}
/**
* 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->ScheduledTasks->add($short_name, $event_string, $run_interval, $status);
}
/**
* Run registered scheduled tasks with specified event type
*
* @param bool $from_cron
* @access public
*/
public function runScheduledTasks($from_cron = false)
{
$this->ScheduledTasks->runAll($from_cron);
}
/**
* Runs scheduled task based on given data
*
* @param Array $scheduled_task_data
* @return bool
* @access public
*/
public function runScheduledTask($scheduled_task_data)
{
return $this->ScheduledTasks->run($scheduled_task_data);
}
/**
* Registers Hook from sub-prefix 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->Hooks->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->buildEvents[$pseudo_class] = $event_name;
}
/**
* Runs build event for given $pseudo_class instance, when defined
*
* @param string $prefix_special
* @param string $pseudo_class
* @param Array $event_params
* @access public
*/
public function runBuildEvent($prefix_special, $pseudo_class, $event_params)
{
if ( !isset($this->buildEvents[$pseudo_class]) ) {
return ;
}
$event = new kEvent($prefix_special . ':' . $this->buildEvents[$pseudo_class], $event_params);
$this->HandleEvent($event);
}
/**
* Check if event is called twice, that causes recursion
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function isRecursion($event)
{
$event_key = $event->getPrefixSpecial() . ':' . $event->Name;
return in_array($event_key, $this->recursionStack);
}
/**
* Adds event to recursion stack
*
* @param kEvent $event
* @access protected
*/
protected function pushEvent($event)
{
$event_key = $event->getPrefixSpecial() . ':' . $event->Name;
array_push($this->recursionStack, $event_key);
}
/**
* Removes event from recursion stack
*
* @access protected
*/
protected function popEvent()
{
array_pop($this->recursionStack);
}
/**
* Allows to process any type of event
*
* @param kEvent $event
+ * @return void
* @access public
*/
public function HandleEvent($event)
{
if ( $this->isRecursion($event) || !$this->verifyEventPrefix($event) ) {
return;
}
$this->pushEvent($event);
if ( !$event->SkipBeforeHooks ) {
$this->Hooks->runHooks($event, hBEFORE);
if ( $event->status == kEvent::erFATAL ) {
return;
}
}
$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
/* @var $event_handler kEventHandler */
$event_handler->processEvent($event);
if ( $event->status == kEvent::erFATAL ) {
return;
}
if ( !$event->SkipAfterHooks ) {
$this->Hooks->runHooks($event, hAFTER);
}
$this->popEvent();
}
/**
+ * Notifies event subscribers, that event has occured
+ *
+ * @param kEvent $event
+ * @return void
+ */
+ public function notifySubscribers(kEvent $event)
+ {
+ if ( $event->status != kEvent::erSUCCESS ) {
+ return;
+ }
+
+ $cache_key = 'email_to_event_mapping[%EmaileventsSerial%]';
+ $event_mapping = $this->Application->getCache($cache_key);
+
+ if ( $event_mapping === false ) {
+ $this->Conn->nextQueryCachable = true;
+ $sql = 'SELECT EventId, Event, Type, BindToSystemEvent
+ FROM ' . TABLE_PREFIX . 'EmailEvents
+ WHERE BindToSystemEvent <> ""';
+ $event_mapping = $this->Conn->Query($sql, 'BindToSystemEvent');
+
+ $this->Application->setCache($cache_key, $event_mapping);
+ }
+
+ $email_event = Array ();
+
+ if ( isset($event_mapping[(string)$event]) ) {
+ $email_event = $event_mapping[(string)$event];
+ }
+ elseif ( isset($event_mapping[$event->Prefix . '.*:' . $event->Name]) ) {
+ $email_event = $event_mapping[$event->Prefix . '.*:' . $event->Name];
+ }
+
+ if ( !$email_event ) {
+ return;
+ }
+
+ $category_ids = $item_id = $parent_item_id = false;
+ $where_clause = Array ();
+ $where_clause['EmailEventId'] = 'EmailEventId = ' . $email_event['EventId'];
+
+ try {
+ $category = $this->Application->recallObject('c');
+ /* @var $category kDBItem */
+
+ if ( $category->isLoaded() ) {
+ $category_ids = trim(str_replace('|', ',', $category->GetDBField('ParentPath')), ',');
+ }
+ }
+ catch (Exception $e) {
+ }
+
+ $where_clause['CategoryId'] = $this->_getSubscriberFilter('CategoryId', $category_ids, true);
+
+ try {
+ $object = $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( $object->isLoaded() ) {
+ $item_id = $object->GetID();
+ $parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
+
+ if ( $parent_prefix ) {
+ $parent_item_id = $object->getParentId($parent_prefix);
+ }
+ }
+ }
+ catch (Exception $e) {
+ }
+
+ $where_clause['ItemId'] = $this->_getSubscriberFilter('ItemId', $item_id);
+ $where_clause['ParentItemId'] = $this->_getSubscriberFilter('ParentItemId', $parent_item_id);
+
+ $event_params = Array (
+ 'EmailEventId' => $email_event['EventId'],
+ 'CategoryIds' => $category_ids,
+ 'ItemId' => $item_id,
+ 'ParentId' => $parent_item_id,
+ 'where_clause' => $where_clause,
+ );
+
+ $sql_event = new kEvent($event->getPrefixSpecial() . ':OnGetEventSubscribersQuery', $event_params);
+ $sql_event->MasterEvent = $event;
+ $this->HandleEvent($sql_event);
+
+ $subscribers = $this->Conn->GetIterator($sql_event->getEventParam('sql'));
+
+ if ( !count($subscribers) ) {
+ // mapping exists, but nobody has subscribed
+ return;
+ }
+
+ $send_params = Array (
+ 'Prefix' => $event->Prefix,
+ 'Special' => $event->Special,
+ 'PrefixSpecial' => $event->getPrefixSpecial(),
+ );
+
+ $send_method = $email_event['Type'] == EmailEvent::EVENT_TYPE_FRONTEND ? 'EmailEventUser' : 'EmailEventAdmin';
+
+ foreach ($subscribers as $subscriber_info) {
+ $send_params['to_name'] = $subscriber_info['SubscriberEmail'];
+ $send_params['to_email'] = $subscriber_info['SubscriberEmail'];
+ $this->Application->$send_method($email_event['Event'], $subscriber_info['UserId'], $send_params);
+ }
+ }
+
+ /**
+ * Returns filter for searching event subscribers
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param bool $use_in_clause
+ * @return string
+ * @access protected
+ */
+ protected function _getSubscriberFilter($field, $value, $use_in_clause = false)
+ {
+ if ( $value ) {
+ // send to this item subscribers AND to subscribers to all items
+ $clause = $use_in_clause ? ' IN (' . $value . ')' : ' = ' . $this->Conn->qstr($value);
+
+ return $field . $clause . ' OR ' . $field . ' IS NULL';
+ }
+
+ // send to subscribers to all items
+ return $field . ' IS NULL';
+ }
+
+ /**
* Checks, that given event is implemented
*
* @param kEvent $event
* @return bool
* @access public
*/
public function eventImplemented(kEvent $event)
{
if ( !$this->verifyEventPrefix($event, true) ) {
return false;
}
$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
/* @var $event_handler kEventHandler */
return $event_handler->getEventMethod($event) != '';
}
/**
* Checks if event prefix is valid
*
* @param kEvent $event
* @param bool $is_fatal
* @return string
* @access public
*/
public function verifyEventPrefix($event, $is_fatal = false)
{
if ( !$this->Application->prefixRegistred($event->Prefix) ) {
// when "l-cdata" is requested, then load "l", that will clone "l-cdata" unit config
$this->Application->UnitConfigReader->loadConfig($event->Prefix);
if ( !$this->Application->prefixRegistred($event->Prefix) ) {
$error_msg = 'Prefix "<strong>' . $event->Prefix . '</strong>" not registred (requested event "<strong>' . $event->Name . '</strong>")';
if ($is_fatal) {
throw new Exception($error_msg);
}
else {
trigger_error($error_msg, E_USER_WARNING);
}
return false;
}
}
return true;
}
/**
* Processes request
*
* @access public
*/
public function ProcessRequest()
{
$this->Request->process();
}
/**
* Allows to add new element to opener stack
*
* @param string $template
* @param Array $params
* @access public
*/
public function openerStackPush($template = '', $params = Array ())
{
$this->Request->openerStackPush($template, $params);
}
/**
* Set's new event for $prefix_special
* passed
*
* @param string $prefix_special
* @param string $event_name
* @access public
*/
public function setEvent($prefix_special,$event_name)
{
$actions = $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set('events[' . $prefix_special . ']', $event_name);
}
/**
* Allows to determine, that required event is beeing processed right now
*
* @param string $event_key Event name in format prefix[.special]:event_name
* @return bool
* @access public
*/
public function eventRunning($event_key)
{
return array_search($event_key, $this->recursionStack) !== false;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/http_query.php
===================================================================
--- branches/5.2.x/core/kernel/utility/http_query.php (revision 15251)
+++ branches/5.2.x/core/kernel/utility/http_query.php (revision 15252)
@@ -1,760 +1,761 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kHTTPQuery extends Params {
/**
* Cache of QueryString parameters
* from config, that are represented
* in environment variable
*
* @var Array
*/
protected $discoveredUnits = Array ();
/**
* $_POST vars
*
* @var Array
* @access private
*/
var $Post;
/**
* $_GET vars
*
* @var Array
* @access private
*/
var $Get;
/**
* $_COOKIE vars
*
* @var Array
* @access private
*/
var $Cookie;
/**
* $_SERVER vars
*
* @var Array
* @access private
*/
var $Server;
/**
* $_ENV vars
*
* @var Array
* @access private
*/
var $Env;
/**
* Order in what write
* all vars together in
* the same array
*
* @var string
*/
var $Order;
/**
* Uploaded files info
*
* @var Array
* @access private
*/
var $Files;
var $specialsToRemove = Array();
/**
* SessionID is given via "sid" variable in query string
*
* @var bool
*/
var $_sidInQueryString = false;
/**
* Loads info from $_POST, $_GET and
* related arrays into common place
*
* @param string $order
* @access public
*/
public function __construct($order = 'CGPF')
{
parent::__construct();
$this->Order = $order;
if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
// when AJAX request is made from jQuery, then create ajax variable,
// so any logic based in it (like redirects) will not break down
$_GET['ajax'] = 'yes';
}
}
/**
* Discovers unit form request and returns it's QueryString option on success
*
* @param string $prefix_special
*
* @return Array|bool
* @access public
*/
public function discoverUnit($prefix_special)
{
list($prefix) = explode('.', $prefix_special);
$query_string = $this->getQueryString($prefix);
if ($query_string) {
// only units with QueryString option can be discovered
$this->discoveredUnits[$prefix_special] = $query_string;
return $query_string;
}
unset( $this->discoveredUnits[$prefix] );
return false;
}
/**
* Returns units, passed in request
*
* @param bool $prefix_special_only
* @return Array
* @access protected
*/
public function getDiscoveredUnits($prefix_special_only = true)
{
return $prefix_special_only ? array_keys( $this->discoveredUnits ) : $this->discoveredUnits;
}
/**
* Returns QueryMap for requested unit config.
* In case if unit config is a clone, then get parent item's (from prefix) config to create clone
*
* @param string $prefix
* @return Array
* @access protected
*/
protected function getQueryString($prefix)
{
$ret = $this->Application->getUnitOption($prefix, 'QueryString', Array ());
if ( !$ret && preg_match('/(.*?)-(.*)/', $prefix, $regs) ) {
// "#prefix" (new format), "prefix" (old format)
return $this->_getQueryString('#' . $regs[2]);
}
return $ret;
}
/**
* Returns query string (with safety check against missing prefixes)
*
* @param string $prefix
* @return Array
*/
private function _getQueryString($prefix)
{
if ( $this->Application->prefixRegistred($prefix) ) {
return $this->Application->getUnitOption($prefix, 'QueryString');
}
return substr($prefix, 0, 1) == '#' ? $this->_getQueryString( substr($prefix, 1) ) : Array ();
}
/**
* Removes specials from request
*
* @param Array $array
* @return Array
* @access protected
*/
protected function _removeSpecials($array)
{
$ret = Array ();
$removed = false;
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ( $flag ) {
$removed = true;
list ($prefix, $special) = explode('.', $prefix_special, 2);
foreach ($array as $key => $val) {
$new_key = preg_match("/^" . $prefix . "[._]{1}" . $special . "(.*)/", $key, $regs) ? $prefix . $regs[1] : $key;
$ret[$new_key] = is_array($val) ? $this->_removeSpecials($val) : $val;
}
}
}
return $removed ? $ret : $array;
}
public function process()
{
$this->AddAllVars();
$this->removeSpecials();
ini_set('magic_quotes_gpc', 0);
$this->Application->UrlManager->LoadStructureTemplateMapping();
$this->AfterInit();
}
/**
* All all requested vars to
* common storage place
*
* @return void
* @access protected
*/
protected function AddAllVars()
{
for ($i = 0; $i < strlen($this->Order); $i++) {
switch ($this->Order[$i]) {
case 'G':
$this->Get = $this->AddVars($_GET);
if ( array_key_exists('sid', $_GET) ) {
$this->_sidInQueryString = true;
}
$vars = $this->Application->processQueryString($this->Get(ENV_VAR_NAME));
if ( array_key_exists('sid', $vars) ) {
// used by Session::GetPassedSIDValue
$this->Get['sid'] = $vars['sid'];
}
$this->AddParams($vars);
break;
case 'P':
$this->Post = $this->AddVars($_POST);
$this->convertPostEvents();
$this->_processPostEnvVariables();
break;
case 'C':
$this->Cookie = $this->AddVars($_COOKIE);
break;
/*case 'E';
$this->Env = $this->AddVars($_ENV, false); //do not strip slashes!
break;
case 'S';
$this->Server = $this->AddVars($_SERVER, false); //do not strip slashes!
break;*/
case 'F';
$this->convertFiles();
$this->Files = $this->MergeVars($_FILES, false); //do not strip slashes!
break;
}
}
}
/**
* Allow POST variables, that names were transformed by PHP ("." replaced with "_") to
* override variables, that were virtually created through environment variable parsing
*
*/
function _processPostEnvVariables()
{
$passed = $this->Get('passed');
if ( !$passed ) {
return;
}
$passed = explode(',', $passed);
foreach ($passed as $prefix_special) {
if ( strpos($prefix_special, '.') === false ) {
continue;
}
list ($prefix, $special) = explode('.', $prefix_special);
$query_map = $this->getQueryString($prefix);
$post_prefix_special = $prefix . '_' . $special;
foreach ($query_map as $var_name) {
if ( array_key_exists($post_prefix_special . '_' . $var_name, $this->Post) ) {
$this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]);
}
}
}
}
/**
* Removes requested specials from all request variables
*
* @return void
* @access protected
*/
protected function removeSpecials()
{
$this->specialsToRemove = $this->Get('remove_specials');
if ( $this->specialsToRemove ) {
foreach ($this->specialsToRemove as $prefix_special => $flag) {
if ( $flag && strpos($prefix_special, '.') === false ) {
unset($this->specialsToRemove[$prefix_special]);
trigger_error('Incorrect usage of "<strong>remove_specials[' . $prefix_special . ']</strong>" field (no special found)', E_USER_NOTICE);
}
}
$this->_Params = $this->_removeSpecials($this->_Params);
}
}
/**
* Finishes initialization of kHTTPQuery class
*
* @return void
* @access protected
- * TODO: only uses build-in rewrite listeners, when cache is build for the first time
+ * @todo: only uses build-in rewrite listeners, when cache is build for the first time
*/
protected function AfterInit()
{
$rewrite_url = $this->Get('_mod_rw_url_');
if ( $this->Application->RewriteURLs() || $rewrite_url ) {
// maybe call onafterconfigread here
$this->Application->UrlManager->initRewrite();
if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileStart('url_parsing', 'Parsing <b>MOD_REWRITE</b> url');
$this->Application->UrlManager->rewrite->parseRewriteURL();
$description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>' . $this->Get('t') . '</b>)';
$this->Application->Debugger->profileFinish('url_parsing', $description);
}
else {
$this->Application->UrlManager->rewrite->parseRewriteURL();
}
if ( !$rewrite_url && $this->rewriteRedirectRequired() ) {
// rewrite url is missing (e.g. not a script from tools folder)
$url_params = $this->getRedirectParams();
// no idea about how to check, that given template require category to be passed with it, so pass anyway
$url_params['pass_category'] = 1;
$url_params['response_code'] = 301; // Moved Permanently
+ trigger_error('Non mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" used', E_USER_NOTICE);
$this->Application->Redirect('', $url_params);
}
}
else {
$this->Application->VerifyThemeId();
$this->Application->VerifyLanguageId();
}
}
/**
* Checks, that non-rewrite url was visited and it's automatic rewrite is required
*
* @return bool
*/
function rewriteRedirectRequired()
{
$redirect_conditions = Array (
!$this->IsHTTPSRedirect(), // not https <-> http redirect
!$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains)
!defined('GW_NOTIFY'), // not in payment gateway notification script
preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited
$this->Get('t') != 'index', // not on index page
);
$perform_redirect = true;
foreach ($redirect_conditions as $redirect_condition) {
$perform_redirect = $perform_redirect && $redirect_condition;
if (!$perform_redirect) {
return false;
}
}
return true;
}
/**
* This is redirect from https to http or via versa
*
* @return bool
*/
function IsHTTPSRedirect()
{
$http_referer = array_key_exists('HTTP_REFERER', $_SERVER) ? $_SERVER['HTTP_REFERER'] : false;
return (
( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) )
||
( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) )
);
}
/**
* Checks, that referer is out site
*
* @return bool
*/
function refererIsOurSite()
{
if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) {
// no referer -> don't care what happens
return false;
}
$site_helper = $this->Application->recallObject('SiteHelper');
/* @var $site_helper SiteHelper */
$found = false;
$http_referer = $_SERVER['HTTP_REFERER'];
preg_match('/^(.*?):\/\/(.*?)(\/|$)/', $http_referer, $regs); // 1 - protocol, 2 - domain
if ($regs[1] == 'https') {
$found = $site_helper->getDomainByName('SSLUrl', $http_referer) > 0;
if (!$found) {
// check if referer starts with our ssl url
$ssl_url = $this->Application->ConfigValue('SSL_URL');
$found = $ssl_url && preg_match('/^' . preg_quote($ssl_url, '/') . '/', $http_referer);
}
}
else {
$found = $site_helper->getDomainByName('DomainName', $regs[2]) > 0;
if (!$found) {
$found = $regs[2] == DOMAIN;
}
}
return $found;
}
function convertFiles()
{
if ( !$_FILES ) {
return ;
}
$tmp = Array ();
$file_keys = Array ('error', 'name', 'size', 'tmp_name', 'type');
foreach ($_FILES as $file_name => $file_info) {
if ( is_array($file_info['error']) ) {
$tmp[$file_name] = $this->getArrayLevel($file_info['error'], $file_name);
}
else {
$normal_files[$file_name] = $file_info;
}
}
if ( !$tmp ) {
return ;
}
$files = $_FILES;
$_FILES = Array ();
foreach ($tmp as $prefix => $prefix_files) {
$anchor =& $_FILES;
foreach ($prefix_files['keys'] as $key) {
$anchor =& $anchor[$key];
}
foreach ($prefix_files['value'] as $field_name) {
unset($inner_anchor, $copy);
$work_copy = $prefix_files['keys'];
foreach ($file_keys as $file_key) {
$inner_anchor =& $files[$prefix][$file_key];
if ( isset($copy) ) {
$work_copy = $copy;
}
else {
$copy = $work_copy;
}
array_shift($work_copy);
foreach ($work_copy as $prefix_file_key) {
$inner_anchor =& $inner_anchor[$prefix_file_key];
}
$anchor[$field_name][$file_key] = $inner_anchor[$field_name];
}
}
}
// keys: img_temp, 0, values: LocalPath, ThumbPath
}
function getArrayLevel(&$level, $prefix='')
{
$ret['keys'] = $prefix ? Array($prefix) : Array();
$ret['value'] = Array();
foreach($level as $level_key => $level_value)
{
if( is_array($level_value) )
{
$ret['keys'][] = $level_key;
$tmp = $this->getArrayLevel($level_value);
$ret['keys'] = array_merge($ret['keys'], $tmp['keys']);
$ret['value'] = array_merge($ret['value'], $tmp['value']);
}
else
{
$ret['value'][] = $level_key;
}
}
return $ret;
}
/**
* Overwrites GET events with POST events in case if they are set and not empty
*
* @return void
* @access protected
*/
protected function convertPostEvents()
{
$events = $this->Get('events', Array ());
/* @var $events Array */
if ( is_array($events) ) {
$events = array_filter($events);
foreach ($events as $prefix_special => $event_name) {
$this->Set($prefix_special . '_event', $event_name);
}
}
}
function finalizeParsing($passed = Array())
{
if (!$passed) {
return;
}
foreach ($passed as $passed_prefix) {
$this->discoverUnit($passed_prefix); // from mod-rewrite url parsing
}
$this->Set('passed', implode(',', $this->getDiscoveredUnits()));
}
/**
* Saves variables from array specified
* into common variable storage place
*
* @param Array $array
* @param bool $strip_slashes
* @return Array
* @access private
*/
function AddVars($array, $strip_slashes = true)
{
if ( $strip_slashes ) {
$array = $this->StripSlashes($array);
}
foreach ($array as $key => $value) {
$this->Set($key, $value);
}
return $array;
}
function MergeVars($array, $strip_slashes = true)
{
if ( $strip_slashes ) {
$array = $this->StripSlashes($array);
}
foreach ($array as $key => $value_array) {
// $value_array is an array too
$this->_Params = kUtil::array_merge_recursive($this->_Params, Array ($key => $value_array));
}
return $array;
}
function StripSlashes($array)
{
static $magic_quotes = null;
if (!isset($magic_quotes)) {
$magic_quotes = get_magic_quotes_gpc();
}
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = $this->StripSlashes($value);
}
else {
if ($magic_quotes) {
$value = stripslashes($value);
}
if (!$this->Application->isAdmin) {
$value = htmlspecialchars($value);
}
$array[$key] = $value;
}
}
return $array;
}
/**
* Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls
*
* @param bool $access_error Method is called during no_permission, require login, session expiration link preparation
* @return Array
*/
function getRedirectParams($access_error = false)
{
$vars = $this->Get;
$unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action');
if (!$this->_sidInQueryString) {
$unset_vars[] = 'sid';
}
// remove system variables
foreach ($unset_vars as $var_name) {
if (array_key_exists($var_name, $vars)) {
unset($vars[$var_name]);
}
}
if ($access_error) {
// place 1 of 2 (also in UsersEventHandler::OnSessionExpire)
$vars = $this->_removePassThroughVariables($vars);
}
// transform arrays
return $this->_transformArrays($vars);
}
/**
* Removes all pass_though variables from redirect params
*
* @param Array $url_params
* @return Array
*/
function _removePassThroughVariables($url_params)
{
$pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : '';
if (!$pass_through) {
return $url_params;
}
$pass_through = explode(',', $pass_through . ',pass_through');
foreach ($pass_through as $pass_through_var) {
unset($url_params[$pass_through_var]);
}
$url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again
return $url_params;
}
function _transformArrays($array, $level_prefix = '')
{
$ret = Array ();
foreach ($array as $var_name => $var_value) {
$new_var_name = $level_prefix ? $level_prefix . '[' . $var_name . ']' : $var_name;
if (is_array($var_value)) {
$ret = array_merge($ret, $this->_transformArrays($var_value, $new_var_name));
}
else {
$ret[$new_var_name] = $var_value;
}
}
return $ret;
}
function writeRequestLog($filename)
{
$log_file = (defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/' . $filename;
if ( is_writable(dirname($log_file)) ) {
$fp = fopen($log_file, 'a');
if ( $fp ) {
$session = $this->Application->recallObject('Session');
/* @var $session Session */
$user_id = $session->GetField('PortalUserId');
$admin_mark = $this->Application->isAdmin ? 'ADMIN' : 'FRONT';
$data = '[' . date('D M d H:i:s Y') . '] ' . $admin_mark . '; ip: ' . $_SERVER['REMOTE_ADDR'] . '; user_id: ' . $user_id . '; sid: ' . $this->Application->GetSID() . '; request: ' . "\n";
if ( $this->Get ) {
$data .= "_GET:\n" . print_r($this->Get, true);
}
if ( $this->Post ) {
$data .= "_POST:\n" . print_r($this->Post, true);
}
if ( $this->Cookie ) {
$data .= "_COOKIE:\n" . print_r($this->Cookie, true);
}
$data .= str_repeat('=', 100) . "\n";
fwrite($fp, $data);
fclose($fp);
}
else {
trigger_error('Request Log directory not writable', E_USER_WARNING);
}
}
else {
trigger_error('Request Log directory not writable', E_USER_WARNING);
}
}
/**
* Checks, that url is empty
*
* @return bool
* @access public
*/
public function isEmptyUrl()
{
if ( $this->Application->RewriteURLs() ) {
return !$this->Get('_mod_rw_url_');
}
return !count($this->Get);
}
}
\ 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 15251)
+++ branches/5.2.x/core/kernel/utility/debugger.php (revision 15252)
@@ -1,1952 +1,1963 @@
<?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 = '@' . 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
* @access public
*/
public function highlightString($string)
{
if ( !(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError ) {
return nl2br($string);
}
$string = str_replace(Array ('\\', '/'), Array ('_no_match_string_', '_n_m_s_'), $string);
$this->_compileError = true; // next line is possible cause of compile error
$string = highlight_string('<?php ' . $string . ' ?>', true);
$this->_compileError = false;
$string = str_replace(Array ('_no_match_string_', '_n_m_s_'), Array ('\\', '/'), $string);
if ( strlen($string) >= 65536 ) {
// preg_replace will fail, when string is longer, then 65KB
return str_replace(Array ('&lt;?php&nbsp;', '?&gt;'), '', $string);
}
return preg_replace('/&lt;\?(.*)php&nbsp;(.*)\?&gt;/Us', '\\2', $string);
}
/**
* Determine by php type of browser used to show debugger
*
* @return bool
* @access private
*/
private function isGecko()
{
// we need isset because we may run scripts from shell with no user_agent at all
return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false;
}
/**
* Returns link for editing php file (from error) in external editor
*
* @param string $file filename with path from root folder
* @param int $lineno line number in file where error is found
* @param string $title text to show on file edit link
* @return string
* @access public
*/
public function getFileLink($file, $lineno = 1, $title = '')
{
if ( !$title ) {
$title = str_replace('/', '\\', $this->getLocalFile($file));
}
if ( $this->isGecko() ) {
return '<a href="file://' . $this->getLocalFile($file) . '">' . $title . '</a>';
}
else {
return '<a href="javascript:$Debugger.editFile(\'' . $this->getLocalFile($file) . '\', ' . $lineno . ');" title="' . $file . '">' . $title . '</a>';
}
}
/**
* Converts filepath on server to filepath in mapped DocumentRoot on developer pc
*
* @param string $remoteFile
* @return string
* @access private
*/
private function getLocalFile($remoteFile)
{
return preg_replace('/^' . preg_quote(DOC_ROOT, '/') . '/', DBG_LOCAL_BASE_PATH, $remoteFile, 1);
}
/**
* Appends call trace till this method call
*
* @param int $levels_to_shift
* @return void
* @access public
*/
public function appendTrace($levels_to_shift = 1)
{
$levels_shifted = 0;
$trace = debug_backtrace();
while ( $levels_shifted < $levels_to_shift ) {
array_shift($trace);
$levels_shifted++;
}
$this->Data[] = Array ('trace' => $trace, 'debug_type' => 'trace');
}
/**
* Appends call trace till this method call
*
* @param Exception $exception
* @return void
* @access private
*/
private function appendExceptionTrace(&$exception)
{
$trace = $exception->getTrace();
$this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace');
}
/**
* Adds memory usage statistics
*
* @param string $msg
* @param int $used
* @return void
* @access public
*/
public function appendMemoryUsage($msg, $used = null)
{
if ( !isset($used) ) {
$used = round(memory_get_usage() / 1024);
}
$this->appendHTML('<b>Memory usage</b> ' . $msg . ' ' . $used . 'Kb');
}
/**
* Appends HTML code without transformations
*
* @param string $html
* @return void
* @access public
*/
public function appendHTML($html)
{
$this->Data[] = Array ('html' => $html, 'debug_type' => 'html');
}
/**
* 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);
$debugger_params = Array (
'RowSeparator' => $this->rowSeparator,
'ErrorsCount' => (int)$this->getProfilerTotal('error_handling'),
'IsFatalError' => $this->IsFatalError ? 'true' : 'false',
'SQLCount' => (int)$this->getProfilerTotal('sql'),
'SQLTime' => isset($this->ProfilerTotals['sql']) ? sprintf('%.5f', $this->ProfilerTotals['sql']) : 0,
'ScriptTime' => sprintf('%.5f', $this->ProfilerData['script_runtime']['ends'] - $this->ProfilerData['script_runtime']['begins']),
'ScriptMemory' => DebuggerUtil::formatSize($this->getMemoryUsed($debugger_start)),
);
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?v3"></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 json_encode($debugger_params); ?>);
$Debugger.createEnvironment(<?php echo DBG_WINDOW_WIDTH; ?>, <?php echo $this->getWindowWidth(); ?>);
$Debugger.DOMViewerURL = '<?php echo constant('DBG_DOMVIEWER'); ?>';
$Debugger.EditorPath = '<?php echo defined('DBG_EDITOR') ? addslashes(DBG_EDITOR) : '' ?>';
$Debugger.DebugURL = '<?php echo $this->baseURL.'/debugger_responce.php?sid='.$this->rowSeparator.'&path='.urlencode($dbg_path); ?>';
$Debugger.EventURL = '<?php echo /*is_object($application->Factory) &&*/ $application->InitDone ? $application->HREF('dummy', '', Array ('pass' => 'm', '__NO_REWRITE__' => 1)) : ''; ?>';
$Debugger.BasePath = '<?php echo $this->basePath; ?>';
<?php
$is_install = defined('IS_INSTALL') && IS_INSTALL;
if ( $this->IsFatalError || (!$is_install && DBG_RAISE_ON_WARNINGS && $this->WarningCount) ) {
echo '$Debugger.Toggle();';
}
if ( DBG_TOOLBAR_BUTTONS ) {
echo '$Debugger.AddToolbar("$Debugger");';
}
?>
window.focus();
</script>
<?php
if ( $returnResult ) {
$ret = ob_get_contents();
if ( $clean_output_buffer ) {
ob_end_clean();
}
$ret .= $this->getShortReport($this->getMemoryUsed($debugger_start));
$this->reportDone = true;
return $ret;
}
else {
if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) {
$this->breakOutofBuffering();
}
elseif ( $clean_output_buffer ) {
ob_clean();
}
echo $this->getShortReport($this->getMemoryUsed($debugger_start));
$this->reportDone = true;
}
return '';
}
function getMemoryUsed($debugger_start)
{
if ( !isset($this->ProfilerTotals['error_handling']) ) {
$memory_used = $debugger_start;
$this->ProfilerTotalCount['error_handling'] = 0;
}
else {
$memory_used = $debugger_start - $this->ProfilerTotals['error_handling'];
}
return $memory_used;
}
/**
* Format's memory usage report by debugger
*
* @param int $memory_used
* @return string
* @access private
*/
private function getShortReport($memory_used)
{
if ( DebuggerUtil::constOn('DBG_TOOLBAR_BUTTONS') ) {
// evenrything is in toolbar - don't duplicate
return '';
}
else {
// toolbar not visible, then show sql & error count too
$info = Array (
'Script Runtime' => 'PROFILE:script_runtime',
'SQL\'s Runtime' => 'PROFILE_T:sql',
'-' => 'SEP:-',
'Notice / Warning' => 'PROFILE_TC:error_handling',
'SQLs Count' => 'PROFILE_TC:sql',
);
}
$ret = ''; // '<tr><td>Application:</td><td><b>' . DebuggerUtil::formatSize($memory_used) . '</b> (' . $memory_used . ')</td></tr>';
foreach ($info as $title => $value_key) {
list ($record_type, $record_data) = explode(':', $value_key, 2);
switch ( $record_type ) {
case 'PROFILE': // profiler totals value
$Data =& $this->ProfilerData[$record_data];
$profile_time = ($Data['ends'] - $Data['begins']); // in seconds
$ret .= '<tr><td>' . $title . ':</td><td><b>' . sprintf('%.5f', $profile_time) . ' s</b></td></tr>';
break;
case 'PROFILE_TC': // profile totals record count
$record_cell = '<td>';
if ( $record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0 ) {
$record_cell = '<td class="debug_error">';
}
$ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . $this->ProfilerTotalCount[$record_data] . '</b></td></tr>';
break;
case 'PROFILE_T': // profile total
$record_cell = '<td>';
$total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0;
$ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . sprintf('%.5f', $total) . ' s</b></td></tr>';
break;
case 'SEP':
$ret .= '<tr><td colspan="2" style="height: 1px; background-color: #000000; padding: 0px;"><img src="' . $this->dummyImage . '" height="1" alt=""/></td></tr>';
break;
}
}
return '<br /><table class="dbg_stats_table"><tr><td style="border-color: #FFFFFF;"><table class="dbg_stats_table" align="left">' . $ret . '</table></td></tr></table>';
}
/**
* User-defined error handler
*
* @throws Exception
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param array $errcontext
* @return bool
* @access public
*/
public function saveError($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
{
$this->ProfilerData['error_handling']['begins'] = memory_get_usage();
$errorType = $this->getErrorNameByCode($errno);
if (!$errorType) {
throw new Exception('Unknown error type [' . $errno . ']');
return false;
}
elseif ( substr($errorType, 0, 5) == 'Fatal' ) {
$this->IsFatalError = true;
$this->appendTrace(4);
}
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
+ * Adds exception details into debugger but don't cause fatal error
*
* @param Exception $exception
* @return void
* @access public
*/
- public function saveException($exception)
+ public function appendException($exception)
{
$this->ProfilerData['error_handling']['begins'] = memory_get_usage();
$this->appendExceptionTrace($exception);
$errno = $exception->getCode();
$errstr = $exception->getMessage();
$errfile = $exception->getFile();
$errline = $exception->getLine();
$this->expandError($errstr, $errfile, $errline);
$this->Data[] = Array (
'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline,
'exception_class' => get_class($exception), 'debug_type' => 'exception'
);
$this->ProfilerData['error_handling']['ends'] = memory_get_usage();
$this->profilerAddTotal('error_handling', 'error_handling');
+ }
+ /**
+ * User-defined exception handler
+ *
+ * @param Exception $exception
+ * @return void
+ * @access public
+ */
+ public function saveException($exception)
+ {
+ $this->appendException($exception);
$this->IsFatalError = true;
// append debugger report to data in buffer & clean buffer afterwards
die( $this->breakOutofBuffering(false) . $this->printReport(true) );
}
/**
* Transforms short error messages into long ones
*
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return void
* @access private
*/
private function expandError(&$errstr, &$errfile, &$errline)
{
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/event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/event_handler.php (revision 15251)
+++ branches/5.2.x/core/kernel/event_handler.php (revision 15252)
@@ -1,212 +1,227 @@
<?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!');
/**
* 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 the form which contains $Prefix_Special then note 1st item.
* Example: LinkVar($event->getPrefixSpecial(true).'_varname', $event->getPrefixSpecial().'_varname')
*
*/
/**
* Default event handler. Mostly abstract class
*
*/
class kEventHandler extends kBase {
/**
* In case if event should be handled with method, which name differs from
* event name, then it should be specified here.
* key - event name, value - event method
*
* @var Array
* @access protected
*/
protected $eventMethods = Array ();
/**
* Defines mapping vs event names and permission names
*
* @var Array
* @access protected
*/
protected $permMapping = Array ();
public function __construct()
{
parent::__construct();
$this->mapEvents();
$this->mapPermissions();
}
/**
* Define alternative event processing method names
*
* @return void
* @see kEventHandler::$eventMethods
* @access protected
*/
protected function mapEvents()
{
}
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
}
/**
* Returns prefix and special (when present) joined by a "."
*
* @return string
* @access private
*/
public function getPrefixSpecial()
{
throw new Exception('Usage of getPrefixSpecial() method is forbidden in kEventHandler class children. Use $event->getPrefixSpecial(true); instead');
}
/**
* Executes event, specified in $event variable
*
* @param kEvent $event
* @return void
* @access public
*/
public function processEvent(kEvent $event)
{
$event_name = $this->getEventMethod($event);
$this->$event_name($event);
}
/**
* Returns method name, that should called to process given event.
* When no such method exists and exception is thrown.
*
* @param kEvent $event
* @return string
* @throws Exception
*/
public function getEventMethod(kEvent $event)
{
$event_name = $event->Name;
if ( array_key_exists($event_name, $this->eventMethods) ) {
$event_name = $this->eventMethods[$event_name];
}
if ( method_exists($this, $event_name) ) {
return $event_name;
}
throw new Exception('Event "<strong>' . $event->Name . '</strong>" not implemented in class "<strong>' . get_class($this) . '</strong>"');
}
/**
* Sample dummy event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBuild(kEvent $event)
{
}
/**
* Returns to previous template in opener stack
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnGoBack(kEvent $event)
{
$url = $this->Application->RecallVar('export_finish_url');
if ( $url ) {
$this->Application->Redirect('external:' . $url);
}
$event->SetRedirectParam('opener', 'u');
}
/**
* 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)
{
}
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
$perm_helper = $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
return $perm_helper->CheckEventPermission($event, $this->permMapping);
}
/**
* Occurs, when config was parsed, allows to change config data dynamically
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
}
+ /**
+ * Returns sql query to be used for event subscriber list selection
+ *
+ * @param kEvent $event
+ * @return string
+ * @access protected
+ */
+ protected function OnGetEventSubscribersQuery(kEvent $event)
+ {
+ $sql = 'SELECT SubscriberEmail, UserId
+ FROM ' . TABLE_PREFIX . 'EmailEventSubscribers
+ WHERE (' . implode(') AND (', $event->getEventParam('where_clause')) . ')';
+ $event->setEventParam('sql', $sql);
+ }
+
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/kbase.php
===================================================================
--- branches/5.2.x/core/kernel/kbase.php (revision 15251)
+++ branches/5.2.x/core/kernel/kbase.php (revision 15252)
@@ -1,1084 +1,1160 @@
<?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!');
/**
* Base
*
*/
class kBase {
/**
* Reference to global kApplication instance
*
* @var kApplication
* @access protected
*/
protected $Application = null;
/**
* Connection to database
*
* @var kDBConnection
* @access protected
*/
protected $Conn = null;
/**
* Prefix, used during object creation
*
* @var string
* @access public
*/
public $Prefix = '';
/**
* Special, used during object creation
*
* @var string
* @access public
*/
public $Special = '';
/**
* Joined prefix and special, usually taken directly from
* tag beeing processed, to use in kApplication::recallObject method
*
* @var string
* @access protected
* @see kApplication::recallObject
*/
protected $prefixSpecial = '';
/**
* Set's references to kApplication and kDBConnection class instances
*
* @access public
* @see kApplication
* @see kDBConnection
*/
public function __construct()
{
$this->Application =& kApplication::Instance();
$this->Conn =& $this->Application->GetADODBConnection();
}
/**
* Set's prefix and special
*
* @param string $prefix
* @param string $special
* @access public
*/
public function Init($prefix, $special)
{
$prefix = explode('_', $prefix, 2);
$this->Prefix = $prefix[0];
$this->Special = $special;
$this->prefixSpecial = rtrim($this->Prefix . '.' . $this->Special, '.');
}
/**
* Returns prefix and special (when present) joined by a "."
*
* @return string
* @access public
*/
public function getPrefixSpecial()
{
return $this->prefixSpecial;
}
}
+
class kHelper extends kBase {
/**
* Performs helper initialization
*
* @access public
*/
public function InitHelper()
{
}
/**
* Append prefix and special to tag
* params (get them from tagname) like
* they were really passed as params
*
* @param string $prefix_special
* @param Array $tag_params
* @return Array
* @access protected
*/
protected function prepareTagParams($prefix_special, $tag_params = Array())
{
$parts = explode('.', $prefix_special);
$ret = $tag_params;
$ret['Prefix'] = $parts[0];
$ret['Special'] = count($parts) > 1 ? $parts[1] : '';
$ret['PrefixSpecial'] = $prefix_special;
return $ret;
}
}
+
abstract class kDBBase extends kBase {
/**
* Name of primary key field for the unit
*
* @var string
* @access public
* @see kDBBase::TableName
*/
public $IDField = '';
/**
* Unit's database table name
*
* @var string
* @access public
*/
public $TableName = '';
/**
* Form name, used for validation
*
* @var string
*/
protected $formName = '';
/**
* Final form configuration
*
* @var Array
*/
protected $formConfig = Array ();
/**
* SELECT, FROM, JOIN parts of SELECT query (no filters, no limit, no ordering)
*
* @var string
* @access protected
*/
protected $SelectClause = '';
/**
* Unit fields definitions (fields from database plus virtual fields)
*
* @var Array
* @access protected
*/
protected $Fields = Array ();
/**
* Mapping between unit custom field IDs and their names
*
* @var Array
* @access protected
*/
protected $customFields = Array ();
/**
* Unit virtual field definitions
*
* @var Array
* @access protected
* @see kDBBase::getVirtualFields()
* @see kDBBase::setVirtualFields()
*/
protected $VirtualFields = Array ();
/**
* Fields that need to be queried using custom expression, e.g. IF(...) AS value
*
* @var Array
* @access protected
*/
protected $CalculatedFields = Array ();
/**
* Fields that contain aggregated functions, e.g. COUNT, SUM, etc.
*
* @var Array
* @access protected
*/
protected $AggregatedCalculatedFields = Array ();
/**
* Tells, that multilingual fields sould not be populated by default.
* Can be overriden from kDBBase::Configure method
*
* @var bool
* @access protected
*/
protected $populateMultiLangFields = false;
/**
* Event, that was used to create this object
*
* @var kEvent
* @access protected
*/
protected $parentEvent = null;
/**
* Sets new parent event to the object
*
* @param kEvent $event
* @return void
* @access public
*/
public function setParentEvent($event)
{
$this->parentEvent = $event;
}
/**
* Set object' TableName to LIVE table, defined in unit config
*
* @access public
*/
public function SwitchToLive()
{
$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
}
/**
* Set object' TableName to TEMP table created based on table, defined in unit config
*
* @access public
*/
public function SwitchToTemp()
{
$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
$this->TableName = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix);
}
/**
* Checks if object uses temp table
*
* @return bool
* @access public
*/
public function IsTempTable()
{
return $this->Application->IsTempTable($this->TableName);
}
/**
* Sets SELECT part of list' query
*
* @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE
* @access public
*/
public function SetSelectSQL($sql)
{
$this->SelectClause = $sql;
}
/**
* Returns object select clause without any transformations
*
* @return string
* @access public
*/
public function GetPlainSelectSQL()
{
return $this->SelectClause;
}
/**
* Returns SELECT part of list' query.
* 1. Occurrences of "%1$s" and "%s" are replaced to kDBBase::TableName
* 2. Occurrences of "%3$s" are replaced to temp table prefix (only for table, using TABLE_PREFIX)
*
* @param string $base_query given base query will override unit default select query
* @param bool $replace_table replace all possible occurrences
* @return string
* @access public
* @see kDBBase::replaceModePrefix
*/
public function GetSelectSQL($base_query = null, $replace_table = true)
{
if (!isset($base_query)) {
$base_query = $this->SelectClause;
}
if (!$replace_table) {
return $base_query;
}
$query = str_replace(Array('%1$s', '%s'), $this->TableName, $base_query);
return $this->replaceModePrefix($query);
}
/**
* Allows sub-stables to be in same mode as main item (e.g. LEFT JOINED ones)
*
* @param string $query
* @return string
* @access protected
*/
protected function replaceModePrefix($query)
{
$live_table = substr($this->Application->GetLiveName($this->TableName), strlen(TABLE_PREFIX));
if (preg_match('/'.preg_quote(TABLE_PREFIX, '/').'(.*)'.preg_quote($live_table, '/').'/', $this->TableName, $rets)) {
// will only happen, when table has a prefix (like in K4)
return str_replace('%3$s', $rets[1], $query);
}
// will happen, when K3 table without prefix is used
return $query;
}
/**
* Sets calculated fields
*
* @param Array $fields
* @access public
*/
public function setCalculatedFields($fields)
{
$this->CalculatedFields = $fields;
}
/**
* Adds calculated field declaration to object.
*
* @param string $name
* @param string $sql_clause
* @access public
*/
public function addCalculatedField($name, $sql_clause)
{
$this->CalculatedFields[$name] = $sql_clause;
}
/**
* Returns required mixing of aggregated & non-aggregated calculated fields
*
* @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only
* @return Array
* @access public
*/
public function getCalculatedFields($aggregated = 1)
{
switch ($aggregated) {
case 0:
$fields = array_merge($this->CalculatedFields, $this->AggregatedCalculatedFields);
break;
case 1:
$fields = $this->CalculatedFields;
break;
case 2:
$fields = $this->AggregatedCalculatedFields; // TODO: never used
break;
default:
$fields = Array();
break;
}
return $fields;
}
/**
* Checks, that given field is a calculated field
*
* @param string $field
* @return bool
* @access public
*/
public function isCalculatedField($field)
{
return array_key_exists($field, $this->CalculatedFields);
}
/**
* Insert calculated fields sql into query in place of %2$s,
* return processed query.
*
* @param string $query
* @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only
* @return string
* @access protected
*/
protected function addCalculatedFields($query, $aggregated = 1)
{
$fields = $this->getCalculatedFields($aggregated);
if ($fields) {
$sql = Array ();
$fields = str_replace('%2$s', $this->Application->GetVar('m_lang'), $fields);
foreach ($fields as $field_name => $field_expression) {
$sql[] = '('.$field_expression.') AS `'.$field_name.'`';
}
$sql = implode(',',$sql);
return $this->Application->ReplaceLanguageTags( str_replace('%2$s', ','.$sql, $query) );
}
return str_replace('%2$s', '', $query);
}
/**
* Performs initial object configuration, which includes setting the following:
* - primary key and table name
* - field definitions (including field modifiers, formatters, default values)
*
* @param bool $populate_ml_fields create all ml fields from db in config or not
* @param string $form_name form name for validation
* @access public
*/
public function Configure($populate_ml_fields = null, $form_name = null)
{
if ( isset($populate_ml_fields) ) {
$this->populateMultiLangFields = $populate_ml_fields;
}
$this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField');
$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
$this->initForm($form_name);
$this->defineFields();
$this->ApplyFieldModifiers(null, true); // should be called only after all fields definitions been set
$this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant!
// only set on first call of method
if ( isset($populate_ml_fields) ) {
$this->SetDefaultValues();
}
}
/**
* Adjusts object according to given form name
*
* @param string $form_name
* @return void
* @access protected
*/
protected function initForm($form_name = null)
{
$forms = $this->Application->getUnitOption($this->Prefix, 'Forms', Array ());
$this->formName = $form_name;
$this->formConfig = isset($forms['default']) ? $forms['default'] : Array ();
if ( !$this->formName ) {
return ;
}
if ( !isset($forms[$this->formName]) ) {
trigger_error('Form "<strong>' . $this->formName . '</strong>" isn\'t declared in "<strong>' . $this->Prefix . '</strong>" unit config.', E_USER_NOTICE);
}
else {
$this->formConfig = kUtil::array_merge_recursive($this->formConfig, $forms[$this->formName]);
}
}
/**
* Add field definitions from all possible sources
* Used field sources: database fields, custom fields, virtual fields, calculated fields, aggregated calculated fields
*
* @access protected
*/
protected function defineFields()
{
$this->Fields = $this->getFormOption('Fields', Array ());
$this->customFields = $this->getFormOption('CustomFields', Array());
$this->setVirtualFields( $this->getFormOption('VirtualFields', Array ()) );
$calculated_fields = $this->getFormOption('CalculatedFields', Array());
$this->CalculatedFields = $this->getFieldsBySpecial($calculated_fields);
$aggregated_calculated_fields = $this->getFormOption('AggregatedCalculatedFields', Array());
$this->AggregatedCalculatedFields = $this->getFieldsBySpecial($aggregated_calculated_fields);
}
/**
* Returns form name, used for validation
*
* @return string
*/
public function getFormName()
{
return $this->formName;
}
/**
* Reads unit (specified by $prefix) option specified by $option and applies form change to it
*
* @param string $option
* @param mixed $default
* @return string
* @access public
*/
public function getFormOption($option, $default = false)
{
$ret = $this->Application->getUnitOption($this->Prefix, $option, $default);
if ( isset($this->formConfig[$option]) ) {
$ret = kUtil::array_merge_recursive($ret, $this->formConfig[$option]);
}
return $ret;
}
/**
* Only exteracts fields, that match current object Special
*
* @param Array $fields
* @return Array
* @access protected
*/
protected function getFieldsBySpecial($fields)
{
if ( array_key_exists($this->Special, $fields) ) {
return $fields[$this->Special];
}
return array_key_exists('', $fields) ? $fields[''] : Array();
}
/**
* Sets aggeregated calculated fields
*
* @param Array $fields
* @access public
*/
public function setAggregatedCalculatedFields($fields)
{
$this->AggregatedCalculatedFields = $fields;
}
/**
* Set's field names from table from config
*
* @param Array $fields
* @access public
*/
public function setCustomFields($fields)
{
$this->customFields = $fields;
}
/**
* Returns custom fields information from table from config
*
* @return Array
* @access public
*/
public function getCustomFields()
{
return $this->customFields;
}
/**
* Set's fields information from table from config
*
* @param Array $fields
* @access public
*/
public function setFields($fields)
{
$this->Fields = $fields;
}
/**
* Returns fields information from table from config
*
* @return Array
* @access public
*/
public function getFields()
{
return $this->Fields;
}
/**
* Checks, that given field exists
*
* @param string $field
* @return bool
* @access public
*/
public function isField($field)
{
return array_key_exists($field, $this->Fields);
}
/**
* Override field options with ones defined in submit via "field_modfiers" array (common for all prefixes)
*
* @param Array $field_modifiers
* @param bool $from_submit
* @return void
* @access public
* @author Alex
*/
public function ApplyFieldModifiers($field_modifiers = null, $from_submit = false)
{
$allowed_modifiers = Array ('required', 'multiple');
if ( $this->Application->isAdminUser ) {
// can change upload dir on the fly (admin only!)
$allowed_modifiers[] = 'upload_dir';
}
if ( !isset($field_modifiers) ) {
$field_modifiers = $this->Application->GetVar('field_modifiers');
if ( !$field_modifiers ) {
// no field modifiers
return;
}
$field_modifiers = getArrayValue($field_modifiers, $this->getPrefixSpecial());
}
if ( !$field_modifiers ) {
// no field modifiers for current prefix_special
return;
}
$fields = $this->Application->getUnitOption($this->Prefix, 'Fields', Array ());
$virtual_fields = $this->Application->getUnitOption($this->Prefix, 'VirtualFields', Array ());
foreach ($field_modifiers as $field => $field_options) {
foreach ($field_options as $option_name => $option_value) {
if ( !in_array(strtolower($option_name), $allowed_modifiers) ) {
continue;
}
if ( $from_submit ) {
// there are no "lN_FieldName" fields, since ApplyFieldModifiers is
// called before PrepareOptions method, which creates them
$field = preg_replace('/^l[\d]+_(.*)/', '\\1', $field);
}
if ( $this->isVirtualField($field) ) {
$virtual_fields[$field][$option_name] = $option_value;
$this->SetFieldOption($field, $option_name, $option_value, true);
}
$fields[$field][$option_name] = $option_value;
$this->SetFieldOption($field, $option_name, $option_value);
}
}
$this->Application->setUnitOption($this->Prefix, 'Fields', $fields);
$this->Application->setUnitOption($this->Prefix, 'VirtualFields', $virtual_fields);
}
/**
* Set fields (+options) for fields that physically doesn't exist in database
*
* @param Array $fields
* @access public
*/
public function setVirtualFields($fields)
{
if ($fields) {
$this->VirtualFields = $fields;
$this->Fields = array_merge($this->VirtualFields, $this->Fields);
}
}
/**
* Returns virtual fields
*
* @return Array
* @access public
*/
public function getVirtualFields()
{
return $this->VirtualFields;
}
/**
* Checks, that given field is a virtual field
*
* @param string $field
* @return bool
* @access public
*/
public function isVirtualField($field)
{
return array_key_exists($field, $this->VirtualFields);
}
/**
* Performs additional initialization for field default values
*
* @access protected
*/
protected function SetDefaultValues()
{
foreach ($this->Fields as $field => $options) {
if ( array_key_exists('default', $options) && $options['default'] === '#NOW#' ) {
$this->Fields[$field]['default'] = adodb_mktime();
}
}
}
/**
* Overwrites field definition in unit config
*
* @param string $field
* @param Array $options
* @param bool $is_virtual
* @access public
*/
public function SetFieldOptions($field, $options, $is_virtual = false)
{
if ($is_virtual) {
$this->VirtualFields[$field] = $options;
$this->Fields = array_merge($this->VirtualFields, $this->Fields);
}
else {
$this->Fields[$field] = $options;
}
}
/**
* Changes/sets given option's value in given field definiton
*
* @param string $field
* @param string $option_name
* @param mixed $option_value
* @param bool $is_virtual
* @access public
*/
public function SetFieldOption($field, $option_name, $option_value, $is_virtual = false)
{
if ($is_virtual) {
$this->VirtualFields[$field][$option_name] = $option_value;
}
$this->Fields[$field][$option_name] = $option_value;
}
/**
* Returns field definition from unit config.
* Also executes sql from "options_sql" field option to form "options" field option
*
* @param string $field
* @param bool $is_virtual
* @return Array
* @access public
*/
public function GetFieldOptions($field, $is_virtual = false)
{
$property_name = $is_virtual ? 'VirtualFields' : 'Fields';
if ( !array_key_exists($field, $this->$property_name) ) {
return Array ();
}
if (!$is_virtual) {
if (!array_key_exists('options_prepared', $this->Fields[$field]) || !$this->Fields[$field]['options_prepared']) {
// executes "options_sql" from field definition, only when field options are accessed (late binding)
$this->PrepareFieldOptions($field);
$this->Fields[$field]['options_prepared'] = true;
}
}
return $this->{$property_name}[$field];
}
/**
* Returns field option
*
* @param string $field
* @param string $option_name
* @param bool $is_virtual
* @param mixed $default
* @return mixed
* @access public
*/
public function GetFieldOption($field, $option_name, $is_virtual = false, $default = false)
{
$field_options = $this->GetFieldOptions($field, $is_virtual);
if ( !$field_options && strpos($field, '#') === false ) {
// we use "#FIELD_NAME#" as field for InputName tag in JavaScript, so ignore it
$form_name = $this->getFormName();
trigger_error('Field "<strong>' . $field . '</strong>" is not defined' . ($form_name ? ' on "<strong>' . $this->getFormName() . '</strong>" form' : '') . ' in "<strong>' . $this->Prefix . '</strong>" unit config', E_USER_WARNING);
return false;
}
return array_key_exists($option_name, $field_options) ? $field_options[$option_name] : $default;
}
/**
* Returns formatted field value
*
* @param string $name
* @param string $format
*
* @return string
* @access protected
*/
public function GetField($name, $format = null)
{
$formatter_class = $this->GetFieldOption($name, 'formatter');
if ( $formatter_class ) {
$value = ($formatter_class == 'kMultiLanguage') && !preg_match('/^l[0-9]+_/', $name) ? '' : $this->GetDBField($name);
$formatter = $this->Application->recallObject($formatter_class);
/* @var $formatter kFormatter */
return $formatter->Format($value, $name, $this, $format);
}
return $this->GetDBField($name);
}
/**
* Returns unformatted field value
*
* @param string $field
* @return string
* @access protected
*/
abstract protected function GetDBField($field);
/**
* Checks of object has given field
*
* @param string $name
* @return bool
* @access protected
*/
abstract protected function HasField($name);
/**
* Returns field values
*
* @return Array
* @access protected
*/
abstract protected function GetFieldValues();
/**
* Populates values of sub-fields, based on formatters, set to mater fields
*
* @param Array $fields
* @access public
* @todo Maybe should not be publicly accessible
*/
public function UpdateFormattersSubFields($fields = null)
{
if ( !is_array($fields) ) {
$fields = array_keys($this->Fields);
}
foreach ($fields as $field) {
if ( isset($this->Fields[$field]['formatter']) ) {
$formatter = $this->Application->recallObject($this->Fields[$field]['formatter']);
/* @var $formatter kFormatter */
$formatter->UpdateSubFields($field, $this->GetDBField($field), $this->Fields[$field], $this);
}
}
}
/**
* Use formatters, specified in field declarations to perform additional field initialization in unit config
*
* @access protected
*/
protected function prepareConfigOptions()
{
$field_names = array_keys($this->Fields);
foreach ($field_names as $field_name) {
if ( !array_key_exists('formatter', $this->Fields[$field_name]) ) {
continue;
}
$formatter = $this->Application->recallObject( $this->Fields[$field_name]['formatter'] );
/* @var $formatter kFormatter */
$formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this);
}
}
/**
* Escapes fields only, not expressions
*
* @param string $field_expr
* @return string
* @access protected
*/
protected function escapeField($field_expr)
{
return preg_match('/[.(]/', $field_expr) ? $field_expr : '`' . $field_expr . '`';
}
/**
* Replaces current language id in given field options
*
* @param string $field_name
* @param Array $field_option_names
* @access protected
*/
protected function _replaceLanguageId($field_name, $field_option_names)
{
// don't use GetVar('m_lang') since it's always equals to default language on editing form in admin
$current_language_id = $this->Application->Phrases->LanguageId;
$primary_language_id = $this->Application->GetDefaultLanguageId();
$field_options =& $this->Fields[$field_name];
foreach ($field_option_names as $option_name) {
$field_options[$option_name] = str_replace('%2$s', $current_language_id, $field_options[$option_name]);
$field_options[$option_name] = str_replace('%3$s', $primary_language_id, $field_options[$option_name]);
}
}
/**
* Transforms "options_sql" field option into valid "options" array for given field
*
* @param string $field_name
* @access protected
*/
protected function PrepareFieldOptions($field_name)
{
$field_options =& $this->Fields[$field_name];
if (array_key_exists('options_sql', $field_options) ) {
// get options based on given sql
$replace_options = Array ('option_title_field', 'option_key_field', 'options_sql');
$this->_replaceLanguageId($field_name, $replace_options);
$select_clause = $this->escapeField($field_options['option_title_field']) . ',' . $this->escapeField($field_options['option_key_field']);
$sql = sprintf($field_options['options_sql'], $select_clause);
if (array_key_exists('serial_name', $field_options)) {
// try to cache option sql on serial basis
$cache_key = 'sql_' . crc32($sql) . '[%' . $field_options['serial_name'] . '%]';
$dynamic_options = $this->Application->getCache($cache_key);
if ($dynamic_options === false) {
$this->Conn->nextQueryCachable = true;
$dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field']));
$this->Application->setCache($cache_key, $dynamic_options);
}
}
else {
// don't cache options sql
$dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field']));
}
$options_hash = array_key_exists('options', $field_options) ? $field_options['options'] : Array ();
$field_options['options'] = kUtil::array_merge_recursive($options_hash, $dynamic_options); // because of numeric keys
}
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
public function GetID()
{
return $this->GetDBField($this->IDField);
}
/**
* Allows kDBTagProcessor.SectionTitle to detect if it's editing or new item creation
*
* @return bool
* @access public
*/
public function IsNewItem()
{
return $this->GetID() ? false : true;
}
/**
* Returns parent table information
*
* @param string $special special of main item
* @param bool $guess_special if object retrieved with specified special is not loaded, then try not to use special
* @return Array
* @access public
*/
public function getLinkedInfo($special = '', $guess_special = false)
{
$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');
if ($parent_prefix) {
// if this is linked table, then set id from main table
$table_info = Array (
'TableName' => $this->Application->getUnitOption($this->Prefix,'TableName'),
'IdField' => $this->Application->getUnitOption($this->Prefix,'IDField'),
'ForeignKey' => $this->Application->getUnitOption($this->Prefix,'ForeignKey'),
'ParentTableKey' => $this->Application->getUnitOption($this->Prefix,'ParentTableKey'),
'ParentPrefix' => $parent_prefix
);
if (is_array($table_info['ForeignKey'])) {
$table_info['ForeignKey'] = getArrayValue($table_info, 'ForeignKey', $parent_prefix);
}
if (is_array($table_info['ParentTableKey'])) {
$table_info['ParentTableKey'] = getArrayValue($table_info, 'ParentTableKey', $parent_prefix);
}
$main_object = $this->Application->recallObject($parent_prefix.'.'.$special, null, Array ('raise_warnings' => 0));
/* @var $main_object kDBItem */
if (!$main_object->isLoaded() && $guess_special) {
$main_object = $this->Application->recallObject($parent_prefix);
}
return array_merge($table_info, Array('ParentId'=> $main_object->GetDBField( $table_info['ParentTableKey'] ) ) );
}
return false;
}
/**
* Returns true, when list/item was queried/loaded
*
* @return bool
* @access protected
*/
abstract protected function isLoaded();
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @return Array
* @access protected
*/
abstract protected function GetCol($field);
-}
\ No newline at end of file
+}
+
+
+/**
+ * Base class for exceptions, that trigger redirect action once thrown
+ */
+class kRedirectException extends Exception {
+
+ /**
+ * Redirect template
+ *
+ * @var string
+ * @access protected
+ */
+ protected $template = '';
+
+ /**
+ * Redirect params
+ *
+ * @var Array
+ * @access protected
+ */
+ protected $params = Array ();
+
+ /**
+ * Creates redirect exception
+ *
+ * @param string $message
+ * @param int $code
+ * @param Exception $previous
+ */
+ public function __construct($message = '', $code = 0, $previous = NULL)
+ {
+ parent::__construct($message, $code, $previous);
+ }
+
+ /**
+ * Initializes exception
+ *
+ * @param string $template
+ * @param Array $params
+ * @return void
+ * @access public
+ */
+ public function setup($template, $params = Array ())
+ {
+ $this->template = $template;
+ $this->params = $params;
+ }
+
+ /**
+ * Display exception details in debugger (only useful, when DBG_REDIRECT is enabled) and performs redirect
+ *
+ * @return void
+ * @access public
+ */
+ public function run()
+ {
+ $application =& kApplication::Instance();
+
+ if ( $application->isDebugMode() ) {
+ $application->Debugger->appendException($this);
+ }
+
+ $application->Redirect($this->template, $this->params);
+ }
+}
+
+
+/**
+ * Exception, that is thrown when user don't have permission to perform requested action
+ */
+class kNoPermissionException extends kRedirectException {
+
+}
Index: branches/5.2.x/core/units/languages/languages_item.php
===================================================================
--- branches/5.2.x/core/units/languages/languages_item.php (revision 15251)
+++ branches/5.2.x/core/units/languages/languages_item.php (revision 15252)
@@ -1,268 +1,281 @@
<?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 LanguagesItem extends kDBItem
{
function generateID()
{
$sql = 'SELECT MAX('.$this->IDField.') FROM '.$this->Application->getUnitOption($this->Prefix, 'TableName');
return $this->Conn->GetOne($sql) + 1;
}
/**
* Set's current language as new primary and return previous primary language
*
* @param bool $reset_primary
* @param bool $admin_language
* @return int
*/
function setPrimary($reset_primary = true, $admin_language = false)
{
$prev_primary = false;
$primary_field = $admin_language ? 'AdminInterfaceLang' : 'PrimaryLang';
if ($reset_primary) {
$sql = 'SELECT ' . $this->IDField . '
FROM ' . $this->TableName . '
WHERE ' . $primary_field . ' = 1';
$prev_primary = $this->Conn->GetOne($sql);
$sql = 'UPDATE '.$this->TableName.'
SET '.$primary_field.' = 0';
$this->Conn->Query($sql);
}
$sql = 'UPDATE '.$this->TableName.'
SET '.$primary_field.' = 1, Enabled = 1
WHERE '.$this->IDField.' = '.$this->GetID();
$this->Conn->Query($sql);
// in case, when Update method is called for this langauge object
$this->SetDBField($primary_field, 1);
$this->SetDBField('Enabled', 1);
// increment serial by hand, since no Update method is called
$this->Application->incrementCacheSerial($this->Prefix);
$this->Application->incrementCacheSerial($this->Prefix, $this->GetID());
return $prev_primary;
}
/**
* Copies missing data on current language from given language
*
* @param int $from_language
*/
function copyMissingData($from_language)
{
if ( !is_numeric($from_language) || ($from_language == $this->GetID()) ) {
// invalid or same language
return ;
}
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$to_language = $this->GetID();
$this->Application->UnitConfigReader->ReReadConfigs();
foreach ($this->Application->UnitConfigReader->configData as $prefix => $config_data) {
$ml_helper->copyMissingData($prefix, $from_language, $to_language);
}
}
/**
* Allows to format number according to regional settings
*
* @param float $number
* @param int $precision
* @return float
*/
function formatNumber($number, $precision = null)
{
if (is_null($precision)) {
$precision = preg_match('/[\.,]+/', $number) ? strlen(preg_replace('/^.*[\.,]+/', '', $number)) : 0;
}
return number_format($number, $precision, (string)$this->GetDBField('DecimalPoint'), (string)$this->GetDBField('ThousandSep'));
}
/**
* Returns language id based on HTTP header "Accept-Language"
*
* @return int
*/
function processAcceptLanguage()
{
if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER) || !$_SERVER['HTTP_ACCEPT_LANGUAGE']) {
return false;
}
$accepted_languages = Array ();
$language_string = explode(',', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
foreach ($language_string as $mixed_language) {
if (strpos($mixed_language, ';') !== false) {
list ($language_locale, $language_quality) = explode(';', $mixed_language);
$language_quality = (float)substr($language_quality, 2); // format "q=4.45"
}
else {
$language_locale = $mixed_language;
$language_quality = 1.00;
}
$accepted_languages[ trim($language_locale) ] = trim($language_quality);
}
arsort($accepted_languages, SORT_NUMERIC);
foreach ($accepted_languages as $language_locale => $language_quality) {
$language_id = $this->getAvailableLanguage($language_locale);
if ($language_id) {
return $language_id;
}
}
return false;
}
/**
* Returns language ID based on given locale
*
* @param string $locale
* @return int
*/
function getAvailableLanguage($locale)
{
$cache_key = 'available_languages[%LangSerial%]';
$available_languages = $this->Application->getCache($cache_key);
if ($available_languages === false) {
$this->Conn->nextQueryCachable = true;
$sql = 'SELECT LanguageId, LOWER(Locale) AS Locale
FROM ' . TABLE_PREFIX . 'Languages
WHERE Enabled = 1';
$available_languages = $this->Conn->GetCol($sql, 'Locale');
$this->Application->setCache($cache_key, $available_languages);
}
if (strpos($locale, '-') !== false) {
// exact language match requested
$language_id = array_key_exists($locale, $available_languages) ? $available_languages[$locale] : false;
if ($language_id && $this->siteDomainLanguageEnabled($language_id)) {
return $language_id;
}
return false;
}
// partial (like "en" matches "en-GB" and "en-US") language match required
foreach ($available_languages as $language_code => $language_id) {
list ($language_code, ) = explode('-', $language_code);
if (($locale == $language_code) && $this->siteDomainLanguageEnabled($language_id)) {
return $language_id;
}
}
return false;
}
+ /**
+ * Loads item from the database by given id
+ *
+ * @access public
+ * @param mixed $id item id of keys->values hash to load item by
+ * @param string $id_field_name Optional parameter to load item by given Id field
+ * @param bool $cachable cache this query result based on it's prefix serial
+ * @return bool True if item has been loaded, false otherwise
+ * @throws kRedirectException
+ */
public function Load($id, $id_field_name = null, $cachable = true)
{
$default = false;
if ($id == 'default') {
// domain based primary language
$default = true;
$id = $this->Application->siteDomainField('PrimaryLanguageId');
if ($id) {
$res = parent::Load($id, $id_field_name, $cachable);
}
else {
$res = parent::Load(1, 'PrimaryLang', $cachable);
}
if (
!$this->Application->isAdmin && !defined('GW_NOTIFY') && preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']) &&
$this->Application->HttpQuery->isEmptyUrl() && $this->Application->ConfigValue('UseContentLanguageNegotiation')
) {
$language_id = $this->processAcceptLanguage();
if ($language_id != $this->GetID()) {
// redirect to same page with found language
$url_params = Array (
'm_cat_id' => 0, 'm_cat_page' => 1,
'm_lang' => $language_id, 'm_opener' => 's',
'pass' => 'm'
);
- $this->Application->Redirect('', $url_params);
+ $exception = new kRedirectException('Redirect into language ID = <strong>' . $language_id . '</strong> according to "<strong>Accepted-Language</strong>" header "<strong>' . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . '</strong>"');
+ $exception->setup('', $url_params);
+
+ throw $exception;
}
}
}
else {
$res = parent::Load($id, $id_field_name, $cachable);
}
if ($res) {
if (!$this->siteDomainLanguageEnabled($this->GetID())) {
// language isn't allowed in site domain
return $this->Clear();
}
}
if ($default) {
if (!$res) {
if ($this->Application->isAdmin) {
$res = parent::Load(1, $id_field_name, false);
}
else {
if (defined('IS_INSTALL')) {
// during first language import prevents sql errors
$this->setID(1);
$res = true;
}
else {
$this->Application->ApplicationDie('No Primary Language Selected');
}
}
}
$this->Application->SetVar('lang.current_id', $this->GetID() );
$this->Application->SetVar('m_lang', $this->GetID() );
}
return $res;
}
/**
* Checks, that language is enabled in site domain
*
* @param int $id
* @return bool
*/
function siteDomainLanguageEnabled($id)
{
$available_languages = $this->Application->siteDomainField('Languages');
if ($available_languages) {
return strpos($available_languages, '|' .$id . '|') !== false;
}
return true;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/units/email_events/email_events_event_handler.php
===================================================================
--- branches/5.2.x/core/units/email_events/email_events_event_handler.php (revision 15251)
+++ branches/5.2.x/core/units/email_events/email_events_event_handler.php (revision 15252)
@@ -1,675 +1,716 @@
<?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 EmailEventsEventsHandler extends kDBEventHandler
{
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnFrontOnly' => Array ('self' => 'edit'),
'OnSaveSelected' => Array ('self' => 'view'),
'OnProcessEmailQueue' => Array ('self' => 'add|edit'),
'OnExportEmailEvents' => Array ('self' => 'view'),
'OnSuggestAddress' => Array ('self' => 'add|edit'),
// events only for developers
'OnPreCreate' => Array ('self' => 'debug'),
'OnDelete' => Array ('self' => 'debug'),
'OnDeleteAll' => Array ('self' => 'debug'),
'OnMassDelete' => Array ('self' => 'debug'),
'OnMassApprove' => Array ('self' => 'debug'),
'OnMassDecline' => Array ('self' => 'debug'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Changes permission section to one from REQUEST, not from config
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
$module = $this->Application->GetVar('module');
if ( strlen($module) > 0 ) {
// checking permission when lising module email events in separate section
$module = explode(':', $module, 2);
if ( count($module) == 1 ) {
$main_prefix = $this->Application->findModule('Name', $module[0], 'Var');
}
else {
$exceptions = Array ('Category' => 'c', 'Users' => 'u');
$main_prefix = $exceptions[$module[1]];
}
$section = $this->Application->getUnitOption($main_prefix . '.email', 'PermSection');
$event->setEventParam('PermSection', $section);
}
// checking permission when listing all email events when editing language
return parent::CheckPermission($event);
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
if ( $event->Special == 'module' ) {
$module = $this->Application->GetVar('module');
$object->addFilter('module_filter', '%1$s.Module = ' . $this->Conn->qstr($module));
}
if ( !$event->Special && !$this->Application->isDebugMode() ) {
// no special
$object->addFilter('enabled_filter', '%1$s.Enabled <> ' . STATUS_DISABLED);
}
}
/**
* Prepares new kDBItem object
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnNew(kEvent $event)
{
parent::OnNew($event);
$mapping = Array ('conf' => 'VariableValue', 'site-domain' => 'DefaultEmailRecipients');
if ( isset($mapping[$event->Special]) ) {
$object = $event->getObject();
/* @var $object kDBItem */
$target_object = $this->Application->recallObject($event->Special);
/* @var $target_object kDBList */
$object->SetDBField('Recipients', $target_object->GetDBField($mapping[$event->Special]));
}
}
/**
* Set default headers
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent $event)
{
parent::OnPreCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('Headers', $this->Application->ConfigValue('Smtp_DefaultHeaders'));
$this->setRequired($event);
}
/**
* Sets status Front-End Only to selected email events
*
* @param kEvent $event
*/
function OnFrontOnly($event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$ids = implode(',', $this->StoreSelectedIDs($event));
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
$sql = 'UPDATE ' . $table_name . '
SET FrontEndOnly = 1
WHERE EventId IN (' . $ids . ')';
$this->Conn->Query($sql);
$this->clearSelectedIDs($event);
}
/**
* Sets selected user to email events selected
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSelectUser(kEvent $event)
{
if ( $event->Special != 'module' ) {
parent::OnSelectUser($event);
return;
}
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$items_info = $this->Application->GetVar('u');
if ( $items_info ) {
$user_id = array_shift(array_keys($items_info));
$selected_ids = $this->getSelectedIDs($event, true);
$ids = $this->Application->RecallVar($event->getPrefixSpecial() . '_selected_ids');
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
$sql = 'UPDATE ' . $table_name . '
SET ' . $this->Application->RecallVar('dst_field') . ' = ' . $user_id . '
WHERE ' . $id_field . ' IN (' . $ids . ')';
$this->Conn->Query($sql);
}
$this->finalizePopup($event);
}
/**
* Saves selected ids to session
*
* @param kEvent $event
*/
function OnSaveSelected($event)
{
$this->StoreSelectedIDs($event);
}
/**
* Process emails from queue
*
* @param kEvent $event
* @todo Move to MailingList
*/
function OnProcessEmailQueue($event)
{
$deliver_count = $event->getEventParam('deliver_count');
if ( $deliver_count === false ) {
$deliver_count = $this->Application->ConfigValue('MailingListSendPerStep');
if ( $deliver_count === false ) {
$deliver_count = 10; // 10 emails per script run (if not specified directly)
}
}
$processing_type = $this->Application->GetVar('type');
if ( $processing_type = 'return_progress' ) {
$email_queue_progress = $this->Application->RecallVar('email_queue_progress');
if ( $email_queue_progress === false ) {
$emails_sent = 0;
$sql = 'SELECT COUNT(*)
FROM ' . TABLE_PREFIX . 'EmailQueue
WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ')';
$total_emails = $this->Conn->GetOne($sql);
$this->Application->StoreVar('email_queue_progress', $emails_sent . ':' . $total_emails);
}
else {
list ($emails_sent, $total_emails) = explode(':', $email_queue_progress);
}
}
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'EmailQueue
WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ')
LIMIT 0,' . $deliver_count;
$messages = $this->Conn->Query($sql);
$message_count = count($messages);
if ( !$message_count ) {
// no messages left to send in queue
if ( $processing_type = 'return_progress' ) {
$this->Application->RemoveVar('email_queue_progress');
$this->Application->Redirect($this->Application->GetVar('finish_template'));
}
return;
}
$mailing_list_helper = $this->Application->recallObject('MailingListHelper');
/* @var $mailing_list_helper MailingListHelper */
$mailing_list_helper->processQueue($messages);
if ( $processing_type = 'return_progress' ) {
$emails_sent += $message_count;
if ( $emails_sent >= $total_emails ) {
$this->Application->RemoveVar('email_queue_progress');
$this->Application->Redirect($this->Application->GetVar('finish_template'));
}
$this->Application->StoreVar('email_queue_progress', $emails_sent . ':' . $total_emails);
$event->status = kEvent::erSTOP;
echo ($emails_sent / $total_emails) * 100;
}
}
/**
* Prefills module dropdown
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
$options = Array ();
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
if ( $module_name == 'In-Portal' ) {
continue;
}
$options[$module_name] = $module_name;
}
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$fields['Module']['options'] = $options;
$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
if ( $this->Application->GetVar('regional') ) {
$this->Application->setUnitOption($event->Prefix, 'PopulateMlFields', true);
}
}
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnEdit(kEvent $event)
{
parent::OnEdit($event);
// use language from grid, instead of primary language used by default
$event->SetRedirectParam('m_lang', $this->Application->GetVar('m_lang'));
}
/**
* Fixes default recipient type
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
$object = $event->getObject();
/* @var $object kDBItem */
if ( !$this->Application->isDebugMode(false) ) {
if ( $object->GetDBField('AllowChangingRecipient') ) {
$object->SetDBField('RecipientType', EmailEvent::RECIPIENT_TYPE_TO);
}
else {
$object->SetDBField('RecipientType', EmailEvent::RECIPIENT_TYPE_CC);
}
}
// process replacement tags
$records = Array ();
$replacement_tags = $object->GetDBField('ReplacementTags');
$replacement_tags = $replacement_tags ? unserialize($replacement_tags) : Array ();
foreach ($replacement_tags as $tag => $replacement) {
$records[] = Array ('Tag' => $tag, 'Replacement' => $replacement);
}
$minput_helper = $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
$xml = $minput_helper->prepareMInputXML($records, Array ('Tag', 'Replacement'));
$object->SetDBField('ReplacementTagsXML', $xml);
$this->setRequired($event);
}
/**
* Performs custom validation + keep read-only fields
*
* @param kEvent $event
*/
function _itemChanged($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
if ( !$this->Application->isDebugMode(false) ) {
// only allow to enable/disable event while in debug mode
$to_restore = Array ('Enabled', 'AllowChangingSender', 'AllowChangingRecipient');
if ( !$object->GetOriginalField('AllowChangingSender') ) {
$to_restore = array_merge($to_restore, Array ('CustomSender', 'SenderName', 'SenderAddressType', 'SenderAddress'));
}
if ( !$object->GetOriginalField('AllowChangingRecipient') ) {
$to_restore = array_merge($to_restore, Array ('CustomRecipient' /*, 'Recipients'*/));
}
// prevent specific fields from editing
foreach ($to_restore as $restore_field) {
$original_value = $object->GetOriginalField($restore_field);
if ( $object->GetDBField($restore_field) != $original_value ) {
$object->SetDBField($restore_field, $original_value);
}
}
}
// process replacement tags
if ( $object->GetDBField('ReplacementTagsXML') ) {
$minput_helper = $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
$replacement_tags = Array ();
$records = $minput_helper->parseMInputXML($object->GetDBField('ReplacementTagsXML'));
foreach ($records as $record) {
$replacement_tags[trim($record['Tag'])] = trim($record['Replacement']);
}
$object->SetDBField('ReplacementTags', $replacement_tags ? serialize($replacement_tags) : NULL);
}
if ( $this->translationChanged($object) ) {
$object->SetDBField('LastChanged_date', TIMENOW);
$object->SetDBField('LastChanged_time', TIMENOW);
}
$this->setRequired($event);
}
/**
* Dynamically changes required fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function setRequired(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$language_prefix = 'l' . $this->Application->GetVar('m_lang') . '_';
$object->setRequired($language_prefix . 'HtmlBody', !$object->GetField('PlainTextBody'));
$object->setRequired($language_prefix . 'PlainTextBody', !$object->GetField('HtmlBody'));
}
/**
* Checks, that at least one of phrase's translations was changed
*
* @param kDBItem $object
* @return bool
*/
function translationChanged($object)
{
$changed_fields = array_keys($object->GetChangedFields());
$translation_fields = Array ('Subject', 'HtmlBody', 'PlainTextBody');
foreach ($changed_fields as $changed_field) {
$changed_field = preg_replace('/^l[\d]+_/', '', $changed_field);
if ( in_array($changed_field, $translation_fields) ) {
return true;
}
}
return false;
}
/**
* Don't allow to enable/disable events in non-debug mode
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$this->_itemChanged($event);
}
/**
* Don't allow to enable/disable events in non-debug mode
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->_itemChanged($event);
}
/**
* Suggest address based on typed address and selected address type
*
* @param kEvent $event
*/
function OnSuggestAddress($event)
{
$event->status = kEvent::erSTOP;
$address_type = $this->Application->GetVar('type');
$address = $this->Application->GetVar('value');
$limit = $this->Application->GetVar('limit');
if ( !$limit ) {
$limit = 20;
}
switch ($address_type) {
case EmailEvent::ADDRESS_TYPE_EMAIL:
$field = 'Email';
$table_name = TABLE_PREFIX . 'Users';
break;
case EmailEvent::ADDRESS_TYPE_USER:
$field = 'Username';
$table_name = TABLE_PREFIX . 'Users';
break;
case EmailEvent::ADDRESS_TYPE_GROUP:
$field = 'Name';
$table_name = TABLE_PREFIX . 'UserGroups';
break;
default:
$field = $table_name = '';
break;
}
if ( $field ) {
$sql = 'SELECT DISTINCT ' . $field . '
FROM ' . $table_name . '
WHERE ' . $field . ' LIKE ' . $this->Conn->qstr($address . '%') . '
ORDER BY ' . $field . ' ASC
LIMIT 0,' . $limit;
$data = $this->Conn->GetCol($sql);
}
else {
$data = Array ();
}
$this->Application->XMLHeader();
echo '<suggestions>';
foreach ($data as $item) {
echo '<item>' . htmlspecialchars($item) . '</item>';
}
echo '</suggestions>';
}
/**
* Does custom validation
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemValidate(kEvent $event)
{
parent::OnBeforeItemValidate($event);
$object = $event->getObject();
/* @var $object kDBItem */
// validate email subject and body for parsing errors
$this->_validateEmailTemplate($object);
// validate sender and recipient addresses
if ( $object->GetDBField('CustomSender') ) {
$this->_validateAddress($event, 'Sender');
}
$this->_validateAddress($event, 'Recipient');
+ $this->_validateBindEvent($object);
}
/**
* Validates subject and body fields of Email template
*
* @param kDBItem $object
* @return void
* @access protected
*/
protected function _validateEmailTemplate($object)
{
$email_message_helper = $this->Application->recallObject('kEmailMessageHelper');
/* @var $email_message_helper kEmailMessageHelper */
$email_message_helper->parseField($object, 'Subject');
$email_message_helper->parseField($object, 'HtmlBody');
$email_message_helper->parseField($object, 'PlainTextBody');
}
/**
* Validates address using given field prefix
*
* @param kEvent $event
* @param string $field_prefix
* @return void
* @access protected
*/
protected function _validateAddress($event, $field_prefix)
{
$object = $event->getObject();
/* @var $object kDBItem */
$address_type = $object->GetDBField($field_prefix . 'AddressType');
$object->setRequired($field_prefix . 'Address', $address_type > 0);
$address = $object->GetDBField($field_prefix . 'Address');
if ( !$address ) {
// don't validate against empty address
return;
}
switch ($address_type) {
case EmailEvent::ADDRESS_TYPE_EMAIL:
if ( !preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $address) ) {
$object->SetError($field_prefix . 'Address', 'invalid_email');
}
break;
case EmailEvent::ADDRESS_TYPE_USER:
$sql = 'SELECT PortalUserId
FROM ' . TABLE_PREFIX . 'Users
WHERE Username = ' . $this->Conn->qstr($address);
if ( !$this->Conn->GetOne($sql) ) {
$object->SetError($field_prefix . 'Address', 'invalid_user');
}
break;
case EmailEvent::ADDRESS_TYPE_GROUP:
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroups
WHERE Name = ' . $this->Conn->qstr($address);
if ( !$this->Conn->GetOne($sql) ) {
$object->SetError($field_prefix . 'Address', 'invalid_group');
}
break;
}
}
/**
+ * Checks that bind event is specified in correct format and exists
+ *
+ * @param kDBItem $object
+ */
+ protected function _validateBindEvent($object)
+ {
+ $event_string = $object->GetDBField('BindToSystemEvent');
+
+ if ( !$event_string ) {
+ return;
+ }
+
+ try {
+ $this->Application->eventImplemented(new kEvent($event_string));
+ }
+ catch (Exception $e) {
+ $object->SetError('BindToSystemEvent', 'invalid_event', '+' . $e->getMessage());
+ }
+ }
+
+ /**
* Stores ids of selected phrases and redirects to export language step 1
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnExportEmailEvents(kEvent $event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$this->Application->setUnitOption('phrases', 'AutoLoad', false);
$this->StoreSelectedIDs($event);
$this->Application->StoreVar('export_language_ids', $this->Application->GetVar('m_lang'));
$event->setRedirectParams(
Array (
'phrases.export_event' => 'OnNew',
'pass' => 'all,phrases.export',
'export_mode' => $event->Prefix,
)
);
}
+
+ /**
+ * Deletes all subscribers to e-mail event after it was deleted
+ *
+ * @param kEvent $event
+ * @return void
+ * @access protected
+ */
+ protected function OnAfterItemDelete(kEvent $event)
+ {
+ parent::OnAfterItemDelete($event);
+
+ $object = $event->getObject();
+ /* @var $object kDBItem */
+
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailEventSubscribers
+ WHERE EmailEventId = ' . $object->GetID();
+ $this->Conn->Query($sql);
+ }
}
\ No newline at end of file
Index: branches/5.2.x/core/units/email_events/email_events_config.php
===================================================================
--- branches/5.2.x/core/units/email_events/email_events_config.php (revision 15251)
+++ branches/5.2.x/core/units/email_events/email_events_config.php (revision 15252)
@@ -1,265 +1,266 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'emailevents',
'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'EmailEventsEventsHandler', 'file' => 'email_events_event_handler.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'EmailEventTagProcessor', 'file' => 'email_event_tp.php', 'build_event' => 'OnBuild'),
'AutoLoad' => true,
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
5 => 'mode',
),
'IDField' => 'EventId',
'StatusField' => Array ('Enabled'),
'TitleField' => 'Event',
'TitlePresets' => Array (
'default' => Array (
'new_status_labels' => Array ('emailevents' => '!la_title_Adding_E-mail!'),
'edit_status_labels' => Array ('emailevents' => '!la_title_EditingEmailEvent!' /*'!la_title_Editing_E-mail!'*/),
'new_titlefield' => Array ('emailevents' => '!la_title_NewEmailEvent!'),
),
// for separate grid with email editing
'email_message_list' => Array (
'prefixes' => Array ('emailevents_List'), 'format' => "!la_title_EmailMessages!",
'toolbar_buttons' => Array ('new_item', 'edit', 'delete', 'approve', 'decline', 'frontend_mail', 'export', 'view', 'dbl-click'),
),
'email_message_edit' => Array (
'prefixes' => Array ('emailevents'),
'format' => '#emailevents_status# - #emailevents_titlefield# - !la_section_General!',
'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'),
),
'email_message_edit_settings' => Array (
'prefixes' => Array ('emailevents'),
'format' => '#emailevents_status# - #emailevents_titlefield# - !la_section_Settings!',
'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'),
),
// for mass mailing
'email_send_form' => Array ('prefixes' => Array (), 'format' => '!la_title_SendEmail!'),
'email_send' => Array ('prefixes' => Array (), 'format' => '!la_title_SendingPreparedEmails!. !la_title_PleaseWait!'),
'email_send_complete' => Array ('prefixes' => Array (), 'format' => '!la_title_SendMailComplete!'),
),
'EditTabPresets' => Array (
'Default' => Array (
'general' => Array ('title' => 'la_tab_General', 't' => 'languages/email_message_edit', 'priority' => 1),
'settings' => Array ('title' => 'la_tab_Settings', 't' => 'languages/email_message_settings', 'priority' => 2),
),
),
'PermSection' => Array ('main' => 'in-portal:configemail'),
'Sections' => Array (
'in-portal:configemail' => Array (
'parent' => 'in-portal:site',
'icon' => 'email_templates',
'label' => 'la_tab_E-mails',
'url' => Array ('t' => 'languages/email_message_list', 'pass' => 'm'),
'permissions' => Array ('view', 'add', 'edit', 'delete'),
'priority' => 5,
'type' => stTREE,
),
),
'TableName' => TABLE_PREFIX . 'EmailEvents',
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s FROM %1$s',
),
'ListSortings' => Array (
'' => Array (
'ForcedSorting' => Array ('Enabled' => 'desc'),
'Sorting' => Array ('Event' => 'asc'),
),
'module' => Array (
'ForcedSorting' => Array ('Enabled' => 'desc'),
'Sorting' => Array ('Description' => 'asc')
),
),
'Fields' => Array (
'EventId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'Event' => Array ('type' => 'string', 'not_null' => 1, 'unique' => Array ('Type'), 'required' => 1, 'default' => ''),
'Headers' => Array ('type' => 'string', 'default' => NULL),
'ReplacementTags' => Array ('type' => 'string', 'default' => NULL),
'AllowChangingSender' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'CustomSender' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_opt_DefaultAddress', 1 => 'la_opt_CustomSender'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'SenderName' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'SenderAddressType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Email', 2 => 'la_opt_User'), 'use_phrases' => 1,
'not_null' => 1, 'error_field' => 'SenderAddress', 'default' => 0
),
'SenderAddress' => Array (
'type' => 'string', 'max_len' => 255,
'error_msgs' => Array (
'invalid_email' => '!la_err_invalid_format!',
'invalid_user' => '!la_error_UserNotFound!',
),
'not_null' => 1, 'default' => ''
),
'AllowChangingRecipient' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'CustomRecipient' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_opt_DefaultAddress', 1 => 'la_opt_CustomRecipients'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'Recipients' => Array ('type' => 'string', 'default' => NULL),
'Subject' => Array (
'type' => 'string',
'formatter' => 'kMultiLanguage', 'db_type' => 'text',
'error_msgs' => Array ('parsing_error' => '!la_error_ParsingError!'),
'required' => 1, 'default' => null
),
'HtmlBody' => Array (
'type' => 'string',
'formatter' => 'kMultiLanguage', 'db_type' => 'longtext',
'error_msgs' => Array ('parsing_error' => '!la_error_ParsingError!'),
'default' => null
),
'PlainTextBody' => Array (
'type' => 'string',
'formatter' => 'kMultiLanguage', 'db_type' => 'longtext',
'error_msgs' => Array ('parsing_error' => '!la_error_ParsingError!'),
'default' => null
),
'Enabled' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 1
),
'FrontEndOnly' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'Module' => Array (
'type' => 'string',
'formatter' => 'kOptionsFormatter', 'options' => Array (),
'not_null' => 1, 'required' => 1, 'default' => 'Core'
),
'Description' => Array ('type' => 'string', 'default' => NULL),
'Type' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Text_Admin', 0 => 'la_Text_User'), 'use_phrases' => 1,
'not_null' => 1, 'unique' => Array ('Event'), 'required' => 1, 'default' => 0
),
'LastChanged' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
+ 'BindToSystemEvent' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
),
'VirtualFields' => Array (
'RecipientType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'To', 2 => 'Cc', 3 => 'Bcc'),
'default' => 1
),
'RecipientName' => Array ('type' => 'string', 'max_len' => 255, 'default' => ''),
'RecipientAddressType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Email', 2 => 'la_opt_User', 3 => 'la_opt_Group'), 'use_phrases' => 1,
'error_field' => 'RecipientAddress', 'default' => 0
),
'RecipientAddress' => Array (
'type' => 'string', 'max_len' => 255,
'error_msgs' => Array (
'invalid_email' => '!la_err_invalid_format!',
'invalid_user' => '!la_error_UserNotFound!',
'invalid_group' => '!la_error_GroupNotFound!',
),
'default' => ''
),
'Tag' => Array ('type' => 'string', 'default' => ''),
'Replacement' => Array ('type' => 'string', 'default' => ''),
'ReplacementTagsXML' => Array ('type' => 'string', 'default' => ''),
),
'Grids' => Array (
// used on "Email Events" tab in language editing in "Regional" section
'Default' => Array (
'Icons' => Array (
'default' => 'icon16_item.png',
0 => 'icon16_disabled.png',
1 => 'icon16_item.png',
),
'Fields' => Array (
'EventId' => Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 70, ),
'Description' => Array ('filter_block' => 'grid_like_filter', 'width' => 250, ),
'Event' => Array ('filter_block' => 'grid_like_filter', 'width' => 250, ),
'Module' => Array ('filter_block' => 'grid_multioptions_filter', 'width' => 100, ),
'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 120, ),
'Enabled' => Array ('title' => 'column:la_fld_Status', 'filter_block' => 'grid_options_filter', 'width' => 80, ),
'LastChanged' => Array ('title' => 'column:la_fld_Modified', 'filter_block' => 'grid_date_range_filter', 'width' => 150),
),
),
// used on "Email Templates" section
'Emails' => Array (
'Icons' => Array (
'default' => 'icon16_item.png',
0 => 'icon16_disabled.png',
1 => 'icon16_item.png',
),
'Fields' => Array (
'EventId' => Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
'Event' => Array ('filter_block' => 'grid_like_filter', 'width' => 250, ),
'Subject' => Array ('filter_block' => 'grid_like_filter', 'no_special' => 0, 'width' => 300, ),
'Description' => Array ('filter_block' => 'grid_like_filter', 'width' => 250, ),
'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 60, ),
'Enabled' => Array ('filter_block' => 'grid_options_filter', 'width' => 70, ),
'Module' => Array ('filter_block' => 'grid_multioptions_filter', 'width' => 100, ),
'FrontEndOnly' => Array ('filter_block' => 'grid_options_filter', 'width' => 120, 'hidden' => 1),
'LastChanged' => Array ('title' => 'column:la_fld_Modified', 'filter_block' => 'grid_date_range_filter', 'width' => 150),
),
),
),
);
\ No newline at end of file
Index: branches/5.2.x/core/admin_templates/incs/form_blocks.tpl
===================================================================
--- branches/5.2.x/core/admin_templates/incs/form_blocks.tpl (revision 15251)
+++ branches/5.2.x/core/admin_templates/incs/form_blocks.tpl (revision 15252)
@@ -1,1227 +1,1227 @@
<inp2:m_Set tab_index="1"/>
<inp2:m_DefineElement name="combined_header" permission_type="view" perm_section="" perm_prefix="" perm_event="" system_permission="1" title_preset="" tab_preset="" additional_title_render_as="" additional_blue_bar_render_as="" pagination_prefix="" parent="1" grid="Default">
<inp2:m_if check="m_Param" name="perm_section" inverse="1">
<inp2:adm_SectionInfo section="$section" info="perm_section" result_to_var="perm_section"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="permission_type">
<inp2:m_RequireLogin permissions="{$perm_section}.{$permission_type}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/>
<inp2:m_else/>
<inp2:m_RequireLogin permissions="{$perm_section}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="prefix" inverse="1"><inp2:adm_SectionInfo section="$section" info="SectionPrefix" result_to_var="prefix"/></inp2:m_if>
<inp2:m_if check="m_get" var="m_wid" inverse="1">
<inp2:m_if check="m_GetConfig" name="UseSmallHeader">
<img src="img/spacer.gif" height="8" width="1" alt=""/>
<inp2:m_else/>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##-->
<tr>
<td valign="top" class="admintitle" align="left" <!--##style="padding-top: 10px; padding-bottom: 10px;"##-->>
<img width="46" height="46" src="<inp2:adm_SectionInfo section='$section' parent='$parent' info='module_path'/>img/icons/icon46_<inp2:adm_SectionInfo section='$section' parent='$parent' info='icon'/>.png" align="absmiddle" title="<inp2:adm_SectionInfo section='$section' parent='$parent' info='label' no_editing='1'/>" alt=""/><inp2:adm_SectionInfo section="$section" parent="$parent" info="label"/>
</td>
<inp2:m_if check="m_Param" name="additional_title_render_as">
<inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/>
</inp2:m_if>
</tr>
</table>
</inp2:m_if>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="additional_title_render_as">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##-->
<tr>
<inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/>
</tr>
</table>
</inp2:m_if>
</inp2:m_if>
<inp2:$prefix_ModifyUnitConfig pass_params="1"/>
<inp2:m_if check="m_Param" name="tabs">
<inp2:m_include t="$tabs" pass_params="1"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="tab_preset">
<inp2:m_RenderElement name="edit_tabs" prefix="$prefix" preset_name="$tab_preset"/>
</inp2:m_if>
<table border="0" cellpadding="2" cellspacing="0" class="bordered-no-bottom" width="100%" style="height: 30px;">
<tr>
<td class="header_left_bg" nowrap="nowrap" style="vertical-align: middle;">
<inp2:adm_SectionInfo section="$section" info="label" result_to_var="default_title"/>
<inp2:adm_SectionInfo section="$section" parent="$parent" info="label" result_to_var="group_title"/>
<span class="tablenav_link" id="blue_bar">
<inp2:$prefix_SectionTitle title_preset="$title_preset" section="$section" title="$default_title" group_title="$group_title" cut_first="100" pass_params="true"/>
</span>
</td>
<td align="right" class="tablenav" style="vertical-align: middle;">
<inp2:m_if check="m_Param" name="additional_blue_bar_render_as">
<inp2:m_RenderElement name="$additional_blue_bar_render_as" pass_params="1"/>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="pagination">
<inp2:$prefix_SelectParam possible_names="pagination_prefix,prefix" result_to_var="pagination_prefix"/>
<inp2:m_RenderElement name="grid_pagination_elem" PrefixSpecial="$pagination_prefix" pass_params="1"/>
</inp2:m_if>
</inp2:m_if>
</td>
</tr>
</table>
<script type="text/javascript">
var $visible_toolbar_buttons = <inp2:m_if check="{$prefix}_VisibleToolbarButtons" title_preset="$title_preset">[<inp2:$prefix_VisibleToolbarButtons title_preset="$title_preset"/>]<inp2:m_else/>true</inp2:m_if>;
var $allow_dbl_click = ($visible_toolbar_buttons === true) || in_array('dbl-click', $visible_toolbar_buttons);
set_window_title( $.trim( $('#blue_bar').text().replace(/\s+/g, ' ') ) + ' - <inp2:m_Phrase label="la_AdministrativeConsole" js_escape="1"/>');
setHelpLink('<inp2:lang.current_Field name="UserDocsUrl" js_escape="1"/>', '<inp2:m_Param name="title_preset" js_escape="1"/>');
</script>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_original_label">
<td><inp2:$prefix.original_Field field="$field" nl2br="1"/></td>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="subsection" prefix="" fields="" colspan="3">
<inp2:m_if check="m_Param" name="prefix" equals_to="">
<tr class="subsectiontitle">
<td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td>
</tr>
<inp2:m_else/>
<inp2:m_if check="{$prefix}_FieldsVisible" fields="$fields">
<tr class="subsectiontitle">
<td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td>
<inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1">
<td><inp2:m_phrase name="$original_title"/></td>
</inp2:m_if>
</tr>
</inp2:m_if>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="form_message" id="" type="warning">
<table width="100%" cellspacing="0" cellpadding="4" class="warning-table"<inp2:m_if check="m_Param" name="id"> id="<inp2:m_Param name='id'/>"</inp2:m_if>>
<tr>
<td valign="top" class="form-<inp2:m_Param name='type'/>">
<inp2:m_Param name="content"/>
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="default_field_caption_element">
<label for="<inp2:m_param name='NamePrefix'/><inp2:{$prefix}_InputName field='$field'/>">
<span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>"><inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title"/></inp2:m_else/><inp2:m_Param name="title_text"/></inp2:m_if></span></span><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required">&nbsp;*</span></inp2:m_if>:<inp2:m_if check="{$prefix}_FieldHintLabel" title_label="$title" direct_label="$hint_label"><span>&nbsp;<img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>" alt="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>"/></inp2:m_if>
</label>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_field_caption" title="la_fld_{$field}" title_text="" hint_label="" NamePrefix="">
<inp2:m_inc param="tab_index" by="1"/>
<td class="label-cell" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:m_if check="m_Param" name="title">
<inp2:m_RenderElement name="$caption_render_as" pass_params="1"/>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="title_text">
<inp2:m_RenderElement name="$caption_render_as" pass_params="1"/>
<inp2:m_else/>
&nbsp;
</inp2:m_if>
</inp2:m_if>
</td>
<td class="control-mid">&nbsp;</td>
<script type="text/javascript">
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title" js_escape="1"/></inp2:m_else/><inp2:m_Param name="title_text" js_escape="1"/></inp2:m_if>'
</script>
</inp2:m_DefineElement>
<!--## design default parameters only avaible in design block ##-->
<inp2:m_DefineElement name="form_row" error_field_suffix="" has_caption="1" caption_render_as="default_field_caption_element" style="" hint_label="" is_last="">
<inp2:m_if check="{$prefix}_FieldVisible" field="$field">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row"<inp2:m_if check="m_Param" name="row_style"> style="<inp2:m_Param name='row_style'/>"</inp2:m_if>>
<inp2:m_if check="m_Param" name="has_caption">
<inp2:m_RenderElement name="inp_edit_field_caption" field="{$field}{$error_field_suffix}" pass_params="1"/>
</inp2:m_if>
<inp2:m_Param name="content" pass_params="1"/>
<inp2:m_RenderElement name="inp_edit_error" prefix="$prefix" field="{$field}{$error_field_suffix}"/>
<inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1">
<inp2:m_RenderElement prefix="$prefix" field="$field" name="inp_original_label"/>
</inp2:m_if>
</tr>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_label" style="" format="" db="" as_label="" currency="" no_special="" nl2br="0" with_hidden="0" after_text="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell" valign="top">
<span style="<inp2:m_Param name='style'/>" id="<inp2:$prefix_InputName field='$field'/>">
<inp2:{$prefix}_Field field="$field" format="$format" as_label="$as_label" currency="$currency" nl2br="$nl2br" no_special="$no_special"/><inp2:m_Param name="after_text"/>
</span>
<inp2:m_if check="m_Param" name="with_hidden">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>">
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_id_label">
<inp2:m_ifnot check="Field" field="$field" equals_to="|0">
<inp2:m_RenderElement name="inp_label" pass_params="true"/>
</inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_error">
<script type="text/javascript">
add_form_error('<inp2:m_Param name="prefix" js_escape="1"/>', '<inp2:m_Param name="field" js_escape="1"/>', '<inp2:{$prefix}_InputName field="$field"/>', '<inp2:{$prefix}_Error field="$field" js_escape="1"/>')
</script>
<!--##<td class="error-cell"><inp2:{$prefix}_Error field="$field"/>&nbsp;</td>##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_box" class="" format="" maxlength="" onblur="" onchange="" size="" onkeyup="" allow_html="" edit_template="popups/editor" style="width: 100%" after_text="" autocomplete="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='$format'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>" onkeyup="<inp2:m_Param name='onkeyup'/>" onchange="<inp2:m_Param name='onchange'/>" autocomplete="<inp2:m_Param name='autocomplete'/>"><inp2:m_Param name="after_text"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_timezone" onchange="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<script type="text/javascript">
var $timezones = {
'Africa': ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Timbuktu', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek'],
'America': ['America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Atka', 'America/Bahia', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Buenos_Aires', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Catamarca', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Coral_Harbour', 'America/Cordoba', 'America/Costa_Rica', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Ensenada', 'America/Fort_Wayne', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Jujuy', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Louisville', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Mendoza', 'America/Menominee', 'America/Merida', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Rosario', 'America/Santa_Isabel', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Shiprock', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Virgin', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'America/Yellowknife'],
'Antarctica': ['Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Vostok'],
'Arctic': ['Arctic/Longyearbyen'],
'Asia': ['Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Ashkhabad', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Calcutta', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Chungking', 'Asia/Colombo', 'Asia/Dacca', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Gaza', 'Asia/Harbin', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Istanbul', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kashgar', 'Asia/Kathmandu', 'Asia/Katmandu', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macao', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qyzylorda', 'Asia/Rangoon', 'Asia/Riyadh', 'Asia/Saigon', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Tel_Aviv', 'Asia/Thimbu', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Ujung_Pandang', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator', 'Asia/Urumqi', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yekaterinburg', 'Asia/Yerevan'],
'Atlantic': ['Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faeroe', 'Atlantic/Faroe', 'Atlantic/Jan_Mayen', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley'],
'Australia': ['Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/North', 'Australia/NSW', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna'],
'Europe': ['Europe/Amsterdam', 'Europe/Andorra', 'Europe/Athens', 'Europe/Belfast', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Nicosia', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Tiraspol', 'Europe/Uzhgorod', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zaporozhye', 'Europe/Zurich'],
'Indian': ['Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion'],
'Pacific': ['Pacific/Apia', 'Pacific/Auckland', 'Pacific/Chatham', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Johnston', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Ponape', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Samoa', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Truk', 'Pacific/Wake', 'Pacific/Wallis', 'Pacific/Yap'],
'Others': ['UTC']
}
function change_timezone_group($timezone_group) {
var $target = $( jq('#<inp2:InputName name="$field"/>') );
$target.hide().empty();
$target.append('<option value=""></option>');
if ($timezone_group == '') {
return ;
}
for (var $i = 0; $i < $timezones[$timezone_group].length; $i++) {
var $timezone = $timezones[$timezone_group][$i];
var $selected = $timezone == $current_timezone ? ' selected="selected"' : '';
$target.append('<option value="' + $timezone + '" ' + $selected + '>' + $timezone + '</option>');
}
$target.show();
}
var $current_timezone = '<inp2:Field name="$field"/>';
$(document).ready(
function () {
$('#timezone_group').change(
function ($e) {
change_timezone_group( $(this).val() );
}
);
var $current_timezone_group = '';
for (var $timezone_group in $timezones) {
if (in_array($current_timezone, $timezones[$timezone_group])) {
$current_timezone_group = $timezone_group;
break;
}
}
$('#timezone_group').val($current_timezone_group).change();
}
);
</script>
<select id="timezone_group" tabindex="<inp2:m_Get name='tab_index'/>">
<option value=""></option>
<option value="Africa">Africa</option>
<option value="America">America</option>
<option value="Antarctica">Antarctica</option>
<option value="Arctic">Arctic</option>
<option value="Asia">Asia</option>
<option value="Atlantic">Atlantic</option>
<option value="Australia">Australia</option>
<option value="Europe">Europe</option>
<option value="Indian">Indian</option>
<option value="Pacific">Pacific</option>
<option value="Others">Others</option>
</select>
&nbsp;&nbsp;
<select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>"></select>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_password" class="" size="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" autocomplete="off" type="password" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" />
<script type="text/javascript">
$(document).ready(
function() {
<inp2:m_ifnot check="{$prefix}_Field" name="{$field}_plain">
$('#' + jq('<inp2:{$prefix}_InputName field="$field"/>')).val('');
</inp2:m_ifnot>
}
);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_upload" class="" size="" thumbnail="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_if check="m_Param" name="thumbnail">
<inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse">
<img src="<inp2:{$prefix}_Field field='$field' format='resize:{$thumbnail}'/>" alt=""/><br />
<table cellpadding="0" cellspacing="0">
<tr>
<td>
<input type="hidden" id="<inp2:{$prefix}_InputName field='Delete{$field}'/>" name="<inp2:{$prefix}_InputName field='Delete{$field}'/>" value="0" />
<input type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>" tabindex="<inp2:m_Get name='tab_index'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='Delete{$field}'/>'));">
</td>
<td>
<label for="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>"><inp2:m_phrase name="la_btn_Delete"/></label>
</td>
</tr>
</table>
</inp2:m_if>
<input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>">
<inp2:m_else/>
<input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>">
<inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse">
(<inp2:{$prefix}_Field field="$field"/>)
</inp2:m_if>
</inp2:m_if>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field'/>">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_box_ml">
<inp2:m_RenderElement name="inp_edit_box" format="no_default" pass_params="true"/>
<!--##
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="label-cell" valign="top">
<span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>" >
<inp2:m_phrase label="$title"/><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required">&nbsp;*</span></inp2:m_if>:</span><br>
<a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name='prefix'/>', '<inp2:m_param name='field'/>', 'popups/translator');" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" style="cursor:hand" border="0"></a>
</td>
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='no_default'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
</td>
</inp2:m_RenderElement>
##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_swf_upload" class="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<div style="width: 63px; height: 21px;" id="<inp2:{$prefix}_InputName field='$field'/>_place_holder">
&nbsp;
</div>
<div id="<inp2:{$prefix}_InputName field='$field'/>_queueinfo" class="uploader-queue"></div>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" value="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" value="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" value="">
<script type="text/javascript">
UploadsManager.AddUploader('<inp2:{$prefix}_InputName field="$field"/>',
{
baseUrl: '<inp2:m_TemplatesBase />',
allowedFiletypesDescription : '<inp2:{$prefix}_FieldOption field="$field" option="files_description" result_to_var="files_description"/><inp2:m_Phrase name="$files_description" js_escape="1"/>',
allowedFiletypes : '<inp2:{$prefix}_FieldOption field="$field" option="file_types"/>',
allowedFilesize : '<inp2:{$prefix}_FieldOption field="$field" option="max_size"/>',
multiple : '<inp2:{$prefix}_FieldOption field="$field" option="multiple"/>',
prefix : '<inp2:m_Param name="prefix"/>',
field : '<inp2:m_Param name="field"/>',
thumb_format: '<inp2:{$prefix}_FieldOption field="$field" option="thumb_format"/>',
urls : '<inp2:{$prefix}_Field field="$field" format="file_urls" js_escape="1"/>',
names : '<inp2:{$prefix}_Field field="$field" format="file_names" js_escape="1"/>',
sizes : '<inp2:{$prefix}_Field field="$field" format="file_sizes" js_escape="1"/>',
flashsid : '<inp2:m_SID/>',
uploadURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnUploadFile" js_escape="1" no_amp="1" />',
deleteURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnDeleteFile" field="#FIELD#" file="#FILE#" js_escape="1" no_amp="1"/>',
tmp_url : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnViewFile" tmp="1" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1" />',
// Button settings
buttonImageURL: 'img/upload.png', // Relative to the Flash file
buttonWidth: 63,
buttonHeight: 21,
buttonText: '<span class="theFont">Browse</span>',
buttonTextStyle: ".theFont { font-size: 12; font-family: arial, sans}",
buttonTextTopPadding: 2,
buttonTextLeftPadding: 9,
buttonPlaceholderId: '<inp2:{$prefix}_InputName field="$field"/>_place_holder'
}
)
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_hidden" db="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>">
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date" class="" error_field_suffix="_date">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif">&nbsp;
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field='{$field}'/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span>
<script type="text/javascript">
Calendar.setup({
inputField : "<inp2:{$prefix}_InputName field='{$field}_date'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}_date' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>,
onUpdate : function(cal) {
runOnChange('<inp2:$prefix_InputName field='{$field}_date'/>');
}
});
</script>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time' input_format='1'/>" value="">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_time" class="" error_field_suffix="_time">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>">&nbsp;
<span class="small">(<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date' input_format='1'/>" value="">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date_time" class="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif">
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span>
<input type="hidden" id="full_date_<inp2:{$prefix}_InputName field='{$field}'/>" value="<inp2:{$prefix}_Field field='{$field}' format=''/>" />
<script type="text/javascript">
Calendar.setup({
inputField : "full_date_<inp2:{$prefix}_InputName field='{$field}'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>,
onUpdate : function(cal) {
document.getElementById('<inp2:{$prefix}_InputName field="{$field}_date"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_date" input_format="1"/>") );
document.getElementById('<inp2:{$prefix}_InputName field="{$field}_time"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_time" input_format="1"/>") );
}
});
</script>
&nbsp;<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>"><span class="small"> (<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date_time_combined" class="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='_input_'/>" tabindex="<inp2:m_get param='tab_index'/>" size="<inp2:{$prefix}_Format field='$field' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"/>
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="$field" input_format="1" human="true"/>)</span>
<script type="text/javascript">
Calendar.setup({
inputField : "<inp2:{$prefix}_InputName field='{$field}'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>
});
</script>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_combined'/>" value="1"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="textarea_field_caption_element">
<inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/>
<br>
<inp2:m_if check="m_ParamEquals" name="allow_html" value="allow_html">
<inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/>
<a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '800x575'); return false;">
<img src="img/icons/icon24_link_editor.gif" border="0">
</a>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_textarea" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="allow_html" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" ><inp2:{$prefix}_Field field="$field" format="fck_ready;{$format}"/></textarea>
<script type="text/javascript">
Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_fck" class="" maxlength="" bgcolor="" onblur="" format="" size="" onkeyup="" style="" has_caption="0" control_options="false">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell" style="padding: 0px;" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:FCKEditor field="$field" width="100%" height="200" bgcolor="$bgcolor" format="$format" late_load="1"/>
<script type="text/javascript">
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>'
Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_codepress" style="width: 100%;" language="html" has_caption="0" control_options="false">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:m_ifnot check="m_Get" name="codepress_included">
<script type="text/javascript" src="<inp2:m_TemplatesBase/>/themes/codepress/codepress.js"></script>
<script type="text/javascript">
CodePress.path = '<inp2:m_TemplatesBase/>/themes/codepress/'; // set path here, because script tags are not found in table cells
</script>
<inp2:m_Set codepress_included="1"/>
</inp2:m_ifnot>
<textarea id="<inp2:$prefix_InputName field='$field'/>" name="<inp2:$prefix_InputName field='$field'/>" class="codepress <inp2:m_Param name='language'/>" style="<inp2:m_Param name='style'/>"><inp2:$prefix_Field field="$field"/></textarea>
<script type="text/javascript">
Application.setHook(
new Array ('<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGoToTab', '<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGo', '<inp2:m_Param name="prefix" js_escape="1"/>:OnSave', '<inp2:m_Param name="prefix" js_escape="1"/>:OnCreate', '<inp2:m_Param name="prefix" js_escape="1"/>:OnUpdate'),
function($event) {
<inp2:m_Param name="field"/>.toggleEditor(); // enable textarea back to save data
$event.status = true;
}
);
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>'
Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="textarea_ml_field_caption_element">
<inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/>
<br>
<inp2:m_if check="m_ParamEquals" name="allow_html" value="allow_html">
<inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/>
<a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '800x575'); return false;">
<img src="img/icons/icon24_link_editor.gif" style="cursor: hand;" border="0">
</a>
</inp2:m_if>
<a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name="prefix"/>', '<inp2:m_param name="field"/>', 'popups/translator', 1);" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" border="0"></a>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_textarea_ml">
<inp2:m_RenderElement name="inp_edit_textarea" format="no_default" pass_params="true"/>
<!--##<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" cols="<inp2:m_param name='cols'/>" rows="<inp2:m_param name='rows'/>" class="<inp2:m_param name='class'/>"><inp2:{$prefix}_Field field="$field" format="fck_ready,{$format}"/></textarea>
<script type="text/javascript">
Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_user" class="" size="" old_style="0" onkeyup="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" onkeyup="<inp2:m_Param name='onkeyup'/>">
<inp2:m_if check="m_ParamEquals" name="old_style" value="1">
<a href="#" onclick="return OpenUserSelector('','kernel_form','<inp2:{$prefix}_InputName field="$field"/>');">
<inp2:m_else/>
<a href="javascript:openSelector('<inp2:m_param name='prefix'/>', '<inp2:m_t t='user_selector' pass='all,$prefix' escape='1'/>', '<inp2:m_param name='field'/>');">
</inp2:m_if>
<img src="img/icons/icon24_link_user.gif" style="cursor:hand;" border="0">
</a>
<script type="text/javascript">
function processUserSelector($field, $selector) {
var $field_mask = '<inp2:$prefix_InputName name="#FIELD_NAME#"/>';
var $user_id = parseInt( $selector.Grids['u'].GetSelected() );
$( jq('#' + $field_mask.replace('#FIELD_NAME#', '<inp2:m_Param name="field"/>')) ).val( $selector.$user_logins[$user_id] );
}
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_category" class="" size="" old_style="0" onkeyup="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_RenderElement name="inp_edit_hidden" pass_params="1"/>
<table cellpadding="0" cellspacing="0">
<tr>
<td id="<inp2:{$prefix}_InputName field='$field'/>_path"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>>
<inp2:$prefix_Field field='$field' db="db" result_to_var="category_id"/>
<inp2:m_include template="categories/category_path" category_id="$category_id"/>
</td>
<td valign="middle">
<img src="img/spacer.gif" width="3" height="1" alt=""/>
<a href="javascript:openSelector('<inp2:m_param name='prefix'/>', '<inp2:adm_SelectorLink prefix='$prefix' selection_mode='single' tab_prefixes='none'/>', '<inp2:m_param name='field'/>');">
<img src="img/icons/icon24_cat.gif" width="24" height="24" border="0"/>
</a>
<a href="#" id="<inp2:{$prefix}_InputName field='$field'/>_disable_link"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>><inp2:m_Phrase name="la_Text_Disable"/></a>
<script type="text/javascript">
function processItemSelector($field, $selector) {
var $item_ids = $selector.Grids[$selector.$Catalog.ActivePrefix].GetSelected();
CategorySelector.setCategory($field, $item_ids.length ? $item_ids.shift() : '');
}
function CategorySelector() {
}
CategorySelector.getField = function ($field) {
return jq('<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>'.replace('#FIELD_NAME#', $field));
}
CategorySelector.setCategory = function ($field, $category_id) {
var $field_id = this.getField($field);
$('#' + $field_id).val($category_id);
$('#' + $field_id + '_path').load(
'<inp2:m_Link template="categories/category_path" category_id="#ID#" no_amp="1" js_escape="1"/>'.replace('#ID#', $category_id),
function () {
$('#' + $field_id + '_path, #' + $field_id + '_disable_link').toggle( $category_id !== '' );
}
);
}
$(document).ready(
function() {
var $field_id = CategorySelector.getField('<inp2:m_Param name="field" js_escape="1"/>');
$('#' + $field_id + '_disable_link').click(
function ($e) {
CategorySelector.setCategory('<inp2:m_Param name="field" js_escape="1"/>', '');
return false;
}
);
}
);
</script>
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
-<inp2:m_DefineElement name="inp_edit_combo_target" caption_render_as="default_field_caption_element" read_only="0" has_empty="" hint_label="">
+<inp2:m_DefineElement name="inp_edit_combo_target" title="la_fld_{$field}" caption_render_as="default_field_caption_element" read_only="0" has_empty="" hint_label="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_if check="m_Param" name="read_only">
<inp2:$prefix_Field name="{$field}Type"/>: "<inp2:$prefix_Field name="$field"/>"
<inp2:m_else/>
<select tabindex="<inp2:m_get param='tab_index'/>" name="<inp2:{$prefix}_InputName field='{$field}Type'/>" id="<inp2:{$prefix}_InputName field='{$field}Type'/>">
<inp2:{$prefix}_PredefinedOptions field="{$field}Type" block="inp_option_phrase" selected="selected" has_empty="$has_empty"/>
</select>
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_get param='tab_index'/>" style="width: 250px;"/>
</inp2:m_if>
<script type="text/javascript">
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>Type'] = '<inp2:m_phrase label="{$title}Type" js_escape="1"/>'
new AJAXDropDown(
'<inp2:$prefix_InputName name="$field" js_escape="1"/>',
function(cur_value) {
var $type = $( jq('#<inp2:$prefix_InputName name="{$field}Type" js_escape="1"/>') ).val();
if ( !isNaN( parseInt($type) ) ) {
var $url = '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnSuggestAddress" value="#VALUE#" type="#TYPE#" no_amp="1"/>';
return $url.replace('#VALUE#', encodeURIComponent(cur_value)).replace('#TYPE#', encodeURIComponent($type));
}
return false;
}
);
$(document).ready(
function () {
$( jq('#<inp2:$prefix_InputName name="{$field}Type"/>') ).change(
function ($e) {
var $me = $(this);
if ($me.attr('disabled')) {
return ;
}
var $type_selected = !isNaN( parseInt( $me.val() ) );
$( jq('#<inp2:$prefix_InputName name="$field"/>') ).attr('disabled', $type_selected ? '' : 'disabled');
}
)
.change();
}
);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="email_recipients_js" prefix="emailevents" to_readonly="auto">
<script type="text/javascript">
Recipients.formatLine = function($record_index) {
if (this.Records[$record_index]['RecipientName']) {
// name specified
if (this.Records[$record_index]['RecipientAddressType'] == 1) {
// email
this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#>';
}
else {
// other
this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#> - #RecipientAddressType#';
}
}
else {
// name not specified
this.ResultMask = '#RecipientType#: #RecipientAddress# - #RecipientAddressType#';
}
return MultiInputControl.prototype.formatLine.call(this, $record_index);
}
Recipients.compareRecords = function($record_a, $record_b) {
var $equals = true;
var $compare_fields = ['RecipientAddressType', 'RecipientAddress'];
for (var $i = 0; $i < $compare_fields.length; $i++) {
var $field_name = $compare_fields[$i];
if ($record_a[$field_name] !== $record_b[$field_name]) {
return false;
}
}
return $equals;
}
<inp2:m_if check="m_Param" name="to_readonly" equals_to="auto">
<inp2:$prefix_ToRecipientReadOnly result_to_var="to_readonly"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="to_readonly">
Recipients.isReadOnly = function($record_index) {
return this.Records[$record_index]['RecipientType'] == <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_TO"/>;
}
<inp2:$prefix_RestoreRecipientType/>
Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_CC"/>});
<inp2:m_else/>
Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientType" option="default"/>});
</inp2:m_if>
Recipients.registerControl('RecipientAddressType', {type: 'select', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientAddressType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientAddressType" option="default"/>});
Recipients.registerControl('RecipientAddress', {type: 'textbox', required: true, 'default': '<inp2:$prefix_FieldOption field="RecipientAddress" option="default" js_escape="1"/>'});
Recipients.registerControl('RecipientName', {type: 'textbox', required: false, 'default': '<inp2:$prefix_FieldOption field="RecipientName" option="default" js_escape="1"/>'});
Recipients.LoadValues();
</script>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_option_item">
<option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_param name="option"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_option_phrase">
<option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_phrase label="$option"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_options" onchange="" has_empty="0" empty_value="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
</inp2:m_if>
</select>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_single_option">
<option value="<inp2:m_param name='value'/>"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> selected</inp2:m_if>><inp2:$prefix_OptionValue field="$field" value="$value"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_multioptions" has_empty="0" empty_value="" style="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>_select" onchange="update_multiple_options('<inp2:{$prefix}_InputName field='$field'/>');">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
</inp2:m_if>
</select>
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_radio_item" onclick="" onchange="">
<input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_param name="option"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_radio_phrase" onclick="" onchange="">
<input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field="$field"/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_phrase label="$option"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_radio" onclick="" onchange="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_phrase" selected="checked" onclick="$onclick" onchange="$onchange" />
<inp2:m_else />
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_item" selected="checked" onclick="$onclick" onchange="$onchange" />
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="js_option_item">
'<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Param name="option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="js_option_phrase">
'<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Phrase name="$option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_json_options">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
{<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_phrase" selected="selected"/>}
<inp2:m_else />
{<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_item" selected="selected"/>}
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkbox" field_class="" onchange="" onclick="" NamePrefix="_cb_">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>">
<input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" name="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_checkbox_item">
<input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_param name="option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/>&nbsp;</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_checkbox_phrase">
<input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_phrase label="$option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/>&nbsp;</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkboxes" no_empty="" has_br="0">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_phrase" selected="checked" has_br="$has_br"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_item" selected="checked" has_br="$has_br"/>
</inp2:m_if>
<inp2:m_RenderElement prefix="$prefix" name="inp_edit_hidden" field="$field" db="db"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_single_checkbox">
<input type="checkbox"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> checked</inp2:m_if> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>" value="<inp2:m_param name='value'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>"><inp2:$prefix_OptionValue field="$field" value="$value"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkbox_allow_html" field_class="" onchange="" onclick="" title="la_enable_html" hint_label="la_Warning_Enable_HTML">
<inp2:m_RenderElement name="inp_edit_checkbox" pass_params="1"/>
<!--##
<inp2:m_if check="{$prefix}_FieldVisible" field="$field">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row">
<td class="control-cell">
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>">
<input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" name="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>">
</td>
<inp2:m_RenderElement name="inp_edit_error" pass_params="1"/>
</tr>
</inp2:m_if>
##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_weight" class="" size="" maxlength="" onblur="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="1">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_kg" />
</inp2:m_if>
<inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="2">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_a'/>" id="<inp2:{$prefix}_InputName field='{$field}_a'/>" value="<inp2:{$prefix}_Field field='{$field}_a'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_lbs" />
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_b'/>" id="<inp2:{$prefix}_InputName field='{$field}_b'/>" value="<inp2:{$prefix}_Field field='{$field}_b'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_oz" />
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_minput" style="" format="" allow_add="1" allow_edit="1" allow_delete="1" allow_move="1" title="">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<table>
<tr>
<td colspan="2">
<input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Add'/>" id="<inp2:$prefix_InputName field='$field'/>_add_button"/>
<input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Cancel'/>" id="<inp2:$prefix_InputName field='$field'/>_cancel_button"/>
</td>
</tr>
<tr>
<td valign="top">
<select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:$prefix_InputName field='$field'/>_minput" style="<inp2:m_Param name='style'/>">
</select>
</td>
<td valign="top">
<inp2:m_if check="m_Param" name="allow_edit">
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Edit'/>" id="<inp2:$prefix_InputName field='$field'/>_edit_button"/><br />
<img src="img/spacer.gif" height="4" width="1" alt=""/><br />
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_delete">
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Delete'/>" id="<inp2:$prefix_InputName field='$field'/>_delete_button"/><br />
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_move">
<br /><br />
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveUp'/>" id="<inp2:$prefix_InputName field='$field'/>_moveup_button"/><br />
<img src="img/spacer.gif" height="4" width="1" alt=""/><br />
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveDown'/>" id="<inp2:$prefix_InputName field='$field'/>_movedown_button"/><br />
</inp2:m_if>
</td>
</tr>
<inp2:m_RenderElement name="inp_edit_hidden" prefix="$prefix" field="$field" db="db"/>
<script type="text/javascript">
var <inp2:m_Param name="field"/> = new MultiInputControl('<inp2:m_Param name="field"/>', '<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>', fields['<inp2:m_Param name="prefix"/>'], '<inp2:m_Param name="format"/>');
<inp2:m_Param name="field"/>.ValidateURL = '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnValidateMInputFields" js_escape="1"/>';
<inp2:m_if check="m_Param" name="allow_add">
<inp2:m_Param name="field"/>.SetPermission('add', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_edit">
<inp2:m_Param name="field"/>.SetPermission('edit', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_delete">
<inp2:m_Param name="field"/>.SetPermission('delete', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_move">
<inp2:m_Param name="field"/>.SetPermission('move', true);
</inp2:m_if>
<inp2:m_Param name="field"/>.InitEvents();
<inp2:m_Param name="field"/>.SetMessage('required_error', '<inp2:m_Phrase name="la_err_required" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('unique_error', '<inp2:m_Phrase name="la_error_unique" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('delete_confirm', '<inp2:m_Phrase label="la_Delete_Confirm" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('add_button', '<inp2:m_Phrase name="la_btn_Add" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('save_button', '<inp2:m_Phrase name="la_btn_Save" escape="1"/>');
</script>
</table>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_picker" has_empty="0" empty_value="" style="width: 225px;" size="15">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<table cellpadding="0" cellspacing="0">
<tr>
<td><strong><inp2:m_Phrase label="la_SelectedItems" /></strong></td>
<td>&nbsp;</td>
<td><strong><inp2:m_Phrase label="la_AvailableItems" /></strong></td>
</tr>
<tr>
<td>
<inp2:m_DefineElement name="picker_option_block">
<option value="<inp2:Field name='$key_field' />"><inp2:Field name="$value_field" /></option>
</inp2:m_DefineElement>
<select multiple id="<inp2:$prefix_InputName name='$field' />_selected" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>">
<inp2:$optprefix.selected_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" per_page="-1" requery="1" link_to_prefix="$prefix" link_to_field="$field"/>
</select>
</td>
<td align="center">
<img src="img/icons/icon_left.gif" id="<inp2:$prefix_InputName name="$field" />_move_left_button"/><br />
<img src="img/icons/icon_right.gif" id="<inp2:$prefix_InputName name="$field" />_move_right_button"/>
</td>
<td>
<select multiple id="<inp2:$prefix_InputName name='$field' />_available" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>">
<inp2:$optprefix.available_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" requery="1" per_page="-1" link_to_prefix="$prefix" link_to_field="$field"/>
</select>
</td>
</tr>
</table>
<input type="hidden" name="<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />" value="<inp2:$prefix_Field field='$field' db='db'/>">
<input type="hidden" name="unselected_<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />_available_field" value="">
<script type="text/javascript">
<inp2:m_Param name="field"/> = new EditPickerControl('<inp2:m_Param name="field"/>', '<inp2:$prefix_InputName name="$field" />');
<inp2:m_Param name="field"/>.SetMessage('nothing_selected', '<inp2:m_Phrase label="la_error_SelectItemToMove" escape="1"/>');
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_filler" control_options="false">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" style="height: auto">
<td class="label-cell-filler" ></td>
<td class="control-mid-filler" ></td>
<td class="control-cell-filler">
<input type="text" style="display: none;"/>
<div id="form_filler" style="width: 100%; height: 5px; background-color: inherit"></div>
<script type="text/javascript">
Form.addControl('form_filler', <inp2:m_param name="control_options"/>);
</script>
</td>
</tr>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="ajax_progress_bar">
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td colspan="2">
<img src="img/spacer.gif" height="10" width="1" alt="" /><br />
<!-- progress bar paddings: begin -->
<table width="90%" cellpadding="2" cellspacing="0" border="0" align="center">
<tr>
<td class="progress-text">0%</td>
<td width="100%">
<!-- progress bar: begin -->
<table cellspacing="0" cellpadding="0" width="100%" border="0" align="center" style="background-color: #FFFFFF; border: 1px solid #E6E6E6;">
<tr>
<td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td>
</tr>
<tr>
<td width="2"><img src="img/spacer.gif" height="13" width="3" alt="" /></td>
<td align="center" width="100%">
<table cellspacing="0" cellpadding="0" width="100%" border="0" style="background: url(img/progress_left.gif) repeat-x;">
<tr>
<td id="progress_bar[done]" style="background: url(img/progress_done.gif);" align="left"></td>
<td id="progress_bar[left]" align="right"><img src="img/spacer.gif" height="9" width="1" alt="" /></td>
</tr>
</table>
</td>
<td width="1"><img src="img/spacer.gif" height="13" width="3" alt="" /></td>
</tr>
<tr>
<td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td>
</tr>
</table>
<!-- progress bar: end -->
</td>
<td class="progress-text">100%</td>
</tr>
</table>
<!-- progress bar paddings: end -->
<img src="img/spacer.gif" height="10" width="1" alt="" /><br />
</td>
</tr>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td width="50%" align="right"><inp2:m_phrase name="la_fld_PercentsCompleted"/>:</td>
<td id="progress_display[percents_completed]">0%</td>
</tr>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td align="right"><inp2:m_phrase name="la_fld_ElapsedTime"/>:</td>
<td id="progress_display[elapsed_time]">00:00</td>
</tr>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td align="right"><inp2:m_phrase name="la_fld_EstimatedTime"/>:</td>
<td id="progress_display[Estimated_time]">00:00</td>
</tr>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td align="center" colspan="2">
<input type="button" class="button" onclick="<inp2:m_param name="cancel_action"/>" value="<inp2:m_phrase name="la_Cancel"/>" />
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_navigation" toolbar="a_toolbar">
<inp2:m_if check="{$prefix}_IsTopmostPrefix">
<inp2:m_if check="{$prefix}_IsSingle">
<inp2:m_param name="toolbar"/>.HideButton('prev');
<inp2:m_param name="toolbar"/>.HideButton('next');
<inp2:m_else/>
<inp2:m_if check="{$prefix}_IsLast">
<inp2:m_param name="toolbar"/>.DisableButton('next');
</inp2:m_if>
<inp2:m_if check="{$prefix}_IsFirst">
<inp2:m_param name="toolbar"/>.DisableButton('prev');
</inp2:m_if>
</inp2:m_if>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="toolbar_button" icon="" title="" short_title="" toolbar="a_toolbar">
<inp2:m_Param name="toolbar"/>.AddButton(
new ToolBarButton(
'<inp2:m_Param name="icon"/>',
<inp2:m_if check="m_Param" name="short_title">
'<inp2:m_Phrase label="$title" escape="1"/>::<inp2:m_phrase label="$short_title" escape="1"/>',
<inp2:m_else/>
'<inp2:m_Phrase label="$title" escape="1"/>',
</inp2:m_if>
function() {
<inp2:m_Param name="content"/>
}
)
);
</inp2:m_DefineElement>
<inp2:m_DefineElement name="tabs_container" tabs_render_as="">
<table cellpadding="0" cellspacing="0" style="width: 100%;">
<tr>
<td style="width: 20px;">
<img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/>
<a href="#" class="scroll-left disabled"></a>
</td>
<td height="23" align="right">
<div id="tab-measure" style="display: none; width: 100%; height: 23px;">&nbsp;</div>
<div style="overflow: hidden; height: 23px;" class="tab-viewport">
<table class="tabs" cellpadding="0" cellspacing="0" height="23">
<tr>
<inp2:m_RenderElement name="$tabs_render_as" pass_params="1"/>
</tr>
</table>
</div>
</td>
<td class="scroll-right-container disabled">
<img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/>
<a href="#" class="scroll-right disabled"></a>
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_tabs_element">
<inp2:m_DefineElement name="edit_tab">
<inp2:m_RenderElement name="tab" title="$title" t="$template" main_prefix="$PrefixSpecial"/>
</inp2:m_DefineElement>
<inp2:{$prefix}_PrintEditTabs render_as="edit_tab" preset_name="$preset_name"/>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_tabs" preset_name="Default">
<inp2:m_if check="{$prefix}_HasEditTabs" preset_name="$preset_name">
<inp2:m_RenderElement name="tabs_container" tabs_render_as="edit_tabs_element" pass_params="1"/>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="ml_selector" prefix="">
<inp2:m_if check="lang_IsMultiLanguage">
<td align="right" style="padding-right: 5px;">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="right">
<inp2:m_phrase name="la_fld_Language"/>:
<select name="language" onchange="submit_event('<inp2:m_param name='prefix'/>', 'OnPreSaveAndChangeLanguage');">
<inp2:m_DefineElement name="lang_elem">
<option value="<inp2:Field name='LanguageId'/>" <inp2:m_if check="SelectedLanguage" type="data">selected="selected"</inp2:m_if> ><inp2:Field name="LocalName" no_special='no_special' /></option>
</inp2:m_DefineElement>
<inp2:lang_PrintList render_as="lang_elem"/>
</select>
</td>
</tr>
<tr>
<td align="right" style="vertical-align: bottom; padding: 2px 0px 5px 2px;">
<span style="color: red">*</span>&nbsp;<span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span>
</td>
</tr>
</table>
</td>
<inp2:m_else/>
<td align="right" style="vertical-align: bottom; padding: 2px 5px 5px 2px;">
<span style="color: red">*</span>&nbsp;<span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span>
</td>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="form_error_warning">
<inp2:m_RenderElement design="form_message" pass_params="1">
<inp2:m_Phrase name="la_Warning_NewFormError"/><br/>
<span id="error_msg_<inp2:m_Param name='prefix'/>" style="font-weight: bold;"><br/></span>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
Index: branches/5.2.x/core/admin_templates/languages/email_message_settings.tpl
===================================================================
--- branches/5.2.x/core/admin_templates/languages/email_message_settings.tpl (revision 15251)
+++ branches/5.2.x/core/admin_templates/languages/email_message_settings.tpl (revision 15252)
@@ -1,193 +1,194 @@
<inp2:adm_SetPopupSize width="875" height="650"/>
<inp2:m_include t="incs/header"/>
<inp2:m_RenderElement name="combined_header" section="in-portal:configemail" prefix="emailevents" title_preset="email_message_edit_settings" tab_preset="Default"/>
<!-- ToolBar -->
<table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td>
<script type="text/javascript">
a_toolbar = new ToolBar();
a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() {
submit_event('emailevents','<inp2:emailevents_SaveEvent/>');
}
) );
a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() {
cancel_edit('emailevents','OnCancelEdit','<inp2:emailevents_SaveEvent/>','<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>');
}
) );
a_toolbar.AddButton( new ToolBarButton('reset_edit', '<inp2:m_phrase label="la_ToolTip_Reset" escape="1"/>', function() {
reset_form('emailevents', 'OnReset', '<inp2:m_Phrase label="la_FormResetConfirmation" escape="1"/>');
}
) );
<inp2:m_if check="emailevents_IsSingle" inverse="inverse">
a_toolbar.AddButton( new ToolBarSeparator('sep1') );
</inp2:m_if>
a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() {
go_to_id('emailevents', '<inp2:emailevents_PrevId/>');
}
) );
a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() {
go_to_id('emailevents', '<inp2:emailevents_NextId/>');
}
) );
a_toolbar.Render();
<inp2:m_RenderElement name="edit_navigation" prefix="emailevents"/>
</script>
</td>
<inp2:m_RenderElement name="ml_selector" prefix="emailevents"/>
</tr>
</tbody>
</table>
<inp2:emailevents_SaveWarning name="grid_save_warning"/>
<inp2:emailevents_ErrorWarning name="form_error_warning"/>
<div id="scroll_container">
<table class="edit-form">
<inp2:m_RenderElement name="subsection" prefix="emailevents" fields="AllowChangingSender,CustomSender,SenderAddress,SenderAddressType,SenderName" title="la_section_SenderInfo"/>
<inp2:m_if check="m_IsDebugMode">
- <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="AllowChangingSender" title="la_fld_AllowChangingSender"/>
+ <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="AllowChangingSender"/>
</inp2:m_if>
<inp2:m_if check="emailevents_IsEditable" check_field="AllowChangingSender">
- <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="CustomSender" title="la_fld_CustomSender"/>
- <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="SenderAddress" title="la_fld_SenderAddress" has_empty="1"/>
- <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="SenderName" title="la_fld_SenderName" style="width: 318px;"/>
+ <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="CustomSender"/>
+ <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="SenderAddress" has_empty="1"/>
+ <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="SenderName" style="width: 318px;"/>
<inp2:m_else/>
- <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="CustomSender" title="la_fld_CustomSender"/>
- <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="SenderAddress" title="la_fld_SenderAddress" read_only="1"/>
- <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="SenderName" title="la_fld_SenderName"/>
+ <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="CustomSender"/>
+ <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="SenderAddress" read_only="1"/>
+ <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="SenderName"/>
</inp2:m_if>
<inp2:m_RenderElement name="subsection" prefix="emailevents" fields="AllowChangingRecipient,CustomRecipient,RecipientType,RecipientAddress,RecipientAddressType,RecipientName,Recipients" title="la_section_RecipientsInfo"/>
<inp2:m_if check="m_IsDebugMode">
- <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="AllowChangingRecipient" title="la_fld_AllowChangingRecipient"/>
+ <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="AllowChangingRecipient"/>
</inp2:m_if>
<inp2:m_if check="emailevents_IsEditable" check_field="AllowChangingRecipient">
- <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="CustomRecipient" title="la_fld_CustomRecipient"/>
+ <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="CustomRecipient"/>
<inp2:m_else/>
- <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="CustomRecipient" title="la_fld_CustomRecipient"/>
+ <inp2:m_RenderElement name="inp_label" prefix="emailevents" field="CustomRecipient"/>
<inp2:emailevents_RemoveToRecipientType/>
</inp2:m_if>
- <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="RecipientType" title="la_fld_RecipientType"/>
- <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="RecipientAddress" title="la_fld_RecipientAddress" has_empty="1"/>
- <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="RecipientName" title="la_fld_RecipientName" style="width: 318px;"/>
- <inp2:m_RenderElement name="inp_edit_minput" prefix="emailevents" field="Recipients" title="la_fld_Recipients" format="auto" style="width: 400px; height: 150px;"/>
+ <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="RecipientType"/>
+ <inp2:m_RenderElement name="inp_edit_combo_target" prefix="emailevents" field="RecipientAddress" has_empty="1"/>
+ <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="RecipientName" style="width: 318px;"/>
+ <inp2:m_RenderElement name="inp_edit_minput" prefix="emailevents" field="Recipients" format="auto" style="width: 400px; height: 150px;"/>
<inp2:m_RenderElement name="subsection" prefix="emailevents" fields="Tag,Replacement,ReplacementTagsXML" title="la_section_ReplacementTags"/>
- <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="Tag" title="la_fld_Tag"/>
- <inp2:m_RenderElement name="inp_edit_textarea" prefix="emailevents" field="Replacement" title="la_fld_Replacement"control_options="{height: 60}"/>
+ <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="Tag"/>
+ <inp2:m_RenderElement name="inp_edit_textarea" prefix="emailevents" field="Replacement" control_options="{height: 60}"/>
<inp2:m_RenderElement name="inp_edit_minput" prefix="emailevents" field="ReplacementTagsXML" title="la_fld_ReplacementTags" allow_move="0" format="#Tag# = #Replacement#" style="width: 600px; height: 100px;"/>
- <inp2:m_RenderElement name="subsection" prefix="emailevents" fields="Enabled,Description,FrontEndOnly,Type,Headers,Module" title="la_section_General"/>
+ <inp2:m_RenderElement name="subsection" prefix="emailevents" fields="Enabled,Description,FrontEndOnly,Type,Headers,Module,BindToSystemEvent" title="la_section_General"/>
<inp2:m_if check="m_IsDebugMode">
- <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="Enabled" title="la_fld_Enabled"/>
+ <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="Enabled"/>
</inp2:m_if>
- <inp2:m_RenderElement name="inp_edit_textarea" prefix="emailevents" field="Description" title="la_fld_Description" control_options="{max_height: 50}"/>
- <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="FrontEndOnly" title="la_fld_FrontEndOnly"/>
- <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="Type" title="la_fld_Type"/>
+ <inp2:m_RenderElement name="inp_edit_textarea" prefix="emailevents" field="Description" control_options="{max_height: 50}"/>
+ <inp2:m_RenderElement name="inp_edit_checkbox" prefix="emailevents" field="FrontEndOnly"/>
+ <inp2:m_RenderElement name="inp_edit_radio" prefix="emailevents" field="Type"/>
<inp2:m_RenderElement name="inp_edit_textarea" prefix="emailevents" field="Headers" title="la_fld_ExtraHeaders" control_options="{max_height: 50}"/>
- <inp2:m_RenderElement name="inp_edit_options" prefix="emailevents" field="Module" title="la_fld_Module" has_empty="1"/>
+ <inp2:m_RenderElement name="inp_edit_options" prefix="emailevents" field="Module" has_empty="1"/>
+ <inp2:m_RenderElement name="inp_edit_box" prefix="emailevents" field="BindToSystemEvent" style="width: 200px;"/>
<inp2:m_RenderElement name="inp_edit_filler"/>
</table>
</div>
<inp2:m_if check="emailevents_FieldVisible" field="Recipients">
<inp2:m_RenderElement name="email_recipients_js"/>
</inp2:m_if>
<script type="text/javascript">
<inp2:m_if check="emailevents_FieldVisible" field="ReplacementTagsXML">
// replacement tags
ReplacementTagsXML.formatLine = function($record_index) {
var $ret = MultiInputControl.prototype.formatLine.call(this, $record_index, false);
if ($ret.length > 80) {
$ret = $ret.substring(0, 80) + ' ...';
}
return this.htmlspecialchars($ret);
}
ReplacementTagsXML.compareRecords = function($record_a, $record_b) {
return $record_a['Tag'] == $record_b['Tag'];
}
ReplacementTagsXML.registerControl('Tag', 'textbox', true);
ReplacementTagsXML.registerControl('Replacement', 'textbox', true);
ReplacementTagsXML.LoadValues();
</inp2:m_if>
// reflect custom sender & custom recipient radio button-dependent controls
var $field_mask = '<inp2:emailevents_InputName name="#FIELD_NAME#" js_escape="1"/>';
function reflectCustomSender() {
var $default_sender = get_control($field_mask, 'CustomSender', '0').checked;
var $type = get_control($field_mask, 'SenderAddressType');
$type.disabled = $default_sender;
get_control($field_mask, 'SenderAddress').disabled = $default_sender;
get_control($field_mask, 'SenderName').disabled = $default_sender;
$($type).change();
}
function reflectCustomRecipient() {
<inp2:m_if check="emailevents_IsEditable" check_field="AllowChangingRecipient">
var $default_recipient = get_control($field_mask, 'CustomRecipient', '0').checked;
var $to_recipient = get_control($field_mask, 'RecipientType', <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_TO"/>);
<inp2:m_else/>
var $default_recipient = <inp2:m_if check="emailevents_Field" name="CustomRecipient" db="db" equals_to="0">true<inp2:m_else/>false</inp2:m_if>;
</inp2:m_if>
if ($default_recipient) {
<inp2:m_if check="emailevents_IsEditable" check_field="AllowChangingRecipient">
if ($to_recipient.checked) {
get_control($field_mask, 'RecipientType', <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_CC"/>).checked = true;
}
</inp2:m_if>
Recipients.registerControl('RecipientType', {'default': <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_CC"/>}, true);
}
else {
Recipients.registerControl('RecipientType', {'default': <inp2:m_GetConst name="EmailEvent::RECIPIENT_TYPE_TO"/>}, true);
}
<inp2:m_if check="emailevents_IsEditable" check_field="AllowChangingRecipient">
$to_recipient.disabled = $default_recipient;
</inp2:m_if>
}
reflectCustomSender();
reflectCustomRecipient();
$(document).ready(
function () {
$("input[name='" + jq($field_mask.replace('#FIELD_NAME#', 'CustomSender')) + "']").click(reflectCustomSender);
$("input[name='" + jq($field_mask.replace('#FIELD_NAME#', 'CustomRecipient')) + "']").click(reflectCustomRecipient);
}
);
// enable all disabled controls before submit
Application.setHook('emailevents:*',
function () {
$(':input:disabled').attr('disabled', '');
}
)
</script>
<inp2:m_include t="incs/footer"/>
Index: branches/5.2.x/core/install/install_schema.sql
===================================================================
--- branches/5.2.x/core/install/install_schema.sql (revision 15251)
+++ branches/5.2.x/core/install/install_schema.sql (revision 15252)
@@ -1,1351 +1,1365 @@
CREATE TABLE CategoryPermissionsConfig (
PermissionConfigId int(11) NOT NULL auto_increment,
PermissionName varchar(255) NOT NULL default '',
Description varchar(255) NOT NULL default '',
ModuleId varchar(20) NOT NULL default '0',
IsSystem tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (PermissionConfigId),
KEY PermissionName (PermissionName)
);
CREATE TABLE Permissions (
PermissionId int(11) NOT NULL auto_increment,
Permission varchar(255) NOT NULL default '',
GroupId int(11) default '0',
PermissionValue int(11) NOT NULL default '0',
`Type` tinyint(4) NOT NULL default '0',
CatId int(11) NOT NULL default '0',
PRIMARY KEY (PermissionId),
UNIQUE KEY PermIndex (Permission,GroupId,CatId,`Type`)
);
CREATE TABLE CustomFields (
CustomFieldId int(11) NOT NULL auto_increment,
`Type` int(11) NOT NULL default '0',
FieldName varchar(255) NOT NULL default '',
FieldLabel varchar(40) default NULL,
MultiLingual tinyint(3) unsigned NOT NULL default '1',
Heading varchar(60) default NULL,
Prompt varchar(60) default NULL,
ElementType varchar(50) NOT NULL default '',
ValueList text,
DefaultValue varchar(255) NOT NULL default '',
DisplayOrder int(11) NOT NULL default '0',
OnGeneralTab tinyint(4) NOT NULL default '0',
IsSystem tinyint(3) unsigned NOT NULL default '0',
IsRequired tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (CustomFieldId),
KEY `Type` (`Type`),
KEY MultiLingual (MultiLingual),
KEY DisplayOrder (DisplayOrder),
KEY OnGeneralTab (OnGeneralTab),
KEY IsSystem (IsSystem),
KEY DefaultValue (DefaultValue)
);
CREATE TABLE SystemSettings (
VariableId int(11) NOT NULL AUTO_INCREMENT,
VariableName varchar(255) NOT NULL DEFAULT '',
VariableValue text,
ModuleOwner varchar(20) DEFAULT 'In-Portal',
Section varchar(255) NOT NULL DEFAULT '',
Heading varchar(255) NOT NULL DEFAULT '',
Prompt varchar(255) NOT NULL DEFAULT '',
ElementType varchar(255) NOT NULL DEFAULT '',
Validation text,
ValueList text,
DisplayOrder double NOT NULL DEFAULT '0',
GroupDisplayOrder double NOT NULL DEFAULT '0',
`Install` int(11) NOT NULL DEFAULT '1',
HintLabel varchar(255) DEFAULT NULL,
PRIMARY KEY (VariableId),
UNIQUE KEY VariableName (VariableName),
KEY DisplayOrder (DisplayOrder),
KEY GroupDisplayOrder (GroupDisplayOrder),
KEY `Install` (`Install`),
KEY HintLabel (HintLabel)
);
CREATE TABLE EmailQueue (
EmailQueueId int(10) unsigned NOT NULL AUTO_INCREMENT,
ToEmail varchar(255) NOT NULL DEFAULT '',
`Subject` varchar(255) NOT NULL DEFAULT '',
MessageHeaders text,
MessageBody longtext,
Queued int(10) unsigned DEFAULT NULL,
SendRetries int(10) unsigned NOT NULL DEFAULT '0',
LastSendRetry int(10) unsigned DEFAULT NULL,
MailingId int(10) unsigned NOT NULL DEFAULT '0',
LogData text,
PRIMARY KEY (EmailQueueId),
KEY LastSendRetry (LastSendRetry),
KEY SendRetries (SendRetries),
KEY MailingId (MailingId)
);
CREATE TABLE EmailEvents (
EventId int(11) NOT NULL AUTO_INCREMENT,
`Event` varchar(40) NOT NULL DEFAULT '',
ReplacementTags text,
AllowChangingSender tinyint(4) NOT NULL DEFAULT '0',
CustomSender tinyint(4) NOT NULL DEFAULT '0',
SenderName varchar(255) NOT NULL DEFAULT '',
SenderAddressType tinyint(4) NOT NULL DEFAULT '0',
SenderAddress varchar(255) NOT NULL DEFAULT '',
AllowChangingRecipient tinyint(4) NOT NULL DEFAULT '0',
CustomRecipient tinyint(4) NOT NULL DEFAULT '0',
Recipients text,
l1_Subject text,
l2_Subject text,
l3_Subject text,
l4_Subject text,
l5_Subject text,
l1_HtmlBody longtext,
l2_HtmlBody longtext,
l3_HtmlBody longtext,
l4_HtmlBody longtext,
l5_HtmlBody longtext,
l1_PlainTextBody longtext,
l2_PlainTextBody longtext,
l3_PlainTextBody longtext,
l4_PlainTextBody longtext,
l5_PlainTextBody longtext,
Headers text,
Enabled int(11) NOT NULL DEFAULT '1',
FrontEndOnly tinyint(3) unsigned NOT NULL DEFAULT '0',
Module varchar(40) NOT NULL DEFAULT 'Core',
Description text,
`Type` int(11) NOT NULL DEFAULT '0',
LastChanged int(10) unsigned DEFAULT NULL,
+ BindToSystemEvent varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (EventId),
KEY `Type` (`Type`),
KEY Enabled (Enabled),
KEY `Event` (`Event`),
KEY FrontEndOnly (FrontEndOnly),
KEY AllowChangingSender (AllowChangingSender),
KEY CustomSender (CustomSender),
KEY SenderAddressType (SenderAddressType),
KEY AllowChangingRecipient (AllowChangingRecipient),
KEY CustomRecipient (CustomRecipient),
KEY l1_HtmlBody (l1_HtmlBody(5)),
KEY l2_HtmlBody (l2_HtmlBody(5)),
KEY l3_HtmlBody (l3_HtmlBody(5)),
KEY l4_HtmlBody (l4_HtmlBody(5)),
KEY l5_HtmlBody (l5_HtmlBody(5)),
KEY l1_PlainTextBody (l1_PlainTextBody(5)),
KEY l2_PlainTextBody (l2_PlainTextBody(5)),
KEY l3_PlainTextBody (l3_PlainTextBody(5)),
KEY l4_PlainTextBody (l4_PlainTextBody(5)),
KEY l5_PlainTextBody (l5_PlainTextBody(5))
);
+CREATE TABLE EmailEventSubscribers (
+ SubscriptionId int(11) NOT NULL AUTO_INCREMENT,
+ EmailEventId int(11) DEFAULT NULL,
+ SubscriberEmail varchar(255) NOT NULL DEFAULT '',
+ UserId int(11) DEFAULT NULL,
+ CategoryId int(11) DEFAULT NULL,
+ ItemId int(11) DEFAULT NULL,
+ ParentItemId int(11) DEFAULT NULL,
+ SubscribedOn int(11) DEFAULT NULL,
+ PRIMARY KEY (SubscriptionId),
+ KEY EmailEventId (EmailEventId)
+);
+
CREATE TABLE IdGenerator (
lastid int(11) default NULL
);
CREATE TABLE Languages (
LanguageId int(11) NOT NULL AUTO_INCREMENT,
PackName varchar(40) NOT NULL DEFAULT '',
LocalName varchar(40) NOT NULL DEFAULT '',
Enabled int(11) NOT NULL DEFAULT '1',
PrimaryLang int(11) NOT NULL DEFAULT '0',
AdminInterfaceLang tinyint(3) unsigned NOT NULL DEFAULT '0',
Priority int(11) NOT NULL DEFAULT '0',
IconURL varchar(255) DEFAULT NULL,
IconDisabledURL varchar(255) DEFAULT NULL,
DateFormat varchar(50) NOT NULL DEFAULT 'm/d/Y',
TimeFormat varchar(50) NOT NULL DEFAULT 'g:i:s A',
InputDateFormat varchar(50) NOT NULL DEFAULT 'm/d/Y',
InputTimeFormat varchar(50) NOT NULL DEFAULT 'g:i:s A',
DecimalPoint varchar(10) NOT NULL DEFAULT '.',
ThousandSep varchar(10) NOT NULL DEFAULT '',
`Charset` varchar(20) NOT NULL DEFAULT 'utf-8',
UnitSystem tinyint(4) NOT NULL DEFAULT '1',
FilenameReplacements text,
Locale varchar(10) NOT NULL DEFAULT 'en-US',
UserDocsUrl varchar(255) NOT NULL DEFAULT '',
SynchronizationModes varchar(255) NOT NULL DEFAULT '',
HtmlEmailTemplate text,
TextEmailTemplate text,
PRIMARY KEY (LanguageId),
KEY Enabled (Enabled),
KEY PrimaryLang (PrimaryLang),
KEY AdminInterfaceLang (AdminInterfaceLang),
KEY Priority (Priority)
);
CREATE TABLE Modules (
`Name` varchar(255) NOT NULL DEFAULT '',
Path varchar(255) NOT NULL DEFAULT '',
Var varchar(100) NOT NULL DEFAULT '',
Version varchar(10) NOT NULL DEFAULT '0.0.0',
Loaded tinyint(4) NOT NULL DEFAULT '1',
LoadOrder tinyint(4) NOT NULL DEFAULT '0',
TemplatePath varchar(255) NOT NULL DEFAULT '',
RootCat int(11) NOT NULL DEFAULT '0',
BuildDate int(10) unsigned DEFAULT NULL,
AppliedDBRevisions text,
PRIMARY KEY (`Name`),
KEY Loaded (Loaded),
KEY LoadOrder (LoadOrder)
);
CREATE TABLE UserPersistentSessionData (
VariableId bigint(20) NOT NULL AUTO_INCREMENT,
PortalUserId int(11) NOT NULL DEFAULT '0',
VariableName varchar(255) NOT NULL DEFAULT '',
VariableValue text,
PRIMARY KEY (VariableId),
KEY UserId (PortalUserId),
KEY VariableName (VariableName)
);
CREATE TABLE LanguageLabels (
PhraseId int(11) NOT NULL AUTO_INCREMENT,
Phrase varchar(255) NOT NULL DEFAULT '',
PhraseKey varchar(255) NOT NULL DEFAULT '',
l1_Translation text,
l2_Translation text,
l3_Translation text,
l4_Translation text,
l5_Translation text,
l1_HintTranslation text,
l2_HintTranslation text,
l3_HintTranslation text,
l4_HintTranslation text,
l5_HintTranslation text,
l1_ColumnTranslation text,
l2_ColumnTranslation text,
l3_ColumnTranslation text,
l4_ColumnTranslation text,
l5_ColumnTranslation text,
PhraseType int(11) NOT NULL DEFAULT '0',
LastChanged int(10) unsigned DEFAULT NULL,
LastChangeIP varchar(15) NOT NULL DEFAULT '',
Module varchar(30) NOT NULL DEFAULT 'In-Portal',
PRIMARY KEY (PhraseId),
KEY Phrase_Index (Phrase),
KEY PhraseKey (PhraseKey),
KEY l1_Translation (l1_Translation(5)),
KEY l1_HintTranslation (l1_HintTranslation(5)),
KEY l1_ColumnTranslation (l1_ColumnTranslation(5))
);
CREATE TABLE PhraseCache (
Template varchar(40) NOT NULL DEFAULT '',
PhraseList text,
CacheDate int(11) NOT NULL DEFAULT '0',
ThemeId int(11) NOT NULL DEFAULT '0',
StylesheetId int(10) unsigned NOT NULL DEFAULT '0',
ConfigVariables text,
PRIMARY KEY (Template),
KEY CacheDate (CacheDate),
KEY ThemeId (ThemeId),
KEY StylesheetId (StylesheetId)
);
CREATE TABLE UserGroups (
GroupId int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL DEFAULT '',
Description varchar(255) DEFAULT NULL,
CreatedOn int(10) unsigned DEFAULT NULL,
System tinyint(4) NOT NULL DEFAULT '0',
Personal tinyint(4) NOT NULL DEFAULT '0',
Enabled tinyint(4) NOT NULL DEFAULT '1',
FrontRegistration tinyint(3) unsigned NOT NULL DEFAULT '0',
IPRestrictions text,
PRIMARY KEY (GroupId),
UNIQUE KEY `Name` (`Name`),
KEY Personal (Personal),
KEY Enabled (Enabled),
KEY CreatedOn (CreatedOn)
);
CREATE TABLE Users (
PortalUserId int(11) NOT NULL AUTO_INCREMENT,
Username varchar(255) NOT NULL DEFAULT '',
`Password` varchar(255) DEFAULT 'd41d8cd98f00b204e9800998ecf8427e',
FirstName varchar(255) NOT NULL DEFAULT '',
LastName varchar(255) NOT NULL DEFAULT '',
Company varchar(255) NOT NULL DEFAULT '',
Email varchar(255) NOT NULL DEFAULT '',
PrevEmails text,
CreatedOn int(11) DEFAULT NULL,
Phone varchar(255) NOT NULL DEFAULT '',
Fax varchar(255) NOT NULL DEFAULT '',
Street varchar(255) NOT NULL DEFAULT '',
Street2 varchar(255) NOT NULL DEFAULT '',
City varchar(255) NOT NULL DEFAULT '',
State varchar(20) NOT NULL DEFAULT '',
Zip varchar(20) NOT NULL DEFAULT '',
Country varchar(20) NOT NULL DEFAULT '',
ResourceId int(11) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '1',
EmailVerified tinyint(4) NOT NULL,
Modified int(11) DEFAULT NULL,
dob int(11) DEFAULT NULL,
TimeZone varchar(255) NOT NULL DEFAULT '',
IPAddress varchar(15) NOT NULL DEFAULT '',
IsBanned tinyint(1) NOT NULL DEFAULT '0',
PwResetConfirm varchar(255) NOT NULL DEFAULT '',
PwRequestTime int(11) unsigned DEFAULT NULL,
FrontLanguage int(11) DEFAULT NULL,
AdminLanguage int(11) DEFAULT NULL,
DisplayToPublic text,
UserType tinyint(4) NOT NULL,
PrimaryGroupId int(11) DEFAULT NULL,
OldStyleLogin tinyint(4) NOT NULL,
IPRestrictions text,
PRIMARY KEY (PortalUserId),
UNIQUE KEY ResourceId (ResourceId),
KEY CreatedOn (CreatedOn),
KEY `Status` (`Status`),
KEY Modified (Modified),
KEY dob (dob),
KEY IsBanned (IsBanned),
KEY UserType (UserType),
KEY Username (Username)
);
CREATE TABLE UserCustomData (
CustomDataId int(11) NOT NULL auto_increment,
ResourceId int(10) unsigned NOT NULL default '0',
KEY ResourceId (ResourceId),
PRIMARY KEY (CustomDataId)
);
CREATE TABLE UserSessionData (
SessionKey varchar(50) NOT NULL DEFAULT '',
VariableName varchar(255) NOT NULL DEFAULT '',
VariableValue longtext,
PRIMARY KEY (SessionKey,VariableName),
KEY SessionKey (SessionKey),
KEY VariableName (VariableName)
);
CREATE TABLE Themes (
ThemeId int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(40) NOT NULL DEFAULT '',
Enabled int(11) NOT NULL DEFAULT '1',
Description varchar(255) DEFAULT NULL,
PrimaryTheme int(11) NOT NULL DEFAULT '0',
CacheTimeout int(11) NOT NULL DEFAULT '0',
StylesheetId int(10) unsigned NOT NULL DEFAULT '0',
LanguagePackInstalled tinyint(3) unsigned NOT NULL DEFAULT '0',
TemplateAliases text,
PRIMARY KEY (ThemeId),
KEY Enabled (Enabled),
KEY StylesheetId (StylesheetId),
KEY PrimaryTheme (PrimaryTheme),
KEY LanguagePackInstalled (LanguagePackInstalled)
);
CREATE TABLE ThemeFiles (
FileId int(11) NOT NULL AUTO_INCREMENT,
ThemeId int(11) NOT NULL DEFAULT '0',
FileName varchar(255) NOT NULL DEFAULT '',
FilePath varchar(255) NOT NULL DEFAULT '',
TemplateAlias varchar(255) NOT NULL DEFAULT '',
Description varchar(255) DEFAULT NULL,
FileType int(11) NOT NULL DEFAULT '0',
FileFound tinyint(3) unsigned NOT NULL DEFAULT '0',
FileMetaInfo text,
PRIMARY KEY (FileId),
KEY theme (ThemeId),
KEY FileName (FileName),
KEY FilePath (FilePath),
KEY FileFound (FileFound),
KEY TemplateAlias (TemplateAlias)
);
CREATE TABLE UserGroupRelations (
PortalUserId int(11) NOT NULL DEFAULT '0',
GroupId int(11) NOT NULL DEFAULT '0',
MembershipExpires int(10) unsigned DEFAULT NULL,
ExpirationReminderSent tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (PortalUserId,GroupId),
KEY GroupId (GroupId),
KEY MembershipExpires (MembershipExpires),
KEY ExpirationReminderSent (ExpirationReminderSent)
);
CREATE TABLE UserSessions (
SessionKey int(10) unsigned NOT NULL DEFAULT '0',
LastAccessed int(10) unsigned NOT NULL DEFAULT '0',
PortalUserId int(11) NOT NULL DEFAULT '-2',
`Language` int(11) NOT NULL DEFAULT '1',
Theme int(11) NOT NULL DEFAULT '1',
GroupId int(11) NOT NULL DEFAULT '0',
IpAddress varchar(20) NOT NULL DEFAULT '0.0.0.0',
`Status` int(11) NOT NULL DEFAULT '1',
GroupList varchar(255) DEFAULT NULL,
TimeZone varchar(255) NOT NULL DEFAULT '',
BrowserSignature varchar(32) NOT NULL DEFAULT '',
PRIMARY KEY (SessionKey),
KEY UserId (PortalUserId),
KEY LastAccessed (LastAccessed),
KEY BrowserSignature (BrowserSignature)
);
CREATE TABLE EmailLog (
EmailLogId int(11) NOT NULL AUTO_INCREMENT,
`From` varchar(255) NOT NULL DEFAULT '',
`To` varchar(255) NOT NULL DEFAULT '',
OtherRecipients text,
`Subject` varchar(255) NOT NULL DEFAULT '',
HtmlBody longtext,
TextBody longtext,
SentOn int(11) DEFAULT NULL,
EventName varchar(255) NOT NULL DEFAULT '',
EventParams text,
AccessKey varchar(32) NOT NULL DEFAULT '',
PRIMARY KEY (EmailLogId),
KEY `timestamp` (SentOn)
);
CREATE TABLE SystemCache (
VarName varchar(255) NOT NULL default '',
Data longtext,
Cached int(11) default NULL,
LifeTime int(11) NOT NULL default '-1',
PRIMARY KEY (VarName),
KEY Cached (Cached)
);
CREATE TABLE CountryStates (
CountryStateId int(11) NOT NULL AUTO_INCREMENT,
`Type` int(11) NOT NULL DEFAULT '1',
StateCountryId int(11) DEFAULT NULL,
l1_Name varchar(255) NOT NULL DEFAULT '',
l2_Name varchar(255) NOT NULL DEFAULT '',
l3_Name varchar(255) NOT NULL DEFAULT '',
l4_Name varchar(255) NOT NULL DEFAULT '',
l5_Name varchar(255) NOT NULL DEFAULT '',
IsoCode char(3) NOT NULL DEFAULT '',
ShortIsoCode char(2) DEFAULT NULL,
PRIMARY KEY (CountryStateId),
KEY `Type` (`Type`),
KEY StateCountryId (StateCountryId),
KEY l1_Name (l1_Name(5))
);
CREATE TABLE Categories (
CategoryId int(11) NOT NULL AUTO_INCREMENT,
`Type` int(11) NOT NULL DEFAULT '1',
SymLinkCategoryId int(10) unsigned DEFAULT NULL,
ParentId int(11) NOT NULL DEFAULT '0',
`Name` varchar(255) NOT NULL DEFAULT '',
l1_Name varchar(255) NOT NULL DEFAULT '',
l2_Name varchar(255) NOT NULL DEFAULT '',
l3_Name varchar(255) NOT NULL DEFAULT '',
l4_Name varchar(255) NOT NULL DEFAULT '',
l5_Name varchar(255) NOT NULL DEFAULT '',
Filename varchar(255) NOT NULL DEFAULT '',
AutomaticFilename tinyint(3) unsigned NOT NULL DEFAULT '1',
Description text,
l1_Description text,
l2_Description text,
l3_Description text,
l4_Description text,
l5_Description text,
CreatedOn int(11) DEFAULT NULL,
EditorsPick tinyint(4) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '1',
Priority int(11) NOT NULL DEFAULT '0',
MetaKeywords text,
CachedDescendantCatsQty int(11) NOT NULL DEFAULT '0',
CachedNavbar text,
l1_CachedNavbar text,
l2_CachedNavbar text,
l3_CachedNavbar text,
l4_CachedNavbar text,
l5_CachedNavbar text,
CreatedById int(11) DEFAULT NULL,
ResourceId int(11) DEFAULT NULL,
ParentPath text,
TreeLeft bigint(20) NOT NULL DEFAULT '0',
TreeRight bigint(20) NOT NULL DEFAULT '0',
NamedParentPath text,
MetaDescription text,
HotItem int(11) NOT NULL DEFAULT '2',
NewItem int(11) NOT NULL DEFAULT '2',
PopItem int(11) NOT NULL DEFAULT '2',
Modified int(11) DEFAULT NULL,
ModifiedById int(11) DEFAULT NULL,
CachedTemplate varchar(255) NOT NULL DEFAULT '',
Template varchar(255) NOT NULL DEFAULT '#inherit#',
UseExternalUrl tinyint(3) unsigned NOT NULL DEFAULT '0',
ExternalUrl varchar(255) NOT NULL DEFAULT '',
UseMenuIconUrl tinyint(3) unsigned NOT NULL DEFAULT '0',
MenuIconUrl varchar(255) NOT NULL DEFAULT '',
l1_Title varchar(255) DEFAULT '',
l2_Title varchar(255) DEFAULT '',
l3_Title varchar(255) DEFAULT '',
l4_Title varchar(255) DEFAULT '',
l5_Title varchar(255) DEFAULT '',
l1_MenuTitle varchar(255) NOT NULL DEFAULT '',
l2_MenuTitle varchar(255) NOT NULL DEFAULT '',
l3_MenuTitle varchar(255) NOT NULL DEFAULT '',
l4_MenuTitle varchar(255) NOT NULL DEFAULT '',
l5_MenuTitle varchar(255) NOT NULL DEFAULT '',
MetaTitle text,
IndexTools text,
IsMenu tinyint(4) NOT NULL DEFAULT '1',
Protected tinyint(4) NOT NULL DEFAULT '0',
FormId int(11) DEFAULT NULL,
FormSubmittedTemplate varchar(255) DEFAULT NULL,
FriendlyURL varchar(255) NOT NULL DEFAULT '',
ThemeId int(10) unsigned NOT NULL DEFAULT '0',
EnablePageCache tinyint(4) NOT NULL DEFAULT '0',
OverridePageCacheKey tinyint(4) NOT NULL DEFAULT '0',
PageCacheKey varchar(255) NOT NULL DEFAULT '',
PageExpiration int(11) DEFAULT NULL,
LiveRevisionNumber int(11) NOT NULL DEFAULT '1',
DirectLinkEnabled tinyint(4) NOT NULL DEFAULT '1',
DirectLinkAuthKey varchar(20) NOT NULL DEFAULT '',
PromoBlockGroupId int(10) unsigned NOT NULL DEFAULT '0',
RequireSSL tinyint(4) NOT NULL DEFAULT '0',
RequireLogin tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (CategoryId),
UNIQUE KEY ResourceId (ResourceId),
KEY ParentId (ParentId),
KEY Modified (Modified),
KEY Priority (Priority),
KEY sorting (`Name`,Priority),
KEY Filename (Filename(5)),
KEY l1_Name (l1_Name(5)),
KEY l2_Name (l2_Name(5)),
KEY l3_Name (l3_Name(5)),
KEY l4_Name (l4_Name(5)),
KEY l5_Name (l5_Name(5)),
KEY l1_Description (l1_Description(5)),
KEY l2_Description (l2_Description(5)),
KEY l3_Description (l3_Description(5)),
KEY l4_Description (l4_Description(5)),
KEY l5_Description (l5_Description(5)),
KEY TreeLeft (TreeLeft),
KEY TreeRight (TreeRight),
KEY SymLinkCategoryId (SymLinkCategoryId),
KEY `Status` (`Status`),
KEY CreatedOn (CreatedOn),
KEY EditorsPick (EditorsPick),
KEY ThemeId (ThemeId),
KEY EnablePageCache (EnablePageCache),
KEY OverridePageCacheKey (OverridePageCacheKey),
KEY PageExpiration (PageExpiration),
KEY Protected (Protected),
KEY LiveRevisionNumber (LiveRevisionNumber),
KEY PromoBlockGroupId (PromoBlockGroupId)
);
CREATE TABLE CategoryCustomData (
CustomDataId int(11) NOT NULL auto_increment,
ResourceId int(10) unsigned NOT NULL default '0',
KEY ResourceId (ResourceId),
PRIMARY KEY (CustomDataId)
);
CREATE TABLE CategoryItems (
CategoryId int(11) NOT NULL default '0',
ItemResourceId int(11) NOT NULL default '0',
PrimaryCat tinyint(4) NOT NULL default '0',
ItemPrefix varchar(50) NOT NULL default '',
Filename varchar(255) NOT NULL default '',
UNIQUE KEY CategoryId (CategoryId,ItemResourceId),
KEY PrimaryCat (PrimaryCat),
KEY ItemPrefix (ItemPrefix),
KEY ItemResourceId (ItemResourceId),
KEY Filename (Filename)
);
CREATE TABLE CategoryPermissionsCache (
PermCacheId int(11) NOT NULL auto_increment,
CategoryId int(11) NOT NULL default '0',
PermId int(11) NOT NULL default '0',
ACL varchar(255) NOT NULL default '',
PRIMARY KEY (PermCacheId),
KEY CategoryId (CategoryId),
KEY PermId (PermId),
KEY ACL (ACL)
);
CREATE TABLE PopupSizes (
PopupId int(10) unsigned NOT NULL auto_increment,
TemplateName varchar(255) NOT NULL default '',
PopupWidth int(11) NOT NULL default '0',
PopupHeight int(11) NOT NULL default '0',
PRIMARY KEY (PopupId),
KEY TemplateName (TemplateName)
);
CREATE TABLE Counters (
CounterId int(10) unsigned NOT NULL auto_increment,
Name varchar(100) NOT NULL default '',
CountQuery text,
CountValue text,
LastCounted int(10) unsigned default NULL,
LifeTime int(10) unsigned NOT NULL default '3600',
IsClone tinyint(3) unsigned NOT NULL default '0',
TablesAffected text,
PRIMARY KEY (CounterId),
UNIQUE KEY Name (Name),
KEY IsClone (IsClone),
KEY LifeTime (LifeTime),
KEY LastCounted (LastCounted)
);
CREATE TABLE AdminSkins (
SkinId int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) DEFAULT NULL,
CSS text,
Logo varchar(255) DEFAULT NULL,
LogoBottom varchar(255) NOT NULL DEFAULT '',
LogoLogin varchar(255) NOT NULL DEFAULT '',
`Options` text,
LastCompiled int(11) NOT NULL DEFAULT '0',
IsPrimary int(1) NOT NULL DEFAULT '0',
DisplaySiteNameInHeader tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (SkinId),
KEY IsPrimary (IsPrimary),
KEY LastCompiled (LastCompiled)
);
CREATE TABLE ChangeLogs (
ChangeLogId bigint(20) NOT NULL AUTO_INCREMENT,
PortalUserId int(11) NOT NULL DEFAULT '0',
SessionLogId int(11) NOT NULL DEFAULT '0',
`Action` tinyint(4) NOT NULL DEFAULT '0',
OccuredOn int(11) DEFAULT NULL,
Prefix varchar(255) NOT NULL DEFAULT '',
ItemId bigint(20) NOT NULL DEFAULT '0',
Changes text,
MasterPrefix varchar(255) NOT NULL DEFAULT '',
MasterId bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (ChangeLogId),
KEY PortalUserId (PortalUserId),
KEY SessionLogId (SessionLogId),
KEY `Action` (`Action`),
KEY OccuredOn (OccuredOn),
KEY Prefix (Prefix),
KEY MasterPrefix (MasterPrefix)
);
CREATE TABLE UserSessionLogs (
SessionLogId bigint(20) NOT NULL AUTO_INCREMENT,
PortalUserId int(11) NOT NULL DEFAULT '0',
SessionId int(10) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '1',
SessionStart int(11) DEFAULT NULL,
SessionEnd int(11) DEFAULT NULL,
IP varchar(15) NOT NULL DEFAULT '',
AffectedItems int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (SessionLogId),
KEY SessionId (SessionId),
KEY `Status` (`Status`),
KEY PortalUserId (PortalUserId)
);
CREATE TABLE StatisticsCapture (
StatisticsId int(10) unsigned NOT NULL auto_increment,
TemplateName varchar(255) NOT NULL default '',
Hits int(10) unsigned NOT NULL default '0',
LastHit int(11) NOT NULL default '0',
ScriptTimeMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
ScriptTimeAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
ScriptTimeMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlTimeMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlTimeAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlTimeMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlCountMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlCountAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
SqlCountMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000',
PRIMARY KEY (StatisticsId),
KEY TemplateName (TemplateName),
KEY Hits (Hits),
KEY LastHit (LastHit),
KEY ScriptTimeMin (ScriptTimeMin),
KEY ScriptTimeAvg (ScriptTimeAvg),
KEY ScriptTimeMax (ScriptTimeMax),
KEY SqlTimeMin (SqlTimeMin),
KEY SqlTimeAvg (SqlTimeAvg),
KEY SqlTimeMax (SqlTimeMax),
KEY SqlCountMin (SqlCountMin),
KEY SqlCountAvg (SqlCountAvg),
KEY SqlCountMax (SqlCountMax)
);
CREATE TABLE SlowSqlCapture (
CaptureId int(10) unsigned NOT NULL AUTO_INCREMENT,
TemplateNames text,
Hits int(10) unsigned NOT NULL DEFAULT '0',
LastHit int(11) NOT NULL DEFAULT '0',
SqlQuery text,
TimeMin decimal(40,20) unsigned NOT NULL DEFAULT '0.00000000000000000000',
TimeAvg decimal(40,20) unsigned NOT NULL DEFAULT '0.00000000000000000000',
TimeMax decimal(40,20) unsigned NOT NULL DEFAULT '0.00000000000000000000',
QueryCrc bigint(11) NOT NULL DEFAULT '0',
PRIMARY KEY (CaptureId),
KEY Hits (Hits),
KEY LastHit (LastHit),
KEY TimeMin (TimeMin),
KEY TimeAvg (TimeAvg),
KEY TimeMax (TimeMax),
KEY QueryCrc (QueryCrc)
);
CREATE TABLE ScheduledTasks (
ScheduledTaskId int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL DEFAULT '',
`Type` tinyint(3) unsigned NOT NULL DEFAULT '1',
`Status` tinyint(3) unsigned NOT NULL DEFAULT '1',
`Event` varchar(255) NOT NULL DEFAULT '',
RunInterval int(10) unsigned NOT NULL DEFAULT '0',
LastRunOn int(10) unsigned DEFAULT NULL,
LastRunStatus tinyint(3) unsigned NOT NULL DEFAULT '1',
NextRunOn int(11) DEFAULT NULL,
RunTime int(10) unsigned NOT NULL DEFAULT '0',
Timeout int(10) unsigned DEFAULT NULL,
LastTimeoutOn int(10) unsigned DEFAULT NULL,
SiteDomainLimitation varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (ScheduledTaskId),
KEY `Status` (`Status`),
KEY RunInterval (RunInterval),
KEY LastRunOn (LastRunOn),
KEY LastRunStatus (LastRunStatus),
KEY RunTime (RunTime),
KEY NextRunOn (NextRunOn),
KEY SiteDomainLimitation (SiteDomainLimitation),
KEY Timeout (Timeout),
KEY `Type` (`Type`)
);
CREATE TABLE SpellingDictionary (
SpellingDictionaryId int(11) NOT NULL auto_increment,
MisspelledWord varchar(255) NOT NULL default '',
SuggestedCorrection varchar(255) NOT NULL default '',
PRIMARY KEY (SpellingDictionaryId),
KEY MisspelledWord (MisspelledWord),
KEY SuggestedCorrection (SuggestedCorrection)
);
CREATE TABLE Thesaurus (
ThesaurusId int(11) NOT NULL auto_increment,
SearchTerm varchar(255) NOT NULL default '',
ThesaurusTerm varchar(255) NOT NULL default '',
ThesaurusType tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (ThesaurusId),
KEY ThesaurusType (ThesaurusType),
KEY SearchTerm (SearchTerm)
);
CREATE TABLE LocalesList (
LocaleId int(11) NOT NULL auto_increment,
LocaleIdentifier varchar(6) NOT NULL default '',
LocaleName varchar(255) NOT NULL default '',
Locale varchar(20) NOT NULL default '',
ScriptTag varchar(255) NOT NULL default '',
ANSICodePage varchar(10) NOT NULL default '',
PRIMARY KEY (LocaleId)
);
CREATE TABLE UserBanRules (
RuleId int(11) NOT NULL auto_increment,
RuleType tinyint(4) NOT NULL default '0',
ItemField varchar(255) default NULL,
ItemVerb tinyint(4) NOT NULL default '0',
ItemValue varchar(255) NOT NULL default '',
ItemType int(11) NOT NULL default '0',
Priority int(11) NOT NULL default '0',
Status tinyint(4) NOT NULL default '1',
ErrorTag varchar(255) default NULL,
PRIMARY KEY (RuleId),
KEY Status (Status),
KEY Priority (Priority),
KEY ItemType (ItemType)
);
CREATE TABLE CountCache (
ListType int(11) NOT NULL default '0',
ItemType int(11) NOT NULL default '-1',
Value int(11) NOT NULL default '0',
CountCacheId int(11) NOT NULL auto_increment,
LastUpdate int(11) NOT NULL default '0',
ExtraId varchar(50) default NULL,
TodayOnly tinyint(4) NOT NULL default '0',
PRIMARY KEY (CountCacheId)
);
CREATE TABLE UserFavorites (
FavoriteId int(11) NOT NULL auto_increment,
PortalUserId int(11) NOT NULL default '0',
ResourceId int(11) NOT NULL default '0',
ItemTypeId int(11) NOT NULL default '0',
Modified int(11) NOT NULL default '0',
PRIMARY KEY (FavoriteId),
UNIQUE KEY main (PortalUserId,ResourceId),
KEY Modified (Modified),
KEY ItemTypeId (ItemTypeId)
);
CREATE TABLE CatalogImages (
ImageId int(11) NOT NULL auto_increment,
ResourceId int(11) NOT NULL default '0',
Url varchar(255) NOT NULL default '',
Name varchar(255) NOT NULL default '',
AltName VARCHAR(255) NOT NULL DEFAULT '',
ImageIndex int(11) NOT NULL default '0',
LocalImage tinyint(4) NOT NULL default '1',
LocalPath varchar(240) NOT NULL default '',
Enabled int(11) NOT NULL default '1',
DefaultImg int(11) NOT NULL default '0',
ThumbUrl varchar(255) default NULL,
Priority int(11) NOT NULL default '0',
ThumbPath varchar(255) default NULL,
LocalThumb tinyint(4) NOT NULL default '1',
SameImages tinyint(4) NOT NULL default '1',
PRIMARY KEY (ImageId),
KEY ResourceId (ResourceId),
KEY Enabled (Enabled),
KEY Priority (Priority)
);
CREATE TABLE CatalogRatings (
RatingId int(11) NOT NULL auto_increment,
IPAddress varchar(255) NOT NULL default '',
CreatedOn INT UNSIGNED NULL DEFAULT NULL,
RatingValue int(11) NOT NULL default '0',
ItemId int(11) NOT NULL default '0',
PRIMARY KEY (RatingId),
KEY CreatedOn (CreatedOn),
KEY ItemId (ItemId),
KEY RatingValue (RatingValue)
);
CREATE TABLE CatalogReviews (
ReviewId int(11) NOT NULL AUTO_INCREMENT,
CreatedOn int(10) unsigned DEFAULT NULL,
ReviewText longtext,
Rating tinyint(3) unsigned NOT NULL DEFAULT '0',
IPAddress varchar(255) NOT NULL DEFAULT '',
ItemId int(11) NOT NULL DEFAULT '0',
CreatedById int(11) DEFAULT NULL,
ItemType tinyint(4) NOT NULL DEFAULT '0',
Priority int(11) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '2',
TextFormat int(11) NOT NULL DEFAULT '0',
Module varchar(255) NOT NULL DEFAULT '',
HelpfulCount int(11) NOT NULL,
NotHelpfulCount int(11) NOT NULL,
PRIMARY KEY (ReviewId),
KEY CreatedOn (CreatedOn),
KEY ItemId (ItemId),
KEY ItemType (ItemType),
KEY Priority (Priority),
KEY `Status` (`Status`)
);
CREATE TABLE ItemFilters (
FilterId int(11) NOT NULL AUTO_INCREMENT,
ItemPrefix varchar(255) NOT NULL DEFAULT '',
FilterField varchar(255) NOT NULL DEFAULT '',
FilterType varchar(100) NOT NULL DEFAULT '',
Enabled tinyint(4) NOT NULL DEFAULT '1',
RangeCount int(11) DEFAULT NULL,
PRIMARY KEY (FilterId),
KEY ItemPrefix (ItemPrefix),
KEY Enabled (Enabled)
);
CREATE TABLE SpamReports (
ReportId int(11) NOT NULL AUTO_INCREMENT,
ItemPrefix varchar(255) NOT NULL DEFAULT '',
ItemId int(11) NOT NULL,
MessageText text,
ReportedOn int(11) DEFAULT NULL,
ReportedById int(11) DEFAULT NULL,
PRIMARY KEY (ReportId),
KEY ItemPrefix (ItemPrefix),
KEY ItemId (ItemId),
KEY ReportedById (ReportedById)
);
CREATE TABLE ItemTypes (
ItemType int(11) NOT NULL default '0',
Module varchar(50) NOT NULL default '',
Prefix varchar(20) NOT NULL default '',
SourceTable varchar(100) NOT NULL default '',
TitleField varchar(50) default NULL,
CreatorField varchar(255) NOT NULL default '',
PopField varchar(255) default NULL,
RateField varchar(255) default NULL,
LangVar varchar(255) NOT NULL default '',
PrimaryItem int(11) NOT NULL default '0',
EditUrl varchar(255) NOT NULL default '',
ClassName varchar(40) NOT NULL default '',
ItemName varchar(50) NOT NULL default '',
PRIMARY KEY (ItemType),
KEY Module (Module)
);
CREATE TABLE CatalogFiles (
FileId int(11) NOT NULL AUTO_INCREMENT,
ResourceId int(11) unsigned NOT NULL DEFAULT '0',
FileName varchar(255) NOT NULL DEFAULT '',
FilePath varchar(255) NOT NULL DEFAULT '',
Size int(11) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '1',
CreatedOn int(11) unsigned DEFAULT NULL,
CreatedById int(11) DEFAULT NULL,
MimeType varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (FileId),
KEY ResourceId (ResourceId),
KEY CreatedOn (CreatedOn),
KEY `Status` (`Status`)
);
CREATE TABLE CatalogRelationships (
RelationshipId int(11) NOT NULL auto_increment,
SourceId int(11) default NULL,
TargetId int(11) default NULL,
SourceType tinyint(4) NOT NULL default '0',
TargetType tinyint(4) NOT NULL default '0',
Type int(11) NOT NULL default '0',
Enabled int(11) NOT NULL default '1',
Priority int(11) NOT NULL default '0',
PRIMARY KEY (RelationshipId),
KEY RelSource (SourceId),
KEY RelTarget (TargetId),
KEY `Type` (`Type`),
KEY Enabled (Enabled),
KEY Priority (Priority),
KEY SourceType (SourceType),
KEY TargetType (TargetType)
);
CREATE TABLE SearchConfig (
TableName varchar(40) NOT NULL default '',
FieldName varchar(40) NOT NULL default '',
SimpleSearch tinyint(4) NOT NULL default '1',
AdvancedSearch tinyint(4) NOT NULL default '1',
Description varchar(255) default NULL,
DisplayName varchar(80) default NULL,
ModuleName VARCHAR(20) NOT NULL DEFAULT 'In-Portal',
ConfigHeader varchar(255) default NULL,
DisplayOrder int(11) NOT NULL default '0',
SearchConfigId int(11) NOT NULL auto_increment,
Priority int(11) NOT NULL default '0',
FieldType varchar(20) NOT NULL default 'text',
ForeignField TEXT,
JoinClause TEXT,
IsWhere text,
IsNotWhere text,
ContainsWhere text,
NotContainsWhere text,
CustomFieldId int(11) default NULL,
PRIMARY KEY (SearchConfigId),
KEY SimpleSearch (SimpleSearch),
KEY AdvancedSearch (AdvancedSearch),
KEY DisplayOrder (DisplayOrder),
KEY Priority (Priority),
KEY CustomFieldId (CustomFieldId)
);
CREATE TABLE SearchLogs (
SearchLogId int(11) NOT NULL auto_increment,
Keyword varchar(255) NOT NULL default '',
Indices bigint(20) NOT NULL default '0',
SearchType int(11) NOT NULL default '0',
PRIMARY KEY (SearchLogId),
KEY Keyword (Keyword),
KEY SearchType (SearchType)
);
CREATE TABLE SpamControl (
ItemResourceId int(11) NOT NULL default '0',
IPaddress varchar(20) NOT NULL default '',
Expire INT UNSIGNED NULL DEFAULT NULL,
PortalUserId int(11) NOT NULL default '0',
DataType varchar(20) default NULL,
KEY PortalUserId (PortalUserId),
KEY Expire (Expire),
KEY DataType (DataType),
KEY ItemResourceId (ItemResourceId)
);
CREATE TABLE StatItem (
StatItemId int(11) NOT NULL auto_increment,
Module varchar(20) NOT NULL default '',
ValueSQL varchar(255) default NULL,
ResetSQL varchar(255) default NULL,
ListLabel varchar(255) NOT NULL default '',
Priority int(11) NOT NULL default '0',
AdminSummary int(11) NOT NULL default '0',
PRIMARY KEY (StatItemId),
KEY AdminSummary (AdminSummary),
KEY Priority (Priority)
);
CREATE TABLE ImportScripts (
ImportId int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL DEFAULT '',
Description text,
Prefix varchar(10) NOT NULL DEFAULT '',
Module varchar(50) NOT NULL DEFAULT '',
ExtraFields varchar(255) NOT NULL DEFAULT '',
`Type` varchar(10) NOT NULL DEFAULT '',
`Status` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (ImportId),
KEY Module (Module),
KEY `Status` (`Status`)
);
CREATE TABLE UserVisits (
VisitId int(11) NOT NULL AUTO_INCREMENT,
VisitDate int(10) unsigned DEFAULT NULL,
Referer varchar(255) NOT NULL DEFAULT '',
IPAddress varchar(15) NOT NULL DEFAULT '',
AffiliateId int(10) unsigned NOT NULL DEFAULT '0',
PortalUserId int(11) NOT NULL DEFAULT '-2',
PRIMARY KEY (VisitId),
KEY PortalUserId (PortalUserId),
KEY AffiliateId (AffiliateId),
KEY VisitDate (VisitDate)
);
CREATE TABLE ImportCache (
CacheId int(11) NOT NULL AUTO_INCREMENT,
CacheName varchar(255) NOT NULL DEFAULT '',
VarName bigint(11) NOT NULL DEFAULT '0',
VarValue text,
PRIMARY KEY (CacheId),
KEY CacheName (CacheName),
KEY VarName (VarName)
);
CREATE TABLE CategoryRelatedSearches (
RelatedSearchId int(11) NOT NULL auto_increment,
ResourceId int(11) NOT NULL default '0',
Keyword varchar(255) NOT NULL default '',
ItemType tinyint(4) NOT NULL default '0',
Enabled tinyint(4) NOT NULL default '1',
Priority int(11) NOT NULL default '0',
PRIMARY KEY (RelatedSearchId),
KEY Enabled (Enabled),
KEY ItemType (ItemType),
KEY ResourceId (ResourceId)
);
CREATE TABLE StopWords (
StopWordId int(11) NOT NULL auto_increment,
StopWord varchar(255) NOT NULL default '',
PRIMARY KEY (StopWordId),
KEY StopWord (StopWord)
);
CREATE TABLE MailingLists (
MailingId int(10) unsigned NOT NULL AUTO_INCREMENT,
PortalUserId int(11) NOT NULL DEFAULT '-1',
`To` longtext,
ToParsed longtext,
Attachments text,
`Subject` varchar(255) NOT NULL DEFAULT '',
MessageText longtext,
MessageHtml longtext,
`Status` tinyint(3) unsigned NOT NULL DEFAULT '1',
EmailsQueued int(10) unsigned NOT NULL DEFAULT '0',
EmailsSent int(10) unsigned NOT NULL DEFAULT '0',
EmailsTotal int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (MailingId),
KEY EmailsTotal (EmailsTotal),
KEY EmailsSent (EmailsSent),
KEY EmailsQueued (EmailsQueued),
KEY `Status` (`Status`),
KEY PortalUserId (PortalUserId)
);
CREATE TABLE PageContent (
PageContentId int(11) NOT NULL AUTO_INCREMENT,
ContentNum bigint(11) NOT NULL DEFAULT '0',
PageId int(11) NOT NULL DEFAULT '0',
RevisionId int(11) NOT NULL,
l1_Content text,
l2_Content text,
l3_Content text,
l4_Content text,
l5_Content text,
PRIMARY KEY (PageContentId),
KEY ContentNum (ContentNum,PageId),
KEY RevisionId (RevisionId)
);
CREATE TABLE PageRevisions (
RevisionId int(11) NOT NULL AUTO_INCREMENT,
PageId int(11) NOT NULL,
RevisionNumber int(11) NOT NULL,
IsDraft tinyint(4) NOT NULL,
FromRevisionId int(11) NOT NULL,
CreatedById int(11) DEFAULT NULL,
CreatedOn int(11) DEFAULT NULL,
AutoSavedOn int(11) DEFAULT NULL,
`Status` tinyint(4) NOT NULL DEFAULT '2',
PRIMARY KEY (RevisionId),
KEY PageId (PageId),
KEY RevisionNumber (RevisionNumber),
KEY IsDraft (IsDraft),
KEY `Status` (`Status`)
);
CREATE TABLE FormFields (
FormFieldId int(11) NOT NULL AUTO_INCREMENT,
FormId int(11) NOT NULL DEFAULT '0',
`Type` int(11) NOT NULL DEFAULT '0',
FieldName varchar(255) NOT NULL DEFAULT '',
FieldLabel varchar(255) DEFAULT NULL,
Heading varchar(255) DEFAULT NULL,
Prompt varchar(255) DEFAULT NULL,
ElementType varchar(50) NOT NULL DEFAULT '',
ValueList varchar(255) DEFAULT NULL,
Priority int(11) NOT NULL DEFAULT '0',
IsSystem tinyint(3) unsigned NOT NULL DEFAULT '0',
Required tinyint(1) NOT NULL DEFAULT '0',
DisplayInGrid tinyint(1) NOT NULL DEFAULT '1',
DefaultValue text,
Validation tinyint(4) NOT NULL DEFAULT '0',
UploadExtensions varchar(255) NOT NULL DEFAULT '',
UploadMaxSize int(11) DEFAULT NULL,
Visibility tinyint(4) NOT NULL DEFAULT '1',
EmailCommunicationRole tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (FormFieldId),
KEY `Type` (`Type`),
KEY FormId (FormId),
KEY Priority (Priority),
KEY IsSystem (IsSystem),
KEY DisplayInGrid (DisplayInGrid),
KEY Visibility (Visibility),
KEY EmailCommunicationRole (EmailCommunicationRole)
);
CREATE TABLE FormSubmissions (
FormSubmissionId int(11) NOT NULL AUTO_INCREMENT,
FormId int(11) NOT NULL DEFAULT '0',
SubmissionTime int(11) DEFAULT NULL,
IPAddress varchar(15) NOT NULL DEFAULT '',
ReferrerURL text NULL,
LogStatus tinyint(3) unsigned NOT NULL DEFAULT '2',
LastUpdatedOn int(10) unsigned DEFAULT NULL,
Notes text,
MessageId varchar(255) DEFAULT NULL,
PRIMARY KEY (FormSubmissionId),
KEY FormId (FormId),
KEY SubmissionTime (SubmissionTime),
KEY LogStatus (LogStatus),
KEY LastUpdatedOn (LastUpdatedOn),
KEY MessageId (MessageId)
);
CREATE TABLE FormSubmissionReplies (
SubmissionLogId int(11) NOT NULL AUTO_INCREMENT,
FormSubmissionId int(10) unsigned NOT NULL,
FromEmail varchar(255) NOT NULL DEFAULT '',
ToEmail varchar(255) NOT NULL DEFAULT '',
Cc text,
Bcc text,
`Subject` varchar(255) NOT NULL DEFAULT '',
Message text,
Attachment text,
ReplyStatus tinyint(3) unsigned NOT NULL DEFAULT '0',
SentStatus tinyint(3) unsigned NOT NULL DEFAULT '0',
SentOn int(10) unsigned DEFAULT NULL,
RepliedOn int(10) unsigned DEFAULT NULL,
VerifyCode varchar(32) NOT NULL DEFAULT '',
DraftId int(10) unsigned NOT NULL DEFAULT '0',
MessageId varchar(255) NOT NULL DEFAULT '',
BounceInfo text,
BounceDate int(11) DEFAULT NULL,
PRIMARY KEY (SubmissionLogId),
KEY FormSubmissionId (FormSubmissionId),
KEY ReplyStatus (ReplyStatus),
KEY SentStatus (SentStatus),
KEY SentOn (SentOn),
KEY RepliedOn (RepliedOn),
KEY VerifyCode (VerifyCode),
KEY DraftId (DraftId),
KEY BounceDate (BounceDate),
KEY MessageId (MessageId)
);
CREATE TABLE FormSubmissionReplyDrafts (
DraftId int(11) NOT NULL AUTO_INCREMENT,
FormSubmissionId int(10) unsigned NOT NULL DEFAULT '0',
CreatedOn int(10) unsigned DEFAULT NULL,
CreatedById int(11) DEFAULT NULL,
Message text,
PRIMARY KEY (DraftId),
KEY FormSubmissionId (FormSubmissionId),
KEY CreatedOn (CreatedOn),
KEY CreatedById (CreatedById)
);
CREATE TABLE Forms (
FormId int(11) NOT NULL AUTO_INCREMENT,
Title varchar(255) NOT NULL DEFAULT '',
Description text,
RequireLogin tinyint(4) NOT NULL DEFAULT '0',
UseSecurityImage tinyint(4) NOT NULL DEFAULT '0',
SubmitNotifyEmail varchar(255) NOT NULL DEFAULT '',
EnableEmailCommunication tinyint(4) NOT NULL DEFAULT '0',
ProcessUnmatchedEmails tinyint(4) NOT NULL DEFAULT '0',
ReplyFromName varchar(255) NOT NULL DEFAULT '',
ReplyFromEmail varchar(255) NOT NULL DEFAULT '',
ReplyCc varchar(255) NOT NULL DEFAULT '',
ReplyBcc varchar(255) NOT NULL DEFAULT '',
ReplyMessageSignature text,
ReplyServer varchar(255) NOT NULL DEFAULT '',
ReplyPort int(11) NOT NULL DEFAULT '110',
ReplyUsername varchar(255) NOT NULL DEFAULT '',
ReplyPassword varchar(255) NOT NULL DEFAULT '',
BounceEmail varchar(255) NOT NULL DEFAULT '',
BounceServer varchar(255) NOT NULL DEFAULT '',
BouncePort int(11) NOT NULL DEFAULT '110',
BounceUsername varchar(255) NOT NULL DEFAULT '',
BouncePassword varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (FormId),
KEY UseSecurityImage (UseSecurityImage),
KEY RequireLogin (RequireLogin),
KEY EnableEmailCommunication (EnableEmailCommunication),
KEY ProcessUnmatchedEmails (ProcessUnmatchedEmails)
);
CREATE TABLE Semaphores (
SemaphoreId int(11) NOT NULL AUTO_INCREMENT,
SessionKey int(10) unsigned NOT NULL DEFAULT '0',
`Timestamp` int(10) unsigned NOT NULL DEFAULT '0',
MainPrefix varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (SemaphoreId),
KEY SessionKey (SessionKey),
KEY `Timestamp` (`Timestamp`),
KEY MainPrefix (MainPrefix)
);
CREATE TABLE CachedUrls (
UrlId int(11) NOT NULL AUTO_INCREMENT,
Url varchar(255) NOT NULL DEFAULT '',
DomainId int(11) NOT NULL DEFAULT '0',
`Hash` bigint(11) NOT NULL DEFAULT '0',
Prefixes varchar(255) NOT NULL DEFAULT '',
ParsedVars text,
Cached int(10) unsigned DEFAULT NULL,
LifeTime int(11) NOT NULL DEFAULT '-1',
PRIMARY KEY (UrlId),
KEY Url (Url),
KEY `Hash` (`Hash`),
KEY Prefixes (Prefixes),
KEY Cached (Cached),
KEY LifeTime (LifeTime),
KEY DomainId (DomainId)
);
CREATE TABLE SiteDomains (
DomainId int(11) NOT NULL AUTO_INCREMENT,
DomainName varchar(255) NOT NULL DEFAULT '',
DomainNameUsesRegExp tinyint(4) NOT NULL DEFAULT '0',
SSLUrl varchar(255) NOT NULL DEFAULT '',
SSLUrlUsesRegExp tinyint(4) NOT NULL DEFAULT '0',
AdminEmail varchar(255) NOT NULL DEFAULT '',
DefaultEmailRecipients text,
Country varchar(3) NOT NULL DEFAULT '',
PrimaryLanguageId int(11) NOT NULL DEFAULT '0',
Languages varchar(255) NOT NULL DEFAULT '',
PrimaryThemeId int(11) NOT NULL DEFAULT '0',
Themes varchar(255) NOT NULL DEFAULT '',
DomainIPRange text,
ExternalUrl varchar(255) NOT NULL DEFAULT '',
RedirectOnIPMatch tinyint(4) NOT NULL DEFAULT '0',
Priority int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (DomainId),
KEY DomainName (DomainName),
KEY DomainNameUsesRegExp (DomainNameUsesRegExp),
KEY SSLUrl (SSLUrl),
KEY SSLUrlUsesRegExp (SSLUrlUsesRegExp),
KEY AdminEmail (AdminEmail),
KEY Country (Country),
KEY PrimaryLanguageId (PrimaryLanguageId),
KEY Languages (Languages),
KEY PrimaryThemeId (PrimaryThemeId),
KEY Themes (Themes),
KEY ExternalUrl (ExternalUrl),
KEY RedirectOnIPMatch (RedirectOnIPMatch),
KEY Priority (Priority)
);
CREATE TABLE CurlLog (
LogId int(11) NOT NULL AUTO_INCREMENT,
Message varchar(255) NOT NULL DEFAULT '',
PageUrl varchar(255) NOT NULL DEFAULT '',
RequestUrl varchar(255) NOT NULL DEFAULT '',
PortalUserId int(11) NOT NULL,
SessionKey int(11) NOT NULL,
IsAdmin tinyint(4) NOT NULL,
PageData text,
RequestData text,
ResponseData text,
RequestDate int(11) DEFAULT NULL,
ResponseDate int(11) DEFAULT NULL,
ResponseHttpCode int(11) NOT NULL,
CurlError varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (LogId),
KEY Message (Message),
KEY PageUrl (PageUrl),
KEY RequestUrl (RequestUrl),
KEY PortalUserId (PortalUserId),
KEY SessionKey (SessionKey),
KEY IsAdmin (IsAdmin),
KEY RequestDate (RequestDate),
KEY ResponseDate (ResponseDate),
KEY ResponseHttpCode (ResponseHttpCode),
KEY CurlError (CurlError)
);
CREATE TABLE PromoBlocks (
BlockId int(11) NOT NULL AUTO_INCREMENT,
Title varchar(50) NOT NULL DEFAULT '',
Priority int(11) NOT NULL DEFAULT '0',
Status tinyint(1) NOT NULL DEFAULT '1',
l1_Image varchar(255) NOT NULL DEFAULT '',
l2_Image varchar(255) NOT NULL DEFAULT '',
l3_Image varchar(255) NOT NULL DEFAULT '',
l4_Image varchar(255) NOT NULL DEFAULT '',
l5_Image varchar(255) NOT NULL DEFAULT '',
CSSClassName varchar(255) NOT NULL DEFAULT '',
LinkType tinyint(1) NOT NULL DEFAULT '1',
CategoryId INT(11) DEFAULT NULL,
ExternalLink varchar(255) NOT NULL DEFAULT '',
OpenInNewWindow tinyint(3) unsigned NOT NULL DEFAULT '0',
ScheduleFromDate int(11) DEFAULT NULL,
ScheduleToDate int(11) DEFAULT NULL,
NumberOfClicks int(11) NOT NULL DEFAULT '0',
NumberOfViews int(11) NOT NULL DEFAULT '0',
Sticky tinyint(1) NOT NULL DEFAULT '0',
l1_Html text,
l2_Html text,
l3_Html text,
l4_Html text,
l5_Html text,
PromoBlockGroupId int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (BlockId),
KEY OpenInNewWindow (OpenInNewWindow),
KEY PromoBlockGroupId (PromoBlockGroupId)
);
CREATE TABLE PromoBlockGroups (
PromoBlockGroupId int(11) NOT NULL AUTO_INCREMENT,
Title varchar(255) NOT NULL DEFAULT '',
CreatedOn int(10) unsigned DEFAULT NULL,
`Status` tinyint(1) NOT NULL DEFAULT '1',
RotationDelay decimal(9,2) DEFAULT NULL,
TransitionTime decimal(9,2) DEFAULT NULL,
TransitionControls tinyint(1) NOT NULL DEFAULT '1',
TransitionEffect varchar(255) NOT NULL DEFAULT '',
TransitionEffectCustom varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (PromoBlockGroupId)
);
\ No newline at end of file
Index: branches/5.2.x/core/install/remove_schema.sql
===================================================================
--- branches/5.2.x/core/install/remove_schema.sql (revision 15251)
+++ branches/5.2.x/core/install/remove_schema.sql (revision 15252)
@@ -1,77 +1,78 @@
DROP TABLE CategoryPermissionsConfig;
DROP TABLE Permissions;
DROP TABLE CustomFields;
DROP TABLE SystemSettings;
DROP TABLE EmailQueue;
DROP TABLE EmailEvents;
+DROP TABLE EmailEventSubscribers;
DROP TABLE IdGenerator;
DROP TABLE Languages;
DROP TABLE Modules;
DROP TABLE UserPersistentSessionData;
DROP TABLE LanguageLabels;
DROP TABLE PhraseCache;
DROP TABLE UserGroups;
DROP TABLE Users;
DROP TABLE UserCustomData;
DROP TABLE UserSessionData;
DROP TABLE Themes;
DROP TABLE ThemeFiles;
DROP TABLE UserGroupRelations;
DROP TABLE UserSessions;
DROP TABLE EmailLog;
DROP TABLE SystemCache;
DROP TABLE CountryStates;
DROP TABLE Categories;
DROP TABLE CategoryCustomData;
DROP TABLE CategoryItems;
DROP TABLE CategoryPermissionsCache;
DROP TABLE Stylesheets;
DROP TABLE PopupSizes;
DROP TABLE Counters;
DROP TABLE AdminSkins;
DROP TABLE ChangeLogs;
DROP TABLE UserSessionLogs;
DROP TABLE StatisticsCapture;
DROP TABLE SlowSqlCapture;
DROP TABLE ScheduledTasks;
DROP TABLE SpellingDictionary;
DROP TABLE Thesaurus;
DROP TABLE LocalesList;
DROP TABLE UserBanRules;
DROP TABLE CountCache;
DROP TABLE UserFavorites;
DROP TABLE CatalogImages;
DROP TABLE CatalogRatings;
DROP TABLE CatalogReviews;
DROP TABLE ItemFilters;
DROP TABLE SpamReports;
DROP TABLE ItemTypes;
DROP TABLE CatalogFiles;
DROP TABLE CatalogRelationships;
DROP TABLE SearchConfig;
DROP TABLE SearchLogs;
DROP TABLE SpamControl;
DROP TABLE StatItem;
DROP TABLE SysCache;
DROP TABLE TagLibrary;
DROP TABLE TagAttributes;
DROP TABLE ImportScripts;
DROP TABLE StylesheetSelectors;
DROP TABLE UserVisits;
DROP TABLE ImportCache;
DROP TABLE CategoryRelatedSearches;
DROP TABLE StopWords;
DROP TABLE MailingLists;
DROP TABLE PageContent;
DROP TABLE PageRevisions;
DROP TABLE FormFields;
DROP TABLE FormSubmissions;
DROP TABLE FormSubmissionReplies;
DROP TABLE FormSubmissionReplyDrafts;
DROP TABLE Forms;
DROP TABLE Semaphores;
DROP TABLE CachedUrls;
DROP TABLE SiteDomains;
DROP TABLE CurlLog;
DROP TABLE PromoBlocks;
DROP TABLE PromoBlockGroups;

Event Timeline