Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:03 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_event_handler.php (revision 16246)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php (revision 16247)
@@ -1,3565 +1,3568 @@
<?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;
}
/**
* 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);
$this->Application->UrlManager->show404();
}
/**
* 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
$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' => kUtil::escape('external:' . $_SERVER['REQUEST_URI'], kUtil::ESCAPE_URL),
);
}
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');
/* @var $parent_event kEvent */
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 ) {
$sortings = explode('|', $sort_by);
list ($cur_sort1, $cur_sort1_dir) = explode(',', $sortings[0]);
if ( isset($sortings[1]) ) {
list ($cur_sort2, $cur_sort2_dir) = explode(',', $sortings[1]);
}
}
}
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->_getDefaultSorting($event);
// use default if not specified in session
if ( !$cur_sort1 || !$cur_sort1_dir ) {
$sorting = getArrayValue($list_sortings, '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, '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);
}
}
/**
* Returns default list sortings
*
* @param kEvent $event
* @return Array
* @access protected
*/
protected function _getDefaultSorting(kEvent $event)
{
$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']);
}
return isset($list_sortings[$sorting_prefix]) ? $list_sortings[$sorting_prefix] : Array ();
}
/**
* 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
$default_value = $this->Application->isAdmin ? ALLOW_DEFAULT_SETTINGS : false;
$variable_value = $this->Application->RecallPersistentVar($storage_prefix . '_' . $variable_name . '.' . $view_name, $default_value);
/*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->setID($id);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $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');
}
/**
* 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);
$event->setEventParam('form_data', $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);
$object = $event->getObject(Array('skip_autoload' => true));
/* @var $object kDBItem */
$object->setPendingActions(null, true);
$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());
$simultaneous_edit_message = $this->Application->GetVar('_simultaneous_edit_message');
if ( $simultaneous_edit_message ) {
$event->SetRedirectParam('_simultaneous_edit_message', kUtil::escape($simultaneous_edit_message, kUtil::ESCAPE_URL));
}
}
/**
* 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);
}
/**
* Analog of OnPreSave event for usage in AJAX request
*
* @param kEvent $event
*
* @return void
*/
protected function OnPreSaveAjax(kEvent $event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnPreSave');
}
/**
* [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 */
$object->setID(0);
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $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;
}
}
/**
* 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 = $object->getStatusField();
$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->_processPendingActions($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->_processPendingActions($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 */
// 1. delete direct subscriptions to item, that was deleted
$this->_deleteSubscriptions($event->Prefix, 'ItemId', $object->GetID());
$sub_items = $this->Application->getUnitOption($event->Prefix, 'SubItems', Array ());
/* @var $sub_items Array */
// 2. delete this item sub-items subscriptions, that reference item, that was deleted
foreach ($sub_items as $sub_prefix) {
$this->_deleteSubscriptions($sub_prefix, 'ParentItemId', $object->GetID());
}
}
/**
* Deletes all subscriptions, associated with given item
*
* @param string $prefix
* @param string $field
* @param int $value
* @return void
* @access protected
*/
protected function _deleteSubscriptions($prefix, $field, $value)
{
$sql = 'SELECT TemplateId
FROM ' . $this->Application->getUnitOption('email-template', 'TableName') . '
WHERE BindToSystemEvent REGEXP "' . $this->Conn->escape($prefix) . '(\\\\.[^:]*:.*|:.*)"';
$email_template_ids = $this->Conn->GetCol($sql);
if ( !$email_template_ids ) {
return;
}
// e-mail events, connected to that unit prefix are found
$sql = 'SELECT SubscriptionId
FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions
WHERE ' . $field . ' = ' . $value . ' AND EmailTemplateId IN (' . implode(',', $email_template_ids) . ')';
$ids = $this->Conn->GetCol($sql);
if ( !$ids ) {
return;
}
$temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems('system-event-subscription', '', $ids);
}
/**
* 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)
{
$object = $event->getObject(array('skip_autoload' => true));
/* @var $object kDBItem */
$object->SwitchToLive();
$object->Load($event->getEventParam('id'));
$this->_processPendingActions($event);
}
/**
* Processing file pending actions (e.g. delete scheduled files)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function _processPendingActions(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$update_required = false;
$temp_id = $event->getEventParam('temp_id');
$id = $temp_id !== false ? $temp_id : $object->GetID();
foreach ($object->getPendingActions($id) as $data) {
switch ( $data['action'] ) {
case 'delete':
unlink($data['file']);
break;
case 'make_live':
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
if ( !file_exists($data['file']) ) {
// file removal was requested too
continue;
}
$old_name = basename($data['file']);
$new_name = $file_helper->ensureUniqueFilename(dirname($data['file']), kUtil::removeTempExtension($old_name));
rename($data['file'], dirname($data['file']) . '/' . $new_name);
$db_value = $object->GetDBField($data['field']);
$object->SetDBField($data['field'], str_replace($old_name, $new_name, $db_value));
$update_required = true;
break;
default:
trigger_error('Unsupported pending action "' . $data['action'] . '" for "' . $event->getPrefixSpecial() . '" unit', E_USER_WARNING);
break;
}
}
// remove pending actions before updating to prevent recursion
$object->setPendingActions();
if ( $update_required ) {
$object->Update();
}
}
/**
* 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 ) {
list ($user_id, ) = each($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_special', $event->Special);
$this->Application->StoreVar('export_grid', $this->Application->GetVar('grid', 'Default'));
$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;
}
$value = $this->Application->HttpQuery->unescapeRequestVariable($value);
$tmp_path = WRITEABLE . '/tmp/';
$filename = $value['name'] . '.tmp';
$id = $this->Application->GetVar('id');
if ( $id ) {
$filename = $id . '_' . $filename;
}
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 */
$filename = $file_helper->ensureUniqueFilename($tmp_path, $filename);
$storage_format = $this->_getStorageFormat($this->Application->GetVar('field'), $event);
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 . $filename);
}
else {
move_uploaded_file($value['tmp_name'], $tmp_path . $filename);
}
echo preg_replace('/^' . preg_quote($id, '/') . '_/', '', $filename);
$this->deleteTempFiles($tmp_path);
- if ( file_exists($tmp_path . 'resized/') ) {
- $this->deleteTempFiles($tmp_path . 'resized/');
+ $thumbs_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $tmp_path, 1);
+ $thumbs_path = FULL_PATH . THUMBS_PATH . $thumbs_path;
+
+ if ( file_exists($thumbs_path) ) {
+ $this->deleteTempFiles($thumbs_path);
}
}
/**
* Gets storage format for a given field
*
* @param string $field_name
* @param kEvent $event
* @return bool
* @access protected
*/
protected function _getStorageFormat($field_name, kEvent $event)
{
$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];
return isset($field_options['storage_format']) ? $field_options['storage_format'] : false;
}
/**
* 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) {
+ 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;
$filename = $this->_getSafeFilename();
if ( !$filename ) {
return;
}
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$field_id = $this->Application->GetVar('field_id');
if ( !preg_match_all('/\[([^\[\]]*)\]/', $field_id, $regs) ) {
return;
}
$field = $regs[1][1];
$record_id = $regs[1][0];
$pending_actions = $object->getPendingActions($record_id);
$upload_dir = $object->GetFieldOption($field, 'upload_dir');
$pending_actions[] = Array (
'action' => 'delete', 'id' => $record_id, 'field' => $field, 'file' => FULL_PATH . $upload_dir . $filename
);
$object->setPendingActions($pending_actions, $record_id);
}
/**
* Returns url for viewing uploaded file
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnViewFile(kEvent $event)
{
$event->status = kEvent::erSTOP;
$filename = $this->_getSafeFilename();
if ( !$filename ) {
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') . '_' . $filename);
}
else {
$object->SetDBField($field, $filename);
}
// get url to uploaded file
if ( $this->Application->GetVar('thumb') ) {
$url = $object->GetField($field, $options['thumb_format']);
}
else {
$url = $object->GetField($field, 'raw_url');
}
$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="' . kUtil::removeTempExtension($filename) . '"');
readfile($path);
}
/**
* Returns safe version of filename specified in url
*
* @return bool|string
* @access protected
*/
protected function _getSafeFilename()
{
$filename = $this->Application->GetVar('file');
$filename = $this->Application->unescapeRequestVariable($filename);
if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
return false;
}
return $filename;
}
/**
* 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);
$event->setEventParam('form_data', $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, false);
}
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$response['other_errors'] = $ajax_form_helper->getErrorMessages($object);
$response['uploader_info'] = $ajax_form_helper->getUploaderInfo($object, array_keys($field_values));
$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>' . kUtil::escape($item, kUtil::ESCAPE_HTML) . '</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 ('', ''));
}
}
Index: branches/5.2.x/core/kernel/constants.php
===================================================================
--- branches/5.2.x/core/kernel/constants.php (revision 16246)
+++ branches/5.2.x/core/kernel/constants.php (revision 16247)
@@ -1,206 +1,208 @@
<?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!');
// item statuses
define('STATUS_DISABLED', 0);
define('STATUS_ACTIVE', 1);
define('STATUS_PENDING', 2);
define('STATUS_PENDING_EDITING', -2);
// sections
define('stTREE', 1);
define('stTAB', 2);
define('smHIDE', 0); // always hide section from tree
define('smNORMAL', 1); // show section even, if they were marked as smDEBUG or smSUPER_ADMIN before
define('smDEBUG', 2); // show section in debug mode only
define('smSUPER_ADMIN', 4); // show section in super admin & debug mode
// permission types
define('ptCATEGORY', 0);
define('ptSYSTEM', 1);
define('EDIT_MARK', '&|edit|&'); // replace this sequence inside filters to SID[_main_wid]
$application =& kApplication::Instance();
$spacer_url = $application->BaseURL().'core/admin_templates/img/spacer.gif';
define('SPACER_URL', $spacer_url);
if (!$application->isAdmin) {
// don't show debugger buttons on front (if not overridden in "debug.php")
kUtil::safeDefine('DBG_TOOLBAR_BUTTONS', 0);
}
// common usage regular expressions
define('REGEX_EMAIL_USER', '[-a-zA-Z0-9!\#$%&*+\/=?^_`{|}~.]+');
define('REGEX_EMAIL_DOMAIN', '[a-zA-Z0-9]{1}[-.a-zA-Z0-9_]*\.[a-zA-Z]{2,6}');
define('ALLOW_DEFAULT_SETTINGS', '_USE_DEFAULT_USER_DATA_'); //Allow persistent vars to take data from default user's persistent data
class ChangeLog {
const CREATE = 1;
const UPDATE = 2;
const DELETE = 3;
}
// Separator for ValueList fields
define('VALUE_LIST_SEPARATOR', '||');
// template editing modes
define('EDITING_MODE_BROWSE', 1); // no changes, front-end as users see it
define('EDITING_MODE_CONTENT', 2); // content blocks + phrase editing
define('EDITING_MODE_DESIGN', 3); // all other blocks
class ScheduledTask {
const TYPE_USER = 1;
const TYPE_SYSTEM = 2;
const LAST_RUN_SUCCEEDED = 1;
const LAST_RUN_FAILED = 0;
const LAST_RUN_RUNNING = 2;
}
// place for product file uploads (sort of "/system/images" but for all other files)
define('ITEM_FILES_PATH', WRITEBALE_BASE . '/downloads/');
+ define('THUMBS_PATH', WRITEBALE_BASE . '/thumbs');
+
class MailingList {
const NOT_PROCESSED = 1;
const PARTIALLY_PROCESSED = 2;
const PROCESSED = 3;
const CANCELLED = 4;
}
// theme file statuses (related to structure creation process)
define('SMS_MODE_AUTO', 1);
define('SMS_MODE_FORCE', 2);
// Means, that actual category Template field value should inherited from parent category
define('CATEGORY_TEMPLATE_INHERIT', '#inherit#');
define('REWRITE_MODE_BUILD', 1);
define('REWRITE_MODE_PARSE', 2);
define('SESSION_LOG_ACTIVE', 0);
define('SESSION_LOG_LOGGED_OUT', 1);
define('SESSION_LOG_EXPIRED', 2);
class LoginResult {
const OK = 0;
const INVALID_LOGIN = 1;
const INVALID_PASSWORD = 2;
const BANNED = 3;
const NO_PERMISSION = 4;
}
define('DESTINATION_TYPE_COUNTRY', 1);
define('DESTINATION_TYPE_STATE', 2);
class SubmissionFormField {
const VISIBILITY_EVERYONE = 1;
const VISIBILITY_UNREGISTERED = 2;
const COMMUNICATION_ROLE_NAME = 1;
const COMMUNICATION_ROLE_EMAIL = 2;
const COMMUNICATION_ROLE_SUBJECT = 3;
const COMMUNICATION_ROLE_BODY = 4;
}
// form submission statuses
define('SUBMISSION_REPLIED', 1); // submission was replied by admin
define('SUBMISSION_NOT_REPLIED', 2); // submission has no client replies (no messages at all)
define('SUBMISSION_NEW_EMAIL', 3); // submission have new reply/email from client
define('SUBMISSION_BOUNCE', 4); // submission have bounce from client
// submission log statuses
define('SUBMISSION_LOG_SENT', 1);
define('SUBMISSION_LOG_BOUNCE', 2);
define('SUBMISSION_LOG_REPLIED', 1);
define('SUBMISSION_LOG_ATTACHMENT_PATH', WRITEBALE_BASE . '/user_files/submission_log/');
define('TIMENOW', adodb_mktime()); // for faster message processing
define('USER_TITLE_FIELD', 'IF(Email = "", Username, Email)');
// site domains
define('SITE_DOMAIN_REDIRECT_CURRENT', 1);
define('SITE_DOMAIN_REDIRECT_EXTERNAL', 2);
class EmailTemplate {
const TEMPLATE_TYPE_FRONTEND = 0;
const TEMPLATE_TYPE_ADMIN = 1;
const ADDRESS_TYPE_EMAIL = 1;
const ADDRESS_TYPE_USER = 2;
const ADDRESS_TYPE_GROUP = 3;
const RECIPIENT_TYPE_TO = 1;
const RECIPIENT_TYPE_CC = 2;
const RECIPIENT_TYPE_BCC = 3;
}
define('PAGE_TYPE_VIRTUAL', 1);
define('PAGE_TYPE_TEMPLATE', 2);
define('CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL', 5 * 60); // 5 minutes
define('CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL', 20); // 20 seconds
define('hBEFORE', 1);
define('hAFTER', 2);
class UserType {
const USER = 0;
const ADMIN = 1;
}
class PasswordHashingMethod {
const NONE = 0;
const MD5 = 1;
const MD5_AND_PHPPASS = 2;
const PHPPASS = 3;
}
// selectors
define('STYLE_BASE', 1);
define('STYLE_BLOCK', 2);
class Language {
const SYNCHRONIZE_TO_OTHERS = 1;
const SYNCHRONIZE_FROM_OTHERS = 2;
const SYNCHRONIZE_DEFAULT = '|1|2|';
const PHRASE_TYPE_FRONT = 0;
const PHRASE_TYPE_ADMIN = 1;
const PHRASE_TYPE_COMMON = 2;
}
class PromoBlockType {
const INTERNAL = 1;
const EXTERNAL = 2;
}
class StorageEngine {
const HASH = 1;
const TIMESTAMP = 2;
const PS_DATE_TIME = 'DATE-TIME';
const PS_PREFIX = 'PREFIX';
const PS_USER = 'USER';
}
class CategoryPermissionRebuild {
const MANUAL = 1;
const SILENT = 2;
const AUTOMATIC = 3;
}
Index: branches/5.2.x/core/units/helpers/image_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/image_helper.php (revision 16246)
+++ branches/5.2.x/core/units/helpers/image_helper.php (revision 16247)
@@ -1,720 +1,731 @@
<?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 ImageHelper extends kHelper {
/**
* File helper reference
*
* @var FileHelper
*/
var $fileHelper = null;
public function __construct()
{
parent::__construct();
ini_set('gd.jpeg_ignore_warning', 1);
$this->fileHelper = $this->Application->recallObject('FileHelper');
}
/**
* Parses format string into array
*
* @param string $format sample format: "resize:300x500;wm:inc/wm.png|c|-20"
* @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20)
*/
function parseFormat($format)
{
$res = Array ();
$format_parts = explode(';', $format);
foreach ($format_parts as $format_part) {
if (preg_match('/^resize:(\d*)x(\d*)$/', $format_part, $regs)) {
$res['max_width'] = $regs[1];
$res['max_height'] = $regs[2];
}
elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) {
$res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1];
$res['h_margin'] = strtolower($regs[2]);
$res['v_margin'] = strtolower($regs[3]);
}
elseif (preg_match('/^crop:([^\|]*)\|([^\|]*)$/', $format_part, $regs)) {
$res['crop_x'] = strtolower($regs[1]);
$res['crop_y'] = strtolower($regs[2]);
}
elseif ($format_part == 'img_size' || $format_part == 'img_sizes') {
$res['image_size'] = true;
}
elseif (preg_match('/^fill:(.*)$/', $format_part, $regs)) {
$res['fill'] = $regs[1];
- } elseif (preg_match('/^default:(.*)$/', $format_part, $regs)) {
- $res['default'] = FULL_PATH.THEMES_PATH.'/'.$regs[1];
+ }
+ elseif ( preg_match('/^default:(.*)$/', $format_part, $regs) ) {
+ $default_image = FULL_PATH . THEMES_PATH . '/' . $regs[1];
+
+ if ( strpos($default_image, '../') !== false ) {
+ $default_image = realpath($default_image);
+ }
+
+ $res['default'] = $default_image;
}
}
return $res;
}
/**
* Resized given image to required dimensions & saves resized image to "resized" subfolder in source image folder
*
* @param string $src_image full path to image (on server)
* @param mixed $max_width maximal allowed resized image width or false if no limit
* @param mixed $max_height maximal allowed resized image height or false if no limit
*
* @return string direct url to resized image
* @throws RuntimeException When image doesn't exist.
*/
function ResizeImage($src_image, $max_width, $max_height = false)
{
$image_size = false;
if (is_numeric($max_width)) {
$params['max_width'] = $max_width;
$params['max_height'] = $max_height;
}
else {
$params = $this->parseFormat($max_width);
if (array_key_exists('image_size', $params)) {
// image_size param shouldn't affect resized file name (crc part)
$image_size = $params['image_size'];
unset($params['image_size']);
}
}
if ((!$src_image || !file_exists($src_image)) && array_key_exists('default', $params) && !(defined('DBG_IMAGE_RECOVERY') && DBG_IMAGE_RECOVERY)) {
$src_image = $params['default'];
}
if ( !strlen($src_image) || !file_exists($src_image) ) {
throw new RuntimeException(sprintf('Image "%s" doesn\'t exist', $src_image));
}
if ($params['max_width'] > 0 || $params['max_height'] > 0) {
list ($params['target_width'], $params['target_height'], $needs_resize) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params);
if (!is_numeric($params['max_width'])) {
$params['max_width'] = $params['target_width'];
}
if (!is_numeric($params['max_height'])) {
$params['max_height'] = $params['target_height'];
}
$src_path = dirname($src_image);
$transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename');
// Resize required OR watermarking required -> change resulting image name !
if ( $needs_resize || array_intersect(array_keys($params), $transform_keys) ) {
// Escape replacement patterns, like "\<number>".
$src_path_escaped = preg_replace('/(\\\[\d]+)/', '\\\\\1', $src_path);
$params_hash = kUtil::crc32(serialize($this->fileHelper->makeRelative($params)));
$dst_image = preg_replace(
'/^' . preg_quote($src_path, '/') . '(.*)\.(.*)$/',
- $src_path_escaped . DIRECTORY_SEPARATOR . 'resized\\1_' . $params_hash . '.\\2',
+ $src_path_escaped . '\\1_' . $params_hash . '.\\2',
$src_image
);
+ // Keep resized version of all images under "/system/thumbs/" folder.
+ $dst_image = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $dst_image, 1);
+ $dst_image = FULL_PATH . THUMBS_PATH . $dst_image;
+
$this->fileHelper->CheckFolder( dirname($dst_image) );
if (!file_exists($dst_image) || filemtime($src_image) > filemtime($dst_image)) {
// resized image not available OR should be recreated due source image change
$params['dst_image'] = $dst_image;
$image_resized = $this->ScaleImage($src_image, $params);
if (!$image_resized) {
// resize failed, because of server error
$dst_image = $src_image;
}
}
// resize/watermarking ok
$src_image = $dst_image;
}
}
if ($image_size) {
// return only image size (resized or not)
$image_info = $this->getImageInfo($src_image);
return $image_info ? $image_info[3] : '';
}
return $this->fileHelper->pathToUrl($src_image);
}
/**
* Proportionally resizes given image to destination dimensions
*
* @param string $src_image full path to source image (already existing)
* @param Array $params
* @return bool
*/
function ScaleImage($src_image, $params)
{
$image_info = $this->getImageInfo($src_image);
if (!$image_info) {
return false;
}
/*list ($params['max_width'], $params['max_height'], $resized) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params);
if (!$resized) {
// image dimensions are smaller or equals to required dimensions
return false;
}*/
if (!$this->Application->ConfigValue('ForceImageMagickResize') && function_exists('imagecreatefromjpeg')) {
// try to resize using GD
$resize_map = Array (
'image/jpeg' => 'imagecreatefromjpeg:imagejpeg:jpg',
'image/gif' => 'imagecreatefromgif:imagegif:gif',
'image/png' => 'imagecreatefrompng:imagepng:png',
'image/bmp' => 'imagecreatefrombmp:imagejpeg:bmp',
'image/x-ms-bmp' => 'imagecreatefrombmp:imagejpeg:bmp',
);
$mime_type = $image_info['mime'];
if (!isset($resize_map[$mime_type])) {
return false;
}
list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]);
// when source image has large dimensions (over 1MB filesize), then 16M is not enough
kUtil::setResourceLimit();
$src_image_rs = @$read_function($src_image);
if ($src_image_rs) {
$dst_image_rs = imagecreatetruecolor($params['target_width'], $params['target_height']); // resize target size
$preserve_transparency = ($file_extension == 'gif') || ($file_extension == 'png');
if ($preserve_transparency) {
// preserve transparency of PNG and GIF images
$dst_image_rs = $this->_preserveTransparency($src_image_rs, $dst_image_rs, $image_info[2]);
}
// 1. resize
imagecopyresampled($dst_image_rs, $src_image_rs, 0, 0, 0, 0, $params['target_width'], $params['target_height'], $image_info[0], $image_info[1]);
$watermark_size = 'target';
if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) {
// 2.1. crop image to given size
$dst_image_rs =& $this->_cropImage($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false);
$watermark_size = 'max';
} elseif (array_key_exists('fill', $params)) {
// 2.2. fill image margins from resize with given color
$dst_image_rs =& $this->_applyFill($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false);
$watermark_size = 'max';
}
// 3. apply watermark
$dst_image_rs =& $this->_applyWatermark($dst_image_rs, $params[$watermark_size . '_width'], $params[$watermark_size . '_height'], $params);
if ($write_function == 'imagegif') {
return @$write_function($dst_image_rs, $params['dst_image']);
}
return @$write_function($dst_image_rs, $params['dst_image'], $write_function == 'imagepng' ? 0 : 100);
}
}
else {
// try to resize using ImageMagick
// TODO: implement crop and watermarking using imagemagick
exec('/usr/bin/convert '.$src_image.' -resize '.$params['target_width'].'x'.$params['target_height'].' '.$params['dst_image'], $shell_output, $exec_status);
return $exec_status == 0;
}
return false;
}
/**
* Preserve transparency for GIF and PNG images
*
* @param resource $src_image_rs
* @param resource $dst_image_rs
* @param int $image_type
* @return resource
*/
function _preserveTransparency($src_image_rs, $dst_image_rs, $image_type)
{
$transparent_index = imagecolortransparent($src_image_rs);
// if we have a specific transparent color
if ( $transparent_index >= 0 && $transparent_index < imagecolorstotal($src_image_rs) ) {
// get the original image's transparent color's RGB values
$transparent_color = imagecolorsforindex($src_image_rs, $transparent_index);
// allocate the same color in the new image resource
$transparent_index = imagecolorallocate($dst_image_rs, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
// completely fill the background of the new image with allocated color
imagefill($dst_image_rs, 0, 0, $transparent_index);
// set the background color for new image to transparent
imagecolortransparent($dst_image_rs, $transparent_index);
return $dst_image_rs;
}
// always make a transparent background color for PNGs that don't have one allocated already
if ( $image_type == IMAGETYPE_PNG ) {
// turn off transparency blending (temporarily)
imagealphablending($dst_image_rs, false);
// create a new transparent color for image
$transparent_color = imagecolorallocatealpha($dst_image_rs, 0, 0, 0, 127);
// completely fill the background of the new image with allocated color
imagefill($dst_image_rs, 0, 0, $transparent_color);
// restore transparency blending
imagesavealpha($dst_image_rs, true);
}
return $dst_image_rs;
}
/**
* Fills margins (if any) of resized are with given color
*
* @param resource $src_image_rs resized image resource
* @param Array $params crop parameters
* @param int|bool $image_type
* @return resource
*/
function &_applyFill(&$src_image_rs, $params, $image_type = false)
{
$x_position = round(($params['max_width'] - $params['target_width']) / 2); // center
$y_position = round(($params['max_height'] - $params['target_height']) / 2); // center
// crop resized image
$fill_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']);
if ($image_type !== false) {
$fill_image_rs = $this->_preserveTransparency($src_image_rs, $fill_image_rs, $image_type);
}
$fill = $params['fill'];
if (substr($fill, 0, 1) == '#') {
// hexdecimal color
$color = imagecolorallocate($fill_image_rs, hexdec( substr($fill, 1, 2) ), hexdec( substr($fill, 3, 2) ), hexdec( substr($fill, 5, 2) ));
}
else {
// for now we don't support color names, but we will in future
return $src_image_rs;
}
imagefill($fill_image_rs, 0, 0, $color);
imagecopy($fill_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']);
return $fill_image_rs;
}
/**
* Crop given image resource using given params and return resulting image resource
*
* @param resource $src_image_rs resized image resource
* @param Array $params crop parameters
* @param int|bool $image_type
* @return resource
*/
function &_cropImage(&$src_image_rs, $params, $image_type = false)
{
if ($params['crop_x'] == 'c') {
$x_position = round(($params['max_width'] - $params['target_width']) / 2); // center
}
elseif ($params['crop_x'] >= 0) {
$x_position = $params['crop_x']; // margin from left
}
else {
$x_position = $params['target_width'] - ($params['max_width'] - $params['crop_x']); // margin from right
}
if ($params['crop_y'] == 'c') {
$y_position = round(($params['max_height'] - $params['target_height']) / 2); // center
}
elseif ($params['crop_y'] >= 0) {
$y_position = $params['crop_y']; // margin from top
}
else {
$y_position = $params['target_height'] - ($params['max_height'] - $params['crop_y']); // margin from bottom
}
// crop resized image
$crop_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']);
if ($image_type !== false) {
$crop_image_rs = $this->_preserveTransparency($src_image_rs, $crop_image_rs, $image_type);
}
if (array_key_exists('fill', $params)) {
// fill image margins from resize with given color
$crop_image_rs =& $this->_applyFill($crop_image_rs, $params, $image_type);
}
imagecopy($crop_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']);
return $crop_image_rs;
}
/**
* Apply watermark (transparent PNG image) to given resized image resource
*
* @param resource $src_image_rs
* @param int $max_width
* @param int $max_height
* @param Array $params
* @return resource
*/
function &_applyWatermark(&$src_image_rs, $max_width, $max_height, $params)
{
$watermark_file = array_key_exists('wm_filename', $params) ? $params['wm_filename'] : false;
if (!$watermark_file || !file_exists($watermark_file)) {
// no watermark required, or provided watermark image is missing
return $src_image_rs;
}
$watermark_img_rs = imagecreatefrompng($watermark_file);
list ($watermark_width, $watermark_height) = $this->getImageInfo($watermark_file);
imagealphablending($src_image_rs, true);
if ($params['h_margin'] == 'c') {
$x_position = round($max_width / 2 - $watermark_width / 2); // center
}
elseif ($params['h_margin'] >= 0) {
$x_position = $params['h_margin']; // margin from left
}
else {
$x_position = $max_width - ($watermark_width - $params['h_margin']); // margin from right
}
if ($params['v_margin'] == 'c') {
$y_position = round($max_height / 2 - $watermark_height / 2); // center
}
elseif ($params['v_margin'] >= 0) {
$y_position = $params['v_margin']; // margin from top
}
else {
$y_position = $max_height - ($watermark_height - $params['v_margin']); // margin from bottom
}
imagecopy($src_image_rs, $watermark_img_rs, $x_position, $y_position, 0, 0, $watermark_width, $watermark_height);
return $src_image_rs;
}
/**
* Returns destination image size without actual resizing (useful for <img .../> HTML tag)
*
* @param string $src_image full path to source image (already existing)
* @param int $dst_width destination image width (in pixels)
* @param int $dst_height destination image height (in pixels)
* @param Array $params
* @return Array resized image dimensions (0 - width, 1 - height)
*/
function GetImageDimensions($src_image, $dst_width, $dst_height, $params)
{
$image_info = $this->getImageInfo($src_image);
if (!$image_info) {
return false;
}
$orig_width = $image_info[0];
$orig_height = $image_info[1];
$too_large = is_numeric($dst_width) ? ($orig_width > $dst_width) : false;
$too_large = $too_large || (is_numeric($dst_height) ? ($orig_height > $dst_height) : false);
if ($too_large) {
$width_ratio = $dst_width ? $dst_width / $orig_width : 1;
$height_ratio = $dst_height ? $dst_height / $orig_height : 1;
if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) {
// resize by smallest inverted radio
$resize_by = $this->_getCropImageMinRatio($image_info, $dst_width, $dst_height);
if ($resize_by === false) {
return Array ($orig_width, $orig_height, false);
}
$ratio = $resize_by == 'width' ? $width_ratio : $height_ratio;
}
else {
$ratio = min($width_ratio, $height_ratio);
}
$width = ceil($orig_width * $ratio);
$height = ceil($orig_height * $ratio);
}
else {
$width = $orig_width;
$height = $orig_height;
}
return Array ($width, $height, $too_large);
}
/**
* Returns ratio type with smaller relation of original size to target size
*
* @param Array $image_info image information from "ImageHelper::getImageInfo"
* @param int $dst_width destination image width (in pixels)
* @param int $dst_height destination image height (in pixels)
* @return Array
*/
function _getCropImageMinRatio($image_info, $dst_width, $dst_height)
{
$width_ratio = $dst_width ? $image_info[0] / $dst_width : 1;
$height_ratio = $dst_height ? $image_info[1] / $dst_height : 1;
$minimal_ratio = min($width_ratio, $height_ratio);
if ($minimal_ratio < 1) {
// ratio is less then 1, image will be enlarged -> don't allow that
return false;
}
return $width_ratio < $height_ratio ? 'width' : 'height';
}
/**
* Returns image dimensions + checks if given file is existing image
*
* @param string $src_image full path to source image (already existing)
* @return mixed
*/
function getImageInfo($src_image)
{
if (!file_exists($src_image)) {
return false;
}
$image_info = @getimagesize($src_image);
if (!$image_info) {
trigger_error('Image <b>'.$src_image.'</b> <span class="debug_error">missing or invalid</span>', E_USER_WARNING);
return false;
}
return $image_info;
}
/**
* Returns maximal image size (width & height) among fields specified
*
* @param kDBItem $object
* @param string $fields
* @param string $format any format, that returns full url (e.g. files_resized:WxH, resize:WxH, full_url, full_urls)
* @return string
*/
function MaxImageSize(&$object, $fields, $format = null)
{
static $cached_sizes = Array ();
$cache_key = $object->getPrefixSpecial().'_'.$object->GetID();
if (!isset($cached_sizes[$cache_key])) {
$images = Array ();
$fields = explode(',', $fields);
foreach ($fields as $field) {
$image_data = $object->GetField($field, $format);
if (!$image_data) {
continue;
}
$images = array_merge($images, explode('|', $image_data));
}
$max_width = 0;
$max_height = 0;
$base_url = rtrim($this->Application->BaseURL(), '/');
foreach ($images as $image_url) {
$image_path = preg_replace('/^'.preg_quote($base_url, '/').'(.*)/', FULL_PATH.'\\1', $image_url);
$image_info = $this->getImageInfo($image_path);
$max_width = max($max_width, $image_info[0]);
$max_height = max($max_height, $image_info[1]);
}
$cached_sizes[$cache_key] = Array ($max_width, $max_height);
}
return $cached_sizes[$cache_key];
}
/**
* Puts existing item images (from sub-item) to virtual fields (in main item)
*
* @param kCatDBItem|kDBItem $object
*/
function LoadItemImages(&$object)
{
if (!$this->_canUseImages($object)) {
return ;
}
$max_image_count = $this->Application->ConfigValue($object->Prefix.'_MaxImageCount');
$sql = 'SELECT *
FROM '.TABLE_PREFIX.'CatalogImages
WHERE ResourceId = '.$object->GetDBField('ResourceId').'
ORDER BY Priority DESC
LIMIT 0, ' . (int)$max_image_count;
$item_images = $this->Conn->Query($sql);
$image_counter = 1;
foreach ($item_images as $item_image) {
$image_path = $item_image['ThumbPath'];
if ($item_image['DefaultImg'] == 1 || $item_image['Name'] == 'main') {
// process primary image separately
if ( $object->isField('PrimaryImage') ) {
$object->SetDBField('PrimaryImage', $image_path);
$object->SetOriginalField('PrimaryImage', $image_path);
$object->SetFieldOption('PrimaryImage', 'original_field', $item_image['Name']);
$this->_loadCustomFields($object, $item_image, 0);
}
continue;
}
if (abs($item_image['Priority'])) {
// use Priority as image counter, when specified
$image_counter = abs($item_image['Priority']);
}
if ( $object->isField('Image'.$image_counter) ) {
$object->SetDBField('Image'.$image_counter, $image_path);
$object->SetOriginalField('Image'.$image_counter, $image_path);
$object->SetFieldOption('Image'.$image_counter, 'original_field', $item_image['Name']);
$this->_loadCustomFields($object, $item_image, $image_counter);
}
$image_counter++;
}
}
/**
* Saves newly uploaded images to external image table
*
* @param kCatDBItem|kDBItem $object
*/
function SaveItemImages(&$object)
{
if (!$this->_canUseImages($object)) {
return ;
}
$table_name = $this->Application->getUnitOption('img', 'TableName');
$max_image_count = $this->Application->getUnitOption($object->Prefix, 'ImageCount'); // $this->Application->ConfigValue($object->Prefix.'_MaxImageCount');
$i = 0;
while ($i < $max_image_count) {
$field = $i ? 'Image'.$i : 'PrimaryImage';
$field_options = $object->GetFieldOptions($field);
$image_src = $object->GetDBField($field);
if ($image_src) {
if (isset($field_options['original_field'])) {
$key_clause = 'Name = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId');
if ($object->GetDBField('Delete'.$field)) {
// if item was cloned, then new filename is in db (not in $image_src)
$sql = 'SELECT ThumbPath
FROM '.$table_name.'
WHERE '.$key_clause;
$image_src = $this->Conn->GetOne($sql);
if (@unlink(FULL_PATH.$image_src)) {
$sql = 'DELETE FROM '.$table_name.'
WHERE '.$key_clause;
$this->Conn->Query($sql);
}
}
else {
// image record found -> update
$fields_hash = Array (
'ThumbPath' => $image_src,
);
$this->_saveCustomFields($object, $fields_hash, $i);
$this->Conn->doUpdate($fields_hash, $table_name, $key_clause);
}
}
else {
// image record not found -> create
$fields_hash = Array (
'ResourceId' => $object->GetDBField('ResourceId'),
'Name' => $field,
'AltName' => $field,
'Enabled' => STATUS_ACTIVE,
'DefaultImg' => $i ? 0 : 1, // first image is primary, others not primary
'ThumbPath' => $image_src,
'Priority' => ($i == 0)? 0 : $i * (-1),
);
$this->_saveCustomFields($object, $fields_hash, $i);
$this->Conn->doInsert($fields_hash, $table_name);
$field_options['original_field'] = $field;
$object->SetFieldOptions($field, $field_options);
}
}
$i++;
}
}
/**
* Adds ability to load custom fields along with main image field
*
* @param kCatDBItem|kDBItem $object
* @param Array $fields_hash
* @param int $counter 0 - primary image, other number - additional image number
*/
function _loadCustomFields(&$object, $fields_hash, $counter)
{
$field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt';
$object->SetDBField($field_name, (string)$fields_hash['AltName']);
}
/**
* Adds ability to save custom field along with main image save
*
* @param kCatDBItem|kDBItem $object
* @param Array $fields_hash
* @param int $counter 0 - primary image, other number - additional image number
*/
function _saveCustomFields(&$object, &$fields_hash, $counter)
{
$field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt';
$fields_hash['AltName'] = (string)$object->GetDBField($field_name);
}
/**
* Checks, that item can use image upload capabilities
*
* @param kCatDBItem|kDBItem $object
* @return bool
*/
function _canUseImages(&$object)
{
$prefix = $object->Prefix == 'p' ? 'img' : $object->Prefix . '-img';
return $this->Application->prefixRegistred($prefix);
}
}
Index: branches/5.2.x/core/units/images/images_config.php
===================================================================
--- branches/5.2.x/core/units/images/images_config.php (revision 16246)
+++ branches/5.2.x/core/units/images/images_config.php (revision 16247)
@@ -1,184 +1,183 @@
<?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' => 'img',
'Clones' => Array (
'bb-post-img'=> Array('ParentPrefix' => 'bb-post'),
),
'ItemClass' => Array('class'=>'kDBItem','file'=>'','build_event'=>'OnItemBuild'),
'ListClass' => Array('class'=>'kDBList','file'=>'','build_event'=>'OnListBuild'),
'EventHandlerClass' => Array('class'=>'ImageEventHandler','file'=>'image_event_handler.php','build_event'=>'OnBuild'),
'TagProcessorClass' => Array('class'=>'ImageTagProcessor','file'=>'image_tag_processor.php','build_event'=>'OnBuild'),
'AutoLoad' => true,
'AggregateTags' => Array (
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'Image',
'LocalTagName' => 'ItemImageTag',
'LocalSpecial' => '-item',
),
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'ImageSrc',
'LocalTagName' => 'ItemImageTag',
'LocalSpecial' => '-item',
),
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'ImageSize',
'LocalTagName' => 'ItemImageTag',
'LocalSpecial' => '-item',
),
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'ListImages',
'LocalTagName' => 'PrintList2',
'LocalSpecial' => 'list',
),
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'LargeImageExists',
'LocalTagName' => 'LargeImageExists',
),
),
'QueryString' => Array(
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
),
'ScheduledTasks' => Array (
'clean_catalog_images' => Array ('EventName' => 'OnCleanImages', 'RunSchedule' => '0 0 * * 0', 'Status' => STATUS_DISABLED),
- 'clean_resized_catalog_images' => Array ('EventName' => 'OnCleanResizedImages', 'RunSchedule' => '0 0 1 * *', 'Status' => STATUS_DISABLED),
),
'IDField' => 'ImageId',
'StatusField' => Array('Enabled', 'DefaultImg'), // field, that is affected by Approve/Decline events
'TitleField' => 'Name', // field, used in bluebar when editing existing item
'TableName' => TABLE_PREFIX.'CatalogImages',
'ParentTableKey'=> 'ResourceId', // linked field in master table
'ForeignKey' => 'ResourceId', // linked field in subtable
'ParentPrefix' => 'p',
'AutoDelete' => true,
'AutoClone' => true,
'CalculatedFields' => Array(
'' => Array (
'Preview' => '0',
),
),
'ListSQLs' => Array( ''=>'SELECT * FROM %s',
), // key - special, value - list select sql
'ItemSQLs' => Array( ''=>'SELECT * FROM %s',
),
'ListSortings' => Array(
'' => Array(
'ForcedSorting' => Array('Priority' => 'desc'),
'Sorting' => Array('Name' => 'asc'),
)
),
'Fields' => Array (
'ImageId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'ResourceId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'Url' => Array('type' => 'string', 'max_len' => 255, 'default' => '', 'not_null' => 1),
'Name' => Array('type' => 'string', 'max_len' => 255, 'required' => 1, 'not_null' => 1, 'default' => ''),
'AltName' => Array('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'ImageIndex' => Array('type'=>'int', 'default' => 0, 'not_null'=>1),
'LocalImage' => Array('type'=>'int', 'default' => 1, 'not_null'=>1),
'LocalPath' => Array(
'type' => 'string', 'formatter' => 'kPictureFormatter',
'skip_empty' => 1, 'max_len' => 240, 'default' => '',
'not_null' => 1, 'include_path' => 1,
'allowed_types' => Array(
'image/jpeg', 'image/pjpeg', 'image/png',
'image/x-png', 'image/gif', 'image/bmp'
),
'error_msgs' => Array(
'bad_file_format' => '!la_error_InvalidFileFormat!',
'bad_file_size' => '!la_error_FileTooLarge!',
'cant_save_file' => '!la_error_cant_save_file!',
),
),
'Enabled' => Array (
'type' => 'int', 'formatter' => 'kOptionsFormatter',
'options' => Array (1 => 'la_Enabled', 0 => 'la_Disabled',),
'default' => 1, 'not_null' => 1, 'use_phrases' => 1,
),
'DefaultImg' => Array (
'type' => 'int', 'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
'default' => 0, 'not_null' => 1, 'use_phrases' => 1,
),
'ThumbUrl' => Array('type' => 'string', 'max_len' => 255, 'default' => null),
'Priority' => Array('type' => 'int', 'default' => 0, 'not_null'=>1),
'ThumbPath' => Array(
'type' => 'string', 'formatter' => 'kPictureFormatter',
'skip_empty' => 1, 'max_len' => 240, 'default' => NULL,
'include_path' => 1,
'allowed_types' => Array(
'image/jpeg', 'image/pjpeg', 'image/png',
'image/x-png', 'image/gif', 'image/bmp'
),
'error_msgs' => Array(
'bad_file_format' => '!la_error_InvalidFileFormat!',
'bad_file_size' => '!la_error_FileTooLarge!',
'cant_save_file' => '!la_error_cant_save_file!',
),
),
'LocalThumb' => Array('type' => 'int', 'default' => 1, 'not_null'=>1),
'SameImages' => Array (
'type' => 'int', 'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
'default' => 1, 'not_null' => 1, 'use_phrases' => 1,
),
),
'VirtualFields' => Array(
'Preview' => Array ('type' => 'string', 'default' => ''),
'ImageUrl' => Array ('type' => 'string', 'default' => ''),
),
'Grids' => Array(
'Default' => Array(
'Icons' => Array (
'default' => 'icon16_item.png',
'0_0' => 'icon16_disabled.png',
'0_1' => 'icon16_disabled.png',
'1_0' => 'icon16_item.png',
'1_1' => 'icon16_primary.png',
),
'Fields' => Array(
'ImageId' => Array( 'title'=>'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 50, ),
'Name' => Array( 'title'=>'la_col_ImageName' , 'data_block' => 'image_caption_td', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
'AltName' => Array( 'title'=>'la_col_AltName', 'filter_block' => 'grid_like_filter', 'width' => 150, ),
'Url' => Array( 'title'=>'la_col_ImageUrl', 'data_block' => 'image_url_td', 'filter_block' => 'grid_like_filter', 'width' => 200, ),
'Preview' => Array( 'title'=>'la_col_Preview', 'data_block' => 'image_preview_td', 'filter_block' => 'grid_like_filter', 'width' => 200, ),
'Enabled' => Array( 'title'=>'la_col_ImageEnabled', 'filter_block' => 'grid_options_filter', 'width' => 80, ),
),
),
),
- );
\ No newline at end of file
+ );
Index: branches/5.2.x/core/units/images/image_event_handler.php
===================================================================
--- branches/5.2.x/core/units/images/image_event_handler.php (revision 16246)
+++ branches/5.2.x/core/units/images/image_event_handler.php (revision 16247)
@@ -1,496 +1,474 @@
<?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 ImageEventHandler extends kDBEventHandler {
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnCleanImages' => Array ('subitem' => true),
- 'OnCleanResizedImages' => Array ('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()
{
parent::mapEvents(); // ensure auto-adding of approve/decline and so on events
$image_events = Array (
'OnAfterCopyToTemp'=>'ImageAction',
'OnBeforeDeleteFromLive'=>'ImageAction',
'OnBeforeCopyToLive'=>'ImageAction',
'OnBeforeItemDelete'=>'ImageAction',
'OnAfterClone'=>'ImageAction',
);
$this->eventMethods = array_merge($this->eventMethods, $image_events);
}
/**
* Returns special of main item for linking with sub-item
*
* @param kEvent $event
* @return string
* @access protected
*/
protected function getMainSpecial(kEvent $event)
{
if ( $event->Special == 'list' && !$this->Application->isAdmin ) {
// ListImages aggregated tag uses this special
return '';
}
return parent::getMainSpecial($event);
}
/**
* Don't allow to delete primary category item image, when there are no more images
*
* @param kEvent $event
* @param string $type
* @return void
* @access protected
*/
protected function customProcessing(kEvent $event, $type)
{
$object = $event->getObject();
/* @var $object kDBItem */
if ( $event->Name == 'OnMassDelete' && $type == 'before' ) {
$ids = $event->getEventParam('ids');
$parent_info = $object->getLinkedInfo($event->Special);
$sql = 'SELECT ImageId
FROM ' . $object->TableName . '
WHERE DefaultImg = 1 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId'];
$primary_file_id = $this->Conn->GetOne($sql);
if ( $primary_file_id ) {
$file_id_index = array_search($primary_file_id, $ids);
if ( $file_id_index ) {
// allow deleting of primary product file, when there is another file to make primary
$sql = 'SELECT COUNT(*)
FROM ' . $object->TableName . '
WHERE DefaultImg = 0 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId'];
$non_primary_file_count = $this->Conn->GetOne($sql);
if ( $non_primary_file_count ) {
unset($ids[$file_id_index]);
}
}
}
$event->setEventParam('ids', $ids);
}
switch ($type) {
case 'before' :
// empty unused fields
$object->SetDBField($object->GetDBField('LocalImage') ? 'Url' : 'LocalPath', '');
$object->SetDBField($object->GetDBField('LocalThumb') ? 'ThumbUrl' : 'ThumbPath', '');
if ( $object->GetDBField('SameImages') ) {
$object->SetDBField('LocalImage', 1);
$object->SetDBField('LocalPath', '');
$object->SetDBField('Url', '');
}
break;
case 'after':
// make sure, that there is only one primary image for the item
if ( $object->GetDBField('DefaultImg') ) {
$sql = 'UPDATE ' . $object->TableName . '
SET DefaultImg = 0
WHERE ResourceId = ' . $object->GetDBField('ResourceId') . ' AND ImageId <> ' . $object->GetID();
$this->Conn->Query($sql);
}
break;
}
}
/**
* Performs temp-table related action on current image record
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function ImageAction($event)
{
$id = $event->getEventParam('id');
$object = $this->Application->recallObject($event->Prefix . '.-item', $event->Prefix, Array ('skip_autoload' => true));
/* @var $object kDBItem */
if ( in_array($event->Name, Array ('OnBeforeDeleteFromLive', 'OnAfterClone')) ) {
$object->SwitchToLive();
}
elseif ( $event->Name == 'OnBeforeItemDelete' ) {
// keep current table
}
else {
$object->SwitchToTemp();
}
$object->Load($id);
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$fields = Array ('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb');
foreach ($fields as $a_field => $mode_field) {
$file = $object->GetDBField($a_field);
if ( !$file ) {
continue;
}
$source_file = FULL_PATH . $file;
switch ($event->Name) {
// Copy image files to pending dir and update corresponding fields in temp record
// Checking for existing files and renaming if necessary - two users may upload same pending files at the same time!
case 'OnAfterCopyToTemp':
$file = preg_replace('/^' . preg_quote(IMAGES_PATH, '/') . '/', IMAGES_PENDING_PATH, $file, 1);
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
copy($source_file, $dst_file);
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
// Copy image files to live dir (checking if file exists and renaming if necessary)
// and update corresponding fields in temp record (which gets copied to live automatically)
case 'OnBeforeCopyToLive':
if ( $object->GetDBField($mode_field) ) {
// if image is local -> rename file if it exists in live folder
$file = preg_replace('/^' . preg_quote(IMAGES_PENDING_PATH, '/') . '/', IMAGES_PATH, $file, 1);
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
rename($source_file, $dst_file);
}
else {
// if image is remote url - remove local file (if any), update local file field with empty value
if ( file_exists($source_file) ) {
@unlink($source_file);
}
$new_file = '';
}
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
case 'OnBeforeDeleteFromLive': // Delete image files from live folder before copying over from temp
case 'OnBeforeItemDelete': // Delete image files when deleting Image object
@unlink(FULL_PATH . $file);
break;
case 'OnAfterClone':
// Copy files when cloning objects, renaming it on the fly
$new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
$dst_file = FULL_PATH . $new_file;
copy($source_file, $dst_file);
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
}
}
if ( in_array($event->Name, Array ('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) {
$object->Update(null, null, true);
}
}
/**
* Sets primary image of user/category/category item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetPrimary($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('DefaultImg', 1);
$object->Update();
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->processImageStatus($event);
}
/**
* Occurs after creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->processImageStatus($event);
$object = $event->getObject();
/* @var $object kDBItem */
$object->Update();
}
/**
* Occurs before item changed
*
* @param kEvent $event
*/
function processImageStatus($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$id = $object->GetDBField('ResourceId');
$sql = 'SELECT ImageId
FROM ' . $object->TableName . '
WHERE ResourceId = ' . $id . ' AND DefaultImg = 1';
$primary_image_id = $this->Conn->GetOne($sql);
if ( !$primary_image_id ) {
$object->SetDBField('DefaultImg', 1);
}
if ( $object->GetDBField('DefaultImg') && $object->Validate() ) {
$sql = 'UPDATE ' . $object->TableName . '
SET DefaultImg = 0
WHERE ResourceId = ' . $id . ' AND ImageId <> ' . $object->GetDBField('ImageId');
$this->Conn->Query($sql);
$object->SetDBField('Enabled', 1);
}
}
/**
* 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 ( !$this->Application->isAdminUser ) {
$object->addFilter('active', '%1$s.Enabled = 1');
}
$product_id = $event->getEventParam('product_id');
if ( $product_id ) {
$object->removeFilter('parent_filter');
$sql = 'SELECT ResourceId
FROM ' . $this->Application->getUnitOption('p', 'TableName') . '
WHERE ProductId = ' . $product_id;
$resource_id = (int)$this->Conn->GetOne($sql);
$object->addFilter('product_images', '%1$s.ResourceId = ' . $resource_id);
}
$search_helper = $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$types = $event->getEventParam('types');
$except_types = $event->getEventParam('except');
$type_clauses = $this->getTypeClauses($event);
$search_helper->SetComplexFilter($event, $type_clauses, $types, $except_types);
}
/**
* Return type clauses for list bulding on front
*
* @param kEvent $event
* @return Array
*/
function getTypeClauses($event)
{
$type_clauses = Array ();
$type_clauses['additional']['include'] = '%1$s.DefaultImg != 1';
$type_clauses['additional']['except'] = '%1$s.DefaultImg = 1';
$type_clauses['additional']['having_filter'] = false;
return $type_clauses;
}
/**
* [SCHEDULED TASK] Remove unused images from "/system/images" and "/system/images/pending" folders
*
* @param kEvent $event
*/
function OnCleanImages($event)
{
// 1. get images, that are currently in use
$active_images = $this->_getActiveImages( $this->Application->getUnitOption('img', 'TableName') );
$active_images[] = 'noimage.gif';
// 2. get images on disk
$this->_deleteUnusedImages(FULL_PATH . IMAGES_PATH, $active_images);
// 3. get images in use from "images/pending" folder
$active_images = $this->_getPendingImages();
// 4. get image on disk
$this->_deleteUnusedImages(FULL_PATH . IMAGES_PENDING_PATH, $active_images);
}
/**
* Gets image filenames (no path) from given table
*
* @param string $image_table
* @return Array
*/
function _getActiveImages($image_table)
{
$sql = 'SELECT LocalPath, ThumbPath
FROM ' . $image_table . '
WHERE COALESCE(LocalPath, "") <> "" OR COALESCE(ThumbPath) <> ""';
$images = $this->Conn->Query($sql);
$active_images = Array ();
foreach ($images as $image) {
if ($image['LocalPath']) {
$active_images[] = basename($image['LocalPath']);
}
if ($image['ThumbPath']) {
$active_images[] = basename($image['ThumbPath']);
}
}
return $active_images;
}
/**
* Gets active images, that are currently beeing edited inside temporary tables
*
* @return Array
*/
function _getPendingImages()
{
$tables = $this->Conn->GetCol('SHOW TABLES');
$mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_' . TABLE_PREFIX . 'CatalogImages/';
$active_images = Array ();
foreach ($tables as $table) {
if (!preg_match($mask_edit_table, $table)) {
continue;
}
$active_images = array_unique( array_merge($active_images, $this->_getActiveImages($table)) );
}
return $active_images;
}
/**
* Deletes all files in given path, except of given $active_images
*
* @param string $path
* @param Array $active_images
*/
function _deleteUnusedImages($path, &$active_images)
{
$images = glob($path . '*.*');
if ($images) {
$images = array_map('basename', $images);
// delete images, that are on disk, but are not mentioned in CatalogImages table
$delete_images = array_diff($images, $active_images);
foreach ($delete_images as $delete_image) {
unlink($path . $delete_image);
}
}
}
- /**
- * [SCHEDULED TASK] Remove all images from "/system/images/resized" and "/system/images/pending/resized" folders
- *
- * @param kEvent $event
- */
- function OnCleanResizedImages($event)
- {
- $images = glob(FULL_PATH . IMAGES_PATH . 'resized/*.*');
- if ($images) {
- foreach ($images as $image) {
- unlink($image);
- }
- }
-
- $images = glob(FULL_PATH . IMAGES_PENDING_PATH . 'resized/*.*');
- if ($images) {
- foreach ($images as $image) {
- unlink($image);
- }
- }
- }
-}
\ No newline at end of file
+}
Index: branches/5.2.x/core/install/upgrades.php
===================================================================
--- branches/5.2.x/core/install/upgrades.php (revision 16246)
+++ branches/5.2.x/core/install/upgrades.php (revision 16247)
@@ -1,2341 +1,2380 @@
<?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!');
$upgrade_class = 'CoreUpgrades';
/**
* Class, that holds all upgrade scripts for "Core" module
*
*/
class CoreUpgrades extends kUpgradeHelper {
/**
* Tables, that were renamed during 5.2.0 version upgrade
*
* @var Array
* @access private
*/
private $renamedTables = Array (
'ban-rule' => Array ('from' => 'BanRules', 'to' => 'UserBanRules'),
'conf' => Array ('from' => 'ConfigurationValues', 'to' => 'SystemSettings'),
'c' => Array ('from' => 'Category', 'to' => 'Categories'),
'cf' => Array ('from' => 'CustomField', 'to' => 'CustomFields'),
'draft' => Array ('from' => 'Drafts', 'to' => 'FormSubmissionReplyDrafts'),
'email-template' => Array ('from' => 'Events', 'to' => 'EmailEvents'),
'fav' => Array ('from' => 'Favorites', 'to' => 'UserFavorites'),
'img' => Array ('from' => 'Images', 'to' => 'CatalogImages'),
'#file' => Array ('from' => 'ItemFiles', 'to' => 'CatalogFiles'),
'rev' => Array ('from' => 'ItemReview', 'to' => 'CatalogReviews'),
'lang' => Array ('from' => 'Language', 'to' => 'Languages'),
'permission-type' => Array ('from' => 'PermissionConfig', 'to' => 'CategoryPermissionsConfig'),
'phrases' => Array ('from' => 'Phrase', 'to' => 'LanguageLabels'),
'g' => Array ('from' => 'PortalGroup', 'to' => 'UserGroups'),
'user-profile' => Array ('from' => 'PersistantSessionData', 'to' => 'UserPersistentSessionData'),
'u' => Array ('from' => 'PortalUser', 'to' => 'Users'),
'u-cdata' => Array ('from' => 'PortalUserCustomData', 'to' => 'UserCustomData'),
'search' => Array ('from' => 'RelatedSearches', 'to' => 'CategoryRelatedSearches'),
'rel' => Array ('from' => 'Relationship', 'to' => 'CatalogRelationships'),
'search-log' => Array ('from' => 'SearchLog', 'to' => 'SearchLogs'),
'skin' => Array ('from' => 'Skins', 'to' => 'AdminSkins'),
'submission-log' => Array ('from' => 'SubmissionLog', 'to' => 'FormSubmissionReplies'),
'theme' => Array ('from' => 'Theme', 'to' => 'Themes'),
'ug' => Array ('from' => 'UserGroup', 'to' => 'UserGroupRelations'),
'visits' => Array ('from' => 'Visits', 'to' => 'UserVisits'),
'session-log' => Array ('from' => 'SessionLogs', 'to' => 'UserSessionLogs'),
);
/**
* Changes table structure, where multilingual fields of TEXT type are present
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_1_0($mode)
{
if ($mode == 'before') {
// don't user after, because In-Portal calls this method too
$this->_toolkit->SaveConfig();
}
if ($mode == 'after') {
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$this->Application->UnitConfigReader->iterateConfigs(Array (&$this, 'updateTextFields'), $ml_helper->getLanguages());
}
}
/**
* Moves ReplacementTags functionality from EmailMessage to Events table
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_1_1($mode)
{
if ($mode == 'after') {
$sql = 'SELECT ReplacementTags, EventId
FROM '.TABLE_PREFIX.'EmailMessage
WHERE (ReplacementTags IS NOT NULL) AND (ReplacementTags <> "") AND (LanguageId = 1)';
$replacement_tags = $this->Conn->GetCol($sql, 'EventId');
foreach ($replacement_tags as $event_id => $replacement_tag) {
$sql = 'UPDATE '.TABLE_PREFIX.'Events
SET ReplacementTags = '.$this->Conn->qstr($replacement_tag).'
WHERE EventId = '.$event_id;
$this->Conn->Query($sql);
}
// drop moved field from source table
$sql = 'ALTER TABLE '.TABLE_PREFIX.'EmailMessage
DROP `ReplacementTags`';
$this->Conn->Query($sql);
}
}
/**
* Callback function, that makes all ml fields of text type null with same default value
*
* @param string $prefix
* @param Array $config_data
* @param Array $languages
* @return bool
*/
function updateTextFields($prefix, &$config_data, $languages)
{
if (!isset($config_data['TableName']) || !isset($config_data['Fields'])) {
// invalid config found or prefix not found
return false;
}
$table_name = $config_data['TableName'];
$table_structure = $this->Conn->Query('DESCRIBE '.$table_name, 'Field');
if (!$table_structure) {
// table not found
return false;
}
$sqls = Array ();
foreach ($config_data['Fields'] as $field => $options) {
if (isset($options['formatter']) && $options['formatter'] == 'kMultiLanguage' && !isset($options['master_field'])) {
// update all l<lang_id>_<field_name> fields (new format)
foreach ($languages as $language_id) {
$ml_field = 'l'.$language_id.'_'.$field;
if ($table_structure[$ml_field]['Type'] == 'text') {
$sqls[] = 'CHANGE '.$ml_field.' '.$ml_field.' TEXT NULL DEFAULT NULL';
}
}
// update <field_name> if found (old format)
if (isset($table_structure[$field]) && $table_structure[$field]['Type'] == 'text') {
$sqls[] = 'CHANGE '.$field.' '.$field.' TEXT NULL DEFAULT NULL';
}
}
}
if ($sqls) {
$sql = 'ALTER TABLE '.$table_name.' '.implode(', ', $sqls);
$this->Conn->Query($sql);
}
return true;
}
/**
* Replaces In-Portal tags in Forgot Password related email events to K4 ones
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_2_0($mode)
{
if ($mode == 'after') {
// 1. get event ids based on their name and type combination
$event_names = Array (
'USER.PSWD_' . EmailTemplate::TEMPLATE_TYPE_ADMIN,
'USER.PSWD_' . EmailTemplate::TEMPLATE_TYPE_FRONTEND,
'USER.PSWDC_' . EmailTemplate::TEMPLATE_TYPE_FRONTEND,
);
$event_sql = Array ();
foreach ($event_names as $mixed_event) {
list ($event_name, $event_type) = explode('_', $mixed_event, 2);
$event_sql[] = 'Event = "'.$event_name.'" AND Type = '.$event_type;
}
$sql = 'SELECT EventId
FROM '.TABLE_PREFIX.'Events
WHERE ('.implode(') OR (', $event_sql).')';
$event_ids = implode(',', $this->Conn->GetCol($sql));
// 2. replace In-Portal tags to K4 tags
$replacements = Array (
'<inp:touser _Field="Password" />' => '<inp2:u_ForgottenPassword />',
'<inp:m_confirm_password_link />' => '<inp2:u_ConfirmPasswordLink no_amp="1"/>',
);
foreach ($replacements as $old_tag => $new_tag) {
$sql = 'UPDATE '.TABLE_PREFIX.'EmailMessage
SET Template = REPLACE(Template, '.$this->Conn->qstr($old_tag).', '.$this->Conn->qstr($new_tag).')
WHERE EventId IN ('.$event_ids.')';
$this->Conn->Query($sql);
}
}
}
/**
* Makes admin primary language same as front-end - not needed, done in SQL
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_2_1($mode)
{
}
function Upgrade_4_2_2($mode)
{
if ( $mode == 'before' ) {
$sql = 'SELECT LanguageId
FROM ' . TABLE_PREFIX . 'Language
WHERE PrimaryLang = 1';
if ( $this->Conn->GetOne($sql) ) {
return;
}
$this->Conn->Query('UPDATE ' . TABLE_PREFIX . 'Language SET PrimaryLang = 1 ORDER BY LanguageId LIMIT 1');
}
}
/**
* Adds index to "dob" field in "PortalUser" table when it's missing
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_3_1($mode)
{
if ($mode == 'after') {
$sql = 'DESCRIBE ' . TABLE_PREFIX . 'PortalUser';
$structure = $this->Conn->Query($sql);
foreach ($structure as $field_info) {
if ($field_info['Field'] == 'dob') {
if (!$field_info['Key']) {
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'PortalUser
ADD INDEX (dob)';
$this->Conn->Query($sql);
}
break;
}
}
}
}
/**
* Removes duplicate phrases, update file paths in database
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_4_3_9($mode)
{
// 1. find In-Portal old <inp: tags
$sql = 'SELECT EmailMessageId
FROM '.TABLE_PREFIX.'EmailMessage
WHERE Template LIKE \'%<inp:%\'';
$event_ids = implode(',', $this->Conn->GetCol($sql));
// 2. replace In-Portal old <inp: tags to K4 tags
$replacements = Array (
'<inp:m_category_field _Field="Name" _StripHTML="1"' => '<inp2:c_Field name="Name"',
'<inp:touser _Field="password"' => '<inp2:u_Field name="Password_plain"',
'<inp:touser _Field="UserName"' => '<inp2:u_Field name="Login"',
'<inp:touser _Field="' => '<inp2:u_Field name="',
'<inp:m_page_title' => '<inp2:m_BaseUrl',
'<inp:m_theme_url _page="current"' => '<inp2:m_BaseUrl',
'<inp:topic _field="text"' => '<inp2:bb-post_Field name="PostingText"',
'<inp:topic _field="link" _Template="inbulletin/post_list"' => '<inp2:bb_TopicLink template="__default__"',
);
if ($event_ids) {
foreach ($replacements as $old_tag => $new_tag) {
$sql = 'UPDATE '.TABLE_PREFIX.'EmailMessage
SET Template = REPLACE(Template, '.$this->Conn->qstr($old_tag).', '.$this->Conn->qstr($new_tag).')
WHERE EventId IN ('.$event_ids.')';
$this->Conn->Query($sql);
}
}
if ($mode == 'after') {
$this->_insertInPortalData();
$this->_moveDatabaseFolders();
// in case, when In-Portal module is enabled -> turn AdvancedUserManagement on too
if ($this->Application->findModule('Name', 'In-Portal')) {
$sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues
SET VariableValue = 1
WHERE VariableName = "AdvancedUserManagement"';
$this->Conn->Query($sql);
}
}
if ($mode == 'languagepack') {
$this->_removeDuplicatePhrases();
}
}
function _insertInPortalData()
{
$data = Array (
'ConfigurationAdmin' => Array (
'UniqueField' => 'VariableName',
'Records' => Array (
'AllowDeleteRootCats' => "('AllowDeleteRootCats', 'la_Text_General', 'la_AllowDeleteRootCats', 'checkbox', NULL , NULL , 10.09, 0, 0)",
'Catalog_PreselectModuleTab' => "('Catalog_PreselectModuleTab', 'la_Text_General', 'la_config_CatalogPreselectModuleTab', 'checkbox', NULL, NULL, 10.10, 0, 1)",
'RecycleBinFolder' => "('RecycleBinFolder', 'la_Text_General', 'la_config_RecycleBinFolder', 'text', NULL , NULL , 10.11, 0, 0)",
'AdvancedUserManagement' => "('AdvancedUserManagement', 'la_Text_General', 'la_prompt_AdvancedUserManagement', 'checkbox', NULL, NULL, '10.011', 0, 1)",
),
),
'ConfigurationValues' => Array (
'UniqueField' => 'VariableName',
'Records' => Array (
'AdvancedUserManagement' => "(DEFAULT, 'AdvancedUserManagement', 0, 'In-Portal:Users', 'in-portal:configure_users')",
),
),
'ItemTypes' => Array (
'UniqueField' => 'ItemType',
'Records' => Array (
'1' => "(1, 'In-Portal', 'c', 'Category', 'Name', 'CreatedById', NULL, NULL, 'la_ItemTab_Categories', 1, 'admin/category/addcategory.php', 'clsCategory', 'Category')",
'6' => "(6, 'In-Portal', 'u', 'PortalUser', 'Login', 'PortalUserId', NULL, NULL, '', 0, '', 'clsPortalUser', 'User')",
),
),
'PermissionConfig' => Array (
'UniqueField' => 'PermissionName',
'Records' => Array (
'CATEGORY.ADD' => "(DEFAULT, 'CATEGORY.ADD', 'lu_PermName_Category.Add_desc', 'lu_PermName_Category.Add_error', 'In-Portal')",
'CATEGORY.DELETE' => "(DEFAULT, 'CATEGORY.DELETE', 'lu_PermName_Category.Delete_desc', 'lu_PermName_Category.Delete_error', 'In-Portal')",
'CATEGORY.ADD.PENDING' => "(DEFAULT, 'CATEGORY.ADD.PENDING', 'lu_PermName_Category.AddPending_desc', 'lu_PermName_Category.AddPending_error', 'In-Portal')",
'CATEGORY.MODIFY' => "(DEFAULT, 'CATEGORY.MODIFY', 'lu_PermName_Category.Modify_desc', 'lu_PermName_Category.Modify_error', 'In-Portal')",
'ADMIN' => "(DEFAULT, 'ADMIN', 'lu_PermName_Admin_desc', 'lu_PermName_Admin_error', 'Admin')",
'LOGIN' => "(DEFAULT, 'LOGIN', 'lu_PermName_Login_desc', 'lu_PermName_Admin_error', 'Front')",
'DEBUG.ITEM' => "(DEFAULT, 'DEBUG.ITEM', 'lu_PermName_Debug.Item_desc', '', 'Admin')",
'DEBUG.LIST' => "(DEFAULT, 'DEBUG.LIST', 'lu_PermName_Debug.List_desc', '', 'Admin')",
'DEBUG.INFO' => "(DEFAULT, 'DEBUG.INFO', 'lu_PermName_Debug.Info_desc', '', 'Admin')",
'PROFILE.MODIFY' => "(DEFAULT, 'PROFILE.MODIFY', 'lu_PermName_Profile.Modify_desc', '', 'Admin')",
'SHOWLANG' => "(DEFAULT, 'SHOWLANG', 'lu_PermName_ShowLang_desc', '', 'Admin')",
'FAVORITES' => "(DEFAULT, 'FAVORITES', 'lu_PermName_favorites_desc', 'lu_PermName_favorites_error', 'In-Portal')",
'SYSTEM_ACCESS.READONLY' => "(DEFAULT, 'SYSTEM_ACCESS.READONLY', 'la_PermName_SystemAccess.ReadOnly_desc', 'la_PermName_SystemAccess.ReadOnly_error', 'Admin')",
),
),
'Permissions' => Array (
'UniqueField' => 'Permission;GroupId;Type;CatId',
'Records' => Array (
'LOGIN;12;1;0' => "(DEFAULT, 'LOGIN', 12, 1, 1, 0)",
'in-portal:site.view;11;1;0' => "(DEFAULT, 'in-portal:site.view', 11, 1, 1, 0)",
'in-portal:browse.view;11;1;0' => "(DEFAULT, 'in-portal:browse.view', 11, 1, 1, 0)",
'in-portal:advanced_view.view;11;1;0' => "(DEFAULT, 'in-portal:advanced_view.view', 11, 1, 1, 0)",
'in-portal:reviews.view;11;1;0' => "(DEFAULT, 'in-portal:reviews.view', 11, 1, 1, 0)",
'in-portal:configure_categories.view;11;1;0' => "(DEFAULT, 'in-portal:configure_categories.view', 11, 1, 1, 0)",
'in-portal:configure_categories.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_categories.edit', 11, 1, 1, 0)",
'in-portal:configuration_search.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_search.view', 11, 1, 1, 0)",
'in-portal:configuration_search.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_search.edit', 11, 1, 1, 0)",
'in-portal:configuration_email.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_email.view', 11, 1, 1, 0)",
'in-portal:configuration_email.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_email.edit', 11, 1, 1, 0)",
'in-portal:configuration_custom.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.view', 11, 1, 1, 0)",
'in-portal:configuration_custom.add;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.add', 11, 1, 1, 0)",
'in-portal:configuration_custom.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.edit', 11, 1, 1, 0)",
'in-portal:configuration_custom.delete;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.delete', 11, 1, 1, 0)",
'in-portal:users.view;11;1;0' => "(DEFAULT, 'in-portal:users.view', 11, 1, 1, 0)",
'in-portal:user_list.advanced:ban;11;1;0' => "(DEFAULT, 'in-portal:user_list.advanced:ban', 11, 1, 1, 0)",
'in-portal:user_list.advanced:send_email;11;1;0' => "(DEFAULT, 'in-portal:user_list.advanced:send_email', 11, 1, 1, 0)",
'in-portal:user_groups.view;11;1;0' => "(DEFAULT, 'in-portal:user_groups.view', 11, 1, 1, 0)",
'in-portal:user_groups.add;11;1;0' => "(DEFAULT, 'in-portal:user_groups.add', 11, 1, 1, 0)",
'in-portal:user_groups.edit;11;1;0' => "(DEFAULT, 'in-portal:user_groups.edit', 11, 1, 1, 0)",
'in-portal:user_groups.delete;11;1;0' => "(DEFAULT, 'in-portal:user_groups.delete', 11, 1, 1, 0)",
'in-portal:user_groups.advanced:send_email;11;1;0' => "(DEFAULT, 'in-portal:user_groups.advanced:send_email', 11, 1, 1, 0)",
'in-portal:user_groups.advanced:manage_permissions;11;1;0' => "(DEFAULT, 'in-portal:user_groups.advanced:manage_permissions', 11, 1, 1, 0)",
'in-portal:configure_users.view;11;1;0' => "(DEFAULT, 'in-portal:configure_users.view', 11, 1, 1, 0)",
'in-portal:configure_users.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_users.edit', 11, 1, 1, 0)",
'in-portal:user_email.view;11;1;0' => "(DEFAULT, 'in-portal:user_email.view', 11, 1, 1, 0)",
'in-portal:user_email.edit;11;1;0' => "(DEFAULT, 'in-portal:user_email.edit', 11, 1, 1, 0)",
'in-portal:user_custom.view;11;1;0' => "(DEFAULT, 'in-portal:user_custom.view', 11, 1, 1, 0)",
'in-portal:user_custom.add;11;1;0' => "(DEFAULT, 'in-portal:user_custom.add', 11, 1, 1, 0)",
'in-portal:user_custom.edit;11;1;0' => "(DEFAULT, 'in-portal:user_custom.edit', 11, 1, 1, 0)",
'in-portal:user_custom.delete;11;1;0' => "(DEFAULT, 'in-portal:user_custom.delete', 11, 1, 1, 0)",
'in-portal:user_banlist.view;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.view', 11, 1, 1, 0)",
'in-portal:user_banlist.add;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.add', 11, 1, 1, 0)",
'in-portal:user_banlist.edit;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.edit', 11, 1, 1, 0)",
'in-portal:user_banlist.delete;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.delete', 11, 1, 1, 0)",
'in-portal:reports.view;11;1;0' => "(DEFAULT, 'in-portal:reports.view', 11, 1, 1, 0)",
'in-portal:log_summary.view;11;1;0' => "(DEFAULT, 'in-portal:log_summary.view', 11, 1, 1, 0)",
'in-portal:searchlog.view;11;1;0' => "(DEFAULT, 'in-portal:searchlog.view', 11, 1, 1, 0)",
'in-portal:searchlog.delete;11;1;0' => "(DEFAULT, 'in-portal:searchlog.delete', 11, 1, 1, 0)",
'in-portal:sessionlog.view;11;1;0' => "(DEFAULT, 'in-portal:sessionlog.view', 11, 1, 1, 0)",
'in-portal:sessionlog.delete;11;1;0' => "(DEFAULT, 'in-portal:sessionlog.delete', 11, 1, 1, 0)",
'in-portal:emaillog.view;11;1;0' => "(DEFAULT, 'in-portal:emaillog.view', 11, 1, 1, 0)",
'in-portal:emaillog.delete;11;1;0' => "(DEFAULT, 'in-portal:emaillog.delete', 11, 1, 1, 0)",
'in-portal:visits.view;11;1;0' => "(DEFAULT, 'in-portal:visits.view', 11, 1, 1, 0)",
'in-portal:visits.delete;11;1;0' => "(DEFAULT, 'in-portal:visits.delete', 11, 1, 1, 0)",
'in-portal:configure_general.view;11;1;0' => "(DEFAULT, 'in-portal:configure_general.view', 11, 1, 1, 0)",
'in-portal:configure_general.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_general.edit', 11, 1, 1, 0)",
'in-portal:modules.view;11;1;0' => "(DEFAULT, 'in-portal:modules.view', 11, 1, 1, 0)",
'in-portal:mod_status.view;11;1;0' => "(DEFAULT, 'in-portal:mod_status.view', 11, 1, 1, 0)",
'in-portal:mod_status.edit;11;1;0' => "(DEFAULT, 'in-portal:mod_status.edit', 11, 1, 1, 0)",
'in-portal:mod_status.advanced:approve;11;1;0' => "(DEFAULT, 'in-portal:mod_status.advanced:approve', 11, 1, 1, 0)",
'in-portal:mod_status.advanced:decline;11;1;0' => "(DEFAULT, 'in-portal:mod_status.advanced:decline', 11, 1, 1, 0)",
'in-portal:addmodule.view;11;1;0' => "(DEFAULT, 'in-portal:addmodule.view', 11, 1, 1, 0)",
'in-portal:addmodule.add;11;1;0' => "(DEFAULT, 'in-portal:addmodule.add', 11, 1, 1, 0)",
'in-portal:addmodule.edit;11;1;0' => "(DEFAULT, 'in-portal:addmodule.edit', 11, 1, 1, 0)",
'in-portal:tag_library.view;11;1;0' => "(DEFAULT, 'in-portal:tag_library.view', 11, 1, 1, 0)",
'in-portal:configure_themes.view;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.view', 11, 1, 1, 0)",
'in-portal:configure_themes.add;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.add', 11, 1, 1, 0)",
'in-portal:configure_themes.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.edit', 11, 1, 1, 0)",
'in-portal:configure_themes.delete;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.delete', 11, 1, 1, 0)",
'in-portal:configure_styles.view;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.view', 11, 1, 1, 0)",
'in-portal:configure_styles.add;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.add', 11, 1, 1, 0)",
'in-portal:configure_styles.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.edit', 11, 1, 1, 0)",
'in-portal:configure_styles.delete;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.delete', 11, 1, 1, 0)",
'in-portal:configure_lang.advanced:set_primary;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:set_primary', 11, 1, 1, 0)",
'in-portal:configure_lang.advanced:import;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:import', 11, 1, 1, 0)",
'in-portal:configure_lang.advanced:export;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:export', 11, 1, 1, 0)",
'in-portal:tools.view;11;1;0' => "(DEFAULT, 'in-portal:tools.view', 11, 1, 1, 0)",
'in-portal:backup.view;11;1;0' => "(DEFAULT, 'in-portal:backup.view', 11, 1, 1, 0)",
'in-portal:restore.view;11;1;0' => "(DEFAULT, 'in-portal:restore.view', 11, 1, 1, 0)",
'in-portal:export.view;11;1;0' => "(DEFAULT, 'in-portal:export.view', 11, 1, 1, 0)",
'in-portal:main_import.view;11;1;0' => "(DEFAULT, 'in-portal:main_import.view', 11, 1, 1, 0)",
'in-portal:sql_query.view;11;1;0' => "(DEFAULT, 'in-portal:sql_query.view', 11, 1, 1, 0)",
'in-portal:sql_query.edit;11;1;0' => "(DEFAULT, 'in-portal:sql_query.edit', 11, 1, 1, 0)",
'in-portal:server_info.view;11;1;0' => "(DEFAULT, 'in-portal:server_info.view', 11, 1, 1, 0)",
'in-portal:help.view;11;1;0' => "(DEFAULT, 'in-portal:help.view', 11, 1, 1, 0)",
),
),
'SearchConfig' => Array (
'UniqueField' => 'TableName;FieldName;ModuleName',
'Records' => Array (
'Category;NewItem;In-Portal' => "('Category', 'NewItem', 0, 1, 'lu_fielddesc_category_newitem', 'lu_field_newitem', 'In-Portal', 'la_text_category', 18, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;PopItem;In-Portal' => "('Category', 'PopItem', 0, 1, 'lu_fielddesc_category_popitem', 'lu_field_popitem', 'In-Portal', 'la_text_category', 19, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;HotItem;In-Portal' => "('Category', 'HotItem', 0, 1, 'lu_fielddesc_category_hotitem', 'lu_field_hotitem', 'In-Portal', 'la_text_category', 17, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;MetaDescription;In-Portal' => "('Category', 'MetaDescription', 0, 1, 'lu_fielddesc_category_metadescription', 'lu_field_metadescription', 'In-Portal', 'la_text_category', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;ParentPath;In-Portal' => "('Category', 'ParentPath', 0, 1, 'lu_fielddesc_category_parentpath', 'lu_field_parentpath', 'In-Portal', 'la_text_category', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;ResourceId;In-Portal' => "('Category', 'ResourceId', 0, 1, 'lu_fielddesc_category_resourceid', 'lu_field_resourceid', 'In-Portal', 'la_text_category', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;CreatedById;In-Portal' => "('Category', 'CreatedById', 0, 1, 'lu_fielddesc_category_createdbyid', 'lu_field_createdbyid', 'In-Portal', 'la_text_category', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;CachedNavbar;In-Portal' => "('Category', 'CachedNavbar', 0, 1, 'lu_fielddesc_category_cachednavbar', 'lu_field_cachednavbar', 'In-Portal', 'la_text_category', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;CachedDescendantCatsQty;In-Portal' => "('Category', 'CachedDescendantCatsQty', 0, 1, 'lu_fielddesc_category_cacheddescendantcatsqty', 'lu_field_cacheddescendantcatsqty', 'In-Portal', 'la_text_category', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;MetaKeywords;In-Portal' => "('Category', 'MetaKeywords', 0, 1, 'lu_fielddesc_category_metakeywords', 'lu_field_metakeywords', 'In-Portal', 'la_text_category', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;Priority;In-Portal' => "('Category', 'Priority', 0, 1, 'lu_fielddesc_category_priority', 'lu_field_priority', 'In-Portal', 'la_text_category', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;Status;In-Portal' => "('Category', 'Status', 0, 1, 'lu_fielddesc_category_status', 'lu_field_status', 'In-Portal', 'la_text_category', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;EditorsPick;In-Portal' => "('Category', 'EditorsPick', 0, 1, 'lu_fielddesc_category_editorspick', 'lu_field_editorspick', 'In-Portal', 'la_text_category', 6, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;CreatedOn;In-Portal' => "('Category', 'CreatedOn', 0, 1, 'lu_fielddesc_category_createdon', 'lu_field_createdon', 'In-Portal', 'la_text_category', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;Description;In-Portal' => "('Category', 'Description', 1, 1, 'lu_fielddesc_category_description', 'lu_field_description', 'In-Portal', 'la_text_category', 4, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;Name;In-Portal' => "('Category', 'Name', 1, 1, 'lu_fielddesc_category_name', 'lu_field_name', 'In-Portal', 'la_text_category', 3, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;ParentId;In-Portal' => "('Category', 'ParentId', 0, 1, 'lu_fielddesc_category_parentid', 'lu_field_parentid', 'In-Portal', 'la_text_category', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;CategoryId;In-Portal' => "('Category', 'CategoryId', 0, 1, 'lu_fielddesc_category_categoryid', 'lu_field_categoryid', 'In-Portal', 'la_text_category', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;Modified;In-Portal' => "('Category', 'Modified', 0, 1, 'lu_fielddesc_category_modified', 'lu_field_modified', 'In-Portal', 'la_text_category', 20, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'Category;ModifiedById;In-Portal' => "('Category', 'ModifiedById', 0, 1, 'lu_fielddesc_category_modifiedbyid', 'lu_field_modifiedbyid', 'In-Portal', 'la_text_category', 21, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;PortalUserId;In-Portal' => "('PortalUser', 'PortalUserId', -1, 0, 'lu_fielddesc_user_portaluserid', 'lu_field_portaluserid', 'In-Portal', 'la_text_user', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Login;In-Portal' => "('PortalUser', 'Login', -1, 0, 'lu_fielddesc_user_login', 'lu_field_login', 'In-Portal', 'la_text_user', 1, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Password;In-Portal' => "('PortalUser', 'Password', -1, 0, 'lu_fielddesc_user_password', 'lu_field_password', 'In-Portal', 'la_text_user', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;tz;In-Portal' => "('PortalUser', 'tz', -1, 0, 'lu_fielddesc_user_tz', 'lu_field_tz', 'In-Portal', 'la_text_user', 17, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;dob;In-Portal' => "('PortalUser', 'dob', -1, 0, 'lu_fielddesc_user_dob', 'lu_field_dob', 'In-Portal', 'la_text_user', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Modified;In-Portal' => "('PortalUser', 'Modified', -1, 0, 'lu_fielddesc_user_modified', 'lu_field_modified', 'In-Portal', 'la_text_user', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Status;In-Portal' => "('PortalUser', 'Status', -1, 0, 'lu_fielddesc_user_status', 'lu_field_status', 'In-Portal', 'la_text_user', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;ResourceId;In-Portal' => "('PortalUser', 'ResourceId', -1, 0, 'lu_fielddesc_user_resourceid', 'lu_field_resourceid', 'In-Portal', 'la_text_user', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Country;In-Portal' => "('PortalUser', 'Country', -1, 0, 'lu_fielddesc_user_country', 'lu_field_country', 'In-Portal', 'la_text_user', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Zip;In-Portal' => "('PortalUser', 'Zip', -1, 0, 'lu_fielddesc_user_zip', 'lu_field_zip', 'In-Portal', 'la_text_user', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;State;In-Portal' => "('PortalUser', 'State', -1, 0, 'lu_fielddesc_user_state', 'lu_field_state', 'In-Portal', 'la_text_user', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;City;In-Portal' => "('PortalUser', 'City', -1, 0, 'lu_fielddesc_user_city', 'lu_field_city', 'In-Portal', 'la_text_user', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Street;In-Portal' => "('PortalUser', 'Street', -1, 0, 'lu_fielddesc_user_street', 'lu_field_street', 'In-Portal', 'la_text_user', 8, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Phone;In-Portal' => "('PortalUser', 'Phone', -1, 0, 'lu_fielddesc_user_phone', 'lu_field_phone', 'In-Portal', 'la_text_user', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;CreatedOn;In-Portal' => "('PortalUser', 'CreatedOn', -1, 0, 'lu_fielddesc_user_createdon', 'lu_field_createdon', 'In-Portal', 'la_text_user', 6, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;Email;In-Portal' => "('PortalUser', 'Email', -1, 0, 'lu_fielddesc_user_email', 'lu_field_email', 'In-Portal', 'la_text_user', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;LastName;In-Portal' => "('PortalUser', 'LastName', -1, 0, 'lu_fielddesc_user_lastname', 'lu_field_lastname', 'In-Portal', 'la_text_user', 4, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
'PortalUser;FirstName;In-Portal' => "('PortalUser', 'FirstName', -1, 0, 'lu_fielddesc_user_firstname', 'lu_field_firstname', 'In-Portal', 'la_text_user', 3, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
),
),
'StatItem' => Array (
'UniqueField' => 'Module;ListLabel',
'Records' => Array (
'In-Portal;la_prompt_ActiveCategories' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>Category WHERE Status=1 ', NULL, 'la_prompt_ActiveCategories', '0', '1')",
'In-Portal;la_prompt_ActiveUsers' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>PortalUser WHERE Status=1 ', NULL, 'la_prompt_ActiveUsers', '0', '1')",
'In-Portal;la_prompt_CurrentSessions' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>UserSession', NULL, 'la_prompt_CurrentSessions', '0', '1')",
'In-Portal;la_prompt_TotalCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) as CategoryCount FROM <%prefix%>Category', NULL, 'la_prompt_TotalCategories', 0, 2)",
'In-Portal;la_prompt_ActiveCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveCategories FROM <%prefix%>Category WHERE Status = 1', NULL, 'la_prompt_ActiveCategories', 0, 2)",
'In-Portal;la_prompt_PendingCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingCategories FROM <%prefix%>Category WHERE Status = 2', NULL, 'la_prompt_PendingCategories', 0, 2)",
'In-Portal;la_prompt_DisabledCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledCategories FROM <%prefix%>Category WHERE Status = 0', NULL, 'la_prompt_DisabledCategories', 0, 2)",
'In-Portal;la_prompt_NewCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NewCategories FROM <%prefix%>Category WHERE (NewItem = 1) OR ( (UNIX_TIMESTAMP() - CreatedOn) <= <%m:config name=\"Category_DaysNew\"%>*86400 AND (NewItem = 2) )', NULL, 'la_prompt_NewCategories', 0, 2)",
'In-Portal;la_prompt_CategoryEditorsPick' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) FROM <%prefix%>Category WHERE EditorsPick = 1', NULL, 'la_prompt_CategoryEditorsPick', 0, 2)",
'In-Portal;la_prompt_NewestCategoryDate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(CreatedOn)\" type=\"date\"%> FROM <%prefix%>Category', NULL, 'la_prompt_NewestCategoryDate', 0, 2)",
'In-Portal;la_prompt_LastCategoryUpdate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(Modified)\" type=\"date\"%> FROM <%prefix%>Category', NULL, 'la_prompt_LastCategoryUpdate', 0, 2)",
'In-Portal;la_prompt_TopicsUsers' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUsers FROM <%prefix%>PortalUser', NULL, 'la_prompt_TopicsUsers', 0, 2)",
'In-Portal;la_prompt_UsersActive' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveUsers FROM <%prefix%>PortalUser WHERE Status = 1', NULL, 'la_prompt_UsersActive', 0, 2)",
'In-Portal;la_prompt_UsersPending' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingUsers FROM <%prefix%>PortalUser WHERE Status = 2', NULL, 'la_prompt_UsersPending', 0, 2)",
'In-Portal;la_prompt_UsersDisabled' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledUsers FROM <%prefix%>PortalUser WHERE Status = 0', NULL, 'la_prompt_UsersDisabled', 0, 2)",
'In-Portal;la_prompt_NewestUserDate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(CreatedOn)\" type=\"date\"%> FROM <%prefix%>PortalUser', NULL, 'la_prompt_NewestUserDate', 0, 2)",
'In-Portal;la_prompt_UsersUniqueCountries' => "(DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( Country ) ) FROM <%prefix%>PortalUser WHERE LENGTH(Country) > 0', NULL, 'la_prompt_UsersUniqueCountries', 0, 2)",
'In-Portal;la_prompt_UsersUniqueStates' => "(DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( State ) ) FROM <%prefix%>PortalUser WHERE LENGTH(State) > 0', NULL, 'la_prompt_UsersUniqueStates', 0, 2)",
'In-Portal;la_prompt_TotalUserGroups' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUserGroups FROM <%prefix%>PortalGroup', NULL, 'la_prompt_TotalUserGroups', 0, 2)",
'In-Portal;la_prompt_BannedUsers' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS BannedUsers FROM <%prefix%>PortalUser WHERE IsBanned = 1', NULL, 'la_prompt_BannedUsers', 0, 2)",
'In-Portal;la_prompt_NonExpiredSessions' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NonExipedSessions FROM <%prefix%>UserSession WHERE Status = 1', NULL, 'la_prompt_NonExpiredSessions', 0, 2)",
'In-Portal;la_prompt_ThemeCount' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ThemeCount FROM <%prefix%>Theme', NULL, 'la_prompt_ThemeCount', 0, 2)",
'In-Portal;la_prompt_RegionsCount' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS RegionsCount FROM <%prefix%>Language', NULL, 'la_prompt_RegionsCount', 0, 2)",
'In-Portal;la_prompt_TablesCount' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLES\" action=\"COUNT\" field=\"*\"%>', NULL, 'la_prompt_TablesCount', 0, 2)",
'In-Portal;la_prompt_RecordsCount' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLE+STATUS\" action=\"SUM\" field=\"Rows\"%>', NULL, 'la_prompt_RecordsCount', 0, 2)",
'In-Portal;la_prompt_SystemFileSize' => "(DEFAULT, 'In-Portal', '<%m:custom_action sql=\"empty\" action=\"SysFileSize\"%>', NULL, 'la_prompt_SystemFileSize', 0, 2)",
'In-Portal;la_prompt_DataSize' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLE+STATUS\" action=\"SUM\" format_as=\"file\" field=\"Data_length\"%>', NULL, 'la_prompt_DataSize', 0, 2)",
),
),
'StylesheetSelectors' => Array (
'UniqueField' => 'SelectorId',
'Records' => Array (
'169' => "(169, 8, 'Calendar''s selected days', '.calendar tbody .selected', 'a:0:{}', '', 1, 'font-weight: bold;\\\r\\nbackground-color: #9ED7ED;\\r\\nborder: 1px solid #83B2C5;', 0)",
'118' => "(118, 8, 'Data grid row', 'td.block-data-row', 'a:0:{}', '', 2, 'border-bottom: 1px solid #cccccc;', 48)",
'81' => "(81, 8, '\"More\" link', 'a.link-more', 'a:0:{}', '', 2, 'text-decoration: underline;', 64)",
'88' => "(88, 8, 'Block data, separated rows', 'td.block-data-grid', 'a:0:{}', '', 2, 'border: 1px solid #cccccc;', 48)",
'42' => "(42, 8, 'Block Header', 'td.block-header', 'a:4:{s:5:\"color\";s:16:\"rgb(0, 159, 240)\";s:9:\"font-size\";s:4:\"20px\";s:11:\"font-weight\";s:6:\"normal\";s:7:\"padding\";s:3:\"5px\";}', 'Block Header', 1, 'font-family: Verdana, Helvetica, sans-serif;', 0)",
'76' => "(76, 8, 'Navigation bar menu', 'tr.head-nav td', 'a:0:{}', '', 1, 'vertical-align: middle;', 0)",
'48' => "(48, 8, 'Block data', 'td.block-data', 'a:2:{s:9:\"font-size\";s:5:\"12px;\";s:7:\"padding\";s:3:\"5px\";}', '', 1, '', 0)",
'78' => "(78, 8, 'Body main style', 'body', 'a:0:{}', '', 1, 'padding: 0px; \\r\\nbackground-color: #ffffff; \\r\\nfont-family: arial, verdana, helvetica; \\r\\nfont-size: small;\\r\\nwidth: auto;\\r\\nmargin: 0px;', 0)",
'58' => "(58, 8, 'Main table', 'table.main-table', 'a:0:{}', '', 1, 'width: 770px;\\r\\nmargin: 0px;\\r\\n/*table-layout: fixed;*/', 0)",
'79' => "(79, 8, 'Block: header of data block', 'span.block-data-grid-header', 'a:0:{}', '', 1, 'font-family: Arial, Helvetica, sans-serif;\\r\\ncolor: #009DF6;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\nbackground-color: #E6EEFF;\\r\\npadding: 6px;\\r\\nwhite-space: nowrap;', 0)",
'64' => "(64, 8, 'Link', 'a', 'a:0:{}', '', 1, '', 0)",
'46' => "(46, 8, 'Product title link', 'a.link-product1', 'a:0:{}', 'Product title link', 1, 'color: #62A1DE;\\r\\nfont-size: 14px;\\r\\nfont-weight: bold;\\r\\nline-height: 20px;\\r\\npadding-bottom: 10px;', 0)",
'75' => "(75, 8, 'Copy of Main path link', 'table.main-path td a:hover', 'a:0:{}', '', 1, 'color: #ffffff;', 0)",
'160' => "(160, 8, 'Current item in navigation bar', '.checkout-step-current', 'a:0:{}', '', 1, 'color: #A20303;\\r\\nfont-weight: bold;', 0)",
'51' => "(51, 8, 'Right block data', 'td.right-block-data', 'a:1:{s:9:\"font-size\";s:4:\"11px\";}', '', 2, 'padding: 7px;\\r\\nbackground: #e3edf6 url(\"/in-commerce4/themes/default/img/bgr_login.jpg\") repeat-y scroll left top;\\r\\nborder-bottom: 1px solid #64a1df;', 48)",
'67' => "(67, 8, 'Pagination bar: text', 'table.block-pagination td', 'a:3:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"12px\";s:11:\"font-weight\";s:6:\"normal\";}', '', 1, '', 0)",
'45' => "(45, 8, 'Category link', 'a.subcat', 'a:0:{}', 'Category link', 1, 'color: #2069A4', 0)",
'68' => "(68, 8, 'Pagination bar: link', 'table.block-pagination td a', 'a:3:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:5:\"12px;\";s:11:\"font-weight\";s:6:\"normal\";}', '', 1, '', 0)",
'69' => "(69, 8, 'Product description in product list', '.product-list-description', 'a:2:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"12px\";}', '', 1, '', 0)",
'73' => "(73, 8, 'Main path link', 'table.main-path td a', 'a:0:{}', '', 1, 'color: #d5e231;', 0)",
'83' => "(83, 8, 'Product title link in list (shopping cart)', 'a.link-product-cart', 'a:0:{}', 'Product title link', 1, 'color: #18559C;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)",
'72' => "(72, 8, 'Main path block text', 'table.main-path td', 'a:0:{}', '', 1, 'color: #ffffff;\\r\\nfont-size: 10px;\\r\\nfont-weight: normal;\\r\\npadding: 1px;\\r\\n', 0)",
'61' => "(61, 8, 'Block: header of data table', 'td.block-data-grid-header', 'a:6:{s:4:\"font\";s:28:\"Arial, Helvetica, sans-serif\";s:5:\"color\";s:7:\"#009DF6\";s:9:\"font-size\";s:4:\"12px\";s:11:\"font-weight\";s:4:\"bold\";s:16:\"background-color\";s:7:\"#E6EEFF\";s:7:\"padding\";s:3:\"6px\";}', '', 1, 'white-space: nowrap;\\r\\npadding-left: 10px;\\r\\n/*\\r\\nbackground-image: url(/in-commerce4/themes/default/img/bullet1.gif);\\r\\nbackground-position: 10px 12px;\\r\\nbackground-repeat: no-repeat;\\r\\n*/', 0)",
'65' => "(65, 8, 'Link in product list additional row', 'td.product-list-additional a', 'a:1:{s:5:\"color\";s:7:\"#8B898B\";}', '', 2, '', 64)",
'55' => "(55, 8, 'Main table, left column', 'td.main-column-left', 'a:0:{}', '', 1, 'width:180px;\\r\\nborder: 1px solid #62A1DE;\\r\\nborder-top: 0px;', 0)",
'70' => "(70, 8, 'Product title link in list (category)', 'a.link-product-category', 'a:0:{}', 'Product title link', 1, 'color: #18559C;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)",
'66' => "(66, 8, 'Pagination bar block', 'table.block-pagination', 'a:0:{}', '', 1, '', 0)",
'49' => "(49, 8, 'Bulleted list inside block', 'td.block-data ul li', 'a:0:{}', '', 1, ' list-style-image: url(/in-commerce4/themes/default/img/bullet2.gif);\\r\\n margin-bottom: 10px;\\r\\n font-size: 11px;', 0)",
'87' => "(87, 8, 'Cart item input form element', 'td.cart-item-atributes input', 'a:0:{}', '', 1, 'border: 1px solid #7BB2E6;', 0)",
'119' => "(119, 8, 'Data grid row header', 'td.block-data-row-hdr', 'a:0:{}', 'Used in order preview', 2, 'background-color: #eeeeee;\\r\\nborder-bottom: 1px solid #dddddd;\\r\\nborder-top: 1px solid #cccccc;\\r\\nfont-weight: bold;', 48)",
'82' => "(82, 8, '\"More\" link image', 'a.link-more img', 'a:0:{}', '', 2, 'text-decoration: none;\\r\\npadding-left: 5px;', 64)",
'63' => "(63, 8, 'Additional info under product description in list', 'td.product-list-additional', 'a:5:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"11px\";s:11:\"font-weight\";s:6:\"normal\";s:10:\"border-top\";s:18:\"1px dashed #8B898B\";s:13:\"border-bottom\";s:18:\"1px dashed #8B898B\";}', '', 2, '', 48)",
'43' => "(43, 8, 'Block', 'table.block', 'a:2:{s:16:\"background-color\";s:7:\"#E3EEF9\";s:6:\"border\";s:17:\"1px solid #64A1DF\";}', 'Block', 1, 'border: 0; \\r\\nmargin-bottom: 1px;\\r\\nwidth: 100%;', 0)",
'84' => "(84, 8, 'Cart item cell', 'td.cart-item', 'a:0:{}', '', 1, 'background-color: #F6FAFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 4px;', 0)",
'57' => "(57, 8, 'Main table, right column', 'td.main-column-right', 'a:0:{}', '', 1, 'width:220px;\\r\\nborder: 1px solid #62A1DE;\\r\\nborder-top: 0px;', 0)",
'161' => "(161, 8, 'Block for sub categories', 'td.block-data-subcats', 'a:0:{}', '', 2, ' background: #FFFFFF\\r\\nurl(/in-commerce4/themes/default/in-commerce/img/bgr_categories.jpg);\\r\\n background-repeat: no-repeat;\\r\\n background-position: top right;\\r\\nborder-bottom: 5px solid #DEEAFF;\\r\\npadding-left: 10px;', 48)",
'77' => "(77, 8, 'Left block header', 'td.left-block-header', 'a:0:{}', '', 2, 'font-family : verdana, helvetica, sans-serif;\\r\\ncolor : #ffffff;\\r\\nfont-size : 12px;\\r\\nfont-weight : bold;\\r\\ntext-decoration : none;\\r\\nbackground-color: #64a1df;\\r\\npadding: 5px;\\r\\npadding-left: 7px;', 42)",
'80' => "(80, 8, 'Right block data - text', 'td.right-block-data td', 'a:1:{s:9:\"font-size\";s:5:\"11px;\";}', '', 2, '', 48)",
'53' => "(53, 8, 'Right block header', 'td.right-block-header', 'a:0:{}', '', 2, 'font-family : verdana, helvetica, sans-serif;\\r\\ncolor : #ffffff;\\r\\nfont-size : 12px;\\r\\nfont-weight : bold;\\r\\ntext-decoration : none;\\r\\nbackground-color: #64a1df;\\r\\npadding: 5px;\\r\\npadding-left: 7px;', 42)",
'85' => "(85, 8, 'Cart item cell with attributes', 'td.cart-item-attributes', 'a:0:{}', '', 1, 'background-color: #E6EEFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 4px;\\r\\ntext-align: center;\\r\\nvertical-align: middle;\\r\\nfont-size: 12px;\\r\\nfont-weight: normal;', 0)",
'86' => "(86, 8, 'Cart item cell with name', 'td.cart-item-name', 'a:0:{}', '', 1, 'background-color: #F6FAFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 3px;', 0)",
'47' => "(47, 8, 'Block content of featured product', 'td.featured-block-data', 'a:0:{}', '', 1, 'font-family: Arial,Helvetica,sans-serif;\\r\\nfont-size: 12px;', 0)",
'56' => "(56, 8, 'Main table, middle column', 'td.main-column-center', 'a:0:{}', '', 1, '\\r\\n', 0)",
'50' => "(50, 8, 'Product title link in list', 'a.link-product2', 'a:0:{}', 'Product title link', 1, 'color: #62A1DE;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)",
'71' => "(71, 8, 'Main path block', 'table.main-path', 'a:0:{}', '', 1, 'background: #61b0ec url(\"/in-commerce4/themes/default/img/bgr_path.jpg\") repeat-y scroll left top;\\r\\nwidth: 100%;\\r\\nmargin-bottom: 1px;\\r\\nmargin-right: 1px; \\r\\nmargin-left: 1px;', 0)",
'62' => "(62, 8, 'Block: columns header for data table', 'table.block-no-border th', 'a:6:{s:4:\"font\";s:28:\"Arial, Helvetica, sans-serif\";s:5:\"color\";s:7:\"#18559C\";s:9:\"font-size\";s:4:\"11px\";s:11:\"font-weight\";s:4:\"bold\";s:16:\"background-color\";s:7:\"#B4D2EE\";s:7:\"padding\";s:3:\"6px\";}', '', 1, 'text-align: left;', 0)",
'59' => "(59, 8, 'Block without border', 'table.block-no-border', 'a:0:{}', '', 1, 'border: 0px; \\r\\nmargin-bottom: 10px;\\r\\nwidth: 100%;', 0)",
'74' => "(74, 8, 'Main path language selector cell', 'td.main-path-language', 'a:0:{}', '', 1, 'vertical-align: middle;\\r\\ntext-align: right;\\r\\npadding-right: 6px;', 0)",
'171' => "(171, 8, 'Calendar''s highlighted day', '.calendar tbody .hilite', 'a:0:{}', '', 1, 'background-color: #f6f6f6;\\r\\nborder: 1px solid #83B2C5 !important;', 0)",
'175' => "(175, 8, 'Calendar''s days', '.calendar tbody .day', 'a:0:{}', '', 1, 'text-align: right;\\r\\npadding: 2px 4px 2px 2px;\\r\\nwidth: 2em;\\r\\nborder: 1px solid #fefefe;', 0)",
'170' => "(170, 8, 'Calendar''s weekends', '.calendar .weekend', 'a:0:{}', '', 1, 'color: #990000;', 0)",
'173' => "(173, 8, 'Calendar''s control buttons', '.calendar .calendar_button', 'a:0:{}', '', 1, 'color: black;\\r\\nfont-size: 12px;\\r\\nbackground-color: #eeeeee;', 0)",
'174' => "(174, 8, 'Calendar''s day names', '.calendar thead .name', 'a:0:{}', '', 1, 'background-color: #DEEEF6;\\r\\nborder-bottom: 1px solid #000000;', 0)",
'172' => "(172, 8, 'Calendar''s top and bottom titles', '.calendar .title', 'a:0:{}', '', 1, 'color: #FFFFFF;\\r\\nbackground-color: #62A1DE;\\r\\nborder: 1px solid #107DC5;\\r\\nborder-top: 0px;\\r\\npadding: 1px;', 0)",
'60' => "(60, 8, 'Block header for featured product', 'td.featured-block-header', 'a:0:{}', '', 2, '\\r\\n', 42)",
'54' => "(54, 8, 'Right block', 'table.right-block', 'a:0:{}', '', 2, 'background-color: #E3EEF9;\\r\\nborder: 0px;\\r\\nwidth: 100%;', 43)",
'44' => "(44, 8, 'Block content', 'td.block-data-big', 'a:0:{}', 'Block content', 1, ' background: #DEEEF6\\r\\nurl(/in-commerce4/themes/default/img/menu_bg.gif);\\r\\n background-repeat: no-repeat;\\r\\n background-position: top right;\\r\\n', 0)",
),
),
'Stylesheets' => Array (
'UniqueField' => 'StylesheetId',
'Records' => Array (
'8' => "(8, 'Default', 'In-Portal Default Theme', '', 1124952555, 1)",
),
),
'Counters' => Array (
'UniqueField' => 'Name',
'Records' => Array (
'members_count' => "(DEFAULT, 'members_count', 'SELECT COUNT(*) FROM <%PREFIX%>PortalUser WHERE Status = 1', NULL , NULL , '3600', '0', '|PortalUser|')",
'members_online' => "(DEFAULT, 'members_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession WHERE PortalUserId > 0', NULL , NULL , '3600', '0', '|UserSession|')",
'guests_online' => "(DEFAULT, 'guests_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession WHERE PortalUserId <= 0', NULL , NULL , '3600', '0', '|UserSession|')",
'users_online' => "(DEFAULT, 'users_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession', NULL , NULL , '3600', '0', '|UserSession|')",
),
),
);
// check & insert if not found defined before data
foreach ($data as $table_name => $table_info) {
$unique_fields = explode(';', $table_info['UniqueField']);
foreach ($table_info['Records'] as $unique_value => $insert_sql) {
$unique_values = explode(';', $unique_value);
$where_clause = Array ();
foreach ($unique_fields as $field_index => $unique_field) {
$where_clause[] = $unique_field . ' = ' . $this->Conn->qstr($unique_values[$field_index]);
}
$sql = 'SELECT ' . implode(', ', $unique_fields) . '
FROM ' . TABLE_PREFIX . $table_name . '
WHERE (' . implode(') AND (', $where_clause) . ')';
$found = $this->Conn->GetRow($sql);
if ($found) {
$found = implode(';', $found);
}
if ($found != $unique_value) {
$this->Conn->Query('INSERT INTO ' . TABLE_PREFIX . $table_name . ' VALUES ' . $insert_sql);
}
}
}
}
/**
* Removes duplicate phrases per language basis (created during proj-base and in-portal shared installation)
*
*/
function _removeDuplicatePhrases()
{
$id_field = $this->Application->getUnitOption('phrases', 'IDField');
$table_name = $this->Application->getUnitOption('phrases', 'TableName');
$sql = 'SELECT LanguageId, Phrase, MIN(LastChanged) AS LastChanged, COUNT(*) AS DupeCount
FROM ' . $table_name . '
GROUP BY LanguageId, Phrase
HAVING COUNT(*) > 1';
$duplicate_phrases = $this->Conn->Query($sql);
foreach ($duplicate_phrases as $phrase_record) {
// 1. keep phrase, that was added first, because it is selected in PhrasesCache::LoadPhraseByLabel
$where_clause = Array (
'LanguageId = ' . $phrase_record['LanguageId'],
'Phrase = ' . $this->Conn->qstr($phrase_record['Phrase']),
'LastChanged' . ' = ' . $phrase_record['LastChanged'],
);
$sql = 'SELECT ' . $id_field . '
FROM ' . $table_name . '
WHERE (' . implode(') AND (', $where_clause) . ')';
$phrase_id = $this->Conn->GetOne($sql);
// 2. delete all other duplicates
$where_clause = Array (
'LanguageId = ' . $phrase_record['LanguageId'],
'Phrase = ' . $this->Conn->qstr($phrase_record['Phrase']),
$id_field . ' <> ' . $phrase_id,
);
$sql = 'DELETE FROM ' . $table_name . '
WHERE (' . implode(') AND (', $where_clause) . ')';
$this->Conn->Query($sql);
}
}
function _moveDatabaseFolders()
{
// Tables: PageContent, Images
if ($this->Conn->TableFound('PageContent', true)) {
// 1. replaces "/kernel/user_files/" references in content blocks
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$languages = $ml_helper->getLanguages();
$replace_sql = '%1$s = REPLACE(%1$s, "/kernel/user_files/", "/system/user_files/")';
$update_sqls = Array ();
foreach ($languages as $language_id) {
$update_sqls[] = sprintf($replace_sql, 'l' . $language_id . '_Content');
}
if ($update_sqls) {
$sql = 'UPDATE ' . TABLE_PREFIX . 'PageContent
SET ' . implode(', ', $update_sqls);
$this->Conn->Query($sql);
}
}
// 2. replace path of images uploaded via "Images" tab of category items
$this->_replaceImageFolder('/kernel/images/', '/system/images/');
// 3. replace path of images uploaded via "Images" tab of category items (when badly formatted)
$this->_replaceImageFolder('kernel/images/', 'system/images/');
// 4. replace images uploaded via "In-Bulletin -> Emoticons" section
$this->_replaceImageFolder('in-bulletin/images/emoticons/', 'system/images/emoticons/');
// 5. update backup path in config
$this->_toolkit->saveConfigValues(
Array (
'Backup_Path' => FULL_PATH . '/system/backupdata'
)
);
}
/**
* Replaces mentions of "/kernel/images" folder in Images table
*
* @param string $from
* @param string $to
*/
function _replaceImageFolder($from, $to)
{
$replace_sql = '%1$s = REPLACE(%1$s, "' . $from . '", "' . $to . '")';
$sql = 'UPDATE ' . TABLE_PREFIX . 'Images
SET ' . sprintf($replace_sql, 'ThumbPath') . ', ' . sprintf($replace_sql, 'LocalPath');
$this->Conn->Query($sql);
}
/**
* Update colors in skin (only if they were not changed manually)
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_0_0($mode)
{
if ($mode == 'before') {
$this->_removeDuplicatePhrases(); // because In-Commerce & In-Link share some phrases with Proj-CMS
$this->_createProjCMSTables();
$this->_addMissingConfigurationVariables();
}
if ($mode == 'after') {
$this->_fixSkinColors();
$this->_restructureCatalog();
$this->_sortImages();
// $this->_sortConfigurationVariables('In-Portal', 'in-portal:configure_general');
// $this->_sortConfigurationVariables('In-Portal', 'in-portal:configure_advanced');
}
}
function _sortConfigurationVariables($module, $section)
{
$sql = 'SELECT ca.heading, cv.VariableName
FROM ' . TABLE_PREFIX . 'ConfigurationAdmin ca
LEFT JOIN ' . TABLE_PREFIX . 'ConfigurationValues cv USING(VariableName)
WHERE (cv.ModuleOwner = ' . $this->Conn->qstr($module) . ') AND (cv.Section = ' . $this->Conn->qstr($section) . ')
ORDER BY ca.DisplayOrder asc, ca.GroupDisplayOrder asc';
$variables = $this->Conn->GetCol($sql, 'VariableName');
if (!$variables) {
return ;
}
$variables = $this->_groupRecords($variables);
$group_number = 0;
$variable_order = 1;
$prev_heading = '';
foreach ($variables as $variable_name => $variable_heading) {
if ($prev_heading != $variable_heading) {
$group_number++;
$variable_order = 1;
}
$sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationAdmin
SET DisplayOrder = ' . $this->Conn->qstr($group_number * 10 + $variable_order / 100) . '
WHERE VariableName = ' . $this->Conn->qstr($variable_name);
$this->Conn->Query($sql);
$variable_order++;
$prev_heading = $variable_heading;
}
}
/**
* Group list records by header, saves internal order in group
*
* @param Array $variables
* @return Array
*/
function _groupRecords($variables)
{
$sorted = Array();
foreach ($variables as $variable_name => $variable_heading) {
$sorted[$variable_heading][] = $variable_name;
}
$variables = Array();
foreach ($sorted as $heading => $heading_records) {
foreach ($heading_records as $variable_name) {
$variables[$variable_name] = $heading;
}
}
return $variables;
}
/**
* Returns module root category
*
* @param string $module_name
* @param string $module_prefix
* @return int
*/
function _getRootCategory($module_name, $module_prefix)
{
// don't cache anything here (like in static variables), because database value is changed on the fly !!!
$sql = 'SELECT RootCat
FROM ' . TABLE_PREFIX . 'Modules
WHERE LOWER(Name) = ' . $this->Conn->qstr( strtolower($module_name) );
$root_category = $this->Conn->GetOne($sql);
// put to cache too, because CategoriesEventHandler::_prepareAutoPage uses kApplication::findModule
$this->Application->ModuleInfo[$module_name]['Name'] = $module_name;
$this->Application->ModuleInfo[$module_name]['RootCat'] = $root_category;
$this->Application->ModuleInfo[$module_name]['Var'] = $module_prefix;
return $root_category;
}
/**
* Move all categories (except "Content") from "Home" to "Content" category and hide them from menu
*
*/
function _restructureCatalog()
{
$root_category = $this->_getRootCategory('Core', 'adm');
$sql = 'SELECT CategoryId
FROM ' . TABLE_PREFIX . 'Category
WHERE ParentId = 0 AND CategoryId <> ' . $root_category;
$top_categories = $this->Conn->GetCol($sql);
if ($top_categories) {
// hide all categories located outside "Content" category from menu
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET IsMenu = 0
WHERE (ParentPath LIKE "|' . implode('|%") OR (ParentPath LIKE "|', $top_categories) . '|%")';
$this->Conn->Query($sql);
// move all top level categories under "Content" category and make them visible in menu
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET IsMenu = 1, ParentId = ' . $root_category . '
WHERE ParentId = 0 AND CategoryId <> ' . $root_category;
$this->Conn->Query($sql);
}
// make sure, that all categories have valid value for Priority field
$priority_helper = $this->Application->recallObject('PriorityHelper');
/* @var $priority_helper kPriorityHelper */
$event = new kEvent('c:OnListBuild');
// update all categories, because they are all under "Content" category now
$sql = 'SELECT CategoryId
FROM ' . TABLE_PREFIX . 'Category';
$categories = $this->Conn->GetCol($sql);
foreach ($categories as $category_id) {
$priority_helper->recalculatePriorities($event, 'ParentId = ' . $category_id);
}
// create initial theme structure in Category table
$this->_toolkit->rebuildThemes();
// make sure, that all system templates have ThemeId set (only possible during platform project upgrade)
$sql = 'SELECT ThemeId
FROM ' . TABLE_PREFIX . 'Theme
WHERE PrimaryTheme = 1';
$primary_theme_id = $this->Conn->GetOne($sql);
if ($primary_theme_id) {
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET ThemeId = ' . $primary_theme_id . '
WHERE IsSystem = 1 AND ThemeId = 0';
$this->Conn->Query($sql);
}
}
/**
* Changes skin colors to match new ones (only in case, when they match default values)
*
*/
function _fixSkinColors()
{
$skin = $this->Application->recallObject('skin', null, Array ('skip_autoload' => 1));
/* @var $skin kDBItem */
$skin->Load(1, 'IsPrimary');
if ($skin->isLoaded()) {
$skin_options = unserialize( $skin->GetDBField('Options') );
$changes = Array (
// option: from -> to
'HeadBgColor' => Array ('#1961B8', '#007BF4'),
'HeadBarColor' => Array ('#FFFFFF', '#000000'),
'HeadColor' => Array ('#CCFF00', '#FFFFFF'),
'TreeColor' => Array ('#006F99', '#000000'),
'TreeHoverColor' => Array ('', '#009FF0'),
'TreeHighHoverColor' => Array ('', '#FFFFFF'),
'TreeHighBgColor' => Array ('#4A92CE', '#4A92CE'),
'TreeBgColor' => Array ('#FFFFFF', '#DCECF6'),
);
$can_change = true;
foreach ($changes as $option_name => $change) {
list ($change_from, $change_to) = $change;
$can_change = $can_change && ($change_from == $skin_options[$option_name]['Value']);
if ($can_change) {
$skin_options[$option_name]['Value'] = $change_to;
}
}
if ($can_change) {
$skin->SetDBField('Options', serialize($skin_options));
$skin->Update();
$skin_helper = $this->Application->recallObject('SkinHelper');
/* @var $skin_helper SkinHelper */
$skin_file = $skin_helper->getSkinPath();
if (file_exists($skin_file)) {
unlink($skin_file);
}
}
}
}
/**
* 1. Set root category not to generate filename automatically and hide it from catalog
* 2. Hide root category of In-Edit and set it's fields
*
* @param int $category_id
*/
function _resetRootCategory($category_id)
{
$fields_hash = Array (
'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0,
'l1_Description' => 'Content', 'Status' => 4,
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Category', 'CategoryId = ' . $category_id);
}
function _createProjCMSTables()
{
// 0. make sure, that Content category exists
$root_category = $this->_getRootCategory('Proj-CMS', 'st');
if ($root_category) {
// proj-cms module found -> remove it
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Modules
WHERE Name = "Proj-CMS"';
$this->Conn->Query($sql);
unset($this->Application->ModuleInfo['Proj-CMS']);
$this->_resetRootCategory($root_category);
// unhide all structure categories
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET Status = 1
WHERE (Status = 4) AND (CategoryId <> ' . $root_category . ')';
$this->Conn->Query($sql);
} else {
$root_category = $this->_getRootCategory('In-Edit', 'cms');
if ($root_category) {
// in-edit module found -> remove it
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Modules
WHERE Name = "In-Edit"';
$this->Conn->Query($sql);
unset($this->Application->ModuleInfo['In-Edit']);
$this->_resetRootCategory($root_category);
}
}
if (!$root_category) {
// create "Content" category when Proj-CMS/In-Edit module was not installed before
// use direct sql here, because category table structure doesn't yet match table structure in object
$fields_hash = Array (
'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0,
'l1_Description' => 'Content', 'Status' => 4,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Category');
$root_category = $this->Conn->getInsertID();
}
$this->_toolkit->deleteCache();
$this->_toolkit->SetModuleRootCategory('Core', $root_category);
// 1. process "Category" table
$structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'Category', 'Field');
if (!array_key_exists('Template', $structure)) {
// fields from "Pages" table were not added to "Category" table (like before "Proj-CMS" module install)
$sql = "ALTER TABLE " . TABLE_PREFIX . "Category
ADD COLUMN Template varchar(255) default NULL,
ADD COLUMN l1_Title varchar(255) default '',
ADD COLUMN l2_Title varchar(255) default '',
ADD COLUMN l3_Title varchar(255) default '',
ADD COLUMN l4_Title varchar(255) default '',
ADD COLUMN l5_Title varchar(255) default '',
ADD COLUMN l1_MenuTitle varchar(255) NOT NULL default '',
ADD COLUMN l2_MenuTitle varchar(255) NOT NULL default '',
ADD COLUMN l3_MenuTitle varchar(255) NOT NULL default '',
ADD COLUMN l4_MenuTitle varchar(255) NOT NULL default '',
ADD COLUMN l5_MenuTitle varchar(255) NOT NULL default '',
ADD COLUMN MetaTitle text,
ADD COLUMN IndexTools text,
ADD COLUMN IsIndex tinyint(1) NOT NULL default '0',
ADD COLUMN IsMenu TINYINT(4) NOT NULL DEFAULT '1',
ADD COLUMN IsSystem tinyint(4) NOT NULL default '0',
ADD COLUMN FormId int(11) default NULL,
ADD COLUMN FormSubmittedTemplate varchar(255) default NULL,
ADD COLUMN l1_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l2_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l3_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l4_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l5_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN FriendlyURL varchar(255) NOT NULL default '',
ADD INDEX IsIndex (IsIndex),
ADD INDEX l1_Translated (l1_Translated),
ADD INDEX l2_Translated (l2_Translated),
ADD INDEX l3_Translated (l3_Translated),
ADD INDEX l4_Translated (l4_Translated),
ADD INDEX l5_Translated (l5_Translated)";
$this->Conn->Query($sql);
}
if (array_key_exists('Path', $structure)) {
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Category
DROP Path';
$this->Conn->Query($sql);
}
// 2. process "PageContent" table
if ($this->Conn->TableFound(TABLE_PREFIX . 'PageContent', true)) {
$structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'PageContent', 'Field');
if (!array_key_exists('l1_Translated', $structure)) {
$sql = "ALTER TABLE " . TABLE_PREFIX . "PageContent
ADD COLUMN l1_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l2_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l3_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l4_Translated tinyint(4) NOT NULL default '0',
ADD COLUMN l5_Translated tinyint(4) NOT NULL default '0'";
$this->Conn->Query($sql);
}
}
// 3. process "FormFields" table
if ($this->Conn->TableFound(TABLE_PREFIX . 'FormFields', true)) {
$structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'FormFields', 'Field');
if (!$structure['FormId']['Key']) {
$sql = "ALTER TABLE " . TABLE_PREFIX . "FormFields
CHANGE Validation Validation TINYINT NOT NULL DEFAULT '0',
ADD INDEX FormId (FormId),
ADD INDEX Priority (Priority),
ADD INDEX IsSystem (IsSystem),
ADD INDEX DisplayInGrid (DisplayInGrid)";
$this->Conn->Query($sql);
}
}
else {
$this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.view', 11, 1, 1, 0)");
$this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.add', 11, 1, 1, 0)");
$this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.edit', 11, 1, 1, 0)");
$this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.delete', 11, 1, 1, 0)");
}
// 4. process "FormSubmissions" table
if ($this->Conn->TableFound(TABLE_PREFIX . 'FormSubmissions', true)) {
$structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'FormSubmissions', 'Field');
if (!$structure['SubmissionTime']['Key']) {
$sql = "ALTER TABLE " . TABLE_PREFIX . "FormSubmissions
ADD INDEX SubmissionTime (SubmissionTime)";
$this->Conn->Query($sql);
}
}
else {
$this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:submissions.view', 11, 1, 1, 0)");
}
// 5. add missing event
$sql = 'SELECT EventId
FROM ' . TABLE_PREFIX . 'Events
WHERE (Event = "FORM.SUBMITTED") AND (Type = 1)';
$event_id = $this->Conn->GetOne($sql);
if (!$event_id) {
$sql = "INSERT INTO " . TABLE_PREFIX . "Events VALUES (DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core:Category', 'la_event_FormSubmitted', 1)";
$this->Conn->Query($sql);
}
$sql = 'SELECT EventId
FROM ' . TABLE_PREFIX . 'Events
WHERE (Event = "FORM.SUBMITTED") AND (Type = 0)';
$event_id = $this->Conn->GetOne($sql);
if (!$event_id) {
$sql = "INSERT INTO " . TABLE_PREFIX . "Events VALUES (DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core:Category', 'la_event_FormSubmitted', 0)";
$this->Conn->Query($sql);
}
}
function _addMissingConfigurationVariables()
{
$variables = Array (
'cms_DefaultDesign' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('cms_DefaultDesign', 'la_Text_General', 'la_prompt_DefaultDesignTemplate', 'text', NULL, NULL, 10.15, 0, 0)",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'cms_DefaultDesign', '/platform/designs/general', 'In-Portal', 'in-portal:configure_categories')",
),
'Require_AdminSSL' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('Require_AdminSSL', 'la_Text_Website', 'la_config_RequireSSLAdmin', 'checkbox', '', '', 10.105, 0, 1)",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'Require_AdminSSL', '', 'In-Portal', 'in-portal:configure_advanced')",
),
'UsePopups' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('UsePopups', 'la_Text_Website', 'la_config_UsePopups', 'radio', '', '1=la_Yes,0=la_No', 10.221, 0, 0)",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'UsePopups', '1', 'In-Portal', 'in-portal:configure_advanced')",
),
'UseDoubleSorting' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('UseDoubleSorting', 'la_Text_Website', 'la_config_UseDoubleSorting', 'radio', '', '1=la_Yes,0=la_No', 10.222, 0, 0)",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'UseDoubleSorting', '0', 'In-Portal', 'in-portal:configure_advanced')",
),
'MenuFrameWidth' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('MenuFrameWidth', 'la_title_General', 'la_prompt_MenuFrameWidth', 'text', NULL, NULL, 10.31, 0, 0)",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'MenuFrameWidth', 200, 'In-Portal', 'in-portal:configure_advanced')",
),
'DefaultSettingsUserId' => Array (
"INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('DefaultSettingsUserId', 'la_title_General', 'la_prompt_DefaultUserId', 'text', NULL, NULL, '10.06', '0', '0')",
"INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'DefaultSettingsUserId', -1, 'In-Portal:Users', 'in-portal:configure_users')",
),
);
foreach ($variables as $variable_name => $variable_sqls) {
$sql = 'SELECT VariableId
FROM ' . TABLE_PREFIX . 'ConfigurationValues
WHERE VariableName = ' . $this->Conn->qstr($variable_name);
$variable_id = $this->Conn->GetOne($sql);
if ($variable_id) {
continue;
}
foreach ($variable_sqls as $variable_sql) {
$this->Conn->Query($variable_sql);
}
}
}
/**
* Sort images in database (update Priority field)
*
*/
function _sortImages()
{
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'Images
ORDER BY ResourceId ASC , DefaultImg DESC , ImageId ASC';
$images = $this->Conn->Query($sql);
$priority = 0;
$last_resource_id = false;
foreach ($images as $image) {
if ($image['ResourceId'] != $last_resource_id) {
// each item have own priorities among it's images
$priority = 0;
$last_resource_id = $image['ResourceId'];
}
if (!$image['DefaultImg']) {
$priority--;
}
$sql = 'UPDATE ' . TABLE_PREFIX . 'Images
SET Priority = ' . $priority . '
WHERE ImageId = ' . $image['ImageId'];
$this->Conn->Query($sql);
}
}
/**
* Update to 5.0.1
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_0_1($mode)
{
if ($mode == 'after') {
// delete old events
$events_to_delete = Array ('CATEGORY.MODIFY', 'CATEGORY.DELETE');
$sql = 'SELECT EventId
FROM ' . TABLE_PREFIX . 'Events
WHERE Event IN ("' . implode('","', $events_to_delete) . '")';
$event_ids = $this->Conn->GetCol($sql);
if ($event_ids) {
$this->_deleteEvents($event_ids);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Phrase
WHERE Phrase IN ("la_event_category.modify", "la_event_category.delete")';
$this->Conn->Query($sql);
}
// partially delete events
$sql = 'SELECT EventId
FROM ' . TABLE_PREFIX . 'Events
WHERE (Event IN ("CATEGORY.APPROVE", "CATEGORY.DENY")) AND (Type = ' . EmailTemplate::TEMPLATE_TYPE_ADMIN . ')';
$event_ids = $this->Conn->GetCol($sql);
if ($event_ids) {
$this->_deleteEvents($event_ids);
}
}
}
function _deleteEvents($ids)
{
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailMessage
WHERE EventId IN (' . implode(',', $ids) . ')';
$this->Conn->Query($sql);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Events
WHERE EventId IN (' . implode(',', $ids) . ')';
$this->Conn->Query($sql);
}
/**
* Update to 5.0.2-B2; Transforms IsIndex field values to SymLinkCategoryId field
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_0_2_B2($mode)
{
// 0 - Regular, 1 - Category Index, 2 - Container
if ($mode == 'before') {
// fix "Content" category
$fields_hash = Array (
'CreatedById' => USER_ROOT,
'CreatedOn' => time(),
'ResourceId' => $this->Application->NextResourceId(),
);
$category_id = $this->Application->findModule('Name', 'Core', 'RootCat');
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Category', 'CategoryId = ' . $category_id);
// get all categories, marked as category index
$sql = 'SELECT ParentPath, CategoryId
FROM ' . TABLE_PREFIX . 'Category
WHERE IsIndex = 1';
$category_indexes = $this->Conn->GetCol($sql, 'CategoryId');
foreach ($category_indexes as $category_id => $parent_path) {
$parent_path = explode('|', substr($parent_path, 1, -1));
// set symlink to $category_id for each category, marked as container in given category path
$sql = 'SELECT CategoryId
FROM ' . TABLE_PREFIX . 'Category
WHERE CategoryId IN (' . implode(',', $parent_path) . ') AND (IsIndex = 2)';
$category_containers = $this->Conn->GetCol($sql);
if ($category_containers) {
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET SymLinkCategoryId = ' . $category_id . '
WHERE CategoryId IN (' . implode(',', $category_containers) . ')';
$this->Conn->Query($sql);
}
}
}
if ($mode == 'after') {
// scan theme to fill Theme.TemplateAliases and ThemeFiles.TemplateAlias fields
$this->_toolkit->rebuildThemes();
$sql = 'SELECT TemplateAliases, ThemeId
FROM ' . TABLE_PREFIX . 'Theme
WHERE (Enabled = 1) AND (TemplateAliases <> "")';
$template_aliases = $this->Conn->GetCol($sql, 'ThemeId');
$all_template_aliases = Array (); // reversed alias (from real template to alias)
foreach ($template_aliases as $theme_id => $theme_template_aliases) {
$theme_template_aliases = unserialize($theme_template_aliases);
if (!$theme_template_aliases) {
continue;
}
$all_template_aliases = array_merge($all_template_aliases, array_flip($theme_template_aliases));
}
$default_design_replaced = false;
$default_design = trim($this->Application->ConfigValue('cms_DefaultDesign'), '/');
foreach ($all_template_aliases as $from_template => $to_alias) {
// replace default design in configuration variable (when matches alias)
if ($from_template == $default_design) {
// specific alias matched
$sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues
SET VariableValue = ' . $this->Conn->qstr($to_alias) . '
WHERE VariableName = "cms_DefaultDesign"';
$this->Conn->Query($sql);
$default_design_replaced = true;
}
// replace Category.Template and Category.CachedTemplate fields (when matches alias)
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET Template = ' . $this->Conn->qstr($to_alias) . '
WHERE Template IN (' . $this->Conn->qstr('/' . $from_template) . ',' . $this->Conn->qstr($from_template) . ')';
$this->Conn->Query($sql);
$sql = 'UPDATE ' . TABLE_PREFIX . 'Category
SET CachedTemplate = ' . $this->Conn->qstr($to_alias) . '
WHERE CachedTemplate IN (' . $this->Conn->qstr('/' . $from_template) . ',' . $this->Conn->qstr($from_template) . ')';
$this->Conn->Query($sql);
}
if (!$default_design_replaced) {
// in case if current default design template doesn't
// match any of aliases, then set it to #default_design#
$sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues
SET VariableValue = "#default_design#"
WHERE VariableName = "cms_DefaultDesign"';
$this->Conn->Query($sql);
}
// replace data in category custom fields used for category item template storage
$rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor');
/* @var $rewrite_processor kRewriteUrlProcessor */
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$custom_field_id = $rewrite_processor->getItemTemplateCustomField($module_info['Var']);
if (!$custom_field_id) {
continue;
}
foreach ($all_template_aliases as $from_template => $to_alias) {
$sql = 'UPDATE ' . TABLE_PREFIX . 'CategoryCustomData
SET l1_cust_' . $custom_field_id . ' = ' . $this->Conn->qstr($to_alias) . '
WHERE l1_cust_' . $custom_field_id . ' = ' . $this->Conn->qstr($from_template);
$this->Conn->Query($sql);
}
}
}
}
/**
* Update to 5.0.3-B2; Moves CATEGORY.* permission from module root categories to Content category
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_0_3_B2($mode)
{
if ($mode == 'before') {
// get permissions
$sql = 'SELECT PermissionName
FROM ' . TABLE_PREFIX . 'PermissionConfig
WHERE PermissionName LIKE "CATEGORY.%"';
$permission_names = $this->Conn->GetCol($sql);
// get groups
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'PortalGroup';
$user_groups = $this->Conn->GetCol($sql);
$user_group_count = count($user_groups);
// get module root categories
$sql = 'SELECT RootCat
FROM ' . TABLE_PREFIX . 'Modules';
$module_categories = $this->Conn->GetCol($sql);
$module_categories[] = 0;
$module_categories = implode(',', array_unique($module_categories));
$permissions = $delete_permission_ids = Array ();
foreach ($permission_names as $permission_name) {
foreach ($user_groups as $group_id) {
$sql = 'SELECT PermissionId
FROM ' . TABLE_PREFIX . 'Permissions
WHERE (Permission = ' . $this->Conn->qstr($permission_name) . ') AND (PermissionValue = 1) AND (GroupId = ' . $group_id . ') AND (`Type` = 0) AND (CatId IN (' . $module_categories . '))';
$permission_ids = $this->Conn->GetCol($sql);
if ($permission_ids) {
if (!array_key_exists($permission_name, $permissions)) {
$permissions[$permission_name] = Array ();
}
$permissions[$permission_name][] = $group_id;
$delete_permission_ids = array_merge($delete_permission_ids, $permission_ids);
}
}
}
if ($delete_permission_ids) {
// here we can delete some of permissions that will be added later
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Permissions
WHERE PermissionId IN (' . implode(',', $delete_permission_ids) . ')';
$this->Conn->Query($sql);
}
$home_category = $this->Application->findModule('Name', 'Core', 'RootCat');
foreach ($permissions as $permission_name => $permission_groups) {
// optimize a bit
$has_everyone = in_array(15, $permission_groups);
if ($has_everyone || (!$has_everyone && count($permission_groups) == $user_group_count - 1)) {
// has permission for "Everyone" group OR allowed in all groups except "Everyone" group
// so remove all other explicitly allowed permissions
$permission_groups = Array (15);
}
foreach ($permission_groups as $group_id) {
$fields_hash = Array (
'Permission' => $permission_name,
'GroupId' => $group_id,
'PermissionValue' => 1,
'Type' => 0, // category-based permission,
'CatId' => $home_category,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Permissions');
}
}
$updater = $this->Application->makeClass('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
}
}
/**
* Update to 5.1.0-B1; Makes email message fields multilingual
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_1_0_B1($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('from');
// migrate email events
$table_structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'Events', 'Field');
if (!array_key_exists('Headers', $table_structure)) {
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Events
ADD `Headers` TEXT NULL AFTER `ReplacementTags`,
ADD `MessageType` VARCHAR(4) NOT NULL default "text" AFTER `Headers`';
$this->Conn->Query($sql);
}
// alter here, because kMultiLanguageHelper::createFields
// method, called after will expect that to be in database
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Events
ADD AllowChangingSender TINYINT NOT NULL DEFAULT "0" AFTER MessageType ,
ADD CustomSender TINYINT NOT NULL DEFAULT "0" AFTER AllowChangingSender ,
ADD SenderName VARCHAR(255) NOT NULL DEFAULT "" AFTER CustomSender ,
ADD SenderAddressType TINYINT NOT NULL DEFAULT "0" AFTER SenderName ,
ADD SenderAddress VARCHAR(255) NOT NULL DEFAULT "" AFTER SenderAddressType ,
ADD AllowChangingRecipient TINYINT NOT NULL DEFAULT "0" AFTER SenderAddress ,
ADD CustomRecipient TINYINT NOT NULL DEFAULT "0" AFTER AllowChangingRecipient ,
ADD Recipients TEXT AFTER CustomRecipient,
ADD INDEX (AllowChangingSender),
ADD INDEX (CustomSender),
ADD INDEX (SenderAddressType),
ADD INDEX (AllowChangingRecipient),
ADD INDEX (CustomRecipient)';
$this->Conn->Query($sql);
// create multilingual fields for phrases and email events
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$ml_helper->createFields('phrases');
$ml_helper->createFields('email-template');
$languages = $ml_helper->getLanguages();
if ($this->Conn->TableFound(TABLE_PREFIX . 'EmailMessage', true)) {
$email_template_helper = $this->Application->recallObject('kEmailTemplateHelper');
/* @var $email_template_helper kEmailTemplateHelper */
foreach ($languages as $language_id) {
$sql = 'SELECT EmailMessageId, Template, EventId
FROM ' . TABLE_PREFIX . 'EmailMessage
WHERE LanguageId = ' . $language_id;
$translations = $this->Conn->Query($sql, 'EventId');
foreach ($translations as $event_id => $translation_data) {
$parsed = $email_template_helper->parseTemplate($translation_data['Template'], 'html');
$fields_hash = Array (
'l' . $language_id . '_Subject' => $parsed['Subject'],
'l' . $language_id . '_Body' => $parsed['HtmlBody'],
);
if ( $parsed['Headers'] ) {
$fields_hash['Headers'] = $parsed['Headers'];
}
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Events', 'EventId = ' . $event_id);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailMessage
WHERE EmailMessageId = ' . $translation_data['EmailMessageId'];
$this->Conn->Query($sql);
}
}
}
// migrate phrases
$temp_table = $this->Application->GetTempName(TABLE_PREFIX . 'Phrase');
$sqls = Array (
'DROP TABLE IF EXISTS ' . $temp_table,
'CREATE TABLE ' . $temp_table . ' LIKE ' . TABLE_PREFIX . 'Phrase',
'ALTER TABLE ' . $temp_table . ' DROP LanguageId, DROP Translation',
'ALTER IGNORE TABLE ' . $temp_table . ' DROP INDEX LanguageId_2',
'ALTER TABLE ' . $temp_table . ' DROP PhraseId',
'ALTER TABLE ' . $temp_table . ' ADD PhraseId INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
);
foreach ($sqls as $sql) {
$this->Conn->Query($sql);
}
$already_added = Array ();
$primary_language_id = $this->Application->GetDefaultLanguageId();
foreach ($languages as $language_id) {
$sql = 'SELECT Phrase, PhraseKey, Translation AS l' . $language_id . '_Translation, PhraseType, LastChanged, LastChangeIP, Module
FROM ' . TABLE_PREFIX . 'Phrase
WHERE LanguageId = ' . $language_id;
$phrases = $this->Conn->Query($sql, 'Phrase');
foreach ($phrases as $phrase => $fields_hash) {
if (array_key_exists($phrase, $already_added)) {
$this->Conn->doUpdate($fields_hash, $temp_table, 'PhraseId = ' . $already_added[$phrase]);
}
else {
$this->Conn->doInsert($fields_hash, $temp_table);
$already_added[$phrase] = $this->Conn->getInsertID();
}
}
// in case some phrases were found in this language, but not in primary language -> copy them
if ($language_id != $primary_language_id) {
$sql = 'UPDATE ' . $temp_table . '
SET l' . $primary_language_id . '_Translation = l' . $language_id . '_Translation
WHERE l' . $primary_language_id . '_Translation IS NULL';
$this->Conn->Query($sql);
}
}
$this->Conn->Query('DROP TABLE IF EXISTS ' . TABLE_PREFIX . 'Phrase');
$this->Conn->Query('RENAME TABLE ' . $temp_table . ' TO ' . TABLE_PREFIX . 'Phrase');
$this->_updateCountryStatesTable();
$this->_replaceConfigurationValueSeparator();
// save "config.php" in php format, not ini format as before
$this->_toolkit->SaveConfig();
}
if ($mode == 'after') {
$this->_transformEmailRecipients();
$this->_fixSkinColors();
}
}
/**
* Makes sure we rename tables to legacy names before doing other upgrades before 5.2.0-B1 upgrade
*
* @param string $name
* @param Array $arguments
*/
public function __call($name, $arguments)
{
if ( substr($name, 0, 12) == 'Upgrade_5_1_' && $arguments[0] == 'before' ) {
$this->_renameTables('from');
}
if ( substr($name, 0, 13) == 'Upgrade_5_2_0' && $arguments[0] == 'before' ) {
$this->_renameTables('to');
}
}
/**
* Move country/state translations from Phrase to CountryStates table
*
*/
function _updateCountryStatesTable()
{
// refactor StdDestinations table
$sql = 'RENAME TABLE ' . TABLE_PREFIX . 'StdDestinations TO ' . TABLE_PREFIX . 'CountryStates';
$this->Conn->Query($sql);
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'CountryStates
CHANGE DestId CountryStateId INT(11) NOT NULL AUTO_INCREMENT,
CHANGE DestType Type INT(11) NOT NULL DEFAULT \'1\',
CHANGE DestParentId StateCountryId INT(11) NULL DEFAULT NULL,
CHANGE DestAbbr IsoCode CHAR(3) NOT NULL DEFAULT \'\',
CHANGE DestAbbr2 ShortIsoCode CHAR(2) NULL DEFAULT NULL,
DROP INDEX DestType,
DROP INDEX DestParentId,
ADD INDEX (`Type`),
ADD INDEX (StateCountryId)';
$this->Conn->Query($sql);
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$ml_helper->createFields('country-state');
$languages = $ml_helper->getLanguages();
foreach ($languages as $language_id) {
$sub_select = ' SELECT l' . $language_id . '_Translation
FROM ' . TABLE_PREFIX . 'Phrase
WHERE Phrase = DestName';
$sql = 'UPDATE ' . TABLE_PREFIX . 'CountryStates
SET l' . $language_id . '_Name = (' . $sub_select . ')';
$this->Conn->Query($sql);
}
$sql = 'ALTER TABLE ' . TABLE_PREFIX . 'CountryStates
DROP DestName';
$this->Conn->Query($sql);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Phrase
WHERE Phrase LIKE ' . $this->Conn->qstr('la_country_%') . ' OR Phrase LIKE ' . $this->Conn->qstr('la_state_%');
$this->Conn->Query($sql);
}
/**
* Makes configuration values dropdowns use "||" as separator
*
*/
function _replaceConfigurationValueSeparator()
{
$custom_field_helper = $this->Application->recallObject('InpCustomFieldsHelper');
/* @var $custom_field_helper InpCustomFieldsHelper */
$sql = 'SELECT ValueList, VariableName
FROM ' . TABLE_PREFIX . 'ConfigurationAdmin
WHERE ValueList LIKE "%,%"';
$variables = $this->Conn->GetCol($sql, 'VariableName');
foreach ($variables as $variable_name => $value_list) {
$ret = Array ();
$options = $custom_field_helper->GetValuesHash($value_list, ',', false);
foreach ($options as $option_key => $option_title) {
if (substr($option_key, 0, 3) == 'SQL') {
$ret[] = $option_title;
}
else {
$ret[] = $option_key . '=' . $option_title;
}
}
$fields_hash = Array (
'ValueList' => implode(VALUE_LIST_SEPARATOR, $ret),
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'ConfigurationAdmin', 'VariableName = ' . $this->Conn->qstr($variable_name));
}
}
/**
* Transforms "FromUserId" into Sender* and Recipients columns
*
*/
function _transformEmailRecipients()
{
$sql = 'SELECT FromUserId, Type, EventId
FROM ' . TABLE_PREFIX . 'Events
WHERE FromUserId IS NOT NULL AND (FromUserId <> ' . USER_ROOT . ')';
$events = $this->Conn->Query($sql, 'EventId');
$minput_helper = $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
foreach ($events as $event_id => $event_data) {
$sql = 'SELECT Login
FROM ' . TABLE_PREFIX . 'PortalUser
WHERE PortalUserId = ' . $event_data['FromUserId'];
$username = $this->Conn->GetOne($sql);
if (!$username) {
continue;
}
if ($event_data['Type'] == EmailTemplate::TEMPLATE_TYPE_FRONTEND) {
// from user
$fields_hash = Array (
'CustomSender' => 1,
'SenderAddressType' => EmailTemplate::ADDRESS_TYPE_USER,
'SenderAddress' => $username
);
}
if ($event_data['Type'] == EmailTemplate::TEMPLATE_TYPE_ADMIN) {
// to user
$records = Array (
Array (
'RecipientType' => EmailTemplate::RECIPIENT_TYPE_TO,
'RecipientName' => '',
'RecipientAddressType' => EmailTemplate::ADDRESS_TYPE_USER,
'RecipientAddress' => $username
)
);
$fields_hash = Array (
'CustomRecipient' => 1,
'Recipients' => $minput_helper->prepareMInputXML($records, array_keys( reset($records) ))
);
}
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Events', 'EventId = ' . $event_id);
}
$this->Conn->Query('ALTER TABLE ' . TABLE_PREFIX . 'Events DROP FromUserId');
}
/**
* Update to 5.1.0; Fixes refferer of form submissions
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_1_0($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('from');
}
if ( $mode == 'after' ) {
$base_url = $this->Application->BaseURL();
$sql = 'UPDATE ' . TABLE_PREFIX . 'FormSubmissions
SET ReferrerURL = REPLACE(ReferrerURL, ' . $this->Conn->qstr($base_url) . ', "/")';
$this->Conn->Query($sql);
}
}
/**
* Update to 5.1.1-B1; Transforms DisplayToPublic logic
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_1_1_B1($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('from');
}
if ($mode == 'after') {
$this->processDisplayToPublic();
}
}
function processDisplayToPublic()
{
$profile_mapping = Array (
'pp_firstname' => 'FirstName',
'pp_lastname' => 'LastName',
'pp_dob' => 'dob',
'pp_email' => 'Email',
'pp_phone' => 'Phone',
'pp_street' => 'Street',
'pp_city' => 'City',
'pp_state' => 'State',
'pp_zip' => 'Zip',
'pp_country' => 'Country',
);
$fields = array_keys($profile_mapping);
$fields = $this->Conn->qstrArray($fields);
$where_clause = 'VariableName IN (' . implode(',', $fields) . ')';
// 1. get user, that have saved their profile at least once
$sql = 'SELECT DISTINCT PortalUserId
FROM ' . TABLE_PREFIX . 'PersistantSessionData
WHERE ' . $where_clause;
$users = $this->Conn->GetCol($sql);
foreach ($users as $user_id) {
// 2. convert to new format
$sql = 'SELECT VariableValue, VariableName
FROM ' . TABLE_PREFIX . 'PersistantSessionData
WHERE (PortalUserId = ' . $user_id . ') AND ' . $where_clause;
$user_variables = $this->Conn->GetCol($sql, 'VariableName');
// go through mapping to preserve variable order
$value = Array ();
foreach ($profile_mapping as $from_name => $to_name) {
if (array_key_exists($from_name, $user_variables) && $user_variables[$from_name]) {
$value[] = $to_name;
}
}
if ($value) {
$fields_hash = Array (
'DisplayToPublic' => '|' . implode('|', $value) . '|',
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PortalUser', 'PortalUserId = ' . $user_id);
}
// 3. delete old style variables
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'PersistantSessionData
WHERE (PortalUserId = ' . $user_id . ') AND ' . $where_clause;
$this->Conn->Query($sql);
}
}
/**
* Update to 5.1.3; Merges column and field phrases
*
* @param string $mode when called mode {before, after)
*/
function Upgrade_5_1_3($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('from');
}
if ( $mode == 'after' ) {
$this->moveTranslation('LA_COL_', 'LA_FLD_', 'ColumnTranslation');
}
}
/**
* Makes sure table names match upgrade script
*
* @param string $key
* @return void
* @access private
*/
private function _renameTables($key)
{
foreach ($this->renamedTables as $prefix => $table_info) {
$this->Application->setUnitOption($prefix, 'TableName', TABLE_PREFIX . $table_info[$key]);
}
}
/**
* Update to 5.2.0-B1; Transform list sortings storage
*
* @param string $mode when called mode {before, after)
*/
public function Upgrade_5_2_0_B1($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('to');
}
if ( $mode == 'after' ) {
$this->transformSortings();
$this->moveTranslation('LA_COL_', 'LA_FLD_', 'ColumnTranslation'); // because of "la_col_ItemPrefix" phrase
$this->moveTranslation('LA_HINT_', 'LA_FLD_', 'HintTranslation');
$this->moveTranslation('LA_HINT_', 'LA_CONFIG_', 'HintTranslation');
$this->moveTranslation('LA_HINT_', 'LA_TITLE_', 'HintTranslation');
$this->createPageRevisions();
}
}
/**
* Transforms a way, how list sortings are stored
*
* @return void
*/
function transformSortings()
{
$sql = 'SELECT VariableName, PortalUserId
FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE VariableName LIKE "%_Sort1.%"';
$sortings = $this->Conn->Query($sql);
foreach ($sortings AS $sorting) {
if ( !preg_match('/^(.*)_Sort1.(.*)$/', $sorting['VariableName'], $regs) ) {
continue;
}
$user_id = $sorting['PortalUserId'];
$prefix_special = $regs[1] . '_';
$view_name = '.' . $regs[2];
$old_variable_names = Array (
$prefix_special . 'Sort1' . $view_name, $prefix_special . 'Sort1_Dir' . $view_name,
$prefix_special . 'Sort2' . $view_name, $prefix_special . 'Sort2_Dir' . $view_name,
);
$old_variable_names = $this->Conn->qstrArray($old_variable_names);
$sql = 'SELECT VariableValue, VariableName
FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE PortalUserId = ' . $user_id . ' AND VariableName IN (' . implode(',', $old_variable_names) . ')';
$sorting_data = $this->Conn->GetCol($sql, 'VariableName');
// prepare & save new sortings
$new_sorting = Array (
'Sort1' => $sorting_data[$prefix_special . 'Sort1' . $view_name],
'Sort1_Dir' => $sorting_data[$prefix_special . 'Sort1_Dir' . $view_name],
);
if ( isset($sorting_data[$prefix_special . 'Sort2' . $view_name]) ) {
$new_sorting['Sort2'] = $sorting_data[$prefix_special . 'Sort2' . $view_name];
$new_sorting['Sort2_Dir'] = $sorting_data[$prefix_special . 'Sort2_Dir' . $view_name];
}
$fields_hash = Array (
'PortalUserId' => $user_id,
'VariableName' => $prefix_special . 'Sortings' . $view_name,
'VariableValue' => serialize($new_sorting),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData');
// delete sortings, that were already processed
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData
WHERE PortalUserId = ' . $user_id . ' AND VariableName IN (' . implode(',', $old_variable_names) . ')';
$this->Conn->Query($sql);
}
}
/**
* Merges several phrases into one (e.g. la_col_ + la_hint_ into designated columns of la_fld_ phrases)
*
* @param string $source_prefix
* @param string $target_prefix
* @param string $db_column
* @return void
* @access protected
*/
public function moveTranslation($source_prefix, $target_prefix, $db_column)
{
$source_phrases = $this->getPhrasesByMask($source_prefix . '%');
$target_phrases = $this->getPhrasesByMask($target_prefix . '%');
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$delete_ids = Array ();
$ml_helper->createFields('phrases');
$languages = $ml_helper->getLanguages();
$phrase_table = $this->Application->getUnitOption('phrases', 'TableName');
foreach ($source_phrases as $phrase_key => $phrase_info) {
$target_phrase_key = $target_prefix . substr($phrase_key, strlen($source_prefix));
if ( !isset($target_phrases[$target_phrase_key]) ) {
continue;
}
$fields_hash = Array ();
// copy column phrase main translation into field phrase column translation
foreach ($languages as $language_id) {
$fields_hash['l' . $language_id . '_' . $db_column] = $phrase_info['l' . $language_id . '_Translation'];
}
$delete_ids[] = $phrase_info['PhraseId'];
$this->Conn->doUpdate($fields_hash, $phrase_table, 'PhraseId = ' . $target_phrases[$target_phrase_key]['PhraseId']);
}
// delete all column phrases, that were absorbed by field phrases
if ( $delete_ids ) {
$sql = 'DELETE FROM ' . $phrase_table . '
WHERE PhraseId IN (' . implode(',', $delete_ids) . ')';
$this->Conn->Query($sql);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'PhraseCache';
$this->Conn->Query($sql);
}
}
/**
* Returns phrases by mask
*
* @param string $mask
* @return Array
* @access protected
*/
protected function getPhrasesByMask($mask)
{
$sql = 'SELECT *
FROM ' . $this->Application->getUnitOption('phrases', 'TableName') . '
WHERE PhraseKey LIKE ' . $this->Conn->qstr($mask);
return $this->Conn->Query($sql, 'PhraseKey');
}
protected function createPageRevisions()
{
$sql = 'SELECT DISTINCT PageId
FROM ' . TABLE_PREFIX . 'PageContent';
$page_ids = $this->Conn->GetCol($sql);
foreach ($page_ids as $page_id) {
$fields_hash = Array (
'PageId' => $page_id,
'RevisionNumber' => 1,
'IsDraft' => 0,
'FromRevisionId' => 0,
'CreatedById' => USER_ROOT,
'CreatedOn' => adodb_mktime(),
'Status' => STATUS_ACTIVE,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PageRevisions');
$fields_hash = Array (
'RevisionId' => $this->Conn->getInsertID(),
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PageContent', 'PageId = ' . $page_id);
}
}
/**
* Update to 5.2.0-B3; Introduces separate field for plain-text e-mail event translations
*
* @param string $mode when called mode {before, after)
*/
public function Upgrade_5_2_0_B3($mode)
{
if ( $mode == 'before' ) {
$this->_renameTables('to');
}
if ( $mode == 'after' ) {
$this->_splitEmailBody();
$this->_migrateCommonFooter();
}
}
/**
* Splits e-mail body into HTML and Text fields
*
* @return void
* @access private
*/
private function _splitEmailBody()
{
$id_field = $this->Application->getUnitOption('email-template', 'IDField');
$table_name = $this->Application->getUnitOption('email-template', 'TableName');
$fields = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field');
if ( !isset($fields['l1_Body']) ) {
// column dropped - nothing to convert anymore
return;
}
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$languages = $ml_helper->getLanguages();
$ml_helper->createFields('email-template');
$sql = 'SELECT *
FROM ' . $table_name;
$email_events = $this->Conn->Query($sql);
// 1. move data to new columns
foreach ($email_events as $email_event) {
$fields_hash = Array ();
$translation_field = $email_event['MessageType'] == 'html' ? 'HtmlBody' : 'PlainTextBody';
foreach ($languages as $language_id) {
$fields_hash['l' . $language_id . '_' . $translation_field] = $email_event['l' . $language_id . '_Body'];
}
if ( $fields_hash ) {
$this->Conn->doUpdate($fields_hash, $table_name, $id_field . ' = ' . $email_event[$id_field]);
}
}
// 2. drop old columns
$drops = Array ('DROP COLUMN MessageType');
foreach ($languages as $language_id) {
$lang_field = 'l' . $language_id . '_Body';
if ( isset($fields[$lang_field]) ) {
$drops[] = 'DROP COLUMN ' . $lang_field;
}
}
$this->Conn->Query('ALTER TABLE ' . $table_name . ' ' . implode(', ', $drops));
}
/**
* Transforms COMMON.FOOTER e-mail event into new field in Languages table
*/
private function _migrateCommonFooter()
{
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
$languages = $ml_helper->getLanguages();
$event_table = $this->Application->getUnitOption('email-template', 'TableName');
$sql = 'SELECT *
FROM ' . $event_table . '
WHERE Event = "COMMON.FOOTER"';
$footer_data = $this->Conn->GetRow($sql);
if ( !$footer_data ) {
return;
}
$primary_language_id = $this->Application->GetDefaultLanguageId();
$table_name = $this->Application->getUnitOption('lang', 'TableName');
foreach ($languages as $language_id) {
$is_primary = $language_id == $primary_language_id;
$fields_hash = Array (
'HtmlEmailTemplate' => $this->_appendEmailDesignBody($footer_data['l' . $language_id . '_HtmlBody'], $is_primary),
'TextEmailTemplate' => $this->_appendEmailDesignBody($footer_data['l' . $language_id . '_PlainTextBody'], $is_primary),
);
$this->Conn->doUpdate($fields_hash, $table_name, 'LanguageId = ' . $language_id);
}
$sql = 'DELETE FROM ' . $event_table . '
WHERE EventId = ' . $footer_data['EventId'];
$this->Conn->Query($sql);
}
/**
* Adds "$body" to given string
*
* @param string $string
* @param bool $is_primary for primary language
* @return string
* @access private
*/
private function _appendEmailDesignBody($string, $is_primary)
{
if ( !$string ) {
return $is_primary ? '$body' : $string;
}
return '$body' . "\n" . str_replace(Array ("\r\n", "\r"), "\n", $string);
}
/**
* Update to 5.2.0-RC1
*
* @param string $mode when called mode {before, after)
*/
public function Upgrade_5_2_0_RC1($mode)
{
if ( $mode != 'before' ) {
return;
}
$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');
/* @var $ml_helper kMultiLanguageHelper */
// make some promo block fields translatable
$ml_helper->createFields('promo-block');
$table_name = $this->Application->getUnitOption('promo-block', 'TableName');
$table_structure = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field');
if ( isset($table_structure['Title']) ) {
$sql = 'UPDATE ' . $table_name . '
SET l' . $this->Application->GetDefaultLanguageId() . '_Title = Title';
$this->Conn->Query($sql);
$sql = 'ALTER TABLE ' . $table_name . ' DROP Title';
$this->Conn->Query($sql);
}
// fix e-mail event translations
$languages = $ml_helper->getLanguages();
$table_name = $this->Application->getUnitOption('email-template', 'TableName');
$change_fields = Array ('Subject', 'HtmlBody', 'PlainTextBody');
foreach ($languages as $language_id) {
foreach ($change_fields as $change_field) {
$change_field = 'l' . $language_id . '_' . $change_field;
$sql = "UPDATE " . $table_name . "
SET {$change_field} = REPLACE({$change_field}, '<inp2:m_BaseUrl/>', '<inp2:m_Link template=\"index\"/>')
WHERE {$change_field} LIKE '%m_BaseURL%'";
$this->Conn->Query($sql);
}
}
// add new ml columns to phrases/e-mail events
$ml_helper->createFields('phrases');
$ml_helper->createFields('email-template');
}
/**
* Update to 5.2.0
*
* @param string $mode when called mode {before, after)
*/
public function Upgrade_5_2_0($mode)
{
if ( $mode != 'after' ) {
return;
}
$table_name = $this->Application->getUnitOption('c', 'TableName');
$sql = 'SELECT NamedParentPath, CachedTemplate, CategoryId
FROM ' . $table_name;
$categories = $this->Conn->GetIterator($sql);
foreach ($categories as $category_data) {
$fields_hash = Array (
'NamedParentPathHash' => kUtil::crc32(mb_strtolower(preg_replace('/^Content\//i', '', $category_data['NamedParentPath']))),
'CachedTemplateHash' => kUtil::crc32(mb_strtolower($category_data['CachedTemplate'])),
);
$this->Conn->doUpdate($fields_hash, $table_name, 'CategoryId = ' . $category_data['CategoryId']);
}
$rebuild_mode = $this->Application->ConfigValue('QuickCategoryPermissionRebuild') ? CategoryPermissionRebuild::SILENT : CategoryPermissionRebuild::AUTOMATIC;
$this->Application->SetConfigValue('CategoryPermissionRebuildMode', $rebuild_mode);
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemSettings WHERE VariableName = "QuickCategoryPermissionRebuild"';
$this->Conn->Query($sql);
$this->_updateScheduledTaskRunSchedule();
}
/**
* Transforms RunInterval into RunSchedule column for Scheduled Tasks
*
* @return void
* @access protected
*/
protected function _updateScheduledTaskRunSchedule()
{
// minute hour day_of_month month day_of_week
$id_field = $this->Application->getUnitOption('scheduled-task', 'IDField');
$table_name = $this->Application->getUnitOption('scheduled-task', 'TableName');
$sql = 'SELECT RunInterval, ' . $id_field . '
FROM ' . $table_name;
$run_intervals = $this->Conn->GetCol($sql, $id_field);
$ranges = Array (0 => 'min', 1 => 'hour', 2 => 'day', 3 => 'month');
$range_values = Array ('min' => 60, 'hour' => 60, 'day' => 24, 'month' => 30);
$range_masks = Array ('min' => '*/%s * * * *', 'hour' => '0 */%s * * *', 'day' => '0 0 */%s * *', 'month' => '0 0 1 */%s *');
foreach ($run_intervals as $scheduled_task_id => $interval) {
$mask_index = 'month';
foreach ($ranges as $range_index => $range_name) {
$range_value = $range_values[$range_name];
if ( $interval >= $range_value ) {
$interval = ceil($interval / $range_value);
}
else {
$mask_index = $ranges[$range_index - 1];
break;
}
}
$run_schedule = sprintf($range_masks[$mask_index], $interval);
if ( $run_schedule == '0 0 */7 * *' ) {
// once in 7 days = once in a week
$run_schedule = '0 0 * * 0';
}
$run_schedule = preg_replace('/(\*\/1( |$))/', '*\\2', $run_schedule);
$fields_hash = Array ('RunSchedule' => $run_schedule);
$this->Conn->doUpdate($fields_hash, $table_name, $id_field . ' = ' . $scheduled_task_id);
}
// drop RunInterval column
$this->Conn->Query('ALTER TABLE ' . $table_name . ' DROP RunInterval');
}
/**
* Update to 5.2.1-B1
*
* @param string $mode when called mode {before, after)
*/
public function Upgrade_5_2_1_B1($mode)
{
if ( $mode != 'after' ) {
return;
}
$this->_updateUserPasswords();
}
protected function _updateUserPasswords()
{
$user_table = $this->Application->getUnitOption('u', 'TableName');
$sql = 'SELECT Password, PortalUserId
FROM ' . $user_table . '
WHERE PasswordHashingMethod = ' . PasswordHashingMethod::MD5;
$user_passwords = $this->Conn->GetColIterator($sql, 'PortalUserId');
if ( !count($user_passwords) ) {
// no users at all or existing users have converted passwords already
return;
}
kUtil::setResourceLimit();
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
/* @var $password_formatter kPasswordFormatter */
foreach ($user_passwords as $user_id => $user_password) {
$fields_hash = Array (
'Password' => $password_formatter->hashPassword($user_password, '', PasswordHashingMethod::MD5_AND_PHPPASS),
'PasswordHashingMethod' => PasswordHashingMethod::MD5_AND_PHPPASS,
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Users', 'PortalUserId = ' . $user_id);
}
}
- }
\ No newline at end of file
+
+ /**
+ * Update to 5.2.2-B1
+ *
+ * @param string $mode when called mode {before, after)
+ */
+ public function Upgrade_5_2_2_B1($mode)
+ {
+ if ( $mode != 'after' ) {
+ return;
+ }
+
+ $this->deleteThumbnails();
+ }
+
+ /**
+ * Deletes folders, containing thumbnails recursively.
+ *
+ * @param string $folder Folder.
+ *
+ * @return void
+ */
+ protected function deleteThumbnails($folder = WRITEABLE)
+ {
+ foreach ( glob($folder . '/*', GLOB_ONLYDIR) as $sub_folder ) {
+ if ( $sub_folder === WRITEABLE . '/cache' ) {
+ continue;
+ }
+
+ if ( basename($sub_folder) === 'resized' ) {
+ $files = glob($sub_folder . '/*');
+ array_map('unlink', $files);
+ rmdir($sub_folder);
+ }
+ else {
+ $this->deleteThumbnails($sub_folder);
+ }
+ }
+ }
+ }

Event Timeline