Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Wed, Feb 5, 12:04 PM

in-portal

Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_event_handler.php (revision 14607)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php (revision 14608)
@@ -1,3085 +1,3091 @@
<?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 adressing 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 corrent Prefix_Special
* for variables beeing submitted such way (e.g. variable
* name that will be converted by PHP: "users.read_only_id"
* will be submitted as "users_read_only_id".
*
* 2. When using $this->Application-LinkVar on variables submitted
* from form which contain $Prefix_Special then note 1st item. Example:
* LinkVar($event->getPrefixSpecial(true).'_varname',$event->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
*/
function CheckPermission(&$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 standart permission mapping
*
*/
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'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
function mapEvents()
{
$events_map = Array(
'OnRemoveFilters' => 'FilterAction',
'OnApplyFilters' => 'FilterAction',
'OnMassApprove'=>'iterateItems',
'OnMassDecline'=>'iterateItems',
'OnMassMoveUp'=>'iterateItems',
'OnMassMoveDown'=>'iterateItems',
);
$this->eventMethods = array_merge($this->eventMethods, $events_map);
}
/**
* Returns ID of current item to be edited
* by checking ID passed in get/post as prefix_id
* or by looking at first from selected ids, stored.
* Returned id is also stored in Session in case
* it was explicitly passed as get/post
*
* @param kEvent $event
* @return int
*/
function getPassedID(&$event)
{
if ($event->getEventParam('raise_warnings') === false) {
$event->setEventParam('raise_warnings', 1);
}
if (preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1])) {
// <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object)
$main_object =& $this->Application->recallObject($regs[1]);
/* @var $main_object kDBItem */
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
return $main_object->GetDBField($id_field);
}
// 1. get id from post (used in admin)
$ret = $this->Application->GetVar($event->getPrefixSpecial(true).'_id');
if (($ret !== false) && ($ret != '')) {
return $ret;
}
// 2. get id from env (used in front)
$ret = $this->Application->GetVar($event->getPrefixSpecial().'_id');
if (($ret !== false) && ($ret != '')) {
return $ret;
}
// recall selected ids array and use the first one
$ids = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
if ($ids != '') {
$ids = explode(',',$ids);
if ($ids) {
$ret = array_shift($ids);
}
}
else { // if selected ids are not yet stored
$this->StoreSelectedIDs($event);
return $this->Application->GetVar($event->getPrefixSpecial().'_id'); // StoreSelectedIDs sets this variable
}
return $ret;
}
/**
* Prepares and stores selected_ids string
* in Session and Application Variables
* by getting all checked ids from grid plus
* id passed in get/post as prefix_id
*
* @param kEvent $event
* @param Array $direct_ids
*
* @return Array ids stored
*/
function StoreSelectedIDs(&$event, $direct_ids = null)
{
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
$ids = $event->getEventParam('ids');
if (isset($direct_ids) || ($ids !== false)) {
// save ids directly if they given + reset array indexes
$resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false);
if ($resulting_ids) {
$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids));
$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', 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
*/
function getSelectedIDs(&$event, $from_session = false)
{
if ($from_session) {
$wid = $this->Application->GetTopmostWid($event->Prefix);
$var_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
$ret = $this->Application->RecallVar($var_name);
}
else {
$ret = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
}
return explode(',', $ret);
}
/**
* Stores IDs, selected in grid in session
*
* @param kEvent $event
*/
function OnStoreSelected(&$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());
}
}
/**
* Returs associative array of submitted fields for current item
* Could be used while creating/editing single item -
* meaning on any edit form, except grid edit
*
* @param kEvent $event
*/
function getSubmittedFields(&$event)
{
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
$field_values = $items_info ? array_shift($items_info) : Array();
return $field_values;
}
/**
* Removes any information about current/selected ids
* from Application variables and Session
*
* @param kEvent $event
*/
function clearSelectedIDs(&$event)
{
$prefix_special = $event->getPrefixSpecial();
$ids = implode(',', $this->getSelectedIDs($event, true));
$event->setEventParam('ids', $ids);
$wid = $this->Application->GetTopmostWid($event->Prefix);
$session_name = rtrim($prefix_special.'_selected_ids_'.$wid, '_');
$this->Application->RemoveVar($session_name);
$this->Application->SetVar($prefix_special.'_selected_ids', '');
$this->Application->SetVar($prefix_special.'_id', ''); // $event->getPrefixSpecial(true).'_id' too may be
}
/**
* Common builder part for Item & List
*
* @param kDBBase $object
* @param kEvent $event
* @access private
*/
function dbBuild(&$object, &$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
*/
function checkItemStatus(&$event)
{
$status_fields = $this->Application->getUnitOption($event->Prefix,'StatusField');
if (!$status_fields) {
return true;
}
$status_field = array_shift($status_fields);
if ($status_field == 'Status' || $status_field == 'Enabled') {
$object =& $event->getObject();
if (!$object->isLoaded()) {
return true;
}
return $object->GetDBField($status_field) == STATUS_ACTIVE;
}
return true;
}
/**
* Shows not found template content
*
* @param kEvent $event
*
*/
function _errorNotFound(&$event)
{
if ($event->getEventParam('raise_warnings') === 0) {
// when it's possible, that autoload fails do nothing
return ;
}
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_NOTICE);
header('HTTP/1.0 404 Not Found');
while (ob_get_level()) {
ob_end_clean();
}
// object is used inside template parsing, so break out any parsing and return error document
$error_template = $this->Application->ConfigValue('ErrorTemplate');
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$this->Application->SetVar('t', $error_template);
$this->Application->SetVar('m_cat_id', $themes_helper->getPageByTemplate($error_template));
// in case if missing item is recalled first from event (not from template)
$this->Application->InitParser();
$this->Application->HTML = $this->Application->ParseBlock( Array ('name' => $error_template) );
$this->Application->Done();
exit;
}
/**
* Builds item (loads if needed)
*
* Pattern: Prototype Manager
*
* @param kEvent $event
* @access protected
*/
function OnItemBuild(&$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_autload = $event->getEventParam('skip_autoload');
if ($auto_load && !$skip_autload) {
$perm_status = true;
$user_id = $this->Application->RecallVar('user_id');
$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
$status_checked = false;
if ($user_id == 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 pemrission template
if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendTrace();
}
trigger_error('ItemLoad Permission Failed for prefix ['.$event->getPrefixSpecial().'] in <strong>'.($status_checked ? 'checkItemStatus' : 'CheckPermission').'</strong>', E_USER_NOTICE);
$template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
if (MOD_REWRITE) {
$redirect_params = Array (
'm_cat_id' => 0,
'next_template' => urlencode('external:' . $_SERVER['REQUEST_URI']),
);
}
else {
$redirect_params = Array (
'next_template' => $this->Application->GetVar('t'),
);
}
$this->Application->Redirect($template, $redirect_params);
}
}
$actions =& $this->Application->recallObject('kActions');
/* @var $actions Params */
$actions->Set($event->getPrefixSpecial().'_GoTab', '');
$actions->Set($event->getPrefixSpecial().'_GoId', '');
$actions->Set('forms[' . $event->getPrefixSpecial() . ']', $object->getFormName());
}
/**
* Build subtables array from configs
*
* @param kEvent $event
*/
function OnTempHandlerBuild(&$event)
{
$object =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $object kTempTablesHandler */
$object->BuildTables( $event->Prefix, $this->getSelectedIDs($event) );
}
/**
* Checks, that object used in event should use temp tables
*
* @param kEvent $event
* @return bool
*/
function UseTempTables(&$event)
{
$top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual
$special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event);
return $this->Application->IsTempMode($event->Prefix, $special);
}
/**
* Returns table prefix from event (temp or live)
*
* @param kEvent $event
* @return string
* @todo Needed? Should be refactored (by Alex)
*/
function TablePrefix(&$event)
{
return $this->UseTempTables($event) ? $this->Application->GetTempTablePrefix('prefix:'.$event->Prefix).TABLE_PREFIX : TABLE_PREFIX;
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(&$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($id);
}
}
/**
* Builds list
*
* Pattern: Prototype Manager
*
* @param kEvent $event
* @access protected
*/
function OnListBuild(&$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
$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();
$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', '');
}
/**
* Get's special of main item for linking with subitem
*
* @param kEvent $event
* @return string
*/
function getMainSpecial(&$event)
{
$main_special = $event->getEventParam('main_special');
if ($main_special === false) {
// main item's special not passed
if (substr($event->Special, -5) == '-item') {
// temp handler added "-item" to given special -> process that here
return substr($event->Special, 0, -5);
}
// by default subitem's special is used for main item searching
return $event->Special;
}
return $main_special;
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(&$event)
{
}
/**
* Set's new perpage for grid
*
* @param kEvent $event
*/
function OnSetPerPage(&$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');
/*if ($per_page != $list_helper->getDefaultPerPage($event->Prefix)) {
$event->SetRedirectParam('per_page', $per_page);
}*/
}
}
/**
* Occurs when page is changed (only for hooking)
*
* @param kEvent $event
*/
function OnSetPage(&$event)
{
$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
$event->SetRedirectParam($event->getPrefixSpecial() . '_Page', $page);
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
if (!$this->Application->isAdminUser) {
/*if ($page > 1) {
$event->SetRedirectParam('page', $page);
}*/
$this->_passListParams($event, 'page');
}
}
/**
* Passes through main list pagination and sorting
*
* @param kEvent $event
* @param string $skip_var
*/
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
* @access private
* @see OnListBuild
*/
function SetPagination(&$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
*/
function getPerPage(&$event)
{
$object =& $event->getObject();
/* @var $object kDBList */
$per_page = $event->getEventParam('per_page');
if ($per_page) {
// per-page is passed as tag parameter to PrintList, InitList, etc.
$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
// 2. per-page setting is stored in configuration variable
if ($config_mapping) {
// such pseudo per-pages are only defined in templates directly
switch ($per_page) {
case 'short_list':
$per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']);
break;
case 'default':
$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
break;
}
}
return $per_page;
}
if (!$per_page && $object->isMainList()) {
// main lists on Front-End have special get parameter for per-page
$per_page = $this->Application->GetVar('per_page');
}
if (!$per_page) {
// per-page is given in "env" variable for given prefix
$per_page = $this->Application->GetVar($event->getPrefixSpecial() . '_PerPage');
}
if (!$per_page && $event->Special) {
// when not part of env, then variables like "prefix.special_PerPage" are
// replaced (by PHP) with "prefix_special_PerPage", so check for that too
$per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage');
}
if (!$object->isMainList()) {
// per-page given in env and not in main list
$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
if ($per_page) {
// per-page found in request -> store in session and persistent session
$this->setListSetting($event, 'PerPage', $per_page);
}
else {
// per-page not found in request -> get from pesistent session (or session)
$per_page = $this->getListSetting($event, 'PerPage');
}
}
if (!$per_page) {
// per page wan't found in request/session/persistent session
$list_helper =& $this->Application->recallObject('ListHelper');
/* @var $list_helper ListHelper */
// allow to override default per-page value from tag
$default_per_page = $event->getEventParam('default_per_page');
if (!is_numeric($default_per_page)) {
$default_per_page = 10;
}
$per_page = $list_helper->getDefaultPerPage($event->Prefix, $default_per_page);
}
return $per_page;
}
/**
* Set's correct sorting for list
* based on data provided with event
*
* @param kEvent $event
* @access private
* @see OnListBuild
*/
function SetSorting(&$event)
{
$event->setPseudoClass('_List');
$object =& $event->getObject();
/* @var $object kDBList */
if ($object->isMainList()) {
$sort_by = $this->Application->GetVar('sort_by');
$cur_sort1 = $cur_sort1_dir = $cur_sort2 = $cur_sort2_dir = false;
if ($sort_by) {
list ($cur_sort1, $cur_sort1_dir) = explode(',', $sort_by);
}
}
else {
$cur_sort1 = $this->getListSetting($event, 'Sort1');
$cur_sort1_dir = $this->getListSetting($event, 'Sort1_Dir');
$cur_sort2 = $this->getListSetting($event, 'Sort2');
$cur_sort2_dir = $this->getListSetting($event, 'Sort2_Dir');
}
$tag_sort_by = $event->getEventParam('sort_by');
if ($tag_sort_by) {
if ($tag_sort_by == 'random') {
$object->AddOrderField('RAND()', '');
}
else {
// multiple sortings could be specified at once
$tag_sort_by = explode('|', $tag_sort_by);
foreach ($tag_sort_by as $sorting_element) {
list ($by, $dir) = explode(',', $sorting_element);
$object->AddOrderField($by, $dir);
}
}
}
$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
$sorting_prefix = array_key_exists($event->Special, $list_sortings) ? $event->Special : '';
$sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
if ($sorting_configs && array_key_exists('DefaultSorting1Field', $sorting_configs)) {
// sorting defined in configuration variables overrides one from unit config
$list_sortings[$sorting_prefix]['Sorting'] = Array (
$this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']),
$this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']),
);
}
// use default if not specified in session
if (!$cur_sort1 || !$cur_sort1_dir) {
$sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting');
if ($sorting) {
reset($sorting);
$cur_sort1 = key($sorting);
$cur_sort1_dir = current($sorting);
if (next($sorting)) {
$cur_sort2 = key($sorting);
$cur_sort2_dir = current($sorting);
}
}
}
// always add forced sorting before any user sorting fields
$forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting');
/* @var $forced_sorting Array */
-
+
if ($forced_sorting) {
foreach ($forced_sorting as $field => $dir) {
$object->AddOrderField($field, $dir);
}
}
// add user sorting fields
if ($cur_sort1 != '' && $cur_sort1_dir != '') {
$object->AddOrderField($cur_sort1, $cur_sort1_dir);
}
if ($cur_sort2 != '' && $cur_sort2_dir != '') {
$object->AddOrderField($cur_sort2, $cur_sort2_dir);
}
}
/**
* Gets list setting by name (persistent or real session)
*
* @param kEvent $event
* @param string $variable_name
* @return string
*/
function getListSetting(&$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 pesistent session
$variable_value = $this->Application->RecallPersistentVar($storage_prefix . '_' . $variable_name . '.' . $view_name, ALLOW_DEFAULT_SETTINGS);
/*if (!$variable_value) {
// get sorting from session
$variable_value = $this->Application->RecallVar($storage_prefix . '_' . $variable_name);
}*/
return $variable_value;
}
/**
* Sets list setting by name (persistent and real session)
*
* @param kEvent $event
* @param string $variable_name
* @param string $variable_value
*/
function setListSetting(&$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) ) {
$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
*/
function AddFilters(&$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);
}
}
}
}
$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++;
}
}
}
/**
* Set's new sorting for list
*
* @param kEvent $event
* @access protected
*/
function OnSetSorting(&$event)
{
$cur_sort1 = $this->getListSetting($event, 'Sort1');
$cur_sort1_dir = $this->getListSetting($event, 'Sort1_Dir');
$use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting');
if ($use_double_sorting) {
$cur_sort2 = $this->getListSetting($event, 'Sort2');
$cur_sort2_dir = $this->getListSetting($event, 'Sort2_Dir');
}
$passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Sort1');
if ($cur_sort1 == $passed_sort1) {
$cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc';
}
else {
if ($use_double_sorting) {
$cur_sort2 = $cur_sort1;
$cur_sort2_dir = $cur_sort1_dir;
}
$cur_sort1 = $passed_sort1;
$cur_sort1_dir = 'asc';
}
$this->setListSetting($event, 'Sort1', $cur_sort1);
$this->setListSetting($event, 'Sort1_Dir', $cur_sort1_dir);
if ($use_double_sorting) {
$this->setListSetting($event, 'Sort2', $cur_sort2);
$this->setListSetting($event, 'Sort2_Dir', $cur_sort2_dir);
}
}
/**
* Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu)
*
* @param kEvent $event
*/
function OnSetSortingDirect(&$event)
{
// 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, 'Sort1', $field);
$this->setListSetting($event, '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
*/
function OnResetSorting(&$event)
{
$this->setListSetting($event, 'Sort1');
$this->setListSetting($event, 'Sort1_Dir');
$this->setListSetting($event, 'Sort2');
$this->setListSetting($event, 'Sort2_Dir');
}
/**
* Sets grid refresh interval
*
* @param kEvent $event
*/
function OnSetAutoRefreshInterval(&$event)
{
$refresh_interval = $this->Application->GetVar('refresh_interval');
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_interval);
}
/**
* Changes auto-refresh state for grid
*
* @param kEvent $event
*/
function OnAutoRefreshToggle(&$event)
{
$refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals');
if (!$refresh_intervals) {
return ;
}
$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
$auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name);
if ($auto_refresh === false) {
$refresh_intervals = explode(',', $refresh_intervals);
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_intervals[0]);
}
$this->Application->StorePersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name, $auto_refresh ? 0 : 1);
}
/**
* Creates needed sql query to load item,
* if no query is defined in config for
* special requested, then use default
* query
*
* @param kEvent $event
* @access protected
*/
function ItemPrepareQuery(&$event)
{
$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
* @access protected
*/
function ListPrepareQuery(&$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(&$event, $type)
{
}
/* Edit Events mostly used in Admin */
/**
* Creates new kDBItem
*
* @param kEvent $event
* @access protected
*/
function OnCreate(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
list($id,$field_values) = each($items_info);
$object->SetFieldsFromHash($field_values);
}
$this->customProcessing($event,'before');
//look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default
if ( $object->Create($event->getEventParam('ForceCreateId')) ) {
$this->customProcessing($event,'after');
$event->status=kEvent::erSUCCESS;
$event->setRedirectParams(Array('opener'=>'u'), true);
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
$this->Application->SetVar($event->getPrefixSpecial().'_SaveEvent','OnCreate');
$object->setID($id);
}
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @access protected
*/
function OnUpdate(&$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
-
+
if ( $items_info ) {
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$object->SetFieldsFromHash($field_values);
$this->customProcessing($event, 'before');
if ( $object->Update($id) ) {
$this->customProcessing($event, 'after');
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Delete's kDBItem object
*
* @param kEvent $event
* @access protected
*/
function OnDelete(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$temp->DeleteItems($event->Prefix, $event->Special, Array($this->getPassedID($event)));
}
/**
* Deletes all records from table
*
* @param kEvent $event
*/
function OnDeleteAll(&$event)
{
$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName');
$ids = $this->Conn->GetCol($sql);
if ($ids) {
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
}
}
/**
* Prepares new kDBItem object
*
* @param kEvent $event
* @access protected
*/
function OnNew(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$object->Clear(0);
$this->Application->SetVar($event->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(&$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');
/* @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(&$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return ;
}
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @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
*/
function setTempWindowID(&$event)
{
$prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true));
foreach ($prefixes as $prefix) {
$mode = $this->Application->GetVar($prefix . '_mode');
if ($mode == 't') {
$wid = $this->Application->GetVar('m_wid');
$this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid);
break;
}
}
}
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
*/
function OnEdit(&$event)
{
$this->setTempWindowID($event);
$ids = $this->StoreSelectedIDs($event);
$this->Application->RemoveVar( $this->_getPendingActionVariableName($event) );
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
$temp->PrepareEdit();
$event->SetRedirectParam('m_lang', $this->Application->GetDefaultLanguageId());
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', array_shift($ids));
$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
}
/**
* Saves content of temp table into live and
* redirects to event' default redirect (normally grid template)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSave(&$event)
{
$event->CallSubEvent('OnPreSave');
if ($event->status != kEvent::erSUCCESS) {
return ;
}
$skip_master = false;
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @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->setRedirectParams(Array ('opener' => 'u'), true);
$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
// all temp tables are deleted here => all after hooks should think, that it's live mode now
$this->Application->SetVar($event->Prefix . '_mode', '');
}
function SaveLoggedChanges($changes_var_name, $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(&$event)
{
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$temp_handler->CancelEdit();
$this->clearSelectedIDs($event);
$event->setRedirectParams(Array ('opener' => 'u'), true);
$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
}
/**
* Allows to determine if we are creating new item or editing already created item
*
* @param kEvent $event
* @return bool
*/
function isNewItemCreate(&$event)
{
$object =& $event->getObject( Array ('raise_warnings' => 0) );
/* @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(&$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 ;
}
$object =& $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ( $items_info ) {
foreach ($items_info as $id => $field_values) {
$object->Load($id);
$object->SetFieldsFromHash($field_values);
$this->customProcessing($event, 'before');
if ( $object->Update($id) ) {
$this->customProcessing($event, 'after');
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
}
/**
* [HOOK] Saves sub-item
*
* @param kEvent $event
*/
function OnPreSaveSubItem(&$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
*/
function OnPreSaveAndGo(&$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
*/
function OnPreSaveAndGoToTab(&$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
*/
function OnUpdateAndGoToTab(&$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(&$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');
/* @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(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$object->SetFieldsFromHash( $this->getSubmittedFields($event) );
$this->customProcessing($event, 'before');
if ( $object->Create() ) {
$this->customProcessing($event, 'after');
$event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID());
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
$object->setID(0);
}
}
/**
* Reloads form to loose all changes made during item editing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnReset(&$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(&$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return ;
}
$object =& $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$ids = $this->StoreSelectedIDs($event);
if ( $ids ) {
$status_field = array_shift( $this->Application->getUnitOption($event->Prefix, 'StatusField') );
$order_field = $this->Application->getUnitOption($event->Prefix, 'OrderField');
if ( !$order_field ) {
$order_field = 'Priority';
}
foreach ($ids as $id) {
$object->Load($id);
switch ( $event->Name ) {
case 'OnMassApprove':
$object->SetDBField($status_field, 1);
break;
case 'OnMassDecline':
$object->SetDBField($status_field, 0);
break;
case 'OnMassMoveUp':
$object->SetDBField($order_field, $object->GetDBField($order_field) + 1);
break;
case 'OnMassMoveDown':
$object->SetDBField($order_field, $object->GetDBField($order_field) - 1);
break;
}
if ( $object->Update() ) {
$event->status = kEvent::erSUCCESS;
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
break;
}
}
}
$this->clearSelectedIDs($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnMassClone(&$event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return ;
}
$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
/* @var $temp_handler kTempTablesHandler */
$ids = $this->StoreSelectedIDs($event);
if ( $ids ) {
$temp_handler->CloneItems($event->Prefix, $event->Special, $ids);
}
$this->clearSelectedIDs($event);
}
function check_array($records, $field, $value)
{
foreach ($records as $record) {
if ($record[$field] == $value) {
return true;
}
}
return false;
}
/**
* Saves data from editing form to database without checking required fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSavePopup(&$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
/**
* Occurse before loading item, 'id' parameter
* allows to get id of item beeing loaded
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemLoad(&$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(&$event)
{
}
/**
* Occurs before creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(&$event)
{
}
/**
* Occurs after creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
if ( !$object->IsTempTable() ) {
$this->_proccessPendingActions($event);
}
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(&$event)
{
}
/**
* Occurs after updating item
*
* @param kEvent $event
* @access public
*/
function OnAfterItemUpdate(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
if ( !$object->IsTempTable() ) {
$this->_proccessPendingActions($event);
}
}
/**
* Occurse before deleting item, id of item beeing
* deleted is stored as 'id' event param
*
* @param kEvent $event
* @access public
*/
function OnBeforeItemDelete(&$event)
{
}
/**
* Occurse after deleting item, id of deleted item
* is stored as 'id' param of event
*
* @param kEvent $event
* @access public
*/
function OnAfterItemDelete(&$event)
{
}
/**
* Occurs before validation attempt
*
* @param kEvent $event
*/
function OnBeforeItemValidate(&$event)
{
}
/**
* Occurs after successful item validation
*
* @param kEvent $event
*/
function OnAfterItemValidate(&$event)
{
}
/**
* Occures after an item has been copied to temp
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterCopyToTemp(&$event)
{
}
/**
* 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(&$event)
{
}
/**
* Occures before an item is copied to live table (after all foreign keys have been updated)
* Id of item being copied is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnBeforeCopyToLive(&$event)
{
}
/**
* Occures after an item has been copied to live table
* Id of copied item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterCopyToLive(&$event)
{
$this->_proccessPendingActions($event);
}
/**
* Processing file pending actions (e.g. delete scheduled files)
*
* @param kEvent $event
*/
function _proccessPendingActions(&$event)
{
$var_name = $this->_getPendingActionVariableName($event);
$schedule = $this->Application->RecallVar($var_name);
if ( $schedule ) {
$schedule = unserialize($schedule);
foreach ($schedule as $data) {
if ( $data['action'] == 'delete' ) {
unlink( $data['file'] );
}
}
$this->Application->RemoveVar($var_name);
}
}
/**
* Returns variable name, used to store pending file actions
*
* @param kEvent $event
* @return string
*/
function _getPendingActionVariableName(&$event)
{
$window_id = $this->Application->GetTopmostWid($event->Prefix);
return $event->Prefix . '_file_pending_actions' . $window_id;
}
/**
* Occurs before an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(&$event)
{
}
/**
* Occures after an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterClone(&$event)
{
}
/**
* Occures after list is queried
*
* @param kEvent $event
*/
function OnAfterListQuery(&$event)
{
}
/**
* Ensures that popup will be closed automatically
* and parent window will be refreshed with template
* passed
*
* @param kEvent $event
* @return void
* @access protected
* @deprecated
*/
protected function finalizePopup(&$event)
{
$event->SetRedirectParam('opener', 'u');
}
/**
* Create search filters based on search query
*
* @param kEvent $event
* @access protected
*/
function OnSearch(&$event)
{
$event->setPseudoClass('_List');
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->performSearch($event);
}
/**
* Clear search keywords
*
* @param kEvent $event
* @access protected
*/
function OnSearchReset(&$event)
{
$search_helper =& $this->Application->recallObject('SearchHelper');
/* @var $search_helper kSearchHelper */
$search_helper->resetSearch($event);
}
/**
* Set's new filter value (filter_id meaning from config)
*
* @param kEvent $event
*/
function OnSetFilter(&$event)
{
$filter_id = $this->Application->GetVar('filter_id');
$filter_value = $this->Application->GetVar('filter_value');
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array();
$view_filter[$filter_id] = $filter_value;
$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
}
/**
* Sets view filter based on request
- *
+ *
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSetFilterPattern(&$event)
{
$filters = $this->Application->GetVar($event->getPrefixSpecial(true) . '_filters');
if ( !$filters ) {
return;
}
$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
$view_filter = $view_filter ? unserialize($view_filter) : Array ();
$filters = explode(',', $filters);
foreach ($filters as $a_filter) {
list($id, $value) = explode('=', $a_filter);
$view_filter[$id] = $value;
}
-
+
$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
$event->redirect = false;
}
/**
* Add/Remove all filters applied to list from "View" menu
*
* @param kEvent $event
*/
function FilterAction(&$event)
{
$view_filter = Array();
$filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu');
switch ($event->Name)
{
case 'OnRemoveFilters':
$filter_value = 1;
break;
case 'OnApplyFilters':
$filter_value = 0;
break;
}
foreach($filter_menu['Filters'] as $filter_key => $filter_params)
{
if(!$filter_params) continue;
$view_filter[$filter_key] = $filter_value;
}
$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
}
/**
* Enter description here...
*
* @param kEvent $event
* @access protected
*/
protected function OnPreSaveAndOpenTranslator(&$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, true);
// 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
*/
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
*/
function OnSelectUser(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$items_info = $this->Application->GetVar('u');
if ( $items_info ) {
$user_id = array_shift( array_keys($items_info) );
$this->RemoveRequiredFields($object);
$is_new = !$object->isLoaded();
$is_main = substr($this->Application->GetVar($event->Prefix . '_mode'), 0, 1) == 't';
if ( $is_new ) {
$new_event = $is_main ? 'OnPreCreate' : 'OnNew';
$event->CallSubEvent($new_event);
$event->redirect = true;
}
$object->SetDBField($this->Application->RecallVar('dst_field'), $user_id);
if ( $is_new ) {
$object->Create();
}
else {
$object->Update();
}
}
$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $object->GetID());
$event->SetRedirectParam('opener', 'u');
}
/** EXPORT RELATED **/
/**
* Shows export dialog
*
* @param kEvent $event
*/
function OnExport(&$event)
{
$selected_ids = $this->StoreSelectedIDs($event);
if (implode(',', $selected_ids) == '') {
// K4 fix when no ids found bad selected ids array is formed
$selected_ids = false;
}
$this->Application->StoreVar($event->Prefix.'_export_ids', $selected_ids ? implode(',', $selected_ids) : '' );
$this->Application->LinkVar('export_finish_t');
$this->Application->LinkVar('export_progress_t');
$this->Application->StoreVar('export_oroginal_special', $event->Special);
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/*list ($index_file, $env) = explode('|', $this->Application->RecallVar('last_template'));
$finish_url = $this->Application->BaseURL('/admin').$index_file.'?'.ENV_VAR_NAME.'='.$env;
$this->Application->StoreVar('export_finish_url', $finish_url);*/
$redirect_params = Array (
$this->Prefix . '.export_event' => 'OnNew',
'pass' => 'all,' . $this->Prefix . '.export'
);
$event->setRedirectParams($redirect_params);
}
/**
* Apply some special processing to object 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, &$event)
{
if ( $event->Special == 'export' || $event->Special == 'import' ) {
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->prepareExportColumns($event);
}
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
*/
function getCustomExportColumns(&$event)
{
return Array();
}
/**
* Export form validation & processing
*
* @param kEvent $event
*/
function OnExportBegin(&$event)
{
$export_helper =& $this->Application->recallObject('CatItemExportHelper');
/* @var $export_helper kCatDBItemExportHelper */
$export_helper->OnExportBegin($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnExportCancel(&$event)
{
$this->OnGoBack($event);
}
/**
* Allows configuring export options
*
* @param kEvent $event
*/
function OnBeforeExportBegin(&$event)
{
}
/**
* Deletes export preset
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnDeleteExportPreset(&$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
*/
function OnPreSaveAndChangeLanguage(&$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(&$event)
{
$event->status = kEvent::erSTOP;
// define('DBG_SKIP_REPORTING', 0);
$default_msg = "Flash requires that we output something or it won't fire the uploadSuccess event";
if (!$this->Application->HttpQuery->Post) {
// Variables {field, id, flashsid} are always submitted through POST!
// When file size is larger, then "upload_max_filesize" (in php.ini),
// then these variables also are not submitted -> handle such case.
header('HTTP/1.0 413 File size exceeds allowed limit');
echo $default_msg;
return ;
}
if (!$this->_checkFlashUploaderPermission($event)) {
// 403 Forbidden
header('HTTP/1.0 403 You don\'t have permissions to upload');
echo $default_msg;
return ;
}
$value = $this->Application->GetVar('Filedata');
if (!$value || ($value['error'] != UPLOAD_ERR_OK)) {
// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
// to large for web server to accept, see "upload_max_filesize" in php.ini)
header('HTTP/1.0 413 File size exceeds allowed limit');
echo $default_msg;
return ;
}
$tmp_path = WRITEABLE . '/tmp/';
$fname = $value['name'];
$id = $this->Application->GetVar('id');
if ($id) {
$fname = $id . '_' . $fname;
}
$field_name = $this->Application->GetVar('field');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
$field_options = array_key_exists($field_name, $fields) ? $fields[$field_name] : $virtual_fields[$field_name];
$upload_dir = $field_options['upload_dir'];
$storage_format = array_key_exists('storage_format', $field_options) ? $field_options['storage_format'] : false;
if (!is_writable($tmp_path)) {
// 500 Internal Server Error
// check both temp and live upload directory
header('HTTP/1.0 500 Write permissions not set on the server');
echo $default_msg;
return ;
}
$upload_formatter =& $this->Application->recallObject('kUploadFormatter');
/* @var $upload_formatter kUploadFormatter */
$fname = $upload_formatter->_ensureUniqueFilename($tmp_path, $fname);
if ($storage_format) {
$image_helper =& $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
move_uploaded_file($value['tmp_name'], $value['tmp_name'] . '.jpg'); // add extension, so ResizeImage can work
$url = $image_helper->ResizeImage($value['tmp_name'] . '.jpg', $storage_format);
$tmp_name = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', '/', $url);
rename($tmp_name, $tmp_path.$fname);
}
else {
move_uploaded_file($value['tmp_name'], $tmp_path.$fname);
}
echo preg_replace('/^' . preg_quote($id, '/') . '_/', '', $fname);
$this->deleteTempFiles($tmp_path);
}
/**
* Delete temporary files, that won't be used for sure
*
* @param string $path
*/
function deleteTempFiles($path)
{
$files = glob($path . '*.*');
$max_file_date = strtotime('-1 day');
foreach ($files as $file) {
if (filemtime($file) < $max_file_date) {
unlink($file);
}
}
}
/**
* Checks, that flash uploader is allowed to perform upload
*
* @param kEvent $event
* @return bool
*/
function _checkFlashUploaderPermission(&$event)
{
// Flash uploader does NOT send correct cookies, so we need to make our own check
$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
$admin_ses =& $this->Application->recallObject('Session.admin');
/* @var $admin_ses Session */
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;
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnDeleteFile(&$event)
{
$event->status = kEvent::erSTOP;
if (strpos($this->Application->GetVar('file'), '../') !== false) {
return ;
}
$object =& $event->getObject( Array ('skip_autoload' => true) );
$options = $object->GetFieldOptions( $this->Application->GetVar('field') );
$var_name = $this->_getPendingActionVariableName($event);
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : Array ();
$schedule[] = Array ('action' => 'delete', 'file' => FULL_PATH . $options['upload_dir'] . $this->Application->GetVar('file'));
$this->Application->StoreVar($var_name, serialize($schedule));
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnViewFile(&$event)
{
$event->status = kEvent::erSTOP;
$file = $this->Application->GetVar('file');
if ((strpos($file, '../') !== false) || (trim($file) !== $file)) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
return ;
}
$object =& $event->getObject( Array ('skip_autoload' => true));
/* @var $object kDBItem */
$field = $this->Application->GetVar('field');
$options = $object->GetFieldOptions($field);
// set current uploaded file
if ($this->Application->GetVar('tmp')) {
$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
unset($options['include_path']);
$object->SetFieldOptions($field, $options);
$object->SetDBField($field, $this->Application->GetVar('id') . '_' . $file);
}
else {
$object->SetDBField($field, $file);
}
// get url to uploaded file
if ($this->Application->GetVar('thumb')) {
$url = $object->GetField($field, $options['thumb_format']);
}
else {
$url = $object->GetField($field, 'full_url'); // don't use "file_urls" format to prevent recursion
}
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$path = $file_helper->urlToPath($url);
if (!file_exists($path)) {
exit;
}
$type = mime_content_type($path);
header('Content-Length: ' . filesize($path));
header('Content-Type: ' . $type);
header('Content-Disposition: inline; filename="' . $file . '"');
readfile($path);
}
/**
* Validates MInput control fields
*
* @param kEvent $event
*/
function OnValidateMInputFields(&$event)
{
$minput_helper =& $this->Application->recallObject('MInputHelper');
/* @var $minput_helper MInputHelper */
$minput_helper->OnValidateMInputFields($event);
}
/**
* Returns auto-complete values for ajax-dropdown
*
* @param kEvent $event
*/
function OnSuggestValues(&$event)
{
if (!$this->Application->isAdminUser) {
// very careful here, because this event allows to
// view every object field -> limit only to logged-in admins
return ;
}
$event->status = kEvent::erSTOP;
$field = $this->Application->GetVar('field');
$cur_value = $this->Application->GetVar('cur_value');
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$object =& $event->getObject();
if (!$field || !$cur_value || !$object->isField($field)) {
return ;
}
$limit = $this->Application->GetVar('limit');
if (!$limit) {
$limit = 20;
}
$sql = 'SELECT DISTINCT '.$field.'
FROM '.$this->Application->getUnitOption($event->Prefix, 'TableName').'
WHERE '.$field.' LIKE '.$this->Conn->qstr($cur_value.'%').'
ORDER BY '.$field.'
LIMIT 0,' . $limit;
$data = $this->Conn->GetCol($sql);
$this->Application->XMLHeader();
echo '<suggestions>';
foreach ($data as $item) {
echo '<item>' . htmlspecialchars($item) . '</item>';
}
echo '</suggestions>';
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnSaveWidths(&$event)
{
$event->status = kEvent::erSTOP;
/*$lang =& $this->Application->recallObject('lang.current');
header('Content-type: text/xml; charset=' . $lang->GetDBField('Charset'));*/
$picker_helper =& $this->Application->RecallObject('ColumnPickerHelper');
/* @var $picker_helper kColumnPickerHelper */
$picker_helper->PreparePicker($event->getPrefixSpecial(), $this->Application->GetVar('grid_name'));
$picker_helper->SaveWidths($event->getPrefixSpecial(), $this->Application->GetVar('widths'));
echo 'OK';
}
/**
* Called from CSV import script after item fields
* are set and validated, but before actual item create/update.
* If event status is kEvent::erSUCCESS, line will be imported,
* else it will not be imported but added to skipped lines
* and displayed in the end of import.
* Event status is preset from import script.
*
* @param kEvent $event
*/
function OnBeforeCSVLineImport(&$event)
{
// abstract, for hooking
}
/**
* [HOOK] Allows to add cloned subitem to given prefix
*
* @param kEvent $event
*/
function OnCloneSubItem(&$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);
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/db/dbitem.php
===================================================================
--- branches/5.2.x/core/kernel/db/dbitem.php (revision 14607)
+++ branches/5.2.x/core/kernel/db/dbitem.php (revision 14608)
@@ -1,1413 +1,1416 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* DBItem
*
*/
class kDBItem extends kDBBase {
/**
* Description
*
* @var array Associative array of current item' field values
* @access protected
*/
protected $FieldValues = Array ();
/**
* Unformatted field values, before parse
*
* @var Array
* @access protected
*/
protected $DirtyFieldValues = Array ();
/**
* Holds item values after loading (not affected by submit)
*
* @var Array
* @access protected
*/
protected $OriginalFieldValues = Array ();
/**
* If set to true, Update will skip Validation before running
*
* @var array Associative array of current item' field values
* @access public
*/
public $IgnoreValidation = false;
/**
* Remembers if object was loaded
*
* @var bool
* @access protected
*/
protected $Loaded = false;
/**
* Holds item' primary key value
*
* @var int Value of primary key field for current item
* @access protected
*/
protected $ID;
/**
* This object is used in cloning operations
*
* @var bool
* @access public
*/
public $inCloning = false;
/**
* Validator object reference
*
* @var kValidator
*/
protected $validator = null;
/**
* Creates validator object, only when required
*
*/
public function initValidator()
{
if ( !is_object($this->validator) ) {
$validator_class = $this->Application->getUnitOption($this->Prefix, 'ValidatorClass', 'kValidator');
$this->validator =& $this->Application->makeClass($validator_class);
}
$this->validator->setDataSource($this);
}
public function SetDirtyField($field_name, $field_value)
{
$this->DirtyFieldValues[$field_name] = $field_value;
}
public function GetDirtyField($field_name)
{
return $this->DirtyFieldValues[$field_name];
}
public function GetOriginalField($field_name, $formatted = false, $format=null)
{
if (array_key_exists($field_name, $this->OriginalFieldValues)) {
// item was loaded before
$value = $this->OriginalFieldValues[$field_name];
}
else {
// no original fields -> use default field value
$value = $this->Fields[$field_name]['default'];
}
if (!$formatted) {
return $value;
}
$res = $value;
$formatter = $this->GetFieldOption($field_name, 'formatter');
if ( $formatter ) {
$formatter =& $this->Application->recallObject($formatter);
/* @var $formatter kFormatter */
$res = $formatter->Format($value, $field_name, $this, $format);
}
return $res;
}
/**
* Sets original field value (useful for custom virtual fields)
*
* @param string $field_name
*/
public function SetOriginalField($field_name, $field_value)
{
$this->OriginalFieldValues[$field_name] = $field_value;
}
/**
* Set's default values for all fields
*
* @access public
*/
public function SetDefaultValues()
{
parent::SetDefaultValues();
if ($this->populateMultiLangFields) {
$this->PopulateMultiLangFields();
}
foreach ($this->Fields as $field => $field_options) {
$default_value = isset($field_options['default']) ? $field_options['default'] : NULL;
$this->SetDBField($field, $default_value);
}
}
/**
* Sets current item field value
* (applies formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetField($name,$value)
{
$options = $this->GetFieldOptions($name);
$parsed = $value;
if ($value == '') {
$parsed = NULL;
}
// kFormatter is always used, to make sure, that numeric value is converted to normal representation
// according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why)
$formatter =& $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter');
/* @var $formatter kFormatter */
$parsed = $formatter->Parse($value, $name, $this);
$this->SetDBField($name,$parsed);
}
/**
* Sets current item field value
* (doesn't apply formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
public function SetDBField($name,$value)
{
$this->FieldValues[$name] = $value;
}
/**
* Set's field error, if pseudo passed not found then create it with message text supplied.
* Don't overwrite existing pseudo translation.
*
* @param string $field
* @param string $pseudo
* @param string $error_label
* @param Array $error_params
*
* @return bool
* @access public
*/
public function SetError($field, $pseudo, $error_label = null, $error_params = null)
{
$this->initValidator();
return $this->validator->SetError($field, $pseudo, $error_label, $error_params);
}
/**
* Removes error on field
*
* @param string $field
* @access public
*/
public function RemoveError($field)
{
if ( !is_object($this->validator) ) {
return ;
}
$this->validator->RemoveError($field);
}
/**
* Returns error pseudo
*
* @param string $field
* @return string
*/
public function GetErrorPseudo($field)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorPseudo($field);
}
/**
* Return current item' field value by field name
* (doesn't apply formatter)
*
* @param string $name field name to return
* @return mixed
* @access public
*/
public function GetDBField($name)
{
/*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) {
$this->Application->Debugger->appendTrace();
}*/
return $this->FieldValues[$name];
}
public function HasField($name)
{
return array_key_exists($name, $this->FieldValues);
}
public function GetFieldValues()
{
return $this->FieldValues;
}
/**
* Sets item' fields corresponding to elements in passed $hash values.
*
* The function sets current item fields to values passed in $hash, by matching $hash keys with field names
* of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before actually setting the fields
*
* @param Array $hash
* @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
* @return void
* @access public
*/
public function SetFieldsFromHash($hash, $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
// used in formatter which work with multiple fields together
foreach ($set_fields as $field_name) {
$this->SetDirtyField($field_name, $hash[$field_name]);
}
// formats all fields using associated formatters
foreach ($set_fields as $field_name) {
$this->SetField($field_name, $hash[$field_name]);
}
}
/**
* Sets object fields from $hash arrat
* @param Array $hash
* @param Array|null $set_fields
* @return void
* @access public
*/
public function SetDBFieldsFromHash($hash, $set_fields = Array ())
{
if ( !$set_fields ) {
$set_fields = array_keys($hash);
}
$set_fields = array_intersect($set_fields, array_keys($this->Fields));
foreach ($set_fields as $field_name) {
$this->SetDBField($field_name, $hash[$field_name]);
}
}
/**
* Returns part of SQL WHERE clause identifying the record, ex. id = 25
*
* @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method
* @param Array $keys_hash alternative, then item id, keys hash to load item by
* @see kDBItem::Load()
* @see kDBItem::Update()
* @see kDBItem::Delete()
* @return string
* @access protected
*/
protected function GetKeyClause($method = null, $keys_hash = null)
{
if ( !isset($keys_hash) ) {
$keys_hash = Array ($this->IDField => $this->ID);
}
$ret = '';
foreach ($keys_hash as $field => $value) {
if ( !preg_match('/\./', $field) ) {
$ret .= '(`' . $this->TableName . '`.' . $field . ' = ' . $this->Conn->qstr($value) . ') AND ';
}
else {
$ret .= '(' . $field . ' = ' . $this->Conn->qstr($value) . ') AND ';
}
}
return substr($ret, 0, -5);
}
/**
* Loads item from the database by given id
*
* @access public
* @param mixed $id item id of keys->values hash to load item by
* @param string $id_field_name Optional parameter to load item by given Id field
* @param bool $cachable cache this query result based on it's prefix serial
* @return bool True if item has been loaded, false otherwise
*/
public function Load($id, $id_field_name = null, $cachable = false)
{
if ( isset($id_field_name) ) {
$this->IDField = $id_field_name; // set new IDField
}
$keys_sql = '';
if (is_array($id)) {
$keys_sql = $this->GetKeyClause('load', $id);
}
else {
$this->setID($id);
$keys_sql = $this->GetKeyClause('load');
}
if ( isset($id_field_name) ) {
// restore original IDField from unit config
$this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField');
}
if (($id === false) || !$keys_sql) {
return $this->Clear();
}
if (!$this->raiseEvent('OnBeforeItemLoad', $id)) {
return false;
}
$q = $this->GetSelectSQL() . ' WHERE ' . $keys_sql;
if ($cachable && $this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
$serial_name = $this->Application->incrementCacheSerial($this->Prefix == 'st' ? 'c' : $this->Prefix, isset($id_field_name) ? null : $id, false);
$cache_key = 'kDBItem::Load_' . crc32(serialize($id) . '-' . $this->IDField) . '[%' . $serial_name . '%]';
$field_values = $this->Application->getCache($cache_key, false);
if ($field_values === false) {
$field_values = $this->Conn->GetRow($q);
if ($field_values !== false) {
// only cache, when data was retrieved
$this->Application->setCache($cache_key, $field_values);
}
}
}
else {
$field_values = $this->Conn->GetRow($q);
}
if ($field_values) {
$this->FieldValues = array_merge($this->FieldValues, $field_values);
$this->OriginalFieldValues = $this->FieldValues;
}
else {
return $this->Clear();
}
if (is_array($id) || isset($id_field_name)) {
$this->setID($this->FieldValues[$this->IDField]);
}
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $this->GetID());
$this->Loaded = true;
return true;
}
/**
* Loads object from hash (not db)
*
* @param Array $fields_hash
* @param string $id_field
*/
public function LoadFromHash($fields_hash, $id_field = null)
{
if (!isset($id_field)) {
$id_field = $this->IDField;
}
$this->Clear();
if (!$fields_hash || !array_key_exists($id_field, $fields_hash)) {
// no data OR id field missing
return false;
}
$id = $fields_hash[$id_field];
if ( !$this->raiseEvent('OnBeforeItemLoad', $id) ) {
return false;
}
$this->FieldValues = array_merge($this->FieldValues, $fields_hash);
$this->OriginalFieldValues = $this->FieldValues;
$this->setID($id);
$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
$this->raiseEvent('OnAfterItemLoad', $id);
$this->Loaded = true;
return true;
}
/**
* Builds select sql, SELECT ... FROM parts only
*
* @access public
* @return string
*/
/**
* Returns SELECT part of list' query
*
* @param string $base_query
* @param bool $replace_table
* @return string
* @access public
*/
public function GetSelectSQL($base_query = null, $replace_table = true)
{
if (!isset($base_query)) {
$base_query = $this->SelectClause;
}
$base_query = $this->addCalculatedFields($base_query);
return parent::GetSelectSQL($base_query, $replace_table);
}
public function UpdateFormattersMasterFields()
{
$this->initValidator(); // used, when called not from kValidator::Validate method
foreach ($this->Fields as $field => $options) {
if ( isset($options['formatter']) ) {
$formatter =& $this->Application->recallObject($options['formatter']);
/* @var $formatter kFormatter */
$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
}
}
}
/**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
* @param mixed $force_id
* @return bool
*/
public function skipField($field_name, $force_id = false)
{
$skip = false;
// 1. skipping 'virtual' field
$skip = $skip || array_key_exists($field_name, $this->VirtualFields);
// 2. don't write empty field value to db, when "skip_empty" option is set
$field_value = array_key_exists($field_name, $this->FieldValues) ? $this->FieldValues[$field_name] : false;
if (array_key_exists($field_name, $this->Fields)) {
$skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['skip_empty'] : false;
}
else {
// field found in database, but not declared in unit config
$skip_empty = false;
}
$skip = $skip || (!$field_value && $skip_empty);
// 3. skipping field not in Fields (nor virtual, nor real)
$skip = $skip || !array_key_exists($field_name, $this->Fields);
return $skip;
}
/**
* Updates previously loaded record with current item' values
*
* @access public
* @param int $id Primary Key Id to update
* @param bool $system_update
* @return bool
* @access public
*/
public function Update($id = null, $system_update = false)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemUpdate') ) {
return false;
}
if ( !isset($this->ID) ) {
// ID could be set inside OnBeforeItemUpdate event, so don't combine this check with previous one
return false;
}
// validate before updating
if ( !$this->Validate() ) {
return false;
}
if ( !$this->FieldValues ) {
// nothing to update
return true;
}
$sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ( $this->skipField($field_name) ) {
continue;
}
if ( is_null($field_value) ) {
if ( array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null'] ) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$field_value = $this->Fields[$field_name]['default'];
}
}
$sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', ';
}
$sql = 'UPDATE ' . $this->TableName . '
SET ' . substr($sql, 0, -2) . '
WHERE ' . $this->GetKeyClause('update');
if ( $this->Conn->ChangeQuery($sql) === false ) {
// there was and sql error
return false;
}
$affected_rows = $this->Conn->getAffectedRows();
if ( !$system_update && ($affected_rows > 0) ) {
$this->setModifiedFlag(ChangeLog::UPDATE);
}
$this->saveCustomFields();
$this->raiseEvent('OnAfterItemUpdate');
$this->OriginalFieldValues = $this->FieldValues;
$this->Loaded = true;
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return true;
}
/**
* Validates given field
*
* @param string $field
* @return bool
* @access public
*/
public function ValidateField($field)
{
$this->initValidator();
return $this->validator->ValidateField($field);
}
/**
* Validate all item fields based on
* constraints set in each field options
* in config
*
* @return bool
* @access private
*/
public function Validate()
{
if ( $this->IgnoreValidation ) {
return true;
}
$this->initValidator();
// will apply any custom validation to the item
$this->raiseEvent('OnBeforeItemValidate');
if ( $this->validator->Validate() ) {
// no validation errors
$this->raiseEvent('OnAfterItemValidate');
return true;
}
return false;
}
/**
* Check if item has errors
*
* @param Array $skip_fields fields to skip during error checking
* @return bool
*/
public function HasErrors($skip_fields = Array ())
{
if ( !is_object($this->validator) ) {
return false;
}
return $this->validator->HasErrors($skip_fields);
}
/**
* Check if value is set for required field
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access public
* @todo Find a way to get rid of direct call from kMultiLanguage::UpdateMasterFields method
*/
public function ValidateRequired($field, $params)
{
return $this->validator->ValidateRequired($field, $params);
}
/**
* Return error message for field
*
* @param string $field
* @return string
* @access public
*/
public function GetErrorMsg($field, $force_escape = null)
{
if ( !is_object($this->validator) ) {
return '';
}
return $this->validator->GetErrorMsg($field, $force_escape);
}
/**
* Returns field errors
*
* @return Array
* @access public
*/
public function GetFieldErrors()
{
if ( !is_object($this->validator) ) {
return Array ();
}
return $this->validator->GetFieldErrors();
}
/**
* Creates a record in the database table with current item' values
*
* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
* @param bool $system_create
* @return bool
* @access public
*/
public function Create($force_id = false, $system_create = false)
{
if (!$this->raiseEvent('OnBeforeItemCreate')) {
return false;
}
// Validating fields before attempting to create record
if (!$this->Validate()) {
return false;
}
if (is_int($force_id)) {
$this->FieldValues[$this->IDField] = $force_id;
}
elseif (!$force_id || !is_bool($force_id)) {
$this->FieldValues[$this->IDField] = $this->generateID();
}
$fields_sql = '';
$values_sql = '';
foreach ($this->FieldValues as $field_name => $field_value) {
if ($this->skipField($field_name, $force_id)) {
continue;
}
if (is_null($field_value)) {
if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
// "kFormatter::Parse" methods converts empty values to NULL and for
// not-null fields they are replaced with default value here
$values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']);
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
else {
if (($field_name == $this->IDField) && ($field_value == 0) && !is_int($force_id)) {
// don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value
$values_sql .= 'DEFAULT';
}
else {
$values_sql .= $this->Conn->qstr($field_value);
}
}
$fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement
$values_sql .= ', ';
}
$sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ')
VALUES (' . substr($values_sql, 0, -2) . ')';
//Executing the query and checking the result
if ($this->Conn->ChangeQuery($sql) === false) {
return false;
}
$insert_id = $this->Conn->getInsertID();
if ($insert_id == 0) {
// insert into temp table (id is not auto-increment field)
$insert_id = $this->FieldValues[$this->IDField];
}
$this->setID($insert_id);
$this->OriginalFieldValues = $this->FieldValues;
if (!$system_create){
$this->setModifiedFlag(ChangeLog::CREATE);
}
$this->saveCustomFields();
if (!$this->IsTempTable()) {
$this->Application->resetCounters($this->TableName);
}
if ($this->IsTempTable() && ($this->Application->GetTopmostPrefix($this->Prefix) != $this->Prefix) && !is_int($force_id)) {
// temp table + subitem = set negative id
$this->setTempID();
}
$this->raiseEvent('OnAfterItemCreate');
$this->Loaded = true;
return true;
}
/**
* Deletes the record from database
*
* @param int $id
* @return bool
* @access public
*/
public function Delete($id = null)
{
if ( isset($id) ) {
$this->setID($id);
}
if ( !$this->raiseEvent('OnBeforeItemDelete') ) {
return false;
}
$sql = 'DELETE FROM ' . $this->TableName . '
WHERE ' . $this->GetKeyClause('Delete');
$ret = $this->Conn->ChangeQuery($sql);
$affected_rows = $this->Conn->getAffectedRows();
if ( $affected_rows > 0 ) {
$this->setModifiedFlag(ChangeLog::DELETE); // will change affected rows, so get it before this line
// something was actually deleted
$this->raiseEvent('OnAfterItemDelete');
}
if ( !$this->IsTempTable() ) {
$this->Application->resetCounters($this->TableName);
}
return $ret;
}
public function PopulateMultiLangFields()
{
foreach ($this->Fields as $field => $options) {
// master field is set only for CURRENT language
$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
if ( ($formatter == 'kMultiLanguage') && isset($options['master_field']) && isset($options['error_field']) ) {
// MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode,
// we display ML fields directly so we set it back to itself, otherwise error won't be displayed
unset( $this->Fields[$field]['error_field'] );
}
}
}
/**
* Sets new name for item in case if it is beeing copied
* in same table
*
* @param array $master Table data from TempHandler
* @param int $foreign_key ForeignKey value to filter name check query by
* @param string $title_field FieldName to alter, by default - TitleField of the prefix
* @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name
* @access public
*/
public function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s')
{
if (!isset($title_field)) {
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return;
}
$new_name = $this->GetDBField($title_field);
$original_checked = false;
do {
if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) {
$new_name = sprintf($format, ($regs[1]+1), $regs[2]);
}
elseif ($original_checked) {
$new_name = sprintf($format, '', $new_name);
}
// if we are cloning in temp table this will look for names in temp table,
// since object' TableName contains correct TableName (for temp also!)
// if we are cloning live - look in live
$query = 'SELECT '.$title_field.' FROM '.$this->TableName.'
WHERE '.$title_field.' = '.$this->Conn->qstr($new_name);
$foreign_key_field = getArrayValue($master, 'ForeignKey');
$foreign_key_field = is_array($foreign_key_field) ? $foreign_key_field[ $master['ParentPrefix'] ] : $foreign_key_field;
if ($foreign_key_field && isset($foreign_key)) {
$query .= ' AND '.$foreign_key_field.' = '.$foreign_key;
}
$res = $this->Conn->GetOne($query);
/*// if not found in live table, check in temp table if applicable
if ($res === false && $object->Special == 'temp') {
$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
$res = $this->Conn->GetOne($query);
}*/
$original_checked = true;
} while ($res !== false);
$this->SetDBField($title_field, $new_name);
}
protected function raiseEvent($name, $id = null, $additional_params = Array())
{
- if( !isset($id) ) $id = $this->GetID();
- $event = new kEvent( Array('name'=>$name,'prefix'=>$this->Prefix,'special'=>$this->Special) );
- $event->setEventParam('id', $id);
-
- if ($additional_params) {
- foreach ($additional_params as $ap_name => $ap_value) {
- $event->setEventParam($ap_name, $ap_value);
- }
+ $event = new kEvent( $this->getPrefixSpecial() . ':' . $name );
+
+ if ( is_object($this->parentEvent) ) {
+ $event->MasterEvent =& $this->parentEvent;
+ }
+
+ $additional_params['id'] = isset($id) ? $id : $this->GetID();
+
+ foreach ($additional_params as $ap_name => $ap_value) {
+ $event->setEventParam($ap_name, $ap_value);
}
$this->Application->HandleEvent($event);
- return $event->status == kEvent::erSUCCESS ? true : false;
+
+ return $event->status == kEvent::erSUCCESS;
}
/**
* Set's new ID for item
*
* @param int $new_id
* @access public
*/
public function setID($new_id)
{
$this->ID = $new_id;
$this->SetDBField($this->IDField, $new_id);
}
/**
* Generate and set new temporary id
*
* @access private
*/
public function setTempID()
{
$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$this->IDField.') FROM '.$this->TableName);
if($new_id > 0) $new_id = 0;
--$new_id;
$this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID());
if ($this->ShouldLogChanges(true)) {
// Updating TempId in ChangesLog, if changes are disabled
$ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix) . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
if ($changes) {
foreach ($changes as $key => $rec) {
if ($rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID()) {
// change log for record, that's ID was just updated -> update in change log record too
$changes[$key]['ItemId'] = $new_id;
}
if ($rec['MasterPrefix'] == $this->Prefix && $rec['MasterId'] == $this->GetID()) {
// master item id was changed
$changes[$key]['MasterId'] = $new_id;
}
if (in_array($this->Prefix, $rec['ParentPrefix']) && $rec['ParentId'][$this->Prefix] == $this->GetID()) {
// change log record of given item's sub item -> update changed id's in dependent fields
$changes[$key]['ParentId'][$this->Prefix] = $new_id;
if (array_key_exists('DependentFields', $rec)) {
// these are fields from table of $rec['Prefix'] table!
// when one of dependent fields goes into idfield of it's parent item, that was changed
$parent_table_key = $this->Application->getUnitOption($rec['Prefix'], 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$this->Prefix] : $parent_table_key;
if ($parent_table_key == $this->IDField) {
$foreign_key = $this->Application->getUnitOption($rec['Prefix'], 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$this->Prefix] : $foreign_key;
$changes[$key]['DependentFields'][$foreign_key] = $new_id;
}
}
}
}
}
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
$this->SetID($new_id);
}
/**
* Set's modification flag for main prefix of current prefix to true
*
* @access private
* @author Alexey
*/
public function setModifiedFlag($mode = null)
{
$main_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
$this->Application->StoreVar($main_prefix . '_modified', '1', true); // true for optional
if ($this->ShouldLogChanges(true)) {
$this->LogChanges($main_prefix, $mode);
if (!$this->IsTempTable()) {
$handler =& $this->Application->recallObject($this->Prefix . '_EventHandler');
/* @var $handler kDBEventHandler */
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$handler->SaveLoggedChanges($ses_var_name, $this->ShouldLogChanges());
}
}
}
/**
* Determines, that changes made to this item should be written to change log
*
* @param bool $log_changes
* @return bool
*/
public function ShouldLogChanges($log_changes = null)
{
if (!isset($log_changes)) {
// specific logging mode no forced -> use global logging settings
$log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog');
}
return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges');
}
protected function LogChanges($main_prefix, $mode)
{
if ( !$mode ) {
return ;
}
$ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$changes = $this->Application->RecallVar($ses_var_name);
$changes = $changes ? unserialize($changes) : Array ();
$fields_hash = Array (
'Prefix' => $this->Prefix,
'ItemId' => $this->GetID(),
'OccuredOn' => adodb_mktime(),
'MasterPrefix' => $main_prefix,
'Action' => $mode,
);
if ( $this->Prefix == $main_prefix ) {
// main item
$fields_hash['MasterId'] = $this->GetID();
$fields_hash['ParentPrefix'] = Array ($main_prefix);
$fields_hash['ParentId'] = Array ($main_prefix => $this->GetID());
}
else {
// sub item
// collect foreign key values (for serial reset)
$foreign_keys = $this->Application->getUnitOption($this->Prefix, 'ForeignKey', Array ());
$dependent_fields = $fields_hash['ParentId'] = $fields_hash['ParentPrefix'] = Array ();
/* @var $foreign_keys Array */
if ( is_array($foreign_keys) ) {
foreach ($foreign_keys as $prefix => $field_name) {
$dependent_fields[$field_name] = $this->GetDBField($field_name);
$fields_hash['ParentPrefix'][] = $prefix;
$fields_hash['ParentId'][$prefix] = $this->getParentId($prefix);
}
}
else {
$dependent_fields[$foreign_keys] = $this->GetDBField($foreign_keys);
$fields_hash['ParentPrefix'] = Array ( $this->Application->getUnitOption($this->Prefix, 'ParentPrefix') );
$fields_hash['ParentId'][ $fields_hash['ParentPrefix'][0] ] = $this->getParentId('auto');
}
$fields_hash['DependentFields'] = $dependent_fields;
// works only, when main item is present in url, when sub-item is changed
$master_id = $this->Application->GetVar($main_prefix . '_id');
if ( $master_id === false ) {
// works in case of we are not editing topmost item, when sub-item is created/updated/deleted
$master_id = $this->getParentId('auto', true);
}
$fields_hash['MasterId'] = $master_id;
}
switch ( $mode ) {
case ChangeLog::UPDATE:
$to_save = array_merge($this->GetTitleField(), $this->GetChangedFields());
break;
case ChangeLog::CREATE:
$to_save = $this->GetTitleField();
break;
case ChangeLog::DELETE:
$to_save = array_merge($this->GetTitleField(), $this->GetRealFields());
break;
default:
$to_save = Array ();
break;
}
$fields_hash['Changes'] = serialize($to_save);
$changes[] = $fields_hash;
$this->Application->StoreVar($ses_var_name, serialize($changes));
}
/**
* Returns current item parent's ID
*
* @param bool $top_most return topmost parent, when used
* @return int
*/
protected function getParentId($parent_prefix, $top_most = false)
{
$current_id = $this->GetID();
$current_prefix = $this->Prefix;
if ($parent_prefix == 'auto') {
$parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix');
}
if (!$parent_prefix) {
return $current_id;
}
do {
// field in this table
$foreign_key = $this->Application->getUnitOption($current_prefix, 'ForeignKey');
$foreign_key = is_array($foreign_key) ? $foreign_key[$parent_prefix] : $foreign_key;
// get foreign key value for $current_prefix
if ($current_prefix == $this->Prefix) {
$foreign_key_value = $this->GetDBField($foreign_key);
}
else {
$id_field = $this->Application->getUnitOption($current_prefix, 'IDField');
$table_name = $this->Application->getUnitOption($current_prefix, 'TableName');
if ($this->IsTempTable()) {
$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $current_prefix);
}
$sql = 'SELECT ' . $foreign_key . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' = ' . $current_id;
$foreign_key_value = $this->Conn->GetOne($sql);
}
// field in parent table
$parent_table_key = $this->Application->getUnitOption($current_prefix, 'ParentTableKey');
$parent_table_key = is_array($parent_table_key) ? $parent_table_key[$parent_prefix] : $parent_table_key;
$parent_id_field = $this->Application->getUnitOption($parent_prefix, 'IDField');
$parent_table_name = $this->Application->getUnitOption($parent_prefix, 'TableName');
if ($this->IsTempTable()) {
$parent_table_name = $this->Application->GetTempName($parent_table_name, 'prefix:' . $current_prefix);
}
if ($parent_id_field == $parent_table_key) {
// sub-item is related by parent item idfield
$current_id = $foreign_key_value;
}
else {
// sub-item is related by other parent item field
$sql = 'SELECT ' . $parent_id_field . '
FROM ' . $parent_table_name . '
WHERE ' . $parent_table_key . ' = ' . $foreign_key_value;
$current_id = $this->Conn->GetOne($sql);
}
$current_prefix = $parent_prefix;
if (!$top_most) {
break;
}
} while ( $parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix') );
return $current_id;
}
/**
* Returns title field (if any)
*
* @return Array
*/
public function GetTitleField()
{
$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
if ($title_field) {
$value = $this->GetField($title_field);
return $value ? Array ($title_field => $value) : Array ();
}
return Array ();
}
/**
* Returns only fields, that are present in database (no virtual and no calculated fields)
*
* @return Array
*/
public function GetRealFields()
{
return array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields);
}
/**
* Returns only changed database field
*
* @param bool $include_virtual_fields
* @return Array
*/
public function GetChangedFields($include_virtual_fields = false)
{
$changes = Array ();
$fields = $include_virtual_fields ? $this->FieldValues : $this->GetRealFields();
$diff = array_diff_assoc($fields, $this->OriginalFieldValues);
foreach ($diff as $field => $new_value) {
$old_value = $this->GetOriginalField($field, true);
$new_value = $this->GetField($field);
if ($old_value != $new_value) {
// "0.00" and "0.0000" are stored as strings and will differ. Double check to prevent that.
$changes[$field] = Array ('old' => $old_value, 'new' => $new_value);
}
}
return $changes;
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
public function GetID()
{
return $this->ID;
}
/**
* Generates ID for new items before inserting into database
*
* @return int
* @access private
*/
protected function generateID()
{
return 0;
}
/**
* Returns true if item was loaded successfully by Load method
*
* @return bool
*/
public function isLoaded()
{
return $this->Loaded;
}
/**
* Checks if field is required
*
* @param string $field
* @return bool
*/
public function isRequired($field)
{
return isset($this->Fields[$field]['required']) && $this->Fields[$field]['required'];
}
/**
* Sets new required flag to field
*
* @param mixed $fields
* @param bool $is_required
*/
public function setRequired($fields, $is_required = true)
{
if ( !is_array($fields) ) {
$fields = explode(',', $fields);
}
foreach ($fields as $field) {
$this->Fields[$field]['required'] = $is_required;
}
}
/**
* Removes all data from an object
*
* @param int $new_id
* @return bool
* @access public
*/
public function Clear($new_id = null)
{
$this->Loaded = false;
$this->FieldValues = $this->OriginalFieldValues = Array ();
$this->SetDefaultValues(); // will wear off kDBItem::setID effect, so set it later
if ( is_object($this->validator) ) {
$this->validator->reset();
}
$this->setID($new_id);
return $this->Loaded;
}
public function Query($force = false)
{
throw new Exception('<b>Query</b> method is called in class <strong>' . get_class($this) . '</strong> for prefix <strong>' . $this->getPrefixSpecial() . '</strong>');
}
protected function saveCustomFields()
{
if ( !$this->customFields || $this->inCloning ) {
return true;
}
$cdata_key = rtrim($this->Prefix . '-cdata.' . $this->Special, '.');
$cdata =& $this->Application->recallObject($cdata_key, null, Array ('skip_autoload' => true, 'populate_ml_fields' => true));
/* @var $cdata kDBItem */
$resource_id = $this->GetDBField('ResourceId');
$cdata->Load($resource_id, 'ResourceId');
$cdata->SetDBField('ResourceId', $resource_id);
$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
foreach ($this->customFields as $custom_id => $custom_name) {
$force_primary = $cdata->GetFieldOption('cust_' . $custom_id, 'force_primary');
$cdata->SetDBField($ml_formatter->LangFieldName('cust_' . $custom_id, $force_primary), $this->GetDBField('cust_' . $custom_name));
}
return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create();
}
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @param bool $formatted
* @param string $format
* @return Array
*/
public function GetCol($field, $formatted = false, $format = null)
{
if ($formatted) {
return Array (0 => $this->GetField($field, $format));
}
return Array (0 => $this->GetDBField($field));
}
/**
* Set's loaded status of object
*
* @param bool $is_loaded
* @access public
* @todo remove this method, since item can't be marked as loaded externally
*/
public function setLoaded($is_loaded = true)
{
$this->Loaded = $is_loaded;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/event.php
===================================================================
--- branches/5.2.x/core/kernel/utility/event.php (revision 14607)
+++ branches/5.2.x/core/kernel/utility/event.php (revision 14608)
@@ -1,427 +1,439 @@
<?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!');
final class kEvent extends kBase {
/**
* Event finished working succsessfully
*
*/
const erSUCCESS = 0;
/**
* Event finished working, but result is unsuccsessfull
*
*/
const erFAIL = -1;
/**
* Event experienced FATAL error - no hooks should continue!
*
*/
const erFATAL = -2;
/**
* Event failed on internal permission checking (user has no permission)
*
*/
const erPERM_FAIL = -3;
/**
* Event requested to stop processing (don't parse templates)
*
*/
const erSTOP = -4;
/**
* Reference to event, that created given event
*
* @var kEvent
* @access public
*/
public $MasterEvent;
/**
* Event name
*
* @var string
* @access public
*/
public $Name;
/**
* Don't execute hooks, before event processing
*
* @var bool
* @access public
*/
public $SkipBeforeHooks = false;
/**
* Don't execute hooks, after event processing
*
* @var bool
* @access public
*/
public $SkipAfterHooks = false;
/**
* Perform redirect after event processing.
* Redirect after event processing allows to prevent same event being present in resulting url.
* Also could contain template name, that needs to be shown after redirect.
*
* @var mixed
* @access public
*/
public $redirect = true;
/**
* Params, used during redirect url building after event succsessfull processing
*
* @var bool
* @access private
*/
private $redirectParams = Array (
'opener' => 's', // redirect to given template
'pass' => 'all' // pass all discovered units to redirected page
);
/**
* PHP file to redirect to. Defaults to "index.php"
*
* @var string
* @access public
*/
public $redirectScript = null;
/**
* Event processing status
*
* @var int
* @access public
*/
public $status = kEvent::erSUCCESS;
/**
* Event parameters
* Usually indicate, how particular event should be processed.
*
* @var Array
* @access private
*/
private $specificParams = Array ();
/**
* Pseudo class used, to create object, based on event contents
*
* @var string
* @access private
*/
private $pseudoClass = '';
/**
* Create event from given prefix, special, name and specific params.
* Parameter $params could be be an an array with following keys: "prefix", "special" (optional), "name".
* Parameter $params could be a string in format: "prefix:name" or "prefix.special:name".
*
* @param mixed $params
* @param Array $specific_params event specific params (none by default)
* @return kEvent
* @access public
*/
public function __construct($params = Array(), $specific_params = null)
{
parent::__construct();
if ($params) {
if ( is_array($params) ) {
$prefix = isset($params['prefix']) ? $params['prefix'] : false;
$special = isset($params['special']) ? $params['special'] : false;
if ($prefix) {
$this->Init($prefix, $special);
}
$this->Name = isset($params['name']) ? $params['name'] : '';
}
elseif ( is_string($params) ) {
if (preg_match('/([^.:]*)[.]{0,1}([^:]*):(.*)/', $params, $regs)) {
$prefix = $regs[1];
$special = $regs[2];
if ($prefix) {
$this->Init($prefix, $special);
}
$this->Name = $regs[3];
}
else {
throw new Exception('Invalid event string: <strong>' . $params . '</strong>. $params should be "prefix[.special]:OnEvent" format');
}
}
}
if ( isset($specific_params) ) {
$this->specificParams = $specific_params;
}
}
/**
* Returns joined prefix and special if any
*
* @param bool $from_submit if true, then joins prefix & special by "_", uses "." otherwise
* @return string
* @access public
*/
public function getPrefixSpecial($from_submit = false)
{
if (!$from_submit) {
return parent::getPrefixSpecial();
}
return rtrim($this->Prefix . '_' . $this->Special, '_');
}
/**
* Sets event parameter
*
* @param string $name
* @param mixed $value
* @access public
*/
public function setEventParam($name,$value)
{
$this->specificParams[$name] = $value;
}
/**
* Returns event parameter by name (supports digging)
*
* @param string $name
* @return mixed
* @access public
*/
public function getEventParam($name)
{
$args = func_get_args();
if (count($args) > 1) {
kUtil::array_unshift_ref($args, $this->specificParams);
return call_user_func_array('getArrayValue', $args); // getArrayValue($this->specificParams, $name);
}
return array_key_exists($name, $this->specificParams) ? $this->specificParams[$name] : false;
}
/**
* Set's pseudo class that differs from
* the one specified in $Prefix
*
* @param string $appendix
* @access public
*/
public function setPseudoClass($appendix)
{
$this->pseudoClass = $this->Prefix . $appendix;
}
/**
* Performs event initialization
* Also sets pseudo class same $prefix
*
* @param string $prefix
* @param string $special
* @access public
*/
public function Init($prefix, $special)
{
$this->pseudoClass = $prefix;
parent::Init($prefix, $special);
}
/**
* Returns object used in event
*
+ * @param Array $params
* @return kDBBase
* @access public
*/
public function &getObject(array $params = Array())
{
+ if ( !$this->Application->hasObject($this->prefixSpecial) ) {
+ $top_event =& $this;
+
+ // when OnSave calls OnPreSave in first line, then this would make sure OnSave is used
+ while ( is_object($top_event->MasterEvent) ) {
+ $top_event =& $top_event->MasterEvent;
+ }
+
+ $params['parent_event'] =& $top_event;
+ }
+
$object =& $this->Application->recallObject($this->prefixSpecial, $this->pseudoClass, $params);
return $object;
}
/**
* Executes given event in context of current event
* Sub-event gets this event in "kEvent::MasterEvent" attribute.
* Sub-event execution results (status and redirect* properties) are copied back to current event.
*
* @param string $name name of callable event (optionally could contain prefix_special as well)
* @see kEvent::MasterEvent
*/
public function CallSubEvent($name)
{
if ( strpos($name, ':') === false ) {
// PrefixSpecial not specified -> use from current event
$name = $this->getPrefixSpecial() . ':' . $name;
}
$child_event = new kEvent($name);
$child_event->copyFrom($this, true);
$this->Application->HandleEvent($child_event);
$this->copyFrom($child_event);
$this->specificParams = $child_event->specificParams;
}
/**
* Allows to copy data between events
*
* @param kEvent $source_event
* @param bool $inherit
* @access public
*/
public function copyFrom(&$source_event, $inherit = false)
{
if ($inherit) {
$this->MasterEvent =& $source_event;
}
else {
$this->status = $source_event->status;
}
$this->redirect = $source_event->redirect;
$this->redirectParams = $source_event->redirectParams;
$this->redirectScript = $source_event->redirectScript;
$this->specificParams = $source_event->specificParams;
}
/**
* Returns all redirect parameters
*
* @return Array
* @access public
*/
public function getRedirectParams()
{
return $this->redirectParams;
}
/**
* Returns redirect parameter
*
* @param string $name
* @return mixed
* @access public
*/
public function getRedirectParam($name)
{
return array_key_exists($name, $this->redirectParams) ? $this->redirectParams[$name] : false;
}
/**
* Set's redirect param for event
*
* @param string $name
* @param string $value
* @access public
*/
public function SetRedirectParam($name, $value)
{
$this->redirectParams[$name] = $value;
}
/**
* Allows to merge passed redirect params hash with existing ones
*
* @param Array $params
* @param bool $overwrite
* @access public
*/
public function setRedirectParams($params, $overwrite = false)
{
if ($overwrite) {
$this->redirectParams = $params;
return ;
}
// append new parameters to parameters set before
$this->redirectParams = kUtil::array_merge_recursive($this->redirectParams, $params);
}
/**
* Allows to tell if this event was called some how (e.g. subevent, hook) from event requested
*
* @param string $event_key event key in format [prefix[.special]:]event_name
* @return bool
* @access public
*/
public function hasAncestor($event_key)
{
if ( strpos($event_key, ':') === false ) {
$event_key = $this->getPrefixSpecial() . ':' . $event_key;
}
return $this->Application->EventManager->eventRunning($event_key);
}
/**
* Returns permission section associated with event
*
* @return string
* @access public
*/
public function getSection()
{
$perm_section = $this->getEventParam('PermSection');
if ($perm_section) {
return $perm_section;
}
// 1. get section by current top_prefix
$top_prefix = $this->getEventParam('top_prefix');
if ($top_prefix == false) {
$top_prefix = $this->Application->GetTopmostPrefix($this->Prefix, true);
$this->setEventParam('top_prefix', $top_prefix);
}
$section = $this->Application->getUnitOption($top_prefix.'.main', 'PermSection');
// 2. check if this section has perm_prefix mapping to other prefix
$sections_helper =& $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($section);
if ($section_data && isset($section_data['perm_prefix']) && $section_data['perm_prefix'] != $top_prefix) {
$this->setEventParam('top_prefix', $section_data['perm_prefix']);
$section = $this->Application->getUnitOption($section_data['perm_prefix'].'.main', 'PermSection');
}
if (!$section) {
throw new Exception('Permission <strong>section</strong> not specified for prefix <strong>' . $top_prefix . '</strong>');
}
return $section;
}
public function __toString()
{
return $this->getPrefixSpecial() . ':' . $this->Name;
}
}
\ No newline at end of file
Index: branches/5.2.x/core/kernel/kbase.php
===================================================================
--- branches/5.2.x/core/kernel/kbase.php (revision 14607)
+++ branches/5.2.x/core/kernel/kbase.php (revision 14608)
@@ -1,1035 +1,1055 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
//defined('FULL_PATH') or die('restricted access!');
/**
* Base
*
*/
class kBase {
/**
* Reference to global kApplication instance
*
* @var kApplication
* @access protected
*/
protected $Application = null;
/**
* Connection to database
*
* @var kDBConnection
* @access protected
*/
protected $Conn = null;
/**
* Prefix, used during object creation
*
* @var string
* @access public
*/
public $Prefix = '';
/**
* Special, used during object creation
*
* @var string
* @access public
*/
public $Special = '';
/**
* Joined prefix and special, usually taken directly from
* tag beeing processed, to use in kApplication::recallObject method
*
* @var string
* @access protected
* @see kApplication::recallObject
*/
protected $prefixSpecial = '';
/**
* Set's references to kApplication and kDBConnection class instances
*
* @return kBase
* @access public
* @see kApplication
* @see kDBConnection
*/
public function __construct($application = null)
{
if (!isset($application)) {
$this->Application =& kApplication::Instance();
}
else {
$this->Application =& $application;
}
$this->Conn =& $this->Application->GetADODBConnection();
}
/**
* Set's prefix and special
*
* @param string $prefix
* @param string $special
* @access public
*/
public function Init($prefix, $special)
{
$prefix = explode('_', $prefix, 2);
$this->Prefix = $prefix[0];
$this->Special = $special;
$this->prefixSpecial = rtrim($this->Prefix . '.' . $this->Special, '.');
}
/**
* Returns prefix and special (when present) joined by a "."
*
* @return string
* @access public
*/
public function getPrefixSpecial()
{
return $this->prefixSpecial;
}
}
class kHelper extends kBase {
/**
* Performs helper initialization
*
* @access public
*/
public function InitHelper()
{
}
/**
* Append prefix and special to tag
* params (get them from tagname) like
* they were really passed as params
*
* @param string $prefix_special
* @param Array $tag_params
* @return Array
* @access protected
*/
protected function prepareTagParams($prefix_special, $tag_params = Array())
{
$parts = explode('.', $prefix_special);
$ret = $tag_params;
$ret['Prefix'] = $parts[0];
$ret['Special'] = count($parts) > 1 ? $parts[1] : '';
$ret['PrefixSpecial'] = $prefix_special;
return $ret;
}
}
abstract class kDBBase extends kBase {
/**
* Name of primary key field for the unit
*
* @var string
* @access public
* @see kDBBase::TableName
*/
public $IDField = '';
/**
* Unit's database table name
*
* @var string
* @access public
*/
public $TableName = '';
/**
* Form name, used for validation
*
* @var string
*/
protected $formName = '';
/**
* Final form configuration
*
* @var Array
*/
protected $formConfig = Array ();
/**
* SELECT, FROM, JOIN parts of SELECT query (no filters, no limit, no ordering)
*
* @var string
* @access protected
*/
protected $SelectClause = '';
/**
* Unit fields definitions (fields from database plus virtual fields)
*
* @var Array
* @access protected
*/
protected $Fields = Array ();
/**
* Mapping between unit custom field IDs and their names
*
* @var Array
* @access protected
*/
protected $customFields = Array ();
/**
* Unit virtual field definitions
*
* @var Array
* @access protected
*/
protected $VirtualFields = Array ();
/**
* Fields that need to be queried using custom expression, e.g. IF(...) AS value
*
* @var Array
* @access protected
*/
protected $CalculatedFields = Array ();
/**
* Fields that contain aggregated functions, e.g. COUNT, SUM, etc.
*
* @var Array
* @access protected
*/
protected $AggregatedCalculatedFields = Array ();
/**
* Tells, that multilingual fields sould not be populated by default.
* Can be overriden from kDBBase::Configure method
*
* @var bool
* @access protected
*/
protected $populateMultiLangFields = false;
/**
+ * Event, that was used to create this object
+ *
+ * @var kEvent
+ * @access protected
+ */
+ protected $parentEvent = null;
+
+ /**
+ * Sets new parent event to the object
+ *
+ * @param kEvent $event
+ * @return void
+ * @access public
+ */
+ public function setParentEvent(&$event)
+ {
+ $this->parentEvent =& $event;
+ }
+
+ /**
* Set object' TableName to LIVE table, defined in unit config
*
* @access public
*/
public function SwitchToLive()
{
$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
}
/**
* Set object' TableName to TEMP table created based on table, defined in unit config
*
* @access public
*/
public function SwitchToTemp()
{
$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
$this->TableName = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix);
}
/**
* Checks if object uses temp table
*
* @return bool
* @access public
*/
public function IsTempTable()
{
return $this->Application->IsTempTable($this->TableName);
}
/**
* Sets SELECT part of list' query
*
* @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE
* @access public
*/
public function SetSelectSQL($sql)
{
$this->SelectClause = $sql;
}
/**
* Returns SELECT part of list' query.
* 1. Occurences of "%1$s" and "%s" are replaced to kDBBase::TableName
* 2. Occurences of "%3$s" are replaced to temp table prefix (only for table, using TABLE_PREFIX)
*
* @param string $base_query given base query will override unit default select query
* @param bool $replace_table replace all possible occurences
* @return string
* @access public
* @see kDBBase::replaceModePrefix
*/
public function GetSelectSQL($base_query = null, $replace_table = true)
{
if (!isset($base_query)) {
$base_query = $this->SelectClause;
}
if (!$replace_table) {
return $base_query;
}
$query = str_replace(Array('%1$s', '%s'), $this->TableName, $base_query);
return $this->replaceModePrefix($query);
}
/**
* Allows substables to be in same mode as main item (e.g. LEFT JOINED ones)
*
* @param string $query
* @return string
* @access protected
*/
protected function replaceModePrefix($query)
{
$live_table = substr($this->Application->GetLiveName($this->TableName), strlen(TABLE_PREFIX));
if (preg_match('/'.preg_quote(TABLE_PREFIX, '/').'(.*)'.preg_quote($live_table, '/').'/', $this->TableName, $rets)) {
// will only happen, when table has a prefix (like in K4)
return str_replace('%3$s', $rets[1], $query);
}
// will happen, when K3 table without prefix is used
return $query;
}
/**
* Sets calculated fields
*
* @param Array $fields
* @access public
*/
public function setCalculatedFields($fields)
{
$this->CalculatedFields = $fields;
}
/**
* Adds calculated field declaration to object.
*
* @param string $name
* @param string $sql_clause
* @access public
*/
public function addCalculatedField($name, $sql_clause)
{
$this->CalculatedFields[$name] = $sql_clause;
}
/**
* Returns required mixing of aggregated & non-aggregated calculated fields
*
* @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only
* @return Array
* @access public
*/
public function getCalculatedFields($aggregated = 1)
{
switch ($aggregated) {
case 0:
$fields = array_merge($this->CalculatedFields, $this->AggregatedCalculatedFields);
break;
case 1:
$fields = $this->CalculatedFields;
break;
case 2:
$fields = $this->AggregatedCalculatedFields; // TODO: never used
break;
default:
$fields = Array();
break;
}
return $fields;
}
/**
* Checks, that given field is a calculated field
*
* @param string $field
* @return bool
* @access public
*/
public function isCalculatedField($field)
{
return array_key_exists($field, $this->CalculatedFields);
}
/**
* Insert calculated fields sql into query in place of %2$s,
* return processed query.
*
* @param string $query
* @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only
* @return string
* @access protected
*/
protected function addCalculatedFields($query, $aggregated = 1)
{
$fields = $this->getCalculatedFields($aggregated);
if ($fields) {
$sql = Array ();
$fields = str_replace('%2$s', $this->Application->GetVar('m_lang'), $fields);
foreach ($fields as $field_name => $field_expression) {
$sql[] = '('.$field_expression.') AS `'.$field_name.'`';
}
$sql = implode(',',$sql);
return $this->Application->ReplaceLanguageTags( str_replace('%2$s', ','.$sql, $query) );
}
return str_replace('%2$s', '', $query);
}
/**
* Performs initial object configuration, which includes setting the following:
* - primary key and table name
* - field definitions (including field modifiers, formatters, default values)
*
* @param bool $populate_ml_fields create all ml fields from db in config or not
* @param string $form_name form name for validation
* @access public
*/
public function Configure($populate_ml_fields = null, $form_name = null)
{
if ( isset($populate_ml_fields) ) {
$this->populateMultiLangFields = $populate_ml_fields;
}
$this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField');
$this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName');
$this->initForm($form_name);
$this->defineFields();
$this->ApplyFieldModifiers(); // should be called only after all fields definitions been set
$this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant!
// only set on first call of method
if ( isset($populate_ml_fields) ) {
$this->SetDefaultValues();
}
}
/**
* Adjusts object accoding to given form name
*
*/
protected function initForm($form_name = null)
{
$forms = $this->Application->getUnitOption($this->Prefix, 'Forms', Array ());
$this->formName = $form_name;
$this->formConfig = isset($forms['default']) ? $forms['default'] : Array ();
if ( !$this->formName ) {
return ;
}
if ( !isset($forms[$this->formName]) ) {
trigger_error('Form "<strong>' . $this->formName . '</strong>" isn\'t declared in "<strong>' . $this->Prefix . '</strong>" unit config.', E_USER_NOTICE);
}
else {
$this->formConfig = kUtil::array_merge_recursive($this->formConfig, $forms[$this->formName]);
}
}
/**
* Add field definitions from all possible sources
* Used field sources: database fields, custom fields, virtual fields, calculated fields, aggregated calculated fields
*
* @access protected
*/
protected function defineFields()
{
$this->Fields = $this->getFormOption('Fields', Array ());
$this->customFields = $this->getFormOption('CustomFields', Array());
$this->setVirtualFields( $this->getFormOption('VirtualFields', Array ()) );
$calculated_fields = $this->getFormOption('CalculatedFields', Array());
$this->CalculatedFields = $this->getFieldsBySpecial($calculated_fields);
$aggregated_calculated_fields = $this->getFormOption('AggregatedCalculatedFields', Array());
$this->AggregatedCalculatedFields = $this->getFieldsBySpecial($aggregated_calculated_fields);
}
/**
* Returns form name, used for validation
*
* @return string
*/
public function getFormName()
{
return $this->formName;
}
/**
* Reads unit (specified by $prefix) option specified by $option and applies form change to it
*
* @param string $option
* @param mixed $default
* @return string
* @access public
*/
public function getFormOption($option, $default = false)
{
$ret = $this->Application->getUnitOption($this->Prefix, $option, $default);
if ( isset($this->formConfig[$option]) ) {
$ret = kUtil::array_merge_recursive($ret, $this->formConfig[$option]);
}
return $ret;
}
/**
* Only exteracts fields, that match current object Special
*
* @param Array $fields
* @return Array
* @access protected
*/
protected function getFieldsBySpecial($fields)
{
if ( array_key_exists($this->Special, $fields) ) {
return $fields[$this->Special];
}
return array_key_exists('', $fields) ? $fields[''] : Array();
}
/**
* Sets aggeregated calculated fields
*
* @param Array $fields
* @access public
*/
public function setAggregatedCalculatedFields($fields)
{
$this->AggregatedCalculatedFields = $fields;
}
/**
* Set's field names from table from config
*
* @param Array $fields
* @access public
*/
public function setCustomFields($fields)
{
$this->customFields = $fields;
}
/**
* Returns custom fields information from table from config
*
* @return Array
* @access public
*/
public function getCustomFields()
{
return $this->customFields;
}
/**
* Set's fields information from table from config
*
* @param Array $fields
* @access public
*/
public function setFields($fields)
{
$this->Fields = $fields;
}
/**
* Returns fields information from table from config
*
* @return Array
* @access public
*/
public function getFields()
{
return $this->Fields;
}
/**
* Checks, that given field exists
*
* @param string $field
* @return bool
* @access public
*/
public function isField($field)
{
return array_key_exists($field, $this->Fields);
}
/**
* Override field options with ones defined in submit via "field_modfiers" array (common for all prefixes)
*
* @param Array $field_modifiers
* @return void
* @access public
* @author Alex
*/
public function ApplyFieldModifiers($field_modifiers = null)
{
$allowed_modifiers = Array ('required', 'multiple');
if ( $this->Application->isAdminUser ) {
// can change upload dir on the fly (admin only!)
$allowed_modifiers[] = 'upload_dir';
}
if ( !isset($field_modifiers) ) {
$field_modifiers = $this->Application->GetVar('field_modifiers');
if ( !$field_modifiers ) {
// no field modifiers
return ;
}
$field_modifiers = getArrayValue($field_modifiers, $this->getPrefixSpecial());
}
if ( !$field_modifiers ) {
// no field modifiers for current prefix_special
return ;
}
foreach ($field_modifiers as $field => $field_options) {
foreach ($field_options as $option_name => $option_value) {
if ( !in_array(strtolower($option_name), $allowed_modifiers) ) {
continue;
}
$this->Fields[$field][$option_name] = $option_value;
}
}
}
/**
* Set fields (+options) for fields that physically doesn't exist in database
*
* @param Array $fields
* @access public
*/
public function setVirtualFields($fields)
{
if ($fields) {
$this->VirtualFields = $fields;
$this->Fields = array_merge($this->VirtualFields, $this->Fields);
}
}
/**
* Returns virtual fields
*
* @return Array
* @access public
*/
public function getVirtualFields()
{
return $this->VirtualFields;
}
/**
* Checks, that given field is a virtual field
*
* @param string $field
* @return bool
* @access public
*/
public function isVirtualField($field)
{
return array_key_exists($field, $this->VirtualFields);
}
/**
* Performs additional initialization for field default values
*
* @access protected
*/
protected function SetDefaultValues()
{
foreach ($this->Fields as $field => $options) {
if ( array_key_exists('default', $options) && $options['default'] === '#NOW#' ) {
$this->Fields[$field]['default'] = adodb_mktime();
}
}
}
/**
* Overwrites field definition in unit config
*
* @param string $field
* @param Array $options
* @param bool $is_virtual
* @access public
*/
public function SetFieldOptions($field, $options, $is_virtual = false)
{
if ($is_virtual) {
$this->VirtualFields[$field] = $options;
$this->Fields = array_merge($this->VirtualFields, $this->Fields);
}
else {
$this->Fields[$field] = $options;
}
}
/**
* Changes/sets given option's value in given field definiton
*
* @param string $field
* @param string $option_name
* @param mixed $option_value
* @param bool $is_virtual
* @access public
*/
public function SetFieldOption($field, $option_name, $option_value, $is_virtual = false)
{
if ($is_virtual) {
$this->VirtualFields[$field][$option_name] = $option_value;
}
$this->Fields[$field][$option_name] = $option_value;
}
/**
* Returns field definition from unit config.
* Also executes sql from "options_sql" field option to form "options" field option
*
* @param string $field
* @param bool $is_virtual
* @return Array
* @access public
*/
public function GetFieldOptions($field, $is_virtual = false)
{
$property_name = $is_virtual ? 'VirtualFields' : 'Fields';
if ( !array_key_exists($field, $this->$property_name) ) {
return Array ();
}
if (!$is_virtual) {
if (!array_key_exists('options_prepared', $this->Fields[$field]) || !$this->Fields[$field]['options_prepared']) {
// executes "options_sql" from field definition, only when field options are accessed (late binding)
$this->PrepareFieldOptions($field);
$this->Fields[$field]['options_prepared'] = true;
}
}
return $this->{$property_name}[$field];
}
/**
* Returns field option
*
* @param string $field
* @param string $option_name
* @param string $is_virtual
* @param string $default
* @return string
* @access public
*/
public function GetFieldOption($field, $option_name, $is_virtual = false, $default = false)
{
$field_options = $this->GetFieldOptions($field, $is_virtual);
if ( !$field_options && strpos($field, '#') === false ) {
// we use "#FIELD_NAME#" as field for InputName tag in JavaScript, so ignore it
$form_name = $this->getFormName();
trigger_error('Field "<strong>' . $field . '</strong>" is not defined' . ($form_name ? ' on "<strong>' . $this->getFormName() . '</strong>" form' : '') . ' in "<strong>' . $this->Prefix . '</strong>" unit config', E_USER_WARNING);
return false;
}
return array_key_exists($option_name, $field_options) ? $field_options[$option_name] : $default;
}
/**
* Returns formatted field value
*
* @param string $name
* @param string $format
*
* @return string
* @access protected
*/
public function GetField($name, $format = null)
{
$formatter_class = $this->GetFieldOption($name, 'formatter');
if ( $formatter_class ) {
$value = ($formatter_class == 'kMultiLanguage') && !preg_match('/^l[0-9]+_/', $name) ? '' : $this->GetDBField($name);
$formatter =& $this->Application->recallObject($formatter_class);
/* @var $formatter kFormatter */
return $formatter->Format($value, $name, $this, $format);
}
return $this->GetDBField($name);
}
/**
* Returns unformatted field value
*
* @param string $field
* @return string
* @access protected
*/
abstract protected function GetDBField($field);
/**
* Checks of object has given field
*
* @param string $name
* @return bool
* @access protected
*/
abstract protected function HasField($name);
/**
* Returns field values
*
* @return Array
* @access protected
*/
abstract protected function GetFieldValues();
/**
* Populates values of sub-fields, based on formatters, set to mater fields
*
* @param Array $fields
* @access public
* @todo Maybe should not be publicly accessible
*/
public function UpdateFormattersSubFields($fields = null)
{
if ( !is_array($fields) ) {
$fields = array_keys($this->Fields);
}
foreach ($fields as $field) {
if ( isset($this->Fields[$field]['formatter']) ) {
$formatter =& $this->Application->recallObject($this->Fields[$field]['formatter']);
/* @var $formatter kFormatter */
$formatter->UpdateSubFields($field, $this->GetDBField($field), $this->Fields[$field], $this);
}
}
}
/**
* Use formatters, specified in field declarations to perform additional field initialization in unit config
*
* @access protected
*/
protected function prepareConfigOptions()
{
$field_names = array_keys($this->Fields);
foreach ($field_names as $field_name) {
if ( !array_key_exists('formatter', $this->Fields[$field_name]) ) {
continue;
}
$formatter =& $this->Application->recallObject( $this->Fields[$field_name]['formatter'] );
/* @var $formatter kFormatter */
$formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this);
}
}
/**
* Escapes fields only, not expressions
*
* @param string $field_expr
* @return string
* @access protected
*/
protected function escapeField($field_expr)
{
return preg_match('/[.(]/', $field_expr) ? $field_expr : '`' . $field_expr . '`';
}
/**
* Replaces current language id in given field options
*
* @param string $field_name
* @param Array $field_option_names
* @access protected
*/
protected function _replaceLanguageId($field_name, $field_option_names)
{
// don't use GetVar('m_lang') since it's always equals to default language on editing form in admin
$current_language_id = $this->Application->Phrases->LanguageId;
$primary_language_id = $this->Application->GetDefaultLanguageId();
$field_options =& $this->Fields[$field_name];
foreach ($field_option_names as $option_name) {
$field_options[$option_name] = str_replace('%2$s', $current_language_id, $field_options[$option_name]);
$field_options[$option_name] = str_replace('%3$s', $primary_language_id, $field_options[$option_name]);
}
}
/**
* Transforms "options_sql" field option into valid "options" array for given field
*
* @param string $field_name
* @access protected
*/
protected function PrepareFieldOptions($field_name)
{
$field_options =& $this->Fields[$field_name];
if (array_key_exists('options_sql', $field_options) ) {
// get options based on given sql
$replace_options = Array ('option_title_field', 'option_key_field', 'options_sql');
$this->_replaceLanguageId($field_name, $replace_options);
$select_clause = $this->escapeField($field_options['option_title_field']) . ',' . $this->escapeField($field_options['option_key_field']);
$sql = sprintf($field_options['options_sql'], $select_clause);
if (array_key_exists('serial_name', $field_options)) {
// try to cache option sql on serial basis
$cache_key = 'sql_' . crc32($sql) . '[%' . $field_options['serial_name'] . '%]';
$dynamic_options = $this->Application->getCache($cache_key);
if ($dynamic_options === false) {
$this->Conn->nextQueryCachable = true;
$dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field']));
$this->Application->setCache($cache_key, $dynamic_options);
}
}
else {
// don't cache options sql
$dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field']));
}
$options_hash = array_key_exists('options', $field_options) ? $field_options['options'] : Array ();
$field_options['options'] = kUtil::array_merge_recursive($options_hash, $dynamic_options); // because of numeric keys
}
}
/**
* Returns ID of currently processed record
*
* @return int
* @access public
*/
public function GetID()
{
return $this->GetDBField($this->IDField);
}
/**
* Allows kDBTagProcessor.SectionTitle to detect if it's editing or new item creation
*
* @return bool
* @access public
*/
public function IsNewItem()
{
return $this->GetID() ? false : true;
}
/**
* Returns parent table information
*
* @param string $special special of main item
* @param bool $guess_special if object retrieved with specified special is not loaded, then try not to use special
* @return Array
* @access public
*/
public function getLinkedInfo($special = '', $guess_special = false)
{
$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');
if ($parent_prefix) {
// if this is linked table, then set id from main table
$table_info = Array (
'TableName' => $this->Application->getUnitOption($this->Prefix,'TableName'),
'IdField' => $this->Application->getUnitOption($this->Prefix,'IDField'),
'ForeignKey' => $this->Application->getUnitOption($this->Prefix,'ForeignKey'),
'ParentTableKey' => $this->Application->getUnitOption($this->Prefix,'ParentTableKey'),
'ParentPrefix' => $parent_prefix
);
if (is_array($table_info['ForeignKey'])) {
$table_info['ForeignKey'] = getArrayValue($table_info, 'ForeignKey', $parent_prefix);
}
if (is_array($table_info['ParentTableKey'])) {
$table_info['ParentTableKey'] = getArrayValue($table_info, 'ParentTableKey', $parent_prefix);
}
$main_object =& $this->Application->recallObject($parent_prefix.'.'.$special, null, Array ('raise_warnings' => 0));
/* @var $main_object kDBItem */
if (!$main_object->isLoaded() && $guess_special) {
$main_object =& $this->Application->recallObject($parent_prefix);
}
return array_merge($table_info, Array('ParentId'=> $main_object->GetDBField( $table_info['ParentTableKey'] ) ) );
}
return false;
}
/**
* Returns true, when list/item was queried/loaded
*
* @return bool
* @access protected
*/
abstract protected function isLoaded();
/**
* Returns specified field value from all selected rows.
* Don't affect current record index
*
* @param string $field
* @return Array
* @access protected
*/
abstract protected function GetCol($field);
}
\ No newline at end of file

Event Timeline