Property changes on: branches/5.3.x/LICENSE ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.2.x/LICENSE:r15907-16066 Merged /in-portal/releases/5.2.1/LICENSE:r16067-16068 Property changes on: branches/5.3.x/robots.txt ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/releases/5.2.1/robots.txt:r16067-16068 Merged /in-portal/branches/5.2.x/robots.txt:r15907-16066 Index: branches/5.3.x/admin/system_presets/simple/users_u.php =================================================================== --- branches/5.3.x/admin/system_presets/simple/users_u.php (revision 16110) +++ branches/5.3.x/admin/system_presets/simple/users_u.php (revision 16111) @@ -1,121 +1,121 @@ Users 'users_list' => Array (/*'new_item', 'edit', 'delete', 'approve', 'decline',*/ 'frontend_mail', /*'e-mail',*/ 'export', /*'view', 'dbl-click'*/), // "General" tab during user adding/editing // 'users_edit' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'), // "Images" tab during user adding/editing // 'user_edit_images' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'move_up', 'move_down', 'setprimary', 'view', 'dbl-click'), // "Groups" tab during user/admin adding/editing // 'user_edit_groups' => Array ('select', 'cancel', 'prev', 'next', 'selec_user', 'edit', 'delete', 'primary_group', 'view', 'dbl-click'), // "Items" tab during user/admin adding/editing // 'user_edit_items' => Array ('select', 'cancel', 'prev', 'next', 'edit', 'delete', 'view', 'dbl-click'), // "Custom" tab during user/admin adding/editing // 'user_edit_custom' => Array ('select', 'cancel', 'prev', 'next'), // list of administrators; section: Users Managements -> Administrators 'admin_list' => Array (/*'new_item', 'edit', 'delete', 'approve', 'decline',*/'clone', 'refresh',/* 'view', 'dbl-click'*/), // "General" tab during admin adding/editing AND separate password change form for non-"root" users (in top frame) 'admins_edit' => Array (/*'select', 'cancel', */'reset_edit', /*'prev', 'next'*/), // 'regular_users_list' => Array (), // not used // separate password change form for "root" user (in top frame) // 'root_edit' => Array ('select', 'cancel'), // user/admin group membership editing (used on "Groups" tab during user/admin adding/editing) // 'user_edit_group' => Array ('select', 'cancel'), // user image adding/editing (used on "Images" tab during user adding/editing) // 'user_image_edit' => Array ('select', 'cancel', 'prev', 'next'), // user selector // 'user_select' => Array ('select', 'cancel', 'view', 'dbl-click'), // user selector when adding/editing user group // 'group_user_select' => Array ('select', 'cancel', 'view', 'dbl-click'), ); // fields to hide $hidden_fields = Array ( /* 'PortalUserId', 'Username', 'Password', 'FirstName','LastName', 'Company', 'Email', 'CreatedOn', 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status', 'EmailVerified', 'Modified', 'dob',*/ 'TimeZone', 'IPAddress', /*'IsBanned', 'PwResetConfirm', 'PwRequestTime',*/ 'DisplayToPublic', 'FrontLanguage', 'AdminLanguage', 'IPRestrictions', ); // virtual fields to hide $virtual_hidden_fields = Array ( /*'SubscribeEmail', 'PrimaryGroup', 'RootPassword', 'FullName', 'UserGroup'*/ ); // fields to make required $required_fields = Array ( - /*'PortalUserId',*/ 'Username', /*'Password', 'FirstName', 'LastName', 'Company', */'Email', /*'CreatedOn', + /*'PortalUserId', 'Username', 'Password', 'FirstName', 'LastName', 'Company', */'Email', /*'CreatedOn', 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status', 'EmailVerified', 'Modified', 'dob', 'tz', 'IPAddress', 'IsBanned', 'PwResetConfirm', 'PwRequestTime', 'FrontLanguage', 'AdminLanguage', 'IPRestrictions',*/ ); // virtual fields to make required $virtual_required_fields = Array ( /*'SubscribeEmail', 'PrimaryGroup', 'RootPassword', 'FullName', 'UserGroup'*/ ); // tabs during editing $hide_edit_tabs = Array ( // tabs during user editing, when In-Portal module is enabled 'Default' => Array ('general', 'groups', 'images', 'items', 'custom'), // tabs during user editing, when In-Portal module isn't enabled 'RegularUsers' => Array ('general', 'groups'), // tabs during admin editing 'Admins' => Array ('general', 'groups'), ); // hide columns in grids $hide_columns = Array ( // currently not in user // 'Default' => Array ('Username', 'LastName', 'FirstName', 'Email', 'PrimaryGroup', 'CreatedOn', 'Modified'), // user selector // 'UserSelector' => Array ('Username', 'LastName', 'FirstName', 'Email', 'PrimaryGroup', 'CreatedOn', 'Modified'), // admins list; section: Users Management -> Administrators // 'Admins' => Array ('PortalUserId', 'Username', 'FirstName', 'LastName', 'Email'), // users list; section: Users Management -> Users 'RegularUsers' => Array (/*'PortalUserId', 'Username', 'FirstName', 'LastName', 'Email',*/ 'PrimaryGroup', 'CreatedOn', 'Modified', /* 'Status',*/ 'IPAddress', 'EmailVerified'), ); Index: branches/5.3.x/core/kernel/db/cat_dbitem.php =================================================================== --- branches/5.3.x/core/kernel/db/cat_dbitem.php (revision 16110) +++ branches/5.3.x/core/kernel/db/cat_dbitem.php (revision 16111) @@ -1,626 +1,632 @@ CategoryPath = Array(); } /** * Set's prefix and special * * @param string $prefix * @param string $special * @access public */ function Init($prefix, $special) { parent::Init($prefix, $special); $this->usePendingEditing = $this->getUnitConfig()->getUsePendingEditing(); } /** * Assigns primary category for the item * * @access public */ public function assignPrimaryCategory() { if ( $this->GetDBField('CategoryId') <= 0 ) { // set primary category in item object $this->SetDBField('CategoryId', $this->Application->GetVar('m_cat_id')); } $this->assignToCategory($this->GetDBField('CategoryId'), true); } /** * Updates previously loaded record with current item' values * * @access public * @param int $id Primary Key Id to update * @param Array $update_fields * @param bool $system_update * @return bool * @access public */ public function Update($id = null, $update_fields = null, $system_update = false) { if ( $this->useFilenames ) { $this->checkFilename(); $this->generateFilename(); } $ret = parent::Update($id, $update_fields, $system_update); if ( $ret ) { $filename = $this->useFilenames ? (string)$this->GetDBField('Filename') : ''; $sql = 'UPDATE ' . $this->CategoryItemsTable() . ' SET Filename = ' . $this->Conn->qstr($filename) . ' WHERE ItemResourceId = ' . $this->GetDBField('ResourceId'); $this->Conn->Query($sql); } return $ret; } /** * Returns CategoryItems table based on current item mode (temp/live) * * @return string */ function CategoryItemsTable() { $table = TABLE_PREFIX.'CategoryItems'; if ($this->Application->IsTempTable($this->TableName)) { $table = $this->Application->GetTempName($table, 'prefix:'.$this->Prefix); } return $table; } function checkFilename() { if( !$this->GetDBField('AutomaticFilename') ) { $filename = $this->GetDBField('Filename'); $this->SetDBField('Filename', $this->stripDisallowed($filename) ); } } function Copy($cat_id=null) { if (!isset($cat_id)) $cat_id = $this->Application->GetVar('m_cat_id'); $this->NameCopy($cat_id); return $this->Create($cat_id); } /** * Sets new name for item in case if it is being copied in same table * * @param array $master Table data from TempHandler * @param int $foreign_key ForeignKey value to filter name check query by * @param string $title_field FieldName to alter, by default - TitleField of the prefix * @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name * @access public */ public function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s') { $title_field = $this->getUnitConfig()->getTitleField(); if (!$title_field) return; $new_name = $this->GetDBField($title_field); $cat_id = (int)$this->Application->GetVar('m_cat_id'); $original_checked = false; do { if ( preg_match('/Copy ([0-9]*) *of (.*)/', $new_name, $regs) ) { $new_name = 'Copy '.( (int)$regs[1] + 1 ).' of '.$regs[2]; } elseif ($original_checked) { $new_name = 'Copy of '.$new_name; } $query = 'SELECT '.$title_field.' FROM '.$this->TableName.' LEFT JOIN '.TABLE_PREFIX.'CategoryItems ON ('.TABLE_PREFIX.'CategoryItems.ItemResourceId = '.$this->TableName.'.ResourceId) WHERE ('.TABLE_PREFIX.'CategoryItems.CategoryId = '.$cat_id.') AND '. $title_field.' = '.$this->Conn->qstr($new_name); $res = $this->Conn->GetOne($query); $original_checked = true; } while ($res !== false); $this->SetDBField($title_field, $new_name); // this is needed, because Create will create items in its own CategoryId (if it's set), // but we need to create it in target Paste category @see{kCatDBItem::Create} and its primary_category detection $this->SetDBField('CategoryId', $cat_id); } /** * Changes item primary category to given/current category * * @param int $category_id */ function MoveToCat($category_id = null) { // $this->NameCopy(); if (!isset($category_id)) { $category_id = $this->Application->GetVar('m_cat_id'); } $table_name = TABLE_PREFIX . 'CategoryItems'; if ($this->IsTempTable()) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix); } // check if the item already exists in destination category $sql = 'SELECT PrimaryCat FROM ' . $table_name . ' WHERE (CategoryId = ' . (int)$category_id . ') AND (ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; $is_primary = $this->Conn->GetOne($sql); // if it's not found is_primary will be FALSE, if it's found but not primary it will be int 0 $exists = $is_primary !== false; if ($exists) { // if the item already exists in destination category if ($is_primary) { // do nothing when we paste to primary return ; } // if it's not primary - delete it from destination category, as we will move it from current primary below $sql = 'DELETE FROM ' . $table_name . ' WHERE (CategoryId = ' . (int)$category_id . ') AND (ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; $this->Conn->Query($sql); } // change category id in existing primary category record $sql = 'UPDATE ' . $table_name . ' SET CategoryId = ' . (int)$category_id . ' WHERE (ItemResourceId = ' . $this->GetDBField('ResourceId') . ') AND (PrimaryCat = 1)'; $this->Conn->Query($sql); $this->Update(); } /** * When item is deleted, then also delete it from all categories * * @param int $id * @return bool * @access public */ public function Delete($id = null) { if ( isset($id) ) { $this->setID($id); } $this->Load($this->GetID()); $ret = parent::Delete(); if ( $ret ) { // TODO: move to OnAfterItemDelete method $query = ' DELETE FROM ' . $this->CategoryItemsTable() . ' WHERE ItemResourceId = ' . $this->GetDBField('ResourceId'); $this->Conn->Query($query); } return $ret; } /** * Deletes item from categories * * @param Array $delete_category_ids * @author Alex */ function DeleteFromCategories($delete_category_ids) { $id_field = $this->getUnitConfig()->getIDField(); // because item was loaded before by ResourceId $ci_table = $this->Application->getUnitConfig($this->Prefix . '-ci')->getTableName(); $resource_id = $this->GetDBField('ResourceId'); $item_cats_sql = ' SELECT CategoryId FROM %s WHERE ItemResourceId = %s'; $delete_category_items_sql = ' DELETE FROM %s WHERE ItemResourceId = %s AND CategoryId IN (%s)'; $category_ids = $this->Conn->GetCol( sprintf($item_cats_sql, $ci_table, $resource_id) ); $cats_left = array_diff($category_ids, $delete_category_ids); if ( !$cats_left ) { $sql = 'SELECT %s FROM %s WHERE ResourceId = %s'; $ids = $this->Conn->GetCol(sprintf($sql, $id_field, $this->TableName, $resource_id)); $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($this->Prefix, $this->Special, $ids); } else { $this->Conn->Query( sprintf($delete_category_items_sql, $ci_table, $resource_id, implode(',', $delete_category_ids)) ); $sql = 'SELECT CategoryId FROM %s WHERE PrimaryCat = 1 AND ItemResourceId = %s'; $primary_cat_id = $this->Conn->GetCol(sprintf($sql, $ci_table, $resource_id)); if ( count($primary_cat_id) == 0 ) { $sql = 'UPDATE %s SET PrimaryCat = 1 WHERE (CategoryId = %s) AND (ItemResourceId = %s)'; $this->Conn->Query( sprintf($sql, $ci_table, reset($cats_left), $resource_id) ); } } } /** * replace not allowed symbols with "_" chars + remove duplicate "_" chars in result * * @param string $filename * @return string */ function stripDisallowed($filename) { $filenames_helper = $this->Application->recallObject('FilenamesHelper'); /* @var $filenames_helper kFilenamesHelper */ $table = $this->IsTempTable() ? $this->Application->GetTempName(TABLE_PREFIX.'CategoryItems', 'prefix:'.$this->Prefix) : TABLE_PREFIX.'CategoryItems'; return $filenames_helper->stripDisallowed($table, 'ItemResourceId', $this->GetDBField('ResourceId'), $filename); } /* commented out because it's called only from stripDisallowed body, which is moved to helper function checkAutoFilename($filename) { $filenames_helper = $this->Application->recallObject('FilenamesHelper'); return $filenames_helper->checkAutoFilename($this->TableName, $this->IDField, $this->GetID(), $filename); }*/ /** * Generate item's filename based on it's title field value * * @return void * @access protected */ protected function generateFilename() { if ( !$this->GetDBField('AutomaticFilename') && $this->GetDBField('Filename') ) { return ; } $title_field = $this->getUnitConfig()->getTitleField(); if ( preg_match('/l([\d]+)_(.*)/', $title_field, $regs) ) { // if title field is multilingual, then use it's name from primary language $title_field = 'l' . $this->Application->GetDefaultLanguageId() . '_' . $regs[2]; } $name = $this->stripDisallowed( $this->GetDBField($title_field) ); if ( $name != $this->GetDBField('Filename') ) { $this->SetDBField('Filename', $name); } } /** * Adds item to other category * * @param int $category_id * @param bool $is_primary * @return void * @access public */ public function assignToCategory($category_id, $is_primary = false) { $table = $this->CategoryItemsTable(); $key_clause = '(ItemResourceId = ' . $this->GetDBField('ResourceId') . ')'; // get all categories, where item is in $sql = 'SELECT PrimaryCat, CategoryId FROM ' . $table . ' WHERE ' . $key_clause; $item_categories = $this->Conn->GetCol($sql, 'CategoryId'); $primary_found = $item_category_id = false; if ( $item_categories ) { // find primary category foreach ($item_categories as $item_category_id => $primary_found) { if ( $primary_found ) { break; } } } if ( $primary_found && ($item_category_id == $category_id) && !$is_primary ) { // want to make primary category as non-primary :( return; } elseif ( !$primary_found ) { $is_primary = true; } if ( $is_primary && $item_categories ) { // reset primary mark from all other categories $sql = 'UPDATE ' . $table . ' SET PrimaryCat = 0 WHERE ' . $key_clause; $this->Conn->Query($sql); } // UPDATE & INSERT instead of REPLACE because CategoryItems table has no primary key defined in database if ( isset($item_categories[$category_id]) ) { $sql = 'UPDATE ' . $table . ' SET PrimaryCat = ' . ($is_primary ? 1 : 0) . ' WHERE ' . $key_clause . ' AND (CategoryId = ' . $category_id . ')'; $this->Conn->Query($sql); } else { $fields_hash = Array ( 'CategoryId' => $category_id, 'ItemResourceId' => $this->GetField('ResourceId'), 'PrimaryCat' => $is_primary ? 1 : 0, 'ItemPrefix' => $this->Prefix, 'Filename' => $this->useFilenames ? (string)$this->GetDBField('Filename') : '', // because some prefixes does not use filenames, ); if ( $this->Application->IsTempTable($table) ) { $new_id = (int)$this->Conn->GetOne('SELECT MIN(Id) FROM ' . $table .' WHERE Id < 0' ); $fields_hash['Id'] = $new_id - 1; } $this->Conn->doInsert($fields_hash, $table); } // to ensure filename update after adding to another category // this is critical since there may be an item with same filename in newly added category! $this->Update(); } /** * Removes item from category specified * * @param int $category_id */ function removeFromCategory($category_id) { $sql = 'DELETE FROM '.TABLE_PREFIX.'CategoryItems WHERE (CategoryId = %s) AND (ItemResourceId = %s)'; $this->Conn->Query( sprintf($sql, $category_id, $this->GetDBField('ResourceId')) ); } /** * Returns list of columns, that could exist in imported file * * @return Array */ function getPossibleExportColumns() { static $columns = null; if (!is_array($columns)) { $columns = array_merge($this->Fields['AvailableColumns']['options'], $this->Fields['ExportColumns']['options']); } return $columns; } /** * Returns item's primary image data * * @return Array */ function getPrimaryImageData() { $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogImages WHERE (ResourceId = '.$this->GetDBField('ResourceId').') AND (DefaultImg = 1)'; $image_data = $this->Conn->GetRow($sql); if (!$image_data) { // 2. no primary image, then get image with name "main" $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogImages WHERE (ResourceId = '.$this->GetDBField('ResourceId').') AND (Name = "main")'; $image_data = $this->Conn->GetRow($sql); } return $image_data; } function ChangeStatus($new_status, $pending_editing = false) { $status_field = $this->getUnitConfig()->getStatusField(true); if ($new_status != $this->GetDBField($status_field)) { // status was changed $this->sendEmails($new_status, $pending_editing); } $this->SetDBField($status_field, $new_status); return $this->Update(); } function sendEmails($new_status, $pending_editing = false) { $config = $this->getUnitConfig(); $owner_field = $config->getOwnerField('CreatedById'); $event_name = $config->getPermItemPrefix(); if ($pending_editing) { $event_name .= '.MODIFY'; } $event_name .= $new_status == STATUS_ACTIVE ? '.APPROVE' : '.DENY'; $this->Application->emailUser($event_name, $this->GetDBField($owner_field), $this->getEmailParams()); } /** * Approves changes made to category item * * @return bool */ function ApproveChanges() { $original_id = $this->GetDBField('OrgId'); if ( !($this->usePendingEditing && $original_id) ) { // non-pending copy of original link return $this->ChangeStatus(STATUS_ACTIVE); } if ( $this->raiseEvent('OnBeforeDeleteOriginal', null, Array ('original_id' => $original_id)) ) { // delete original item, because changes made in pending copy (this item) got to be approved in this method $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($this->Prefix, $this->Special, Array ($original_id)); $this->raiseEvent('OnAfterDeleteOriginal', null, Array ('original_id' => $original_id)); $this->SetDBField('OrgId', 0); return $this->ChangeStatus(STATUS_ACTIVE, true); } return false; } /** * Decline changes made to category item * * @return bool */ function DeclineChanges() { $original_id = $this->GetDBField('OrgId'); if ( !($this->usePendingEditing && $original_id) ) { // non-pending copy of original link return $this->ChangeStatus(STATUS_DISABLED); } // delete this item, because changes made in pending copy (this item) will be declined in this method $temp_handler = $this->Application->recallObject($this->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($this->Prefix, $this->Special, Array ($this->GetID())); $this->sendEmails(STATUS_DISABLED, true); // original item is not changed here, because it is already enabled (thrus pending copy is visible to item's owner or admin with permission) return true; } function RegisterHit() { $already_viewed = $this->Application->RecallVar($this->getPrefixSpecial().'_already_viewed'); $already_viewed = $already_viewed ? unserialize($already_viewed) : Array (); $id = $this->GetID(); if (!in_array($id, $already_viewed)) { $property_map = $this->getUnitConfig()->getItemPropertyMappings(Array ()); if (!$property_map) { return ; } $hits_field = $property_map['ClickField']; $new_hits = $this->GetDBField($hits_field) + 1; $sql = 'SELECT MAX('.$hits_field.') FROM '.$this->TableName.' WHERE FLOOR('.$hits_field.') = '.$new_hits; $max_hits = $this->Conn->GetOne($sql); if ($max_hits) { $new_hits = $max_hits + 0.000001; } $fields_hash = Array ($hits_field => $new_hits,); $this->Conn->doUpdate($fields_hash, $this->TableName, $this->IDField.' = '.$id); array_push($already_viewed, $id); $this->Application->StoreVar($this->getPrefixSpecial().'_already_viewed', serialize($already_viewed)); } } /** * 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 ( $method == 'load' && !isset($keys_hash) ) { // for item with many categories makes primary to load $ci_table = TABLE_PREFIX . 'CategoryItems'; if ($this->IsTempTable()) { $ci_table = $this->Application->GetTempName($ci_table, 'prefix:' . $this->Prefix); } - // ensures, that CategoryId calculated field has primary category id in it - $keys_hash = Array ( - $this->IDField => $this->ID, - '`' . $ci_table . '`.`PrimaryCat`' => 1 - ); + if ( $this->Application->isAdmin ) { + // When coping new item from temp table, where no CategoryItems record present yet. + return parent::GetKeyClause($method, $keys_hash) . ' ORDER BY `' . $ci_table . '`.PrimaryCat DESC'; + } + else { + // Ensures, that CategoryId calculated field has primary category id in it. + $keys_hash = Array ( + $this->IDField => $this->ID, + '`' . $ci_table . '`.`PrimaryCat`' => 1 + ); + } } return parent::GetKeyClause($method, $keys_hash); } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/db/db_tag_processor.php =================================================================== --- branches/5.3.x/core/kernel/db/db_tag_processor.php (revision 16110) +++ branches/5.3.x/core/kernel/db/db_tag_processor.php (revision 16111) @@ -1,3108 +1,3110 @@ getObject($params); return $object->GetID() <= 0; } /** * Returns view menu name for current prefix * * @param Array $params * @return string */ function GetItemName($params) { $item_name = $this->getUnitConfig()->getViewMenuPhrase(); return $this->Application->Phrase($item_name); } function ViewMenu($params) { $block_params = $params; unset($block_params['block']); $block_params['name'] = $params['block']; $list =& $this->GetList($params); $block_params['PrefixSpecial'] = $list->getPrefixSpecial(); return $this->Application->ParseBlock($block_params); } function SearchKeyword($params) { $list =& $this->GetList($params); return $this->Application->RecallVar($list->getPrefixSpecial() . '_search_keyword'); } /** * Draw filter menu content (for ViewMenu) based on filters defined in config * * @param Array $params * @return string */ function DrawFilterMenu($params) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['spearator_block']; $separator = $this->Application->ParseBlock($block_params); $filter_menu = $this->getUnitConfig()->getFilterMenu(); if ( !$filter_menu ) { trigger_error('no filters defined for prefix ' . $this->Prefix . ', but DrawFilterMenu tag used', E_USER_NOTICE); return ''; } // Params: label, filter_action, filter_status $block_params['name'] = $params['item_block']; $view_filter = $this->Application->RecallVar($this->getPrefixSpecial() . '_view_filter'); if ( $view_filter === false ) { $event_params = Array ('prefix' => $this->Prefix, 'special' => $this->Special, 'name' => 'OnRemoveFilters'); $this->Application->HandleEvent(new kEvent($event_params)); $view_filter = $this->Application->RecallVar($this->getPrefixSpecial() . '_view_filter'); } $view_filter = unserialize($view_filter); $filters = Array (); $prefix_special = $this->getPrefixSpecial(); foreach ($filter_menu['Filters'] as $filter_key => $filter_params) { $group_params = isset($filter_params['group_id']) ? $filter_menu['Groups'][$filter_params['group_id']] : Array (); if ( !isset($group_params['element_type']) ) { $group_params['element_type'] = 'checkbox'; } if ( !$filter_params ) { $filters[] = $separator; continue; } $block_params['label'] = $filter_params['label']; if ( getArrayValue($view_filter, $filter_key) ) { $submit = 0; if ( isset($params['old_style']) ) { $status = $group_params['element_type'] == 'checkbox' ? 1 : 2; } else { $status = $group_params['element_type'] == 'checkbox' ? '[\'img/check_on.gif\']' : '[\'img/menu_dot.gif\']'; } } else { $submit = 1; $status = 'null'; } $block_params['filter_action'] = 'set_filter("' . $prefix_special . '","' . $filter_key . '","' . $submit . '",' . $params['ajax'] . ');'; $block_params['filter_status'] = $status; // 1 - checkbox, 2 - radio, 0 - no image $filters[] = $this->Application->ParseBlock($block_params); } return implode('', $filters); } /** * Draws auto-refresh submenu in View Menu. * * @param Array $params * @return string */ function DrawAutoRefreshMenu($params) { $refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals'); if (!$refresh_intervals) { trigger_error('no refresh intervals defined for prefix '.$this->Prefix.', but DrawAutoRefreshMenu tag used', E_USER_NOTICE); return ''; } $refresh_intervals = explode(',', $refresh_intervals); $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); $current_refresh_interval = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_refresh_interval.'.$view_name); if ($current_refresh_interval === false) { // if no interval was selected before, then choose 1st interval $current_refresh_interval = $refresh_intervals[0]; } $ret = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($refresh_intervals as $refresh_interval) { $block_params['label'] = $this->_formatInterval($refresh_interval); $block_params['refresh_interval'] = $refresh_interval; $block_params['selected'] = $current_refresh_interval == $refresh_interval; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Tells, that current grid is using auto refresh * * @param Array $params * @return bool */ function UseAutoRefresh($params) { $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); return $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_auto_refresh.'.$view_name); } /** * Returns current grid refresh interval * * @param Array $params * @return bool */ function AutoRefreshInterval($params) { $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); return $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_refresh_interval.'.$view_name); } /** * Formats time interval using given text for hours and minutes * * @param int $interval minutes * @param string $hour_text Text for hours * @param string $min_text Text for minutes * @return string */ function _formatInterval($interval, $hour_text = 'h', $min_text = 'min') { // 65 $minutes = $interval % 60; $hours = ($interval - $minutes) / 60; $ret = ''; if ($hours) { $ret .= $hours.$hour_text.' '; } if ($minutes) { $ret .= $minutes.$min_text; } return $ret; } function IterateGridFields($params) { $mode = $params['mode']; $def_block = isset($params['block']) ? $params['block'] : ''; $force_block = isset($params['force_block']) ? $params['force_block'] : false; $grid = $this->getUnitConfig()->getGridByName($params['grid']); $grid_config = $grid['Fields']; $picker_helper = new kColumnPickerHelper($this->getPrefixSpecial(), $params['grid']); $grid_config = $picker_helper->apply($grid_config); if ( $mode == 'fields' ) { return "'" . join("','", array_keys($grid_config)) . "'"; } $object =& $this->GetList($params); $o = ''; $i = 0; foreach ($grid_config as $field => $options) { $i++; $block_params = $this->prepareTagParams($params); $block_params = array_merge($block_params, $options); $block_params['block_name'] = array_key_exists($mode . '_block', $block_params) ? $block_params[$mode . '_block'] : $def_block; $block_params['name'] = $force_block ? $force_block : $block_params['block_name']; $block_params['field'] = $field; $block_params['sort_field'] = isset($options['sort_field']) ? $options['sort_field'] : $field; $block_params['filter_field'] = isset($options['filter_field']) ? $options['filter_field'] : $field; $w = $picker_helper->getWidth($field); if ( $w ) { // column picker width overrides width from unit config $block_params['width'] = $w; } $field_options = $object->GetFieldOptions($field); if ( array_key_exists('use_phrases', $field_options) ) { $block_params['use_phrases'] = $field_options['use_phrases']; } $block_params['is_last'] = ($i == count($grid_config)); $o .= $this->Application->ParseBlock($block_params, 1); } return $o; } function PickerCRC($params) { $picker_helper = new kColumnPickerHelper($this->getPrefixSpecial(), $params['grid']); return $picker_helper->getData()->getChecksum(); } function FreezerPosition($params) { $picker_helper = new kColumnPickerHelper($this->getPrefixSpecial(), $params['grid']); $data = $picker_helper->getData(); $freezer_pos = $data->getOrder('__FREEZER__'); return $freezer_pos === false || $data->isHidden('__FREEZER__') ? 1 : ++$freezer_pos; } function GridFieldsCount($params) { $grid = $this->getUnitConfig()->getGridByName($params['grid']); return count($grid['Fields']); } /** * Prints list content using block specified * * @param Array $params * @return string * @access public */ function PrintList($params) { $params['no_table'] = 1; return $this->PrintList2($params); } function InitList($params) { $list_name = isset($params['list_name']) ? $params['list_name'] : ''; $names_mapping = $this->Application->GetVar('NamesToSpecialMapping', Array ()); if ( getArrayValue($names_mapping, $this->Prefix, $list_name) === false ) { $list =& $this->GetList($params); } } function BuildListSpecial($params) { return $this->Special; } /** * Returns key, that identifies each list on template (used internally, not tag) * * @param Array $params * @return string */ function getUniqueListKey($params) { $types = array_key_exists('types', $params) ? $params['types'] : ''; $except = array_key_exists('except', $params) ? $params['except'] : ''; $list_name = array_key_exists('list_name', $params) ? $params['list_name'] : ''; if (!$list_name) { $list_name = $this->Application->Parser->GetParam('list_name'); } return $types . $except . $list_name; } /** * Enter description here... * * @param Array $params * @return kDBList */ function &GetList($params) { $list_name = $this->SelectParam($params, 'list_name,name'); if ( !$list_name ) { $list_name = $this->Application->Parser->GetParam('list_name'); } $requery = isset($params['requery']) && $params['requery']; $main_list = array_key_exists('main_list', $params) && $params['main_list']; $names_mapping = $this->Application->GetVar('NamesToSpecialMapping', Array ()); if ( !array_key_exists($this->Prefix, $names_mapping) ) { // create prefix-based array to special mapping storage $names_mapping[$this->Prefix] = Array (); } if ( $list_name && !$requery ) { // list with "list_name" parameter if ( !array_key_exists($list_name, $names_mapping[$this->Prefix]) ) { // special missing -> generate one $special = $main_list ? $this->Special : $this->BuildListSpecial($params); } else { // get special, formed during list initialization $special = $names_mapping[$this->Prefix][$list_name]; } } else { // list without "list_name" parameter $special = $main_list ? $this->Special : $this->BuildListSpecial($params); } $prefix_special = rtrim($this->Prefix . '.' . $special, '.'); $params['skip_counting'] = true; $list = $this->Application->recallObject($prefix_special, $this->Prefix . '_List', $params); /* @var $list kDBList */ if ( !array_key_exists('skip_quering', $params) || !$params['skip_quering'] ) { if ( $requery ) { $this->Application->HandleEvent(new kEvent($prefix_special . ':OnListBuild', $params)); } if ( array_key_exists('offset', $params) ) { $list->SetOffset($list->GetOffset() + $params['offset']); // apply custom offset } $list->Query($requery); if ( array_key_exists('offset', $params) ) { $list->SetOffset($list->GetOffset() - $params['offset']); // remove custom offset } } $this->Init($this->Prefix, $special); if ( $list_name ) { $names_mapping[$this->Prefix][$list_name] = $special; $this->Application->SetVar('NamesToSpecialMapping', $names_mapping); } return $list; } function ListMarker($params) { $list =& $this->GetList($params); $ret = $list->getPrefixSpecial(); if (array_key_exists('as_preg', $params) && $params['as_preg']) { $ret = preg_quote($ret, '/'); } return $ret; } function CombinedSortingDropDownName($params) { $list =& $this->GetList($params); return $list->getPrefixSpecial() . '_CombinedSorting'; } /** * Prepares name for field with event in it (used only on front-end) * * @param Array $params * @return string */ function SubmitName($params) { $list =& $this->GetList($params); $prefix_special = $list->getPrefixSpecial(); return 'events[' . $prefix_special . '][' . $params['event'] . ']'; } /** * Prints list content using block specified * * @param Array $params * @return string * @access public */ function PrintList2($params) { $per_page = $this->SelectParam($params, 'per_page,max_items'); if ( $per_page !== false ) { $params['per_page'] = $per_page; } $list =& $this->GetList($params); $o = ''; $direction = (isset($params['direction']) && $params['direction'] == "H") ? "H" : "V"; $columns = (isset($params['columns'])) ? $params['columns'] : 1; $config = $this->getUnitConfig(); $id_field = (isset($params['id_field'])) ? $params['id_field'] : $config->getIDField(); if ( $columns > 1 && $direction == 'V' ) { $records_left = array_splice($list->Records, $list->GetSelectedCount()); // because we have 1 more record for "More..." link detection (don't need to sort it) $list->Records = $this->LinearToVertical($list->Records, $columns, $list->GetPerPage()); $list->Records = array_merge($list->Records, $records_left); } $list->GoFirst(); $block_params = $this->prepareTagParams($params); $block_params['name'] = $this->SelectParam($params, 'render_as,block'); $block_params['pass_params'] = 'true'; $block_params['column_width'] = $params['column_width'] = 100 / $columns; $block_start_row_params = $this->prepareTagParams($params); $block_start_row_params['name'] = $this->SelectParam($params, 'row_start_render_as,block_row_start,row_start_block'); $block_end_row_params = $this->prepareTagParams($params); $block_end_row_params['name'] = $this->SelectParam($params, 'row_end_render_as,block_row_end,row_end_block'); $block_empty_cell_params = $this->prepareTagParams($params); $block_empty_cell_params['name'] = $this->SelectParam($params, 'empty_cell_render_as,block_empty_cell,empty_cell_block'); $i = 0; $backup_id = $this->Application->GetVar($this->Prefix . '_id'); $displayed = Array (); $column_number = 1; $cache_mod_rw = $config->getCacheModRewrite() && $this->Application->RewriteURLs() && !$this->Application->isCachingType(CACHING_TYPE_MEMORY); $limit = isset($params['limit']) ? $params['limit'] : false; while (!$list->EOL() && (!$limit || $i<$limit)) { $this->Application->SetVar($this->getPrefixSpecial() . '_id', $list->GetDBField($id_field)); // for edit/delete links using GET $this->Application->SetVar($this->Prefix . '_id', $list->GetDBField($id_field)); $block_params['is_last'] = ($i == $list->GetSelectedCount() - 1); $block_params['last_row'] = ($i + (($i + 1) % $columns) >= $list->GetSelectedCount() - 1); $block_params['not_last'] = !$block_params['is_last']; // for front-end if ( $cache_mod_rw ) { $serial_name = $this->Application->incrementCacheSerial($this->Prefix, $list->GetDBField($id_field), false); if ( $this->Prefix == 'c' ) { // for listing subcategories in category $this->Application->setCache('filenames[%' . $serial_name . '%]', $list->GetDBField('NamedParentPath')); $this->Application->setCache('category_tree[%CIDSerial:' . $list->GetDBField($id_field) . '%]', $list->GetDBField('TreeLeft') . ';' . $list->GetDBField('TreeRight')); } else { // for listing items in category $this->Application->setCache('filenames[%' . $serial_name . '%]', $list->GetDBField('Filename')); $serial_name = $this->Application->incrementCacheSerial('c', $list->GetDBField('CategoryId'), false); $this->Application->setCache('filenames[%' . $serial_name . '%]', $list->GetDBField('CategoryFilename')); } } if ( $i % $columns == 0 ) { // record in this iteration is first in row, then open row $column_number = 1; $o .= $block_start_row_params['name'] ? $this->Application->ParseBlock($block_start_row_params) : (!isset($params['no_table']) ? '' : ''); } else { $column_number++; } $block_params['first_col'] = $column_number == 1 ? 1 : 0; $block_params['last_col'] = $column_number == $columns ? 1 : 0; $block_params['column_number'] = $column_number; $block_params['num'] = ($i + 1); $this->PrepareListElementParams($list, $block_params); // new, no need to rewrite PrintList $o .= $this->Application->ParseBlock($block_params); array_push($displayed, $list->GetDBField($id_field)); if ( $direction == 'V' && $list->GetSelectedCount() % $columns > 0 && $column_number == ($columns - 1) && ceil(($i + 1) / $columns) > $list->GetSelectedCount() % ceil($list->GetSelectedCount() / $columns) ) { // if vertical output, then draw empty cells vertically, not horizontally $o .= $block_empty_cell_params['name'] ? $this->Application->ParseBlock($block_empty_cell_params) : ' '; $i++; } if ( ($i + 1) % $columns == 0 ) { // record in next iteration is first in row too, then close this row $o .= $block_end_row_params['name'] ? $this->Application->ParseBlock($block_end_row_params) : (!isset($params['no_table']) ? '' : ''); } if ( $this->Special && $this->Application->hasObject($this->Prefix) ) { // object, produced by "kDBList::linkToParent" method, that otherwise would keep it's id $item = $this->Application->recallObject($this->Prefix); /* @var $item kDBBase */ if ( $item instanceof kDBItem ) { $this->Application->removeObject($this->Prefix); } } $list->GoNext(); $i++; } // append empty cells in place of missing cells in last row while ($i % $columns != 0) { // until next cell will be in new row append empty cells $o .= $block_empty_cell_params['name'] ? $this->Application->ParseBlock($block_empty_cell_params) : ' '; if ( ($i + 1) % $columns == 0 ) { // record in next iteration is first in row too, then close this row $o .= $block_end_row_params['name'] ? $this->Application->ParseBlock($block_end_row_params) : ''; } $i++; } $cur_displayed = $this->Application->GetVar($this->Prefix . '_displayed_ids'); if ( !$cur_displayed ) { $cur_displayed = Array (); } else { $cur_displayed = explode(',', $cur_displayed); } $displayed = array_unique(array_merge($displayed, $cur_displayed)); $this->Application->SetVar($this->Prefix . '_displayed_ids', implode(',', $displayed)); $this->Application->SetVar($this->Prefix . '_id', $backup_id); $this->Application->SetVar($this->getPrefixSpecial() . '_id', ''); if ( isset($params['more_link_render_as']) ) { $block_params = $params; $params['render_as'] = $params['more_link_render_as']; $o .= $this->MoreLink($params); } return $o; } /** * Returns ID of previous record (related to current) in list. * Use only on item detail pages. * * @param Array $params * @return int * @access protected */ protected function PreviousResource($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $select_clause = $object->getUnitConfig()->getNavigationSelectClause(null); return $list_helper->getNavigationResource($object, $params['list'], false, $select_clause); } /** * Returns ID of next record (related to current) in list. * Use only on item detail pages. * * @param Array $params * @return int * @access protected */ protected function NextResource($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $select_clause = $object->getUnitConfig()->getNavigationSelectClause(null); return $list_helper->getNavigationResource($object, $params['list'], true, $select_clause); } /** * Allows to modify block params & current list record before PrintList parses record * * @param kDBList $object * @param Array $block_params * @return void * @access protected */ protected function PrepareListElementParams(&$object, &$block_params) { // $fields_hash =& $object->getCurrentRecord(); } /** * Renders given block name, when there there is more data in list, then are displayed right now * * @param Array $params * @return string * @access protected */ protected function MoreLink($params) { $per_page = $this->SelectParam($params, 'per_page,max_items'); if ( $per_page !== false ) { $params['per_page'] = $per_page; } $list =& $this->GetList($params); if ( $list->isCounted() ) { $has_next_page = $list->GetPage() < $list->GetTotalPages(); } else { // selected more, then on the page -> has more $has_next_page = $list->GetPerPage() < $list->GetRecordsCount(); } if ( $has_next_page ) { $block_params = Array ('name' => $this->SelectParam($params, 'render_as,block')); return $this->Application->ParseBlock($block_params); } return ''; } function PageLink($params) { static $default_per_page = Array (); $object =& $this->GetList($params); /* @var $object kDBList */ // process sorting if ($object->isMainList()) { if (!array_key_exists('sort_by', $params)) { $sort_by = $this->Application->GetVar('sort_by'); if ($sort_by !== false) { $params['sort_by'] = $sort_by; } } } $prefix_special = $this->getPrefixSpecial(); // process page $page = array_key_exists('page', $params) ? $params['page'] : $this->Application->GetVar($prefix_special . '_Page'); if (!$page) { // ensure, that page is always present if ($object->isMainList()) { $params[$prefix_special . '_Page'] = $this->Application->GetVar('page', 1); } else { $params[$prefix_special . '_Page'] = 1; } } if (array_key_exists('page', $params)) { $params[$prefix_special . '_Page'] = $params['page']; unset($params['page']); } // process per-page $per_page = array_key_exists('per_page', $params) ? $params['per_page'] : $this->Application->GetVar($prefix_special . '_PerPage'); if (!$per_page) { // ensure, that per-page is always present list ($prefix, ) = explode('.', $prefix_special); if (!array_key_exists($prefix, $default_per_page)) { $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); } if ($object->isMainList()) { $params[$prefix_special . '_PerPage'] = $this->Application->GetVar('per_page', $default_per_page[$prefix]); } else { $params[$prefix_special . '_PerPage'] = $default_per_page[$prefix]; } } if (array_key_exists('per_page', $params)) { $params[$prefix_special . '_PerPage'] = $params['per_page']; unset($params['per_page']); } if (!array_key_exists('pass', $params)) { $params['pass'] = 'm,' . $prefix_special; } // process template $t = array_key_exists('template', $params) ? $params['template'] : ''; unset($params['template']); if (!$t) { $t = $this->Application->GetVar('t'); } return $this->Application->HREF($t, '', $params); } /** * Deprecated * * @param array $params * @return int * @deprecated Parameter "column_width" of "PrintList" tag does that */ function ColumnWidth($params) { $columns = $this->Application->Parser->GetParam('columns'); return round(100/$columns).'%'; } /** * Append prefix and special to tag * params (get them from tagname) like * they were really passed as params * * @param Array $tag_params * @return Array * @access protected */ function prepareTagParams($tag_params = Array()) { $ret = $tag_params; $ret['Prefix'] = $this->Prefix; $ret['Special'] = $this->Special; $ret['PrefixSpecial'] = $this->getPrefixSpecial(); return $ret; } function GetISO($currency, $field_currency = '') { if ( $currency == 'selected' ) { return $this->Application->RecallVar('curr_iso'); } if ( $currency == 'primary' || $currency == '' ) { return $this->Application->GetPrimaryCurrency(); } // explicit currency return $currency == 'field' && $field_currency ? $field_currency : $currency; } /** * Convert primary currency to selected (if they are the same, converter will just return) * * @param float $value * @param string $target_iso * @param string $source_iso * @return float */ function ConvertCurrency($value, $target_iso, $source_iso = 'PRIMARY') { $converter = $this->Application->recallObject('CurrencyRates'); /* @var $converter CurrencyRates */ return $converter->Convert($value, $source_iso, $target_iso); } function AddCurrencySymbol($value, $iso, $decimal_tag = '') { $converter = $this->Application->recallObject('CurrencyRates'); /* @var $converter CurrencyRates */ return $converter->AddCurrencySymbol($value, $iso, $decimal_tag); } /** * Get's requested field value * * @param Array $params * @return string * @access public */ function Field($params) { $field = $this->SelectParam($params, 'name,field'); if (!$this->Application->isAdmin) { // don't apply kUtil::escape() on any field value on Front-End $params['no_special'] = 'no_special'; } $object = $this->getObject($params); /* @var $object kDBItem */ if (array_key_exists('db', $params) && $params['db']) { $value = $object->GetDBField($field); } else { if (array_key_exists('currency', $params) && $params['currency']) { $source_iso = isset($params['currency_field']) ? $object->GetDBField($params['currency_field']) : 'PRIMARY'; $target_iso = $this->GetISO($params['currency'], $source_iso); $original = $object->GetDBField($field); $value = $this->ConvertCurrency($original, $target_iso, $source_iso); $object->SetDBField($field, $value); $object->SetFieldOption($field, 'converted', true); } $format = array_key_exists('format', $params) ? $params['format'] : false; if (!$format || $format == '$format') { $format = NULL; } $value = $object->GetField($field, $format); if (array_key_exists('negative', $params) && $params['negative']) { if (strpos($value, '-') === 0) { $value = substr($value, 1); } else { $value = '-' . $value; } } if (array_key_exists('currency', $params) && $params['currency']) { $decimal_tag = isset($params['decimal_tag']) ? $params['decimal_tag'] : ''; $value = $this->AddCurrencySymbol($value, $target_iso, $decimal_tag); $params['no_special'] = 1; } } if (!array_key_exists('no_special', $params) || !$params['no_special']) { $value = kUtil::escape($value); } if (array_key_exists('checked', $params) && $params['checked']) { $value = ($value == ( isset($params['value']) ? $params['value'] : 1)) ? 'checked' : ''; } if (array_key_exists('plus_or_as_label', $params) && $params['plus_or_as_label']) { $value = substr($value, 0,1) == '+' ? substr($value, 1) : $this->Application->Phrase($value); } elseif (array_key_exists('as_label', $params) && $params['as_label']) { $value = $this->Application->Phrase($value); } $first_chars = $this->SelectParam($params,'first_chars,cut_first'); if ($first_chars) { $stripped_value = strip_tags($value, $this->SelectParam($params, 'allowed_tags')); if ( mb_strlen($stripped_value) > $first_chars ) { $value = preg_replace('/\s+?(\S+)?$/', '', mb_substr($stripped_value, 0, $first_chars + 1)) . ' ...'; } } if (array_key_exists('nl2br', $params) && $params['nl2br']) { $value = nl2br($value); } if ($value != '') { $this->Application->Parser->DataExists = true; } if (array_key_exists('currency', $params) && $params['currency']) { // restoring value in original currency, for other Field tags to work properly $object->SetDBField($field, $original); } return $value; } function FieldHintLabel($params) { if ( isset($params['direct_label']) && $params['direct_label'] ) { $label = $params['direct_label']; $hint = $this->Application->Phrase($label, false); } else { $label = $params['title_label']; $hint = $this->Application->Phrase('hint:' . $label, false); } return $hint != strtoupper('!' . $label . '!') ? $hint : ''; // $hint } /** * Returns formatted date + time on current language * * @param $params */ function DateField($params) { $field = $this->SelectParam($params, 'name,field'); if ($field) { $object = $this->getObject($params); /* @var $object kDBItem */ $timestamp = $object->GetDBField($field); } else { $timestamp = $params['value']; } $date = $timestamp; // prepare phrase replacements $replacements = Array ( 'l' => 'la_WeekDay', 'D' => 'la_WeekDay', 'M' => 'la_Month', 'F' => 'la_Month', ); // cases allow to append phrase suffix based on requested case (e.g. Genitive) $case_suffixes = array_key_exists('case_suffixes', $params) ? $params['case_suffixes'] : false; if ($case_suffixes) { // apply case suffixes (for russian language only) $case_suffixes = explode(',', $case_suffixes); foreach ($case_suffixes as $case_suffux) { list ($replacement_name, $case_suffix_value) = explode('=', $case_suffux, 2); $replacements[$replacement_name] .= $case_suffix_value; } } $format = array_key_exists('format', $params) ? $params['format'] : false; if (preg_match('/_regional_(.*)/', $format, $regs)) { $language = $this->Application->recallObject('lang.current'); /* @var $language kDBItem */ $format = $language->GetDBField($regs[1]); } elseif (!$format) { $format = null; } // escape formats, that are resolved to words by `date` foreach ($replacements as $format_char => $phrase_prefix) { if (strpos($format, $format_char) === false) { unset($replacements[$format_char]); continue; } $replacements[$format_char] = $this->Application->Phrase($phrase_prefix . date($format_char, $date)); $format = str_replace($format_char, '#' . ord($format_char) . '#', $format); } $date_formatted = date($format, $date); // unescape formats, that are resolved to words by `date` foreach ($replacements as $format_char => $format_replacement) { $date_formatted = str_replace('#' . ord($format_char) . '#', $format_replacement, $date_formatted); } return $date_formatted; } function SetField($params) { // $object = $this->getObject($params); /* @var $object kDBItem */ $dst_field = $this->SelectParam($params, 'name,field'); list($prefix_special, $src_field) = explode(':', $params['src']); $src_object = $this->Application->recallObject($prefix_special); /* @var $src_object kDBItem */ $object->SetDBField($dst_field, $src_object->GetDBField($src_field)); } /** * Depricated * * @param Array $params * @return string * @deprecated parameter "as_label" of "Field" tag does the same */ function PhraseField($params) { $field_label = $this->Field($params); $translation = $this->Application->Phrase( $field_label ); return $translation; } function Error($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'name,field'); return $object->GetErrorMsg($field, false); } function HasError($params) { if ($params['field'] == 'any') { $object = $this->getObject($params); /* @var $object kDBItem */ $skip_fields = array_key_exists('except', $params) ? $params['except'] : false; $skip_fields = $skip_fields ? explode(',', $skip_fields) : Array(); return $object->HasErrors($skip_fields); } else { $res = false; $fields = explode(',', $this->SelectParam($params, 'field,fields')); foreach ($fields as $field) { // call kDBTagProcessor::Error instead of kDBItem::GetErrorPseudo to have ability to override Error tag $params['field'] = $field; $res = $res || ($this->Error($params) != ''); } return $res; } } /** * Renders error message block, when there are errors on a form * * @param Array $params * @return string * @access protected */ protected function ErrorWarning($params) { if ( !isset($params['field']) ) { $params['field'] = 'any'; } if ( $this->HasError($params) ) { $params['prefix'] = $this->getPrefixSpecial(); return $this->Application->ParseBlock($params); } return ''; } function IsRequired($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $params['field']; $formatter_class = $object->GetFieldOption($field, 'formatter'); if ( $formatter_class == 'kMultiLanguage' ) { $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kMultiLanguage */ $field = $formatter->LangFieldName($field); } return $object->isRequired($field); } function FieldOption($params) { $object = $this->getObject($params);; $options = $object->GetFieldOptions($params['field']); $ret = isset($options[$params['option']]) ? $options[$params['option']] : ''; if (isset($params['as_label']) && $params['as_label']) $ret = $this->Application->ReplaceLanguageTags($ret); return $ret; } /** * Prints list a all possible field options * * @param Array $params * @return string * @access protected */ protected function PredefinedOptions($params) { $object = $this->getObject($params); /* @var $object kDBList */ $field = $params['field']; $value = array_key_exists('value', $params) ? $params['value'] : $object->GetDBField($field); $field_options = $object->GetFieldOptions($field); if (!array_key_exists('options', $field_options) || !is_array($field_options['options'])) { trigger_error('Options not defined for '.$object->Prefix.' field '.$field.'', E_USER_WARNING); return ''; } $options = $field_options['options']; if ( array_key_exists('has_empty', $params) && $params['has_empty'] ) { $empty_value = array_key_exists('empty_value', $params) ? $params['empty_value'] : ''; $empty_label = isset($params['empty_label']) ? $params['empty_label'] : ''; if ( $empty_label ) { if ( mb_substr($empty_label, 0, 1) == '+' ) { // using plain text instead of phrase label $empty_label = mb_substr($empty_label, 1); } else { $empty_label = $this->Application->Phrase($empty_label, false); } } // don't use other array merge function, because they will reset keys !!! $options = kUtil::array_merge_recursive(Array ($empty_value => $empty_label), $options); } $block_params = $this->prepareTagParams($params); $block_params['name'] = $this->SelectParam($params, 'render_as,block'); $block_params['pass_params'] = 'true'; if (method_exists($object, 'EOL') && count($object->Records) == 0) { // for drawing grid column filter $block_params['field_name'] = ''; } else { // deprecated (produces warning when used as grid filter), but used in Front-End (submission create), admin (submission view) $block_params['field_name'] = $this->InputName($params); } $selected_html = isset($params['selected']) ? $params['selected'] : 'selected'; $selected_param_name = array_key_exists('selected_param', $params) ? $params['selected_param'] : false; if (!$selected_param_name) { $selected_param_name = $selected_html; } $o = ''; if (array_key_exists('no_empty', $params) && $params['no_empty'] && !getArrayValue($options, '')) { // removes empty option, when present (needed?) array_shift($options); } $selected_option_keys = $this->getSelectedOptionKeys($value); if ( isset($params['selected_only']) && $params['selected_only'] ) { $options = $this->getSelectedOptions($options, $selected_option_keys); } $column_changed = false; $option_number = $column_number = 1; $option_count = count($options); $column_count = isset($params['columns']) ? $params['columns'] : 1; $options_per_column = ceil($option_count / $column_count); $block_params['option_count'] = $option_count; foreach ( $options as $option_key => $option_title ) { $block_params['key'] = $option_key; $block_params['option'] = $option_title; $block_params[$selected_param_name] = $this->isOptionSelected($option_key, $selected_option_keys) ? ' ' . $selected_html : ''; $block_params['column_number'] = $column_number; $block_params['column_changed'] = $column_changed; $block_params['option_number'] = $option_number; $block_params['is_last'] = $option_number == $option_count; $o .= $this->Application->ParseBlock($block_params); $column_changed = false; $option_number++; if ( $option_number > $column_number * $options_per_column ) { $column_number++; $column_changed = true; } } return $o; } /** * Returns unified representation of selected options based on field value. * * @param mixed $field_value Field value. * * @return array */ protected function getSelectedOptionKeys($field_value) { if ( strpos($field_value, '|') !== false ) { // multiple checkboxes OR multiselect return explode('|', trim($field_value, '|')); } // single selection radio OR checkboxes OR dropdown return array("$field_value"); } /** * Returns only options, that have been selected. * * @param array $options All options. * @param array $selected_option_keys Selected options. * * @return array */ protected function getSelectedOptions(array $options, array $selected_option_keys) { $ret = array(); foreach ( $options as $option_key => $option_title ) { if ( $this->isOptionSelected($option_key, $selected_option_keys) ) { $ret[$option_key] = $option_title; } } return $ret; } /** * Determines if given option is among selected ones. * * @param mixed $option_key Option key. * @param array $selected_option_keys Selected options. * * @return boolean */ protected function isOptionSelected($option_key, array $selected_option_keys) { return in_array("$option_key", $selected_option_keys, true); } function PredefinedSearchOptions($params) { $object =& $this->GetList($params); /* @var $object kDBList */ $params['value'] = $this->SearchField($params); return $this->PredefinedOptions($params); } function Format($params, $object = null) { $field = $this->SelectParam($params, 'name,field'); if ( !isset($object) ) { $object = $this->getObject($params); /* @var $object kDBItem */ } $options = $object->GetFieldOptions($field); $format = $options[$this->SelectParam($params, 'input_format') ? 'input_format' : 'format']; $formatter_class = array_key_exists('formatter', $options) ? $options['formatter'] : false; if ( $formatter_class ) { $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kFormatter */ $human_format = array_key_exists('human', $params) ? $params['human'] : false; $edit_size = array_key_exists('edit_size', $params) ? $params['edit_size'] : false; $sample = array_key_exists('sample', $params) ? $params['sample'] : false; if ( $sample ) { return $formatter->GetSample($field, $options, $object); } elseif ( $human_format || $edit_size ) { $format = $formatter->HumanFormat($format); return $edit_size ? strlen($format) : $format; } } return $format; } /** * Returns grid padination information * Can return links to pages * * @param Array $params * @return mixed */ function PageInfo($params) { $object =& $this->GetList($params); /* @var $object kDBList */ $type = $params['type']; unset($params['type']); // remove parameters used only by current tag $ret = ''; switch ($type) { case 'current': $ret = $object->GetPage(); break; case 'total': $ret = $object->GetTotalPages(); break; case 'prev': $ret = $object->GetPage() > 1 ? $object->GetPage() - 1 : false; break; case 'next': $ret = $object->GetPage() < $object->GetTotalPages() ? $object->GetPage() + 1 : false; break; } if ($ret && isset($params['as_link']) && $params['as_link']) { unset($params['as_link']); // remove parameters used only by current tag $params['page'] = $ret; $current_page = $object->GetPage(); // backup current page $ret = $this->PageLink($params); $this->Application->SetVar($object->getPrefixSpecial().'_Page', $current_page); // restore page } return $ret; } /** * Print grid pagination using * block names specified * * @param Array $params * @return string * @access public */ function PrintPages($params) { $list =& $this->GetList($params); $prefix_special = $list->getPrefixSpecial(); $total_pages = $list->GetTotalPages(); if ( $total_pages > 1 ) { $this->Application->Parser->DataExists = true; } if ( $total_pages == 0 ) { // display 1st page as selected in case if we have no pages at all $total_pages = 1; } $o = ''; // what are these 2 lines for? $this->Application->SetVar($prefix_special . '_event', ''); $this->Application->SetVar($prefix_special . '_id', ''); $current_page = $list->GetPage(); // $this->Application->RecallVar($prefix_special.'_Page'); $block_params = $this->prepareTagParams($params); $split = (isset($params['split']) ? $params['split'] : 10); $split_start = $current_page - ceil($split / 2); if ( $split_start < 1 ) { $split_start = 1; } $split_end = $split_start + $split - 1; if ( $split_end > $total_pages ) { $split_end = $total_pages; $split_start = max($split_end - $split + 1, 1); } if ( $current_page > 1 ) { $prev_block_params = $this->prepareTagParams($params); if ( $total_pages > $split ) { $prev_block_params['page'] = max($current_page - $split, 1); $prev_block_params['name'] = $this->SelectParam($params, 'prev_page_split_render_as,prev_page_split_block'); if ( $prev_block_params['name'] ) { + $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $prev_block_params['page']); $o .= $this->Application->ParseBlock($prev_block_params); } } $prev_block_params['name'] = 'page'; $prev_block_params['page'] = $current_page - 1; $prev_block_params['name'] = $this->SelectParam($params, 'prev_page_render_as,block_prev_page,prev_page_block'); if ( $prev_block_params['name'] ) { - $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $current_page - 1); + $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $prev_block_params['page']); $o .= $this->Application->ParseBlock($prev_block_params); } } else { $no_prev_page_block = $this->SelectParam($params, 'no_prev_page_render_as,block_no_prev_page'); if ( $no_prev_page_block ) { $block_params['name'] = $no_prev_page_block; $o .= $this->Application->ParseBlock($block_params); } } $total_records = $list->GetRecordsCount(); $separator_params['name'] = $this->SelectParam($params, 'separator_render_as,block_separator'); for ($i = $split_start; $i <= $split_end; $i++) { $from_record = ($i - 1) * $list->GetPerPage(); $to_record = $from_record + $list->GetPerPage(); if ( $to_record > $total_records ) { $to_record = $total_records; } $block_params['from_record'] = $from_record + 1; $block_params['to_record'] = $to_record; if ( $i == $current_page ) { $block = $this->SelectParam($params, 'current_render_as,active_render_as,block_current,active_block'); } else { $block = $this->SelectParam($params, 'link_render_as,inactive_render_as,block_link,inactive_block'); } $block_params['name'] = $block; $block_params['page'] = $i; - $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $i); + $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $block_params['page']); $o .= $this->Application->ParseBlock($block_params); if ( $this->SelectParam($params, 'separator_render_as,block_separator') && $i < $split_end ) { $o .= $this->Application->ParseBlock($separator_params); } } if ( $current_page < $total_pages ) { $next_block_params = $this->prepareTagParams($params); $next_block_params['page'] = $current_page + 1; $next_block_params['name'] = $this->SelectParam($params, 'next_page_render_as,block_next_page,next_page_block'); if ( $next_block_params['name'] ) { - $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $current_page + 1); + $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $next_block_params['page']); $o .= $this->Application->ParseBlock($next_block_params); } if ( $total_pages > $split ) { $next_block_params['page'] = min($current_page + $split, $total_pages); $next_block_params['name'] = $this->SelectParam($params, 'next_page_split_render_as,next_page_split_block'); if ( $next_block_params['name'] ) { + $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $next_block_params['page']); $o .= $this->Application->ParseBlock($next_block_params); } } } else { $no_next_page_block = $this->SelectParam($params, 'no_next_page_render_as,block_no_next_page'); if ( $no_next_page_block ) { $block_params['name'] = $no_next_page_block; $o .= $this->Application->ParseBlock($block_params); } } $this->Application->SetVar($this->getPrefixSpecial() . '_Page', $current_page); return $o; } /** * Print grid pagination using * block names specified * * @param Array $params * @return string * @access public */ function PaginationBar($params) { return $this->PrintPages($params); } function PerPageBar($params) { $object =& $this->GetList($params); $ret = ''; $per_pages = explode(';', $params['per_pages']); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($per_pages as $per_page) { $block_params['per_page'] = $per_page; $this->Application->SetVar($this->getPrefixSpecial() . '_PerPage', $per_page); $block_params['selected'] = $per_page == $object->GetPerPage(); $ret .= $this->Application->ParseBlock($block_params, 1); } $this->Application->SetVar($this->getPrefixSpecial() . '_PerPage', $object->GetPerPage()); return $ret; } /** * Returns field name (processed by kMultiLanguage formatter * if required) and item's id from it's IDField or field required * * @param Array $params * @return Array (id,field) * @access private */ function prepareInputName($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'name,field'); $formatter_class = $object->GetFieldOption($field, 'formatter'); if ($formatter_class == 'kMultiLanguage') { $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kMultiLanguage */ $force_primary = $object->GetFieldOption($field, 'force_primary'); $field = $formatter->LangFieldName($field, $force_primary); } if (array_key_exists('force_id', $params)) { $id = $params['force_id']; } else { $id_field = array_key_exists('IdField', $params) ? $params['IdField'] : false; $id = $id_field ? $object->GetDBField($id_field) : $object->GetID(); } return Array($id, $field); } /** * Returns input field name to * be placed on form (for correct * event processing) * * @param Array $params * @return string * @access public */ function InputName($params) { list($id, $field) = $this->prepareInputName($params); $ret = $this->getPrefixSpecial().'['.$id.']['.$field.']'; if (array_key_exists('as_preg', $params) && $params['as_preg']) { $ret = preg_quote($ret, '/'); } return $ret; } /** * Allows to override various field options through hidden fields with specific names in submit. * This tag generates this special names * * @param Array $params * @return string * @author Alex */ function FieldModifier($params) { list($id, $field) = $this->prepareInputName($params); $ret = 'field_modifiers['.$this->getPrefixSpecial().']['.$field.']['.$params['type'].']'; if (array_key_exists('as_preg', $params) && $params['as_preg']) { $ret = preg_quote($ret, '/'); } if (isset($params['value'])) { $object = $this->getObject($params); $field_modifiers[$field][$params['type']] = $params['value']; $object->ApplyFieldModifiers($field_modifiers); } return $ret; } /** * Returns index where 1st changeable sorting field begins * * @return int * @access private */ function getUserSortIndex() { $list_sortings = $this->getUnitConfig()->getListSortingsBySpecial($this, Array ()); $user_sorting_start = 0; $forced_sorting = getArrayValue($list_sortings, 'ForcedSorting'); return $forced_sorting ? count($forced_sorting) : $user_sorting_start; } /** * Returns order direction for given field * * * * @param Array $params * @return string * @access public */ function Order($params) { $field = $params['field']; $user_sorting_start = $this->getUserSortIndex(); $list =& $this->GetList($params); if ($list->GetOrderField($user_sorting_start) == $field) { return strtolower($list->GetOrderDirection($user_sorting_start)); } elseif($this->Application->ConfigValue('UseDoubleSorting') && $list->GetOrderField($user_sorting_start+1) == $field) { return '2_'.strtolower($list->GetOrderDirection($user_sorting_start+1)); } else { return 'no'; } } /** * Detects, that current sorting is not default * * @param Array $params * @return bool */ function OrderChanged($params) { $list =& $this->GetList($params); $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ return $list_helper->hasUserSorting($list); } /** * Gets information of sorting field at "pos" position, * like sorting field name (type="field") or sorting direction (type="direction") * * @param Array $params * @return string * @access protected */ protected function OrderInfo($params) { $user_sorting_start = $this->getUserSortIndex() + --$params['pos']; $list =& $this->GetList($params); if ( $params['type'] == 'field' ) { return $list->GetOrderField($user_sorting_start); } if ( $params['type'] == 'direction' ) { return $list->GetOrderDirection($user_sorting_start); } return ''; } /** * Checks if sorting field/direction matches passed field/direction parameter * * @param Array $params * @return bool * @access protected */ protected function IsOrder($params) { $params['type'] = isset($params['field']) ? 'field' : 'direction'; $value = $this->OrderInfo($params); if ( isset($params['field']) ) { return $params['field'] == $value; } elseif ( isset($params['direction']) ) { return $params['direction'] == $value; } return false; } /** * Returns list per-page * * @param Array $params * @return int */ function PerPage($params) { $object =& $this->GetList($params); return $object->GetPerPage(); } /** * Checks if list perpage matches value specified * * @param Array $params * @return bool */ function PerPageEquals($params) { $object =& $this->GetList($params); return $object->GetPerPage() == $params['value']; } function SaveEvent($params) { // SaveEvent is set during OnItemBuild, but we may need it before any other tag calls OnItemBuild $object = $this->getObject($params); return $this->Application->GetVar($this->getPrefixSpecial().'_SaveEvent'); } function NextId($params) { $object = $this->getObject($params); $wid = $this->Application->GetTopmostWid($this->Prefix); $session_name = rtrim($this->getPrefixSpecial().'_selected_ids_'.$wid, '_'); $ids = explode(',', $this->Application->RecallVar($session_name)); $cur_id = $object->GetID(); $i = array_search($cur_id, $ids); if ($i !== false) { return $i < count($ids) - 1 ? $ids[$i + 1] : ''; } return ''; } function PrevId($params) { $object = $this->getObject($params); $wid = $this->Application->GetTopmostWid($this->Prefix); $session_name = rtrim($this->getPrefixSpecial().'_selected_ids_'.$wid, '_'); $ids = explode(',', $this->Application->RecallVar($session_name)); $cur_id = $object->GetID(); $i = array_search($cur_id, $ids); if ($i !== false) { return $i > 0 ? $ids[$i - 1] : ''; } return ''; } function IsSingle($params) { return ($this->NextId($params) === '' && $this->PrevId($params) === ''); } function IsLast($params) { return ($this->NextId($params) === ''); } function IsFirst($params) { return ($this->PrevId($params) === ''); } /** * Checks if field value is equal to proposed one * * @param Array $params * @return bool * @deprecated */ function FieldEquals($params) { $object = $this->getObject($params); /* @var $object kDBItem */ return $object->GetDBField( $this->SelectParam($params, 'name,field') ) == $params['value']; } /** * Checks, that grid has icons defined and they should be shown * * @param Array $params * @return bool */ function UseItemIcons($params) { return array_key_exists('Icons', $this->getUnitConfig()->getGridByName($params['grid'])); } /** * Returns corresponding to grid layout selector column width * * @param Array $params * @return int */ function GridSelectorColumnWidth($params) { $width = 0; if ($params['selector']) { $width += $params['selector_width']; } if ($this->UseItemIcons($params)) { $width += $params['icon_width']; } return $width; } /** * Returns grids item selection mode (checkbox, radio, ) * * @param Array $params * @return string */ function GridSelector($params) { $grid = $this->getUnitConfig()->getGridByName($params['grid']); return array_key_exists('Selector', $grid) ? $grid['Selector'] : $params['default']; } function ItemIcon($params) { $config = $this->getUnitConfig(); $grid = $config->getGridByName($params['grid']); if ( !isset($grid['Icons']) ) { return ''; } $icons = $grid['Icons']; if ( isset($params['name']) ) { $icon_name = $params['name']; return isset($icons[$icon_name]) ? $icons[$icon_name] : ''; } $status_fields = $config->getStatusField(false, Array ()); if ( !$status_fields ) { return $icons['default']; } $object = $this->getObject($params); /* @var $object kDBList */ $icon = ''; foreach ($status_fields as $status_field) { $icon .= $object->GetDBField($status_field) . '_'; } $icon = rtrim($icon, '_'); return isset($icons[$icon]) ? $icons[$icon] : $icons['default']; } /** * Generates bluebar title + initializes prefixes used on page * * @param Array $params * @return string */ function SectionTitle($params) { $config = $this->getUnitConfig(); $preset_name = kUtil::replaceModuleSection($params['title_preset']); $title_info = $config->getTitlePresetByName($preset_name); if ( $title_info === false ) { $title = str_replace('#preset_name#', $preset_name, $params['title']); if ( $this->Application->ConfigValue('UseSmallHeader') && isset($params['group_title']) && $params['group_title'] ) { $title .= ' - ' . $params['group_title']; } return $title; } $default_title_preset = $config->getTitlePresetByName('default'); if ( $default_title_preset ) { // use default labels + custom labels specified in preset used $title_info = kUtil::array_merge_recursive($default_title_preset, $title_info); } $title = $title_info['format']; // 1. get objects in use for title construction $objects = Array (); $object_status = Array (); $status_labels = Array (); $prefixes = array_key_exists('prefixes', $title_info) ? $title_info['prefixes'] : false; $all_tag_params = array_key_exists('tag_params', $title_info) ? $title_info['tag_params'] : false; /* @var $prefixes Array */ if ( $prefixes ) { // extract tag_params passed directly to SectionTitle tag for specific prefix foreach ($params as $tp_name => $tp_value) { if ( preg_match('/(.*)\[(.*)\]/', $tp_name, $regs) ) { $all_tag_params[$regs[1]][$regs[2]] = $tp_value; unset($params[$tp_name]); } } $tag_params = Array (); foreach ($prefixes as $prefix_special) { $prefix_data = $this->Application->processPrefix($prefix_special); $prefix_data['prefix_special'] = rtrim($prefix_data['prefix_special'], '.'); if ( $all_tag_params ) { $tag_params = getArrayValue($all_tag_params, $prefix_data['prefix_special']); if ( !$tag_params ) { $tag_params = Array (); } } $tag_params = array_merge($params, $tag_params); $objects[$prefix_data['prefix_special']] = $this->Application->recallObject($prefix_data['prefix_special'], $prefix_data['prefix'], $tag_params); $object_status[$prefix_data['prefix_special']] = $objects[$prefix_data['prefix_special']]->IsNewItem() ? 'new' : 'edit'; // a. set object's status field (adding item/editing item) for each object in title if ( getArrayValue($title_info[$object_status[$prefix_data['prefix_special']] . '_status_labels'], $prefix_data['prefix_special']) ) { $status_labels[$prefix_data['prefix_special']] = $title_info[$object_status[$prefix_data['prefix_special']] . '_status_labels'][$prefix_data['prefix_special']]; $title = str_replace('#' . $prefix_data['prefix_special'] . '_status#', $status_labels[$prefix_data['prefix_special']], $title); } // b. setting object's titlefield value (in titlebar ONLY) to default in case if object beeing created with no titlefield filled in if ( $object_status[$prefix_data['prefix_special']] == 'new' ) { $new_value = $this->getInfo($objects[$prefix_data['prefix_special']], 'titlefield'); if ( !$new_value && getArrayValue($title_info['new_titlefield'], $prefix_data['prefix_special']) ) { $new_value = $this->Application->Phrase($title_info['new_titlefield'][$prefix_data['prefix_special']]); } $title = str_replace('#' . $prefix_data['prefix_special'] . '_titlefield#', $new_value, $title); } } } // replace to section title $section = array_key_exists('section', $params) ? $params['section'] : false; if ( $section ) { $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section); $title = str_replace('#section_label#', '!' . $section_data['label'] . '!', $title); } // 2. replace phrases if any found in format string $title = $this->Application->ReplaceLanguageTags($title, false); // 3. find and replace any replacement vars preg_match_all('/#(.*_.*)#/Uis', $title, $rets); if ( $rets[1] ) { $replacement_vars = array_keys(array_flip($rets[1])); foreach ($replacement_vars as $replacement_var) { $var_info = explode('_', $replacement_var, 2); $object =& $objects[$var_info[0]]; $new_value = $this->getInfo($object, $var_info[1]); $title = str_replace('#' . $replacement_var . '#', $new_value, $title); } } // replace trailing spaces inside title preset + '' occurrences into single space $title = preg_replace('/[ ]*\'\'[ ]*/', ' ', $title); if ( $this->Application->ConfigValue('UseSmallHeader') && isset($params['group_title']) && $params['group_title'] ) { $title .= ' - ' . $params['group_title']; } $first_chars = $this->SelectParam($params, 'first_chars,cut_first'); if ( $first_chars && !preg_match('/(.*)<\/a>/', $title) ) { // don't cut titles, that contain phrase translation links $stripped_title = strip_tags($title, $this->SelectParam($params, 'allowed_tags')); if ( mb_strlen($stripped_title) > $first_chars ) { $title = mb_substr($stripped_title, 0, $first_chars) . ' ...'; } } return $title; } /** * Returns information about list * * @param kDBList $object * @param string $info_type * @return string * @access protected */ protected function getInfo(&$object, $info_type) { switch ( $info_type ) { case 'titlefield': $field = $object->getUnitConfig()->getTitleField(); return $field !== false ? $object->GetField($field) : 'TitleField Missing'; break; case 'recordcount': if ( $object->GetRecordsCount(false) != $object->GetRecordsCount() ) { $of_phrase = $this->Application->Phrase('lc_of'); return $object->GetRecordsCount() . ' ' . $of_phrase . ' ' . $object->GetRecordsCount(false); } return $object->GetRecordsCount(); break; } return $object->GetField($info_type); } function GridInfo($params) { $object =& $this->GetList($params); /* @var $object kDBList */ switch ( $params['type'] ) { case 'filtered': return $object->GetRecordsCount(); case 'total': return $object->GetRecordsCount(false); case 'from': return $object->GetRecordsCount() ? $object->GetOffset() + 1 : 0; //0-based case 'to': $record_count = $object->GetRecordsCount(); return $object->GetPerPage(true) != -1 ? min($object->GetOffset() + $object->GetPerPage(), $record_count) : $record_count; case 'total_pages': return $object->GetTotalPages(); case 'needs_pagination': return ($object->GetPerPage(true) != -1) && (($object->GetRecordsCount() > $object->GetPerPage()) || ($object->GetPage() > 1)); } return false; } /** * Parses block depending on its element type. * For radio and select elements values are taken from 'value_list_field' in key1=value1,key2=value2 * format. key=value can be substituted by SELECT f1 AS OptionName, f2 AS OptionValue... FROM TableName * where prefix is TABLE_PREFIX * * @param Array $params * @return string */ function ConfigFormElement($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $params['field']; $helper = $this->Application->recallObject('InpCustomFieldsHelper'); /* @var $helper InpCustomFieldsHelper */ $element_type = $object->GetDBField($params['element_type_field']); if ($element_type == 'label') { $element_type = 'text'; } $formatter_class = $object->GetFieldOption($field, 'formatter'); switch ($element_type) { case 'select': case 'multiselect': case 'radio': if ($object->GetDBField('DirectOptions')) { // used for custom fields $options = $object->GetDBField('DirectOptions'); } else { // used for configuration $options = $helper->GetValuesHash( $object->GetDBField($params['value_list_field']) ); } $object->SetFieldOption($field, 'formatter', 'kOptionsFormatter'); $object->SetFieldOption($field, 'options', $options); break; case 'text': case 'textarea': case 'upload': $params['field_params'] = $helper->ParseConfigSQL($object->GetDBField($params['value_list_field'])); break; case 'password': case 'checkbox': default: break; } if (!$element_type) { throw new Exception('Element type missing for "' . $object->GetDBField('VariableName') . '" configuration variable'); } $params['name'] = $params['blocks_prefix'] . $element_type; // use $pass_params to pass 'SourcePrefix' parameter from PrintList to CustomInputName tag $ret = $this->Application->ParseBlock($params, 1); $object->SetFieldOption($field, 'formatter', $formatter_class); return $ret; } /** * Get's requested custom field value * * @param Array $params * @return string * @access public */ function CustomField($params) { $params['name'] = 'cust_'.$this->SelectParam($params, 'name,field'); return $this->Field($params); } function CustomFieldLabel($params) { $object = $this->getObject($params); $field = $this->SelectParam($params, 'name,field'); $sql = 'SELECT FieldLabel FROM ' . $this->Application->getUnitConfig('cf')->getTableName() . ' WHERE FieldName = ' . $this->Conn->qstr($field); return $this->Application->Phrase($this->Conn->GetOne($sql)); } /** * transposes 1-dimensional array elements for vertical alignment according to given columns and per_page parameters * * @param array $arr * @param int $columns * @param int $per_page * @return array */ function LinearToVertical(&$arr, $columns, $per_page) { $rows = $columns; // in case if after applying per_page limit record count less then // can fill requrested column count, then fill as much as we can $cols = min(ceil($per_page / $columns), ceil(count($arr) / $columns)); $imatrix = array(); for ($row = 0; $row < $rows; $row++) { for ($col = 0; $col < $cols; $col++) { $source_index = $row * $cols + $col; if (!isset($arr[$source_index])) { // in case if source array element count is less then element count in one row continue; } $imatrix[$col * $rows + $row] = $arr[$source_index]; } } ksort($imatrix); return array_values($imatrix); } /** * If data was modified & is in TempTables mode, then parse block with name passed; * remove modification mark if not in TempTables mode * * @param Array $params * @return string * @access protected */ protected function SaveWarning($params) { $main_prefix = array_key_exists('main_prefix', $params) ? $params['main_prefix'] : false; if ( $main_prefix ) { $top_prefix = $main_prefix; } else { $top_prefix = $this->Application->GetTopmostPrefix($this->Prefix); } $temp_tables = substr($this->Application->GetVar($top_prefix . '_mode'), 0, 1) == 't'; $modified = $this->Application->RecallVar($top_prefix . '_modified'); if ( $temp_tables && $modified ) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $this->SelectParam($params, 'render_as,name'); $block_params['edit_mode'] = $temp_tables ? 1 : 0; return $this->Application->ParseBlock($block_params); } $this->Application->RemoveVar($top_prefix . '_modified'); return ''; } /** * Returns list record count queries (on all pages) * * @param Array $params * @return int */ function TotalRecords($params) { $list =& $this->GetList($params); return $list->GetRecordsCount(); } /** * Range filter field name * * @param Array $params * @return string */ function SearchInputName($params) { $field = $this->SelectParam($params, 'field,name'); $ret = 'custom_filters['.$this->getPrefixSpecial().']['.$params['grid'].']['.$field.']['.$params['filter_type'].']'; if (isset($params['type'])) { $ret .= '['.$params['type'].']'; } if (array_key_exists('as_preg', $params) && $params['as_preg']) { $ret = preg_quote($ret, '/'); } return $ret; } /** * Return range filter field value * * @param Array $params * @return string * @access protected */ protected function SearchField($params) // RangeValue { $field = $this->SelectParam($params, 'field,name'); $view_name = $this->Application->RecallVar($this->getPrefixSpecial() . '_current_view'); $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial() . '_custom_filter.' . $view_name /*, ALLOW_DEFAULT_SETTINGS*/); $custom_filter = $custom_filter ? unserialize($custom_filter) : Array (); if ( isset($custom_filter[$params['grid']][$field]) ) { $ret = $custom_filter[$params['grid']][$field][$params['filter_type']]['submit_value']; if ( isset($params['type']) ) { $ret = $ret[$params['type']]; } if ( array_key_exists('formatted', $params) && $params['formatted'] ) { $object =& $this->GetList($params); $formatter_class = $object->GetFieldOption($field, 'formatter'); if ( $formatter_class ) { $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kFormatter */ $ret = $formatter->Format($ret, $field, $object); } } if ( !array_key_exists('no_special', $params) || !$params['no_special'] ) { $ret = kUtil::escape($ret); } return $ret; } return ''; } /** * Tells, that at least one of search filters is used by now * * @param Array $params * @return bool */ function SearchActive($params) { if ($this->Application->RecallVar($this->getPrefixSpecial() . '_search_keyword')) { // simple search filter is used return true; } $view_name = $this->Application->RecallVar($this->getPrefixSpecial().'_current_view'); $custom_filter = $this->Application->RecallPersistentVar($this->getPrefixSpecial().'_custom_filter.'.$view_name/*, ALLOW_DEFAULT_SETTINGS*/); $custom_filter = $custom_filter ? unserialize($custom_filter) : Array(); return array_key_exists($params['grid'], $custom_filter); } function SearchFormat($params) { $object =& $this->GetList($params); return $this->Format($params, $object); } /** * Returns error of range field * * @param Array $params * @return string * @access protected */ protected function SearchError($params) { $field = $this->SelectParam($params, 'field,name'); $error_var_name = $this->getPrefixSpecial() . '_' . $field . '_error'; $pseudo = $this->Application->RecallVar($error_var_name); if ( $pseudo ) { $this->Application->RemoveVar($error_var_name); } $object = $this->Application->recallObject($this->Prefix . '.' . $this->Special . '-item', null, Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->SetError($field, $pseudo); return $object->GetErrorMsg($field, false); } /** * Returns object used in tag processor * * @param Array $params * @access public * @return kDBItem|kDBList */ function getObject($params = Array()) { $object = $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix, $params); /* @var $object kDBItem */ if ( isset($params['requery']) && $params['requery'] ) { $this->Application->HandleEvent(new kEvent($this->getPrefixSpecial() . ':LoadItem', $params)); } return $object; } /** * Checks if object propery value matches value passed * * @param Array $params * @return bool */ function PropertyEquals($params) { $object = $this->getObject($params); $property_name = $this->SelectParam($params, 'name,var,property'); return $object->$property_name == $params['value']; } function DisplayOriginal($params) { return false; } /*function MultipleEditing($params) { $wid = $this->Application->GetTopmostWid($this->Prefix); $session_name = rtrim($this->getPrefixSpecial().'_selected_ids_'.$wid, '_'); $selected_ids = explode(',', $this->Application->RecallVar($session_name)); $ret = ''; if ($selected_ids) { $selected_ids = explode(',', $selected_ids); $object = $this->getObject( kUtil::array_merge_recursive($params, Array('skip_autoload' => true)) ); $params['name'] = $params['render_as']; foreach ($selected_ids as $id) { $object->Load($id); $ret .= $this->Application->ParseBlock($params); } } return $ret; }*/ /** * Returns import/export process percent * * @param Array $params * @return int * @deprecated Please convert to event-model, not tag based */ function ExportStatus($params) { $export_object = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_object kCatDBItemExportHelper */ $event = new kEvent($this->getPrefixSpecial().':OnDummy'); $action_method = 'perform'.ucfirst($this->Special); $field_values = $export_object->$action_method($event); // finish code is done from JS now if ($field_values['start_from'] >= $field_values['total_records']) { if ($this->Special == 'import') { // this is used? $this->Application->StoreVar('PermCache_UpdateRequired', 1); $this->Application->Redirect('categories/cache_updater', Array('m_opener' => 'r', 'pass' => 'm', 'continue' => 1)); } elseif ($this->Special == 'export') { // used for orders export in In-Commerce $finish_t = $this->Application->RecallVar('export_finish_t'); $this->Application->Redirect($finish_t, Array('pass' => 'all')); $this->Application->RemoveVar('export_finish_t'); } } $export_options = $export_object->loadOptions($event); return $export_options['start_from'] * 100 / $export_options['total_records']; } /** * Returns path where exported category items should be saved * * @param Array $params * @return string * @access protected */ protected function ExportPath($params) { $export_options = unserialize($this->Application->RecallVar($this->getPrefixSpecial() . '_options')); $extension = $export_options['ExportFormat'] == 1 ? 'csv' : 'xml'; $filename = preg_replace('/(.*)\.' . $extension . '$/', '\1', $export_options['ExportFilename']) . '.' . $extension; $path = EXPORT_PATH . '/'; if ( array_key_exists('as_url', $params) && $params['as_url'] ) { $path = str_replace(FULL_PATH . '/', $this->Application->BaseURL(), $path); } return $path . $filename; } function FieldTotal($params) { $list =& $this->GetList($params); $field = $this->SelectParam($params, 'field,name'); $total_function = array_key_exists('function', $params) ? $params['function'] : $list->getTotalFunction($field); if (array_key_exists('function_only', $params) && $params['function_only']) { return $total_function; } if (array_key_exists('currency', $params) && $params['currency']) { $iso = $this->GetISO($params['currency']); $original = $list->getTotal($field, $total_function); $value = $this->ConvertCurrency($original, $iso); $list->setTotal($field, $total_function, $value); } $value = $list->GetFormattedTotal($field, $total_function); if (array_key_exists('currency', $params) && $params['currency']) { $value = $this->AddCurrencySymbol($value, $iso); } return $value; } function FCKEditor($params) { $editor_name = array_key_exists('name', $params) ? $params['name'] : $this->InputName($params); $fck_helper = $this->Application->recallObject('FCKHelper'); /* @var $fck_helper fckFCKHelper */ if ( isset($params['mode']) && $params['mode'] == 'inline' ) { return $fck_helper->CKEditorInlineTag($editor_name, $params); } return $fck_helper->CKEditorTag($editor_name, $this->CKEditorValue($params), $params); } /** * Returns value, used by FCKEditor tag * * @param array $params * * @return string */ protected function CKEditorValue($params) { $params['no_special'] = 1; $params['format'] = array_key_exists('format', $params) ? $params['format'] . ';fck_ready' : 'fck_ready'; return $this->Field($params); } function IsNewItem($params) { $object = $this->getObject($params); return $object->IsNewItem(); } /** * Creates link to an item including only it's id * * @param Array $params * @return string * @access protected */ protected function ItemLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ if ( !isset($params['pass']) ) { $params['pass'] = 'm'; } $params[ $object->getPrefixSpecial() . '_id' ] = $object->GetID(); return $this->Application->ProcessParsedTag('m', 'T', $params); } /** * Creates a button for editing item in Admin Console * * @param Array $params * @return string * @access protected * @throws InvalidArgumentException */ protected function AdminEditButton($params) { if ( EDITING_MODE != EDITING_MODE_CONTENT ) { return ''; } $object = $this->getObject($params); /* @var $object kDBItem */ $item_prefix = isset($params['item_prefix']) ? $params['item_prefix'] : $this->Prefix; if ( isset($params['template']) ) { $template = $params['template']; } else { $item_config = $this->Application->getUnitConfig($item_prefix); $admin_template_prefix = $item_config->getAdminTemplatePrefix(); $template = $item_config->getAdminTemplatePath() . '/' . $admin_template_prefix . 'edit'; if ( !$admin_template_prefix ) { throw new InvalidArgumentException('Automatic admin editing template detection failed because of missing "AdminTemplatePrefix" unit config option in "' . $this->Prefix . '" unit config'); } } $form_name = 'kf_' . str_replace('-', '_', $item_prefix) . '_' . $object->GetID(); $button_icon = isset($params['button_icon']) ? $params['button_icon'] : 'content_mode.png'; $button_class = isset($params['button_class']) ? $params['button_class'] : 'admin-edit-btn'; $button_title = isset($params['button_title']) ? $params['button_title'] : 'la_btn_AdminEditItem'; if ( substr($button_title, 0, 1) == '+' ) { $button_title = substr($button_title, 1); } else { $button_title = $this->Application->Phrase($button_title, false, true); } $icon_url = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/' . $button_icon; $button_onclick = '$form_name = ' . json_encode($form_name) . '; std_edit_item(' . json_encode($item_prefix) . ', ' . json_encode($template) . ');'; $button_code = ''; if ( !isset($params['pass']) ) { $params['pass'] = 'm,' . $item_prefix; } $params['m_opener'] = 'd'; $params[$item_prefix . '_id'] = $object->GetID(); if ( !isset($params['temp_mode']) || (isset($params['temp_mode']) && $params['temp_mode']) ) { $params[$item_prefix . '_mode'] = 't'; $params[$item_prefix . '_event'] = 'OnEdit'; } $params['front'] = 1; // to make opener stack work properly $params['__NO_REWRITE__'] = 1; // since admin link unset($params['button_icon'], $params['button_class'], $params['button_title'], $params['template'], $params['item_prefix'], $params['temp_mode']); // link from Front-End to Admin, don't remove "index.php" $form_name_escaped = kUtil::escape($form_name, kUtil::ESCAPE_HTML); $edit_url = kUtil::escape($this->Application->HREF($template, ADMIN_DIRECTORY, $params, 'index.php'), kUtil::ESCAPE_HTML); $edit_form = '
'; if ( isset($params['forms_later']) && $params['forms_later'] ) { $all_forms = $this->Application->GetVar('all_forms'); $this->Application->SetVar('all_forms', $all_forms . $edit_form); } else { $button_code .= $edit_form; } return $button_code; } /** * Calls OnNew event from template, when no other event submitted * * @param Array $params */ function PresetFormFields($params) { $prefix = $this->getPrefixSpecial(); if ( !$this->Application->GetVar($prefix . '_event') ) { $this->Application->HandleEvent(new kEvent($prefix . ':OnNew')); } } function PrintSerializedFields($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'field'); $data = unserialize($object->GetDBField($field)); $o = ''; $std_params['name'] = $params['render_as']; $std_params['field'] = $params['field']; $std_params['pass_params'] = true; foreach ($data as $key => $row) { $block_params = array_merge($std_params, $row, array('key'=>$key)); $o .= $this->Application->ParseBlock($block_params); } return $o; } /** * Checks if current prefix is main item * * @param Array $params * @return bool */ function IsTopmostPrefix($params) { return $this->Prefix == $this->Application->GetTopmostPrefix($this->Prefix); } function PermSection($params) { $section = $this->SelectParam($params, 'section,name'); return $this->getUnitConfig()->getPermSectionByName($section, ''); } function PerPageSelected($params) { $list =& $this->GetList($params); return $list->GetPerPage(true) == $params['per_page'] ? $params['selected'] : ''; } /** * Returns prefix + generated sepcial + any word * * @param Array $params * @return string */ function VarName($params) { $list =& $this->GetList($params); return $list->getPrefixSpecial() . '_' . $params['type']; } /** * Returns edit tabs by specified preset name or false in case of error * * @param string $preset_name * @return mixed */ function getEditTabs($preset_name) { $presets = $this->getUnitConfig()->getEditTabPresets(); if ( !$presets || !isset($presets[$preset_name]) || count($presets[$preset_name]) == 0 ) { return false; } return count($presets[$preset_name]) > 1 ? $presets[$preset_name] : false; } /** * Detects if specified preset has tabs in it * * @param Array $params * @return bool */ function HasEditTabs($params) { return $this->getEditTabs($params['preset_name']) ? true : false; } /** * Sorts edit tabs based on their priority * * @param Array $tab_a * @param Array $tab_b * @return int */ function sortEditTabs($tab_a, $tab_b) { if ($tab_a['priority'] == $tab_b['priority']) { return 0; } return $tab_a['priority'] < $tab_b['priority'] ? -1 : 1; } /** * Prints edit tabs based on preset name specified * * @param Array $params * @return string * @access protected */ protected function PrintEditTabs($params) { $edit_tabs = $this->getEditTabs($params['preset_name']); if ( !$edit_tabs ) { return ''; } usort($edit_tabs, Array (&$this, 'sortEditTabs')); $ret = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($edit_tabs as $tab_info) { $block_params['title'] = $tab_info['title']; $block_params['template'] = $tab_info['t']; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Performs image resize to required dimensions and returns resulting url (cached resized image) * * @param Array $params * @return string */ function ImageSrc($params) { $max_width = isset($params['MaxWidth']) ? $params['MaxWidth'] : false; $max_height = isset($params['MaxHeight']) ? $params['MaxHeight'] : false; $logo_filename = isset($params['LogoFilename']) ? $params['LogoFilename'] : false; $logo_h_margin = isset($params['LogoHMargin']) ? $params['LogoHMargin'] : false; $logo_v_margin = isset($params['LogoVMargin']) ? $params['LogoVMargin'] : false; $object = $this->getObject($params); $field = $this->SelectParam($params, 'name,field'); return $object->GetField($field, 'resize:'.$max_width.'x'.$max_height.';wm:'.$logo_filename.'|'.$logo_h_margin.'|'.$logo_v_margin); } /** * Allows to retrieve given setting from unit config * * @param Array $params * @return mixed */ function UnitOption($params) { return $this->getUnitConfig()->getSetting($params['name']); } /** * Returns list of allowed toolbar buttons or false, when all is allowed * * @param Array $params * @return string */ function VisibleToolbarButtons($params) { $preset_name = kUtil::replaceModuleSection($params['title_preset']); $preset_info = $this->getUnitConfig()->getTitlePresetByName($preset_name); if ( !$preset_info ) { trigger_error('Title preset not specified or missing (in tag "' . $this->getPrefixSpecial() . ':' . __METHOD__ . '")', E_USER_NOTICE); return false; } if ( !array_key_exists('toolbar_buttons', $preset_info) || !is_array($preset_info['toolbar_buttons']) ) { return false; } // always add search buttons array_push($preset_info['toolbar_buttons'], 'search', 'search_reset_alt'); $toolbar_buttons = array_values($preset_info['toolbar_buttons']); // reset index return $toolbar_buttons ? trim(json_encode($toolbar_buttons), '[]') : 'false'; } /** * Checks, that "To" part of at least one of range filters is used * * @param Array $params * @return bool */ function RangeFiltersUsed($params) { $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ return $search_helper->rangeFiltersUsed($this->getPrefixSpecial(), $params['grid']); } /** * This is abstract tag, used to modify unit config data based on template, where it's used. * Tag is called from "combined_header" block in admin only. * * @param Array $params */ function ModifyUnitConfig($params) { } /** * Checks, that field is visible on edit form * * @param Array $params * @return bool */ function FieldVisible($params) { $check_field = $params['field']; $field_options = $this->_getFieldDefinition($check_field); if ( !$field_options ) { $params['field'] = 'Password'; return $check_field == 'VerifyPassword' ? $this->FieldVisible($params) : true; } $show_mode = array_key_exists('show_mode', $field_options) ? $field_options['show_mode'] : true; if ( $show_mode === smDEBUG ) { return defined('DEBUG_MODE') && DEBUG_MODE; } return $show_mode; } /** * Checks, that there area visible fields in given section on edit form * * @param Array $params * @return bool */ function FieldsVisible($params) { if ( !$params['fields'] ) { return true; } $check_fields = explode(',', $params['fields']); foreach ($check_fields as $check_field) { // when at least one field in subsection is visible, then subsection is visible too $field_options = $this->_getFieldDefinition($check_field); if ( $field_options ) { $show_mode = array_key_exists('show_mode', $field_options) ? $field_options['show_mode'] : true; } else { $show_mode = true; } if ( ($show_mode === true) || (($show_mode === smDEBUG) && (defined('DEBUG_MODE') && DEBUG_MODE)) ) { // field is visible return true; } } return false; } /** * Returns field definition * * @param string $field_name * @return Array * @access protected */ protected function _getFieldDefinition($field_name) { $config = $this->getUnitConfig(); $ret = $config->getFieldByName($field_name); if ( !$ret ) { $ret = $config->getVirtualFieldByName($field_name); } return $ret; } /** * Checks, that requested option is checked inside field value * * @param Array $params * @return bool */ function Selected($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'name,field'); $value = $object->GetDBField($field); if (strpos($value, '|') !== false) { $value = explode('|', substr($value, 1, -1)); return in_array($params['value'], $value); } return $value; } /** * Displays option name by it's value * * @param Array $params * @return string * @access protected */ protected function OptionValue($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $value = $params['value']; $field = $this->SelectParam($params, 'name,field'); $field_options = $object->GetFieldOptions($field); if ( isset($field_options['options'][$value]) ) { $value = $field_options['options'][$value]; $use_phrases = isset($field_options['use_phrases']) ? $field_options['use_phrases'] : false; return $use_phrases ? $this->Application->Phrase($value) : $value; } return ''; } /** * Returns/sets form name for current object * * @param Array $params * @return string */ function FormName($params) { $form_name = $this->SelectParam($params, 'name,form,form_name'); if ( $form_name ) { $prefix = $this->getPrefixSpecial(); if ( $this->Application->hasObject( $this->getPrefixSpecial() ) ) { $object = $this->getObject($params); /* @var $object kDBItem */ if ( $object->getFormName() != $form_name ) { trigger_error('Setting form to "' . $form_name . '" failed, since object "' . $this->getPrefixSpecial() . '" is created before FormName tag (e.g. in event or another tag).', E_USER_WARNING); } } else { $forms = $this->Application->GetVar('forms', Array ()); $forms[ $this->getPrefixSpecial() ] = $form_name; $this->Application->SetVar('forms', $forms); } return ''; } $object = $this->getObject($params); /* @var $object kDBItem */ return $object->getFormName(); } /** * Just reloads the object using given parameters * * @param Array $params * @return string * @access protected */ protected function ReloadItem($params) { $params['requery'] = 1; $object = $this->getObject($params); /* @var $object kDBItem */ return ''; } } Index: branches/5.3.x/core/kernel/db/dblist.php =================================================================== --- branches/5.3.x/core/kernel/db/dblist.php (revision 16110) +++ branches/5.3.x/core/kernel/db/dblist.php (revision 16111) @@ -1,1761 +1,1773 @@ OrderFields = Array(); $filters = $this->getFilterStructure(); foreach ($filters as $filter_params) { $filter =& $this->$filter_params['type']; $filter[ $filter_params['class'] ] = $this->Application->makeClass('kMultipleFilter', Array ($filter_params['join_using'])); } $this->PerPage = -1; } function setGridName($grid_name) { $this->gridName = $grid_name; } /** * Returns information about all possible filter types * * @return Array * @access protected */ protected function getFilterStructure() { $filters = Array ( Array ('type' => 'WhereFilter', 'class' => self::FLT_SYSTEM, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'WhereFilter', 'class' => self::FLT_NORMAL, 'join_using' => self::FLT_TYPE_OR), Array ('type' => 'WhereFilter', 'class' => self::FLT_SEARCH, 'join_using' => self::FLT_TYPE_OR), Array ('type' => 'WhereFilter', 'class' => self::FLT_VIEW, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'WhereFilter', 'class' => self::FLT_CUSTOM, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'HavingFilter', 'class' => self::FLT_SYSTEM, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'HavingFilter', 'class' => self::FLT_NORMAL, 'join_using' => self::FLT_TYPE_OR), Array ('type' => 'HavingFilter', 'class' => self::FLT_SEARCH, 'join_using' => self::FLT_TYPE_OR), Array ('type' => 'HavingFilter', 'class' => self::FLT_VIEW, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'HavingFilter', 'class' => self::FLT_CUSTOM, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'AggregateFilter', 'class' => self::FLT_SYSTEM, 'join_using' => self::FLT_TYPE_AND), Array ('type' => 'AggregateFilter', 'class' => self::FLT_NORMAL, 'join_using' => self::FLT_TYPE_OR), Array ('type' => 'AggregateFilter', 'class' => self::FLT_VIEW, 'join_using' => self::FLT_TYPE_AND), ); return $filters; } /** * Adds new or replaces old filter with same name * * @param string $name filter name (for internal use) * @param string $clause where/having clause part (no OR/AND allowed) * @param int $filter_type is filter having filter or where filter * @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW,FLT_CUSTOM * @access public */ public function addFilter($name, $clause, $filter_type = self::WHERE_FILTER, $filter_scope = self::FLT_SYSTEM) { $filter_source = Array ( self::WHERE_FILTER => 'WhereFilter', self::HAVING_FILTER => 'HavingFilter', self::AGGREGATE_FILTER => 'AggregateFilter' ); $filter_name = $filter_source[$filter_type]; $filter =& $this->$filter_name; $filter =& $filter[$filter_scope]; /* @var $filter kMultipleFilter */ $filter->addFilter($name, $clause); } /** * Reads filter content * * @param string $name filter name (for internal use) * @param int $filter_type is filter having filter or where filter * @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW,FLT_CUSTOM * @return string * @access public */ public function getFilter($name, $filter_type = self::WHERE_FILTER, $filter_scope = self::FLT_SYSTEM) { $filter_source = Array ( self::WHERE_FILTER => 'WhereFilter', self::HAVING_FILTER => 'HavingFilter', self::AGGREGATE_FILTER => 'AggregateFilter' ); $filter_name = $filter_source[$filter_type]; $filter =& $this->$filter_name; $filter =& $filter[$filter_scope]; /* @var $filter kMultipleFilter */ return $filter->getFilter($name); } /** * Removes specified filter from filters list * * @param string $name filter name (for internal use) * @param int $filter_type is filter having filter or where filter * @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW,FLT_CUSTOM * @access public */ public function removeFilter($name, $filter_type = self::WHERE_FILTER, $filter_scope = self::FLT_SYSTEM) { $filter_source = Array ( self::WHERE_FILTER => 'WhereFilter', self::HAVING_FILTER => 'HavingFilter', self::AGGREGATE_FILTER => 'AggregateFilter' ); $filter_name = $filter_source[$filter_type]; $filter =& $this->$filter_name; $filter =& $filter[$filter_scope]; /* @var $filter kMultipleFilter */ $filter->removeFilter($name); } /** * Clear all filters * * @access public */ public function clearFilters() { $filters = $this->getFilterStructure(); foreach ($filters as $filter_params) { $filter =& $this->$filter_params['type']; $filter[ $filter_params['class'] ]->clearFilters(); } } /** * Counts the total number of records base on the query resulted from {@link kDBList::GetSelectSQL()} * * The method modifies the query to substitude SELECT part (fields listing) with COUNT(*). * Special care should be applied when working with lists based on grouped queries, all aggregate function fields * like SUM(), AVERAGE() etc. should be added to CountedSQL by using {@link kDBList::SetCountedSQL()} * * @access protected */ protected function CountRecs() { $all_sql = $this->GetSelectSQL(true,false); $sql = $this->getCountSQL($all_sql); $this->Counted = true; if( $this->GetGroupClause() ) { $this->RecordsCount = count( $this->Conn->GetCol($sql) ); } else { $this->RecordsCount = (int)$this->Conn->GetOne($sql); } $system_sql = $this->GetSelectSQL(true,true); if($system_sql == $all_sql) //no need to query the same again { $this->NoFilterCount = $this->RecordsCount; return; } $sql = $this->getCountSQL($system_sql); if( $this->GetGroupClause() ) { $this->NoFilterCount = count( $this->Conn->GetCol($sql) ); } else { $this->NoFilterCount = (int)$this->Conn->GetOne($sql); } } /** * Returns record count in list with/without user filters applied * * @param bool $with_filters * @return int * @access public */ public function GetRecordsCount($with_filters = true) { if (!$this->Counted) { $this->CountRecs(); } return $with_filters ? $this->RecordsCount : $this->NoFilterCount; } /** * Returns record count, that were actually selected * * @return int * @access public */ public function GetSelectedCount() { return $this->SelectedCount; } /** * Transforms given query into count query (DISTINCT is also processed) * * @param string $sql * @return string * @access public */ public function getCountSQL($sql) { if ( preg_match("/^\s*SELECT\s+DISTINCT(.*?\s)FROM(?!_)/is",$sql,$regs ) ) { return preg_replace("/^\s*SELECT\s+DISTINCT(.*?\s)FROM(?!_)/is", "SELECT COUNT(DISTINCT ".$regs[1].") AS count FROM", $sql); } else { return preg_replace("/^\s*SELECT(.*?\s)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql); } } /** * Queries the database with SQL resulted from {@link kDBList::GetSelectSQL()} and stores result in {@link kDBList::SelectRS} * * All the sorting, pagination, filtration of the list should be set prior to calling Query(). * * @param bool $force force re-query, when already queried * @return bool * @access public */ public function Query($force=false) { if (!$force && $this->Queried) return true; $q = $this->GetSelectSQL(); //$rs = $this->Conn->SelectLimit($q, $this->PerPage, $this->Offset); //in case we have not counted records try to select one more item to find out if we have something more than perpage $limit = $this->Counted ? $this->PerPage : $this->PerPage+1; $sql = $q.' '.$this->Conn->getLimitClause($this->Offset,$limit); $this->Records = $this->Conn->Query($sql); if (!$this->Records && ($this->Page > 1)) { - // no records & page > 1, show 404 page - trigger_error('Unknown page ' . $this->Page . ' in ' . $this->getPrefixSpecial() . ' list, leading to "404 Not Found"', E_USER_NOTICE); - - $this->Application->UrlManager->show404(); + if ( $this->Application->isAdmin ) { + // no records & page > 1, try to reset to 1st page (works only when list in not counted before) + $this->Application->StoreVar($this->getPrefixSpecial() . '_Page', 1, true); + $this->SetPage(1); + $this->Query($force); + } + else { + // no records & page > 1, show 404 page + trigger_error('Unknown page ' . $this->Page . ' in ' . $this->getPrefixSpecial() . ' list, leading to "404 Not Found"', E_USER_NOTICE); + $this->Application->UrlManager->show404(); + } } $this->SelectedCount = count($this->Records); if (!$this->Counted) $this->RecordsCount = $this->SelectedCount; if (!$this->Counted && $this->SelectedCount > $this->PerPage && $this->PerPage != -1) $this->SelectedCount--; if ($this->Records === false) { //handle errors here return false; } $this->Queried = true; $this->Application->HandleEvent(new kEvent($this->getPrefixSpecial() . ':OnAfterListQuery')); return true; } /** * Adds one more record to list virtually and updates all counters * * @param Array $record * @access public */ public function addRecord($record) { $this->Records[] = $record; $this->SelectedCount++; $this->RecordsCount++; } /** * Calculates totals based on config * * @access protected */ protected function CalculateTotals() { $fields = Array (); $this->Totals = Array (); if ( $this->gridName ) { $grid = $this->getUnitConfig()->getGridByName($this->gridName); $grid_fields = $grid['Fields']; } else { $grid_fields = $this->Fields; } foreach ($grid_fields as $field_name => $field_options) { if ( $this->gridName && array_key_exists('totals', $field_options) && $field_options['totals'] ) { $totals = $field_options['totals']; } elseif ( array_key_exists('totals', $this->Fields[$field_name]) && $this->Fields[$field_name]['totals'] ) { $totals = $this->Fields[$field_name]['totals']; } else { continue; } $calculated_field = array_key_exists($field_name, $this->CalculatedFields) && array_key_exists($field_name, $this->VirtualFields); $db_field = !array_key_exists($field_name, $this->VirtualFields); if ( $calculated_field || $db_field ) { $field_expression = $calculated_field ? $this->CalculatedFields[$field_name] : '`' . $this->TableName . '`.`' . $field_name . '`'; $fields[$field_name] = $totals . '(' . $field_expression . ') AS ' . $field_name . '_' . $totals; } } if ( !$fields ) { return; } $sql = $this->GetSelectSQL(true, false); $fields = str_replace('%1$s', $this->TableName, implode(', ', $fields)); if ( preg_match("/DISTINCT(.*?\s)FROM(?!_)/is", $sql, $regs) ) { $sql = preg_replace("/^\s*SELECT DISTINCT(.*?\s)FROM(?!_)/is", 'SELECT ' . $fields . ' FROM', $sql); } else { $sql = preg_replace("/^\s*SELECT(.*?\s)FROM(?!_)/is", 'SELECT ' . $fields . ' FROM ', $sql); } $totals = $this->Conn->Query($sql); foreach ($totals as $totals_row) { foreach ($totals_row as $total_field => $field_value) { if ( !isset($this->Totals[$total_field]) ) { $this->Totals[$total_field] = 0; } $this->Totals[$total_field] += $field_value; } } $this->TotalsCalculated = true; } /** * Returns previously calculated total (not formatted) * * @param string $field * @param string $total_function * @return float * @access public */ public function getTotal($field, $total_function) { if (!$this->TotalsCalculated) { $this->CalculateTotals(); } return $this->Totals[$field . '_' . $total_function]; } function setTotal($field, $total_function, $value) { $this->Totals[$field . '_' . $total_function] = $value; } function getTotalFunction($field) { if ( $this->gridName ) { $grid = $this->getUnitConfig()->getGridByName($this->gridName); $field_options = $grid['Fields'][$field]; } else { $field_options = $this->Fields[$field]; } if ( $this->gridName && array_key_exists('totals', $field_options) && $field_options['totals'] ) { return $field_options['totals']; } elseif ( array_key_exists('totals', $this->Fields[$field]) && $this->Fields[$field]['totals'] ) { return $this->Fields[$field]['totals']; } return false; } /** * Returns previously calculated total (formatted) * * @param string $field * @param string $total_function * @return float * @access public */ function GetFormattedTotal($field, $total_function) { $res = $this->getTotal($field, $total_function); $formatter_class = $this->GetFieldOption($field, 'formatter'); if ( $formatter_class ) { $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kFormatter */ $res = $formatter->Format($res, $field, $this); } return $res; } /** * Builds full select query except for LIMIT clause * * @param bool $for_counting * @param bool $system_filters_only * @param string $keep_clause * @return string * @access public */ public function GetSelectSQL($for_counting = false, $system_filters_only = false, $keep_clause = '') { $q = parent::GetSelectSQL($this->SelectClause); $q = !$for_counting ? $this->addCalculatedFields($q, 0) : str_replace('%2$s', '', $q); $where = $this->GetWhereClause($for_counting,$system_filters_only); $having = $this->GetHavingClause($for_counting,$system_filters_only); $order = $this->GetOrderClause(); $group = $this->GetGroupClause(); if ( $for_counting ) { $usage_string = $where . '|' . $having . '|' . $order . '|' . $group . '|' . $keep_clause; - $optimizer = new LeftJoinOptimizer($q, str_replace('%1$s', $this->TableName, $usage_string)); + $optimizer = new LeftJoinOptimizer($q, $this->replaceModePrefix( str_replace('%1$s', $this->TableName, $usage_string) )); $q = $optimizer->simplify(); } if (!empty($where)) $q .= ' WHERE ' . $where; if (!empty($group)) $q .= ' GROUP BY ' . $group; if (!empty($having)) $q .= ' HAVING ' . $having; if ( !$for_counting && !empty($order) ) $q .= ' ORDER BY ' . $order; return $this->replaceModePrefix( str_replace('%1$s', $this->TableName, $q) ); } /** * Replaces all calculated field occurrences with their associated expressions * * @param string $clause where clause to extract calculated fields from * @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only * @param bool $replace_table * @return string * @access public */ public function extractCalculatedFields($clause, $aggregated = 1, $replace_table = false) { $fields = $this->getCalculatedFields($aggregated); if ( is_array($fields) && count($fields) > 0 ) { foreach ($fields as $field_name => $field_expression) { $clause = preg_replace('/(\\(+)[(,` ]*' . $field_name . '[` ]{1}/', '\1 (' . $field_expression . ') ', $clause); $clause = preg_replace('/[,` ]{1}' . $field_name . '[` ]{1}/', ' (' . $field_expression . ') ', $clause); } } return $replace_table ? str_replace('%1$s', $this->TableName, $clause) : $clause; } /** * Returns WHERE clause of the query * * @param bool $for_counting merge where filters with having filters + replace field names for having fields with their values * @param bool $system_filters_only * @return string * @access private */ private function GetWhereClause($for_counting=false,$system_filters_only=false) { $where = $this->Application->makeClass('kMultipleFilter'); /* @var $where kMultipleFilter */ - $where->addFilter('system_where', $this->WhereFilter[self::FLT_SYSTEM] ); + if ( $for_counting ) { + $where->addFilter('system_where', $this->extractCalculatedFields($this->WhereFilter[self::FLT_SYSTEM]->getSQL()) ); + } + else { + $where->addFilter('system_where', $this->WhereFilter[self::FLT_SYSTEM] ); + } if (!$system_filters_only) { $where->addFilter('view_where', $this->WhereFilter[self::FLT_VIEW] ); $search_w = $this->WhereFilter[self::FLT_SEARCH]->getSQL(); if ($search_w || $for_counting) { // move search_having to search_where in case search_where isset or we are counting $search_h = $this->extractCalculatedFields( $this->HavingFilter[self::FLT_SEARCH]->getSQL() ); $search_w = ($search_w && $search_h) ? $search_w.' OR '.$search_h : $search_w.$search_h; $where->addFilter('search_where', $search_w ); } // CUSTOM $search_w = $this->WhereFilter[self::FLT_CUSTOM]->getSQL(); if ($search_w || $for_counting) { // move search_having to search_where in case search_where isset or we are counting $search_h = $this->extractCalculatedFields( $this->HavingFilter[self::FLT_CUSTOM]->getSQL() ); $search_w = ($search_w && $search_h) ? $search_w.' AND '.$search_h : $search_w.$search_h; $where->addFilter('custom_where', $search_w ); } // CUSTOM } if( $for_counting ) // add system_having and view_having to where { $where->addFilter('system_having', $this->extractCalculatedFields($this->HavingFilter[kDBList::FLT_SYSTEM]->getSQL()) ); if (!$system_filters_only) $where->addFilter('view_having', $this->extractCalculatedFields( $this->HavingFilter[kDBList::FLT_VIEW]->getSQL() ) ); } return $where->getSQL(); } /** * Returns HAVING clause of the query * * @param bool $for_counting don't return having filter in case if this is counting sql * @param bool $system_filters_only return only system having filters * @param int $aggregated 0 - aggregated and having, 1 - having only, 2 - aggregated only * @return string * @access private */ private function GetHavingClause($for_counting=false, $system_filters_only=false, $aggregated = 0) { if ($for_counting) { $aggregate_filter = $this->Application->makeClass('kMultipleFilter'); /* @var $aggregate_filter kMultipleFilter */ $aggregate_filter->addFilter('aggregate_system', $this->AggregateFilter[kDBList::FLT_SYSTEM]); if (!$system_filters_only) { $aggregate_filter->addFilter('aggregate_view', $this->AggregateFilter[kDBList::FLT_VIEW]); } return $this->extractCalculatedFields($aggregate_filter->getSQL(), 2); } $having = $this->Application->makeClass('kMultipleFilter'); /* @var $having kMultipleFilter */ $having->addFilter('system_having', $this->HavingFilter[kDBList::FLT_SYSTEM] ); if ($aggregated == 0) { if (!$system_filters_only) { $having->addFilter('view_aggregated', $this->AggregateFilter[kDBList::FLT_VIEW] ); } $having->addFilter('system_aggregated', $this->AggregateFilter[kDBList::FLT_SYSTEM]); } if (!$system_filters_only) { $having->addFilter('view_having', $this->HavingFilter[kDBList::FLT_VIEW] ); $having->addFilter('custom_having', $this->HavingFilter[kDBList::FLT_CUSTOM] ); $search_w = $this->WhereFilter[kDBList::FLT_SEARCH]->getSQL(); if (!$search_w) { $having->addFilter('search_having', $this->HavingFilter[kDBList::FLT_SEARCH] ); } } return $having->getSQL(); } /** * Returns GROUP BY clause of the query * * @return string * @access protected */ protected function GetGroupClause() { return $this->GroupByFields ? implode(',', $this->GroupByFields) : ''; } /** * Adds new group by field * * @param string $field * @access public */ public function AddGroupByField($field) { $this->GroupByFields[$field] = $field; } /** * Removes group by field added before * * @param string $field * @access public */ public function RemoveGroupByField($field) { unset($this->GroupByFields[$field]); } /** * Adds order field to ORDER BY clause * * @param string $field Field name * @param string $direction Direction of ordering (asc|desc) * @param bool $is_expression this is expression, that should not be escapted by "`" symbols * @return int * @access public */ public function AddOrderField($field, $direction = 'asc', $is_expression = false) { // original multilanguage field - convert to current lang field $formatter = isset($this->Fields[$field]['formatter']) ? $this->Fields[$field]['formatter'] : false; if ($formatter == 'kMultiLanguage' && !isset($this->Fields[$field]['master_field'])) { // for now kMultiLanguage formatter is only supported for real (non-virtual) fields $is_expression = true; $field = $this->getMLSortField($field); } if (!isset($this->Fields[$field]) && $field != 'RAND()' && !$is_expression) { trigger_error('Incorrect sorting defined (field = '.$field.'; direction = '.$direction.') in config for prefix '.$this->Prefix.'', E_USER_NOTICE); } $this->OrderFields[] = Array($field, $direction, $is_expression); return count($this->OrderFields) - 1; } /** * Sets new order fields, replacing existing ones * * @param Array $order_fields * @return void * @access public */ public function setOrderFields($order_fields) { $this->OrderFields = $order_fields; } /** * Changes sorting direction for a given sorting field index * * @param int $field_index * @param string $direction * @return void * @access public */ public function changeOrderDirection($field_index, $direction) { if ( !isset($this->OrderFields[$field_index]) ) { return; } $this->OrderFields[$field_index][1] = $direction; } /** * Returns expression, used to sort given multilingual field * * @param string $field * @return string */ function getMLSortField($field) { $table_name = '`' . $this->TableName . '`'; $lang = $this->Application->GetVar('m_lang'); $primary_lang = $this->Application->GetDefaultLanguageId(); $ret = 'IF(COALESCE(%1$s.l' . $lang . '_' . $field . ', ""), %1$s.l' . $lang . '_' . $field . ', %1$s.l' . $primary_lang . '_' . $field . ')'; return sprintf($ret, $table_name); } /** * Removes all order fields * * @access public */ public function ClearOrderFields() { $this->OrderFields = Array(); } /** * Returns ORDER BY Clause of the query * * The method builds order by clause by iterating {@link kDBList::OrderFields} array and concatenating it. * * @return string * @access private */ private function GetOrderClause() { $ret = ''; foreach ($this->OrderFields as $field) { $name = $field[0]; $ret .= isset($this->Fields[$name]) && !isset($this->VirtualFields[$name]) ? '`'.$this->TableName.'`.' : ''; if ($field[0] == 'RAND()' || $field[2]) { $ret .= $field[0].' '.$field[1].','; } else { $ret .= (strpos($field[0], '.') === false ? '`'.$field[0] . '`' : $field[0]) . ' ' . $field[1] . ','; } } $ret = rtrim($ret, ','); return $ret; } /** * Returns order field name in given position * * @param int $pos * @param bool $no_default * @return string * @access public */ public function GetOrderField($pos = NULL, $no_default = false) { if ( !(isset($this->OrderFields[$pos]) && $this->OrderFields[$pos]) && !$no_default ) { $pos = 0; } if ( isset($this->OrderFields[$pos][0]) ) { $field = $this->OrderFields[$pos][0]; $lang = $this->Application->GetVar('m_lang'); if ( preg_match('/^IF\(COALESCE\(.*?\.(l' . $lang . '_.*?), ""\),/', $field, $regs) ) { // undo result of kDBList::getMLSortField method return $regs[1]; } return $field; } return ''; } /** * Returns list order fields * * @return Array * @access public */ public function getOrderFields() { return $this->OrderFields; } /** * Returns order field direction in given position * * @param int $pos * @param bool $no_default * @return string * @access public */ public function GetOrderDirection($pos = NULL, $no_default = false) { if ( !(isset($this->OrderFields[$pos]) && $this->OrderFields[$pos]) && !$no_default ) { $pos = 0; } return isset($this->OrderFields[$pos][1]) ? $this->OrderFields[$pos][1] : ''; } /** * Returns ID of currently processed record * * @return int * @access public */ public function GetID() { return $this->Queried ? $this->GetDBField($this->IDField) : null; } /** * Allows kDBTagProcessor.SectionTitle to detect if it's editing or new item creation * * @return bool * @access public */ public function IsNewItem() { // no such thing as NewItem for lists :) return false; } /** * Return unformatted field value * * @param string $name * @return string * @access public */ public function GetDBField($name) { $row =& $this->getCurrentRecord(); if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Queried && !array_key_exists($name, $row)) { if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error('Field "' . $name . '" doesn\'t exist in prefix ' . $this->getPrefixSpecial() . '', E_USER_WARNING); return 'NO SUCH FIELD'; } // return "null" for missing fields, because formatter require such behaviour ! return array_key_exists($name, $row) ? $row[$name] : null; } /** * Checks if requested field is present after database query * * @param string $name * @return bool * @access public */ public function HasField($name) { $row =& $this->getCurrentRecord(); return isset($row[$name]); } /** * Returns current record fields * * @return Array * @access public */ public function GetFieldValues() { $record =& $this->getCurrentRecord(); return $record; } /** * Returns current record from list * * @param int $offset Offset relative to current record index * @return Array * @access public */ public function &getCurrentRecord($offset = 0) { $record_index = $this->CurrentIndex + $offset; if ($record_index >=0 && $record_index < $this->SelectedCount) { return $this->Records[$record_index]; } $false = false; return $false; } /** * Goes to record with given index * * @param int $index * @access public */ public function GoIndex($index) { $this->CurrentIndex = $index; } /** * Goes to first record * * @access public */ public function GoFirst() { $this->CurrentIndex = 0; } /** * Goes to next record * * @access public */ public function GoNext() { $this->CurrentIndex++; } /** * Goes to previous record * * @access public */ public function GoPrev() { if ($this->CurrentIndex>0) { $this->CurrentIndex--; } } /** * Checks if there is no end of list * * @return bool * @access public */ public function EOL() { return ($this->CurrentIndex >= $this->SelectedCount); } /** * Returns total page count based on list per-page * * @return int * @access public */ public function GetTotalPages() { if ( !$this->Counted ) { $this->CountRecs(); } if ( $this->PerPage == -1 ) { return 1; } $integer_part = ($this->RecordsCount - ($this->RecordsCount % $this->PerPage)) / $this->PerPage; $reminder = ($this->RecordsCount % $this->PerPage) != 0; // adds 1 if there is a reminder $this->TotalPages = $integer_part + $reminder; return $this->TotalPages; } /** * Sets number of records to query per page * * @param int $per_page Number of records to display per page * @access public */ public function SetPerPage($per_page) { $this->PerPage = $per_page; } /** * Returns records per page count * * @param bool $in_fact * @return int * @access public */ public function GetPerPage($in_fact = false) { if ($in_fact) { return $this->PerPage; } return $this->PerPage == -1 ? $this->RecordsCount : $this->PerPage; } /** * Sets current page in list * * @param int $page * @access public */ public function SetPage($page) { if ($this->PerPage == -1) { $this->Page = 1; return; } if ($page < 1) $page = 1; $this->Offset = ($page-1)*$this->PerPage; if ($this->Counted && $this->Offset > $this->RecordsCount) { $this->SetPage(1); } else { $this->Page = $page; } //$this->GoFirst(); } /** * Returns current list page * * @return int * @access public */ public function GetPage() { return $this->Page; } /** * Sets list query offset * * @param int $offset * @access public */ public function SetOffset($offset) { $this->Offset = $offset; } /** * Gets list query offset * * @return int * @access public */ public function GetOffset() { return $this->Offset; } /** * Sets current item field value (doesn't apply formatting) * * @param string $name Name of the field * @param mixed $value Value to set the field to * @access public */ public function SetDBField($name,$value) { $this->Records[$this->CurrentIndex][$name] = $value; } /** * Apply where clause, that links this object to it's parent item * * @param string $special * @access public */ public function linkToParent($special) { $config = $this->getUnitConfig(); $parent_prefix = $config->getParentPrefix(); if ( $parent_prefix ) { $parent_table_key = $config->getParentTableKey($parent_prefix); $foreign_key_field = $config->getForeignKey($parent_prefix); if ( !$parent_table_key || !$foreign_key_field ) { return; } $parent_object = $this->Application->recallObject($parent_prefix . '.' . $special); /* @var $parent_object kDBItem */ if ( !$parent_object->isLoaded() ) { $this->addFilter('parent_filter', 'FALSE'); trigger_error('Parent ID not found (prefix: "' . rtrim($parent_prefix . '.' . $special, '.') . '"; sub-prefix: "' . $this->getPrefixSpecial() . '")', E_USER_NOTICE); return; } // only for list in this case $parent_id = $parent_object->GetDBField($parent_table_key); $this->addFilter('parent_filter', '`' . $this->TableName . '`.`' . $foreign_key_field . '` = ' . $this->Conn->qstr($parent_id)); } } /** * Returns true if list was queried (same name as for kDBItem for easy usage) * * @return bool * @access public */ public function isLoaded() { return $this->Queried && !$this->EOL(); } /** * 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 * @access public */ public function GetCol($field, $formatted = false, $format = null) { $i = 0; $ret = Array (); if ($formatted && array_key_exists('formatter', $this->Fields[$field])) { $formatter = $this->Application->recallObject($this->Fields[$field]['formatter']); /* @var $formatter kFormatter */ while ($i < $this->SelectedCount) { $ret[] = $formatter->Format($this->Records[$i][$field], $field, $this, $format); $i++; } } else { while ($i < $this->SelectedCount) { $ret[] = $this->Records[$i][$field]; $i++; } } return $ret; } /** * 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 * @see kSearchHelper::processRangeField() * @see kDateFormatter::Parse() */ public function SetError($field, $pseudo, $error_label = null, $error_params = null) { $error_field = isset($this->Fields[$field]['error_field']) ? $this->Fields[$field]['error_field'] : $field; $this->FieldErrors[$error_field]['pseudo'] = $pseudo; $var_name = $this->getPrefixSpecial() . '_' . $field . '_error'; $previous_pseudo = $this->Application->RecallVar($var_name); if ( $previous_pseudo ) { // don't set more then one error on field return false; } $this->Application->StoreVar($var_name, $pseudo); return true; } /** * Returns error pseudo * * @param string $field * @return string * @access public * @see kSearchHelper::processRangeField() */ public function GetErrorPseudo($field) { if ( !isset($this->FieldErrors[$field]) ) { return ''; } return isset($this->FieldErrors[$field]['pseudo']) ? $this->FieldErrors[$field]['pseudo'] : ''; } /** * Removes error on field * * @param string $field * @access public */ public function RemoveError($field) { unset( $this->FieldErrors[$field] ); } /** * Group list records by header, saves internal order in group * * @param string $heading_field * @access public */ public function groupRecords($heading_field) { $i = 0; $sorted = Array (); while ($i < $this->SelectedCount) { $sorted[ $this->Records[$i][$heading_field] ][] = $this->Records[$i]; $i++; } $this->Records = Array (); foreach ($sorted as $heading => $heading_records) { $this->Records = array_merge_recursive($this->Records, $heading_records); } } /** * Reset list (use for requering purposes) * * @access public */ public function reset() { $this->Counted = false; $this->clearFilters(); $this->ClearOrderFields(); } /** * Checks if list was counted * * @return bool * @access public */ public function isCounted() { return $this->Counted; } /** * Tells, that given list is main * * @return bool * @access public */ public function isMainList() { return $this->mainList; } /** * Makes given list as main * * @access public */ public function becameMain() { $this->mainList = true; } /** * Moves recordset pointer to first element * * @return void * @access public * @implements Iterator::rewind */ public function rewind() { $this->Query(); $this->GoFirst(); } /** * Returns value at current position * * @return mixed * @access public * @implements Iterator::current */ function current() { return $this->getCurrentRecord(); } /** * Returns key at current position * * @return mixed * @access public * @implements Iterator::key */ function key() { return $this->CurrentIndex; } /** * Moves recordset pointer to next position * * @return void * @access public * @implements Iterator::next */ function next() { $this->GoNext(); } /** * Detects if current position is within recordset bounds * * @return bool * @access public * @implements Iterator::valid */ public function valid() { return !$this->EOL(); } /** * Counts recordset rows * * @return int * @access public * @implements Countable::count */ public function count() { return $this->SelectedCount; } } class LeftJoinOptimizer { /** * Input sql for optimization * * @var string * @access private */ private $sql = ''; /** * All sql parts, where LEFT JOINed table aliases could be used * * @var string * @access private */ private $usageString = ''; /** * List of discovered LEFT JOINs * * @var Array * @access private */ private $joins = Array (); /** * LEFT JOIN relations * * @var Array * @access private */ private $joinRelations = Array (); /** * LEFT JOIN table aliases scheduled for removal * * @var Array * @access private */ private $aliasesToRemove = Array (); /** * Creates new instance of the class * * @param string $sql * @param string $usage_string */ public function __construct($sql, $usage_string) { $this->sql = $sql; $this->usageString = $usage_string; $this->parseJoins(); } /** * Tries to remove unused LEFT JOINs * * @return string * @access public */ public function simplify() { if ( !$this->joins ) { // no LEFT JOIN used, return unchanged sql return $this->sql; } $this->updateRelations(); $this->removeAliases(); return $this->sql; } /** * Discovers LEFT JOINs based on given sql * * @return void * @access private */ private function parseJoins() { - if ( !preg_match_all('/LEFT\s+JOIN\s+(.*?|.*?\s+AS\s+.*?|.*?\s+.*?)\s+ON\s+(.*?\n|.*?$)/i', $this->sql, $regs) ) { + if ( !preg_match_all('/LEFT\s+JOIN\s+(.*?|.*?\s+AS\s+.*?|.*?\s+.*?)\s+ON\s+(.*?\n|.*?$)/si', $this->sql, $regs) ) { $this->joins = Array (); } // get all LEFT JOIN clause info from sql (without filters) foreach ($regs[1] as $index => $match) { $match_parts = preg_split('/\s+AS\s+|\s+/i', $match, 2); $table_alias = count($match_parts) == 1 ? $match : $match_parts[1]; $this->joins[$table_alias] = Array ( 'table' => $match_parts[0], 'join_clause' => $regs[0][$index], ); } } /** * Detects relations between LEFT JOINs * * @return void * @access private */ private function updateRelations() { foreach ($this->joins as $table_alias => $left_join_info) { $escaped_alias = preg_quote($table_alias, '/'); foreach ($this->joins as $sub_table_alias => $sub_left_join_info) { if ($table_alias == $sub_table_alias) { continue; } if ( $this->matchAlias($escaped_alias, $sub_left_join_info['join_clause']) ) { $this->joinRelations[] = $sub_table_alias . ':' . $table_alias; } } } } /** * Removes scheduled LEFT JOINs, but only if they are not protected * * @return void * @access private */ private function removeAliases() { $this->prepareAliasesRemoval(); foreach ($this->aliasesToRemove as $to_remove_alias) { if ( !$this->aliasProtected($to_remove_alias) ) { $this->sql = str_replace($this->joins[$to_remove_alias]['join_clause'], '', $this->sql); } } } /** * Schedules unused LEFT JOINs to for removal * * @return void * @access private */ private function prepareAliasesRemoval() { foreach ($this->joins as $table_alias => $left_join_info) { $escaped_alias = preg_quote($table_alias, '/'); if ( !$this->matchAlias($escaped_alias, $this->usageString) ) { $this->aliasesToRemove[] = $table_alias; } } } /** * Checks if someone wants to remove LEFT JOIN, but it's used by some other LEFT JOIN, that stays * * @param string $table_alias * @return bool * @access private */ private function aliasProtected($table_alias) { foreach ($this->joinRelations as $relation) { list ($main_alias, $used_alias) = explode(':', $relation); if ( ($used_alias == $table_alias) && !in_array($main_alias, $this->aliasesToRemove) ) { return true; } } return false; } /** * Matches given escaped alias to a string * * @param string $escaped_alias * @param string $string * @return bool * @access private */ private function matchAlias($escaped_alias, $string) { return preg_match('/(`' . $escaped_alias . '`|' . $escaped_alias . ')\./', $string); } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/db/db_event_handler.php =================================================================== --- branches/5.3.x/core/kernel/db/db_event_handler.php (revision 16110) +++ branches/5.3.x/core/kernel/db/db_event_handler.php (revision 16111) @@ -1,3467 +1,3448 @@ getPrefixSpecial(true) instead of * $event->getPrefixSpecial() as usual. This is due PHP * is converting "." symbols in variable names during * submit info "_". $event->getPrefixSpecial optional * 1st parameter returns correct current Prefix_Special * for variables being submitted such way (e.g. variable * name that will be converted by PHP: "users.read_only_id" * will be submitted as "users_read_only_id". * * 2. When using $this->Application-LinkVar on variables submitted * from form which contain $Prefix_Special then note 1st item. Example: * LinkVar($event->getPrefixSpecial(true).'_varname',$event->getPrefixSpecial().'_varname') * */ /** * EventHandler that is used to process * any database related events * */ class kDBEventHandler extends kEventHandler { /** * Checks permissions of user * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $section = $event->getSection(); if ( !$this->Application->isAdmin ) { $allow_events = Array ('OnSearch', 'OnSearchReset', 'OnNew'); if ( in_array($event->Name, $allow_events) ) { // allow search on front return true; } } elseif ( ($event->Name == 'OnPreSaveAndChangeLanguage') && !$this->UseTempTables($event) ) { // allow changing language in grids, when not in editing mode return $this->Application->CheckPermission($section . '.view', 1); } if ( !preg_match('/^CATEGORY:(.*)/', $section) ) { // only if not category item events if ( (substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave') ) { if ( $this->isNewItemCreate($event) ) { return $this->Application->CheckPermission($section . '.add', 1); } else { return $this->Application->CheckPermission($section . '.add', 1) || $this->Application->CheckPermission($section . '.edit', 1); } } } if ( $event->Name == 'OnPreCreate' ) { // save category_id before item create (for item category selector not to destroy permission checking category) $this->Application->LinkVar('m_cat_id'); } return parent::CheckPermission($event); } /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnLoad' => Array ('self' => 'view', 'subitem' => 'view'), 'OnItemBuild' => Array ('self' => 'view', 'subitem' => 'view'), 'OnSuggestValues' => Array ('self' => 'admin', 'subitem' => 'admin'), 'OnSuggestValuesJSON' => Array ('self' => 'admin', 'subitem' => 'admin'), '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' => 'admin', 'subitem' => 'admin'), 'OnValidateMInputFields' => Array ('self' => 'view'), 'OnValidateField' => Array ('self' => true, 'subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Define alternative event processing method names * * @return void * @see kEventHandler::$eventMethods * @access protected */ protected function mapEvents() { $events_map = Array ( 'OnRemoveFilters' => 'FilterAction', 'OnApplyFilters' => 'FilterAction', 'OnMassApprove' => 'iterateItems', 'OnMassDecline' => 'iterateItems', 'OnMassMoveUp' => 'iterateItems', 'OnMassMoveDown' => 'iterateItems', ); $this->eventMethods = array_merge($this->eventMethods, $events_map); } /** * Returns ID of current item to be edited * by checking ID passed in get/post as prefix_id * or by looking at first from selected ids, stored. * Returned id is also stored in Session in case * it was explicitly passed as get/post * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { if ( $event->getEventParam('raise_warnings') === false ) { $event->setEventParam('raise_warnings', 1); } if ( $event->Special == 'previous' || $event->Special == 'next' ) { $object = $this->Application->recallObject($event->getEventParam('item')); /* @var $object kDBItem */ $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $select_clause = $object->getUnitConfig()->getNavigationSelectClause(NULL); return $list_helper->getNavigationResource($object, $event->getEventParam('list'), $event->Special == 'next', $select_clause); } elseif ( $event->Special == 'filter' ) { // temporary object, used to print filter options only return 0; } if ( preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1]) ) { // - returns field DateFormat value from language (LanguageId is extracted from current phrase object) $main_object = $this->Application->recallObject($regs[1]); /* @var $main_object kDBItem */ return $main_object->GetDBField($event->getUnitConfig()->getIDField()); } // 1. get id from post (used in admin) $ret = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id'); if ( ($ret !== false) && ($ret != '') ) { return $ret; } // 2. get id from env (used in front) $ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); if ( ($ret !== false) && ($ret != '') ) { return $ret; } // recall selected ids array and use the first one $ids = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids'); if ( $ids != '' ) { $ids = explode(',', $ids); if ( $ids ) { $ret = array_shift($ids); } } else { // if selected ids are not yet stored $this->StoreSelectedIDs($event); return $this->Application->GetVar($event->getPrefixSpecial() . '_id'); // StoreSelectedIDs sets this variable } return $ret; } /** * Prepares and stores selected_ids string * in Session and Application Variables * by getting all checked ids from grid plus * id passed in get/post as prefix_id * * @param kEvent $event * @param Array $direct_ids * @return Array * @access protected */ protected function StoreSelectedIDs(kEvent $event, $direct_ids = NULL) { $wid = $this->Application->GetTopmostWid($event->Prefix); $session_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_'); $ids = $event->getEventParam('ids'); if ( isset($direct_ids) || ($ids !== false) ) { // save ids directly if they given + reset array indexes $resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false); if ( $resulting_ids ) { $this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids)); $this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', true); $this->Application->SetVar($event->getPrefixSpecial() . '_id', $resulting_ids[0]); return $resulting_ids; } return Array (); } $ret = Array (); // May be we don't need this part: ? $passed = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id'); if ( $passed !== false && $passed != '' ) { array_push($ret, $passed); } $ids = Array (); // get selected ids from post & save them to session $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { $id_field = $event->getUnitConfig()->getIDField(); 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 ' . $event->getPrefixSpecial() . ' not passed', E_USER_NOTICE); } $this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id); return $ret; } /** * Returns stored selected ids as an array * * @param kEvent $event * @param bool $from_session return ids from session (written, when editing was started) * @return Array * @access protected */ protected function getSelectedIDs(kEvent $event, $from_session = false) { if ( $from_session ) { $wid = $this->Application->GetTopmostWid($event->Prefix); $var_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_'); $ret = $this->Application->RecallVar($var_name); } else { $ret = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids'); } return explode(',', $ret); } /** * Stores IDs, selected in grid in session * * @param kEvent $event * @return void * @access protected */ protected function OnStoreSelected(kEvent $event) { $this->StoreSelectedIDs($event); $id = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); if ( $id !== false ) { $event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); } } /** * Returns associative array of submitted fields for current item * Could be used while creating/editing single item - * meaning on any edit form, except grid edit * * @param kEvent $event * @return Array * @access protected */ protected function getSubmittedFields(kEvent $event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); $field_values = $items_info ? array_shift($items_info) : Array (); return $field_values; } /** - * Returns fields, that are not allowed to be changed from request - * - * @param Array $hash - * @return Array - * @access protected - */ - protected function getRequestProtectedFields($hash) - { - // by default don't allow changing ID or foreign key from request - $config = $this->getUnitConfig(); - - $fields = Array (); - $fields[] = $config->getIDField(); - - $parent_prefix = $config->getParentPrefix(); - - if ( $parent_prefix && !$this->Application->isAdmin ) { - $fields[] = $config->getForeignKey($parent_prefix); - } - - return $fields; - } - - /** * Removes any information about current/selected ids * from Application variables and Session * * @param kEvent $event * @return void * @access protected */ protected function clearSelectedIDs(kEvent $event) { $prefix_special = $event->getPrefixSpecial(); $ids = implode(',', $this->getSelectedIDs($event, true)); $event->setEventParam('ids', $ids); $wid = $this->Application->GetTopmostWid($event->Prefix); $session_name = rtrim($prefix_special . '_selected_ids_' . $wid, '_'); $this->Application->RemoveVar($session_name); $this->Application->SetVar($prefix_special . '_selected_ids', ''); $this->Application->SetVar($prefix_special . '_id', ''); // $event->getPrefixSpecial(true) . '_id' too may be } /** * Common builder part for Item & List * * @param kDBBase|kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function dbBuild(&$object, kEvent $event) { // for permission checking inside item/list build events $event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true)); if ( $event->getEventParam('form_name') !== false ) { $form_name = $event->getEventParam('form_name'); } else { $request_forms = $this->Application->GetVar('forms', Array ()); $form_name = (string)getArrayValue($request_forms, $object->getPrefixSpecial()); } $object->Configure($event->getEventParam('populate_ml_fields') || $event->getUnitConfig()->getPopulateMlFields(), $form_name); $this->PrepareObject($object, $event); $parent_event = $event->getEventParam('parent_event'); if ( is_object($parent_event) ) { $object->setParentEvent($parent_event); } // force live table if specified or is original item $live_table = $event->getEventParam('live_table') || $event->Special == 'original'; if ( $this->UseTempTables($event) && !$live_table ) { $object->SwitchToTemp(); } $this->Application->setEvent($event->getPrefixSpecial(), ''); $save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate'; $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', $save_event); } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool * @access protected */ protected function checkItemStatus(kEvent $event) { $status_field = $event->getUnitConfig()->getStatusField(true); if ( !$status_field ) { return true; } if ( $status_field == 'Status' || $status_field == 'Enabled' ) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->isLoaded() ) { return true; } return $object->GetDBField($status_field) == STATUS_ACTIVE; } return true; } /** * Shows not found template content * * @param kEvent $event * @return void * @access protected */ protected function _errorNotFound(kEvent $event) { if ( $event->getEventParam('raise_warnings') === 0 ) { // when it's possible, that autoload fails do nothing return; } if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in checkItemStatus, leading to "404 Not Found"', E_USER_NOTICE); $this->Application->UrlManager->show404(); } /** * Builds item (loads if needed) * * Pattern: Prototype Manager * * @param kEvent $event * @access protected */ protected function OnItemBuild(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $this->dbBuild($object, $event); $sql = $this->ItemPrepareQuery($event); $sql = $this->Application->ReplaceLanguageTags($sql); $object->setSelectSQL($sql); // 2. loads if allowed $auto_load = $event->getUnitConfig()->getAutoLoad(); $skip_autoload = $event->getEventParam('skip_autoload'); if ( $auto_load && !$skip_autoload ) { $perm_status = true; $user_id = $this->Application->InitDone ? $this->Application->RecallVar('user_id') : USER_ROOT; $event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true)); $status_checked = false; if ( $user_id == USER_ROOT || $this->CheckPermission($event) ) { // don't autoload item, when user doesn't have view permission $this->LoadItem($event); $status_checked = true; $editing_mode = defined('EDITING_MODE') ? EDITING_MODE : false; if ( $user_id != USER_ROOT && !$this->Application->isAdmin && !($editing_mode || $this->checkItemStatus($event)) ) { // non-root user AND on front-end AND (not editing mode || incorrect status) $perm_status = false; } } else { $perm_status = false; } if ( !$perm_status ) { // when no permission to view item -> redirect to no permission template $this->_processItemLoadingError($event, $status_checked); } } $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_GoTab', ''); $actions->Set($event->getPrefixSpecial() . '_GoId', ''); $actions->Set('forms[' . $event->getPrefixSpecial() . ']', $object->getFormName()); } /** * Processes case, when item wasn't loaded because of lack of permissions * * @param kEvent $event * @param bool $status_checked * @throws kNoPermissionException * @return void * @access protected */ protected function _processItemLoadingError($event, $status_checked) { $current_template = $this->Application->GetVar('t'); $redirect_template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate'); $error_msg = 'ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in ' . ($status_checked ? 'checkItemStatus' : 'CheckPermission') . ''; if ( $current_template == $redirect_template ) { // don't perform "no_permission" redirect if already on a "no_permission" template if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error($error_msg, E_USER_NOTICE); return; } if ( MOD_REWRITE ) { $redirect_params = Array ( 'm_cat_id' => 0, 'next_template' => 'external:' . $_SERVER['REQUEST_URI'], ); } else { $redirect_params = Array ( 'next_template' => $current_template, ); } $exception = new kNoPermissionException($error_msg); $exception->setup($redirect_template, $redirect_params); throw $exception; } /** * Build sub-tables array from configs * * @param kEvent $event * @return void * @access protected */ protected function OnTempHandlerBuild(kEvent $event) { $object = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler'); /* @var $object kTempTablesHandler */ $parent_event = $event->getEventParam('parent_event'); /* @var $parent_event kEvent */ if ( is_object($parent_event) ) { $object->setParentEvent($parent_event); } $object->BuildTables($event->Prefix, $this->getSelectedIDs($event)); } /** * Checks, that object used in event should use temp tables * * @param kEvent $event * @return bool * @access protected */ protected function UseTempTables(kEvent $event) { $top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual $special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event); return $this->Application->IsTempMode($event->Prefix, $special); } /** * Load item if id is available * * @param kEvent $event * @return void * @access protected */ protected function LoadItem(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $id = $this->getPassedID($event); if ( $object->isLoaded() && !is_array($id) && ($object->GetID() == $id) ) { // object is already loaded by same id return ; } if ( $object->Load($id) ) { $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $object->setID( is_array($id) ? false : $id ); } } /** * Builds list * * Pattern: Prototype Manager * * @param kEvent $event * @access protected */ protected function OnListBuild(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ /*if ( $this->Application->isDebugMode() ) { $event_params = http_build_query($event->getEventParams()); $this->Application->Debugger->appendHTML('InitList "' . $event->getPrefixSpecial() . '" (' . $event_params . ')'); }*/ $this->dbBuild($object, $event); if ( !$object->isMainList() && $event->getEventParam('main_list') ) { // once list is set to main, then even "requery" parameter can't remove that /*$passed = $this->Application->GetVar('passed'); $this->Application->SetVar('passed', $passed . ',' . $event->Prefix);*/ $object->becameMain(); } $object->setGridName($event->getEventParam('grid')); $sql = $this->ListPrepareQuery($event); $sql = $this->Application->ReplaceLanguageTags($sql); $object->setSelectSQL($sql); $object->reset(); if ( $event->getEventParam('skip_parent_filter') === false ) { $object->linkToParent($this->getMainSpecial($event)); } $this->AddFilters($event); $this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex. $this->SetPagination($event); $this->SetSorting($event); $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set('remove_specials[' . $event->getPrefixSpecial() . ']', '0'); $actions->Set($event->getPrefixSpecial() . '_GoTab', ''); } /** * Returns special of main item for linking with sub-item * * @param kEvent $event * @return string * @access protected */ protected function getMainSpecial(kEvent $event) { $main_special = $event->getEventParam('main_special'); if ( $main_special === false ) { // main item's special not passed if ( substr($event->Special, -5) == '-item' ) { // temp handler added "-item" to given special -> process that here return substr($event->Special, 0, -5); } // by default subitem's special is used for main item searching return $event->Special; } return $main_special; } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { } /** * Set's new per-page for grid * * @param kEvent $event * @return void * @access protected */ protected function OnSetPerPage(kEvent $event) { $per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage'); $event->SetRedirectParam($event->getPrefixSpecial() . '_PerPage', $per_page); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); if ( !$this->Application->isAdminUser ) { $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $this->_passListParams($event, 'per_page'); } } /** * Occurs when page is changed (only for hooking) * * @param kEvent $event * @return void * @access protected */ protected function OnSetPage(kEvent $event) { $page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page'); $event->SetRedirectParam($event->getPrefixSpecial() . '_Page', $page); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); if ( !$this->Application->isAdminUser ) { $this->_passListParams($event, 'page'); } } /** * Passes through main list pagination and sorting * * @param kEvent $event * @param string $skip_var * @return void * @access protected */ protected function _passListParams($event, $skip_var) { $param_names = array_diff(Array ('page', 'per_page', 'sort_by'), Array ($skip_var)); $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ foreach ($param_names as $param_name) { $value = $this->Application->GetVar($param_name); switch ($param_name) { case 'page': if ( $value > 1 ) { $event->SetRedirectParam('page', $value); } break; case 'per_page': if ( $value > 0 ) { if ( $value != $list_helper->getDefaultPerPage($event->Prefix) ) { $event->SetRedirectParam('per_page', $value); } } break; case 'sort_by': $event->setPseudoClass('_List'); $object = $event->getObject(Array ('main_list' => 1)); /* @var $object kDBList */ if ( $list_helper->hasUserSorting($object) ) { $event->SetRedirectParam('sort_by', $value); } break; } } } /** * Set's correct page for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetPagination(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ // get PerPage (forced -> session -> config -> 10) $object->SetPerPage($this->getPerPage($event)); // main lists on Front-End have special get parameter for page $page = $object->isMainList() ? $this->Application->GetVar('page') : false; if ( !$page ) { // page is given in "env" variable for given prefix $page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page'); } if ( !$page && $event->Special ) { // when not part of env, then variables like "prefix.special_Page" are // replaced (by PHP) with "prefix_special_Page", so check for that too $page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page'); } if ( !$object->isMainList() ) { // main lists doesn't use session for page storing $this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional if ( $page ) { // page found in request -> store in session $this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional } else { // page not found in request -> get from session $page = $this->Application->RecallVar($event->getPrefixSpecial() . '_Page'); } if ( !$event->getEventParam('skip_counting') ) { // when stored page is larger, then maximal list page number // (such case is also processed in kDBList::Query method) $pages = $object->GetTotalPages(); if ( $page > $pages ) { $page = 1; $this->Application->StoreVar($event->getPrefixSpecial() . '_Page', 1, true); } } } $object->SetPage($page); } /** * Returns current per-page setting for list * * @param kEvent $event * @return int * @access protected */ protected function getPerPage(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ $per_page = $event->getEventParam('per_page'); if ( $per_page ) { // per-page is passed as tag parameter to PrintList, InitList, etc. $config_mapping = $event->getUnitConfig()->getConfigMapping(); // 2. per-page setting is stored in configuration variable if ( $config_mapping ) { // such pseudo per-pages are only defined in templates directly switch ($per_page) { case 'short_list': $per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']); break; case 'default': $per_page = $this->Application->ConfigValue($config_mapping['PerPage']); break; } } return $per_page; } if ( !$per_page && $object->isMainList() ) { // main lists on Front-End have special get parameter for per-page $per_page = $this->Application->GetVar('per_page'); } if ( !$per_page ) { // per-page is given in "env" variable for given prefix $per_page = $this->Application->GetVar($event->getPrefixSpecial() . '_PerPage'); } if ( !$per_page && $event->Special ) { // when not part of env, then variables like "prefix.special_PerPage" are // replaced (by PHP) with "prefix_special_PerPage", so check for that too $per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage'); } if ( !$object->isMainList() ) { // per-page given in env and not in main list $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); if ( $per_page ) { // per-page found in request -> store in session and persistent session $this->setListSetting($event, 'PerPage', $per_page); } else { // per-page not found in request -> get from pesistent session (or session) $per_page = $this->getListSetting($event, 'PerPage'); } } if ( !$per_page ) { // per page wan't found in request/session/persistent session $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ // allow to override default per-page value from tag $default_per_page = $event->getEventParam('default_per_page'); if ( !is_numeric($default_per_page) ) { $default_per_page = $this->Application->ConfigValue('DefaultGridPerPage'); } $per_page = $list_helper->getDefaultPerPage($event->Prefix, $default_per_page); } return $per_page; } /** * Set's correct sorting for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetSorting(kEvent $event) { $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ if ( $object->isMainList() ) { $sort_by = $this->Application->GetVar('sort_by'); $cur_sort1 = $cur_sort1_dir = $cur_sort2 = $cur_sort2_dir = false; if ( $sort_by ) { $sortings = explode('|', $sort_by); list ($cur_sort1, $cur_sort1_dir) = explode(',', $sortings[0]); if ( isset($sortings[1]) ) { list ($cur_sort2, $cur_sort2_dir) = explode(',', $sortings[1]); } } } else { $sorting_settings = $this->getListSetting($event, 'Sortings'); $cur_sort1 = getArrayValue($sorting_settings, 'Sort1'); $cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir'); $cur_sort2 = getArrayValue($sorting_settings, 'Sort2'); $cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir'); } $tag_sort_by = $event->getEventParam('sort_by'); if ( $tag_sort_by ) { if ( $tag_sort_by == 'random' ) { $object->AddOrderField('RAND()', ''); } else { // multiple sortings could be specified at once $tag_sort_by = explode('|', $tag_sort_by); foreach ($tag_sort_by as $sorting_element) { list ($by, $dir) = explode(',', $sorting_element); $object->AddOrderField($by, $dir); } } } $list_sortings = $this->_getDefaultSorting($event); // use default if not specified in session if ( !$cur_sort1 || !$cur_sort1_dir ) { $sorting = getArrayValue($list_sortings, 'Sorting'); if ( $sorting ) { reset($sorting); $cur_sort1 = key($sorting); $cur_sort1_dir = current($sorting); if ( next($sorting) ) { $cur_sort2 = key($sorting); $cur_sort2_dir = current($sorting); } } } // always add forced sorting before any user sorting fields $forced_sorting = getArrayValue($list_sortings, 'ForcedSorting'); /* @var $forced_sorting Array */ if ( $forced_sorting ) { foreach ($forced_sorting as $field => $dir) { $object->AddOrderField($field, $dir); } } // add user sorting fields if ( $cur_sort1 != '' && $cur_sort1_dir != '' ) { $object->AddOrderField($cur_sort1, $cur_sort1_dir); } if ( $cur_sort2 != '' && $cur_sort2_dir != '' ) { $object->AddOrderField($cur_sort2, $cur_sort2_dir); } } /** * Returns default list sortings * * @param kEvent $event * @return Array * @access protected */ protected function _getDefaultSorting(kEvent $event) { $config = $event->getUnitConfig(); $sorting_configs = $config->getConfigMapping(); $list_sortings = $config->getListSortingsBySpecial($event); if ( $sorting_configs && array_key_exists('DefaultSorting1Field', $sorting_configs) ) { // sorting defined in configuration variables overrides one from unit config $list_sortings['Sorting'] = Array ( $this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']), $this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']), ); // TODO: lowercase configuration variable values in db, instead of here $list_sortings['Sorting'] = array_map('strtolower', $list_sortings['Sorting']); } return $list_sortings ? $list_sortings : Array (); } /** * Gets list setting by name (persistent or real session) * * @param kEvent $event * @param string $variable_name * @return string|Array * @access protected */ protected function getListSetting(kEvent $event, $variable_name) { $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); $storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->getPrefixSpecial(); // get sorting from persistent session $default_value = $this->Application->isAdmin ? ALLOW_DEFAULT_SETTINGS : false; $variable_value = $this->Application->RecallPersistentVar($storage_prefix . '_' . $variable_name . '.' . $view_name, $default_value); /*if ( !$variable_value ) { // get sorting from session $variable_value = $this->Application->RecallVar($storage_prefix . '_' . $variable_name); }*/ if ( kUtil::IsSerialized($variable_value) ) { $variable_value = unserialize($variable_value); } return $variable_value; } /** * Sets list setting by name (persistent and real session) * * @param kEvent $event * @param string $variable_name * @param string|Array $variable_value * @return void * @access protected */ protected function setListSetting(kEvent $event, $variable_name, $variable_value = NULL) { $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); // $this->Application->StoreVar($event->getPrefixSpecial() . '_' . $variable_name, $variable_value, true); //true for optional if ( isset($variable_value) ) { if ( is_array($variable_value) ) { $variable_value = serialize($variable_value); } $this->Application->StorePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name, $variable_value, true); //true for optional } else { $this->Application->RemovePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name); } } /** * Add filters found in session * * @param kEvent $event * @return void * @access protected */ protected function AddFilters(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ $edit_mark = rtrim($this->Application->GetSID() . '_' . $this->Application->GetTopmostWid($event->Prefix), '_'); // add search filter $filter_data = $this->Application->RecallVar($event->getPrefixSpecial() . '_search_filter'); if ( $filter_data ) { $filter_data = unserialize($filter_data); foreach ($filter_data as $filter_field => $filter_params) { $filter_type = ($filter_params['type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER; $filter_value = str_replace(EDIT_MARK, $edit_mark, $filter_params['value']); $object->addFilter($filter_field, $filter_value, $filter_type, kDBList::FLT_SEARCH); } } // add custom filter $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); $custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_custom_filter.' . $view_name); if ( $custom_filters ) { $grid_name = $event->getEventParam('grid'); $custom_filters = unserialize($custom_filters); if ( isset($custom_filters[$grid_name]) ) { foreach ($custom_filters[$grid_name] as $field_name => $field_options) { list ($filter_type, $field_options) = each($field_options); if ( isset($field_options['value']) && $field_options['value'] ) { $filter_type = ($field_options['sql_filter_type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER; $filter_value = str_replace(EDIT_MARK, $edit_mark, $field_options['value']); $object->addFilter($field_name, $filter_value, $filter_type, kDBList::FLT_CUSTOM); } } } } // add view filter $view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter'); if ( $view_filter ) { $view_filter = unserialize($view_filter); $temp_filter = $this->Application->makeClass('kMultipleFilter'); /* @var $temp_filter kMultipleFilter */ $filter_menu = $event->getUnitConfig()->getFilterMenu(); $group_key = 0; $group_count = count($filter_menu['Groups']); while ($group_key < $group_count) { $group_info = $filter_menu['Groups'][$group_key]; $temp_filter->setType(constant('kDBList::FLT_TYPE_' . $group_info['mode'])); $temp_filter->clearFilters(); foreach ($group_info['filters'] as $flt_id) { $sql_key = getArrayValue($view_filter, $flt_id) ? 'on_sql' : 'off_sql'; if ( $filter_menu['Filters'][$flt_id][$sql_key] != '' ) { $temp_filter->addFilter('view_filter_' . $flt_id, $filter_menu['Filters'][$flt_id][$sql_key]); } } $object->addFilter('view_group_' . $group_key, $temp_filter, $group_info['type'], kDBList::FLT_VIEW); $group_key++; } } // add item filter if ( $object->isMainList() ) { $this->applyItemFilters($event); } } /** * Applies item filters * * @param kEvent $event * @return void * @access protected */ protected function applyItemFilters($event) { $filter_values = $this->Application->GetVar('filters', Array ()); if ( !$filter_values ) { return; } $object = $event->getObject(); /* @var $object kDBList */ $where_clause = Array ( 'ItemPrefix = ' . $this->Conn->qstr($object->Prefix), 'FilterField IN (' . implode(',', $this->Conn->qstrArray(array_keys($filter_values))) . ')', 'Enabled = 1', ); $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('item-filter')->getTableName() . ' WHERE (' . implode(') AND (', $where_clause) . ')'; $filters = $this->Conn->Query($sql, 'FilterField'); foreach ($filters as $filter_field => $filter_data) { $filter_value = $filter_values[$filter_field]; if ( "$filter_value" === '' ) { // ListManager don't pass empty values, but check here just in case continue; } $table_name = $object->isVirtualField($filter_field) ? '' : '%1$s.'; switch ($filter_data['FilterType']) { case 'radio': $filter_value = $table_name . '`' . $filter_field . '` = ' . $this->Conn->qstr($filter_value); break; case 'checkbox': $filter_value = explode('|', substr($filter_value, 1, -1)); $filter_value = $this->Conn->qstrArray($filter_value, 'escape'); if ( $object->GetFieldOption($filter_field, 'multiple') ) { $filter_value = $table_name . '`' . $filter_field . '` LIKE "%|' . implode('|%" OR ' . $table_name . '`' . $filter_field . '` LIKE "%|', $filter_value) . '|%"'; } else { $filter_value = $table_name . '`' . $filter_field . '` IN (' . implode(',', $filter_value) . ')'; } break; case 'range': $filter_value = $this->Conn->qstrArray(explode('-', $filter_value)); $filter_value = $table_name . '`' . $filter_field . '` BETWEEN ' . $filter_value[0] . ' AND ' . $filter_value[1]; break; } $object->addFilter('item_filter_' . $filter_field, $filter_value, $object->isVirtualField($filter_field) ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER); } } /** * Set's new sorting for list * * @param kEvent $event * @return void * @access protected */ protected function OnSetSorting(kEvent $event) { $sorting_settings = $this->getListSetting($event, 'Sortings'); $cur_sort1 = getArrayValue($sorting_settings, 'Sort1'); $cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir'); $use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting'); if ( $use_double_sorting ) { $cur_sort2 = getArrayValue($sorting_settings, 'Sort2'); $cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir'); } $passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Sort1'); if ( $cur_sort1 == $passed_sort1 ) { $cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc'; } else { if ( $use_double_sorting ) { $cur_sort2 = $cur_sort1; $cur_sort2_dir = $cur_sort1_dir; } $cur_sort1 = $passed_sort1; $cur_sort1_dir = 'asc'; } $sorting_settings = Array ('Sort1' => $cur_sort1, 'Sort1_Dir' => $cur_sort1_dir); if ( $use_double_sorting ) { $sorting_settings['Sort2'] = $cur_sort2; $sorting_settings['Sort2_Dir'] = $cur_sort2_dir; } $this->setListSetting($event, 'Sortings', $sorting_settings); } /** * Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu) * * @param kEvent $event * @return void * @access protected */ protected function OnSetSortingDirect(kEvent $event) { // used on Front-End in category item lists $prefix_special = $event->getPrefixSpecial(); $combined = $this->Application->GetVar($event->getPrefixSpecial(true) . '_CombinedSorting'); if ( $combined ) { list ($field, $dir) = explode('|', $combined); if ( $this->Application->isAdmin || !$this->Application->GetVar('main_list') ) { $this->setListSetting($event, 'Sortings', Array ('Sort1' => $field, 'Sort1_Dir' => $dir)); } else { $event->setPseudoClass('_List'); $this->Application->SetVar('sort_by', $field . ',' . $dir); $object = $event->getObject(Array ('main_list' => 1)); /* @var $object kDBList */ $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $this->_passListParams($event, 'sort_by'); if ( $list_helper->hasUserSorting($object) ) { $event->SetRedirectParam('sort_by', $field . ',' . strtolower($dir)); } $event->SetRedirectParam('pass', 'm'); } return; } // used in "View Menu -> Sort" menu in administrative console $field_pos = $this->Application->GetVar($event->getPrefixSpecial(true) . '_SortPos'); $this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos, $prefix_special . '_Sort' . $field_pos); $this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos . '_Dir', $prefix_special . '_Sort' . $field_pos . '_Dir'); } /** * Reset grid sorting to default (from config) * * @param kEvent $event * @return void * @access protected */ protected function OnResetSorting(kEvent $event) { $this->setListSetting($event, 'Sortings'); } /** * Sets grid refresh interval * * @param kEvent $event * @return void * @access protected */ protected function OnSetAutoRefreshInterval(kEvent $event) { $refresh_interval = $this->Application->GetVar('refresh_interval'); $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); $this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_interval); } /** * Changes auto-refresh state for grid * * @param kEvent $event * @return void * @access protected */ protected function OnAutoRefreshToggle(kEvent $event) { $refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals'); if ( !$refresh_intervals ) { return; } $view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view'); $auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name); if ( $auto_refresh === false ) { $refresh_intervals = explode(',', $refresh_intervals); $this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_intervals[0]); } $this->Application->StorePersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name, $auto_refresh ? 0 : 1); } /** * Creates needed sql query to load item, * if no query is defined in config for * special requested, then use list query * * @param kEvent $event * @return string * @access protected */ protected function ItemPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $sqls = $object->getFormOption('ItemSQLs', Array ()); $special = isset($sqls[$event->Special]) ? $event->Special : ''; // preferred special not found in ItemSQLs -> use analog from ListSQLs return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event); } /** * Creates needed sql query to load list, * if no query is defined in config for * special requested, then use default * query * * @param kEvent $event * @return string * @access protected */ protected function ListPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $sqls = $object->getFormOption('ListSQLs', Array ()); return $sqls[array_key_exists($event->Special, $sqls) ? $event->Special : '']; } /** * Apply custom processing to item * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(kEvent $event, $type) { } /* Edit Events mostly used in Admin */ /** * Creates new kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { return; } list($id, $field_values) = each($items_info); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->setID($id); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); // look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default if ( $object->Create($event->getEventParam('ForceCreateId')) ) { $this->customProcessing($event, 'after'); $event->SetRedirectParam('opener', 'u'); return; } $event->redirect = false; $event->status = kEvent::erFAIL; $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); - $object->setID($id); } /** * Updates kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $this->_update($event); $event->SetRedirectParam('opener', 'u'); } /** * Updates data in database based on request * * @param kEvent $event * @return void * @access protected */ protected function _update(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ( $items_info ) { foreach ($items_info as $id => $field_values) { $object->Load($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); if ( $object->Update($id) ) { $this->customProcessing($event, 'after'); $event->status = kEvent::erSUCCESS; } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } } /** * Delete's kDBItem object * * @param kEvent $event * @return void * @access protected */ protected function OnDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($this->getPassedID($event))); } /** * Deletes all records from table * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteAll(kEvent $event) { $config = $event->getUnitConfig(); $sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName(); $ids = $this->Conn->GetCol($sql); if ( $ids ) { $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($event->Prefix, $event->Special, $ids); } } /** * Prepares new kDBItem object * * @param kEvent $event * @return void * @access protected */ protected function OnNew(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->Clear(0); $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); if ( $event->getEventParam('top_prefix') != $event->Prefix ) { // this is subitem prefix, so use main item special $table_info = $object->getLinkedInfo($this->getMainSpecial($event)); } else { $table_info = $object->getLinkedInfo(); } $object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']); $event->redirect = false; } /** * Cancels kDBItem Editing/Creation * * @param kEvent $event * @return void * @access protected */ protected function OnCancel(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { $delete_ids = Array (); $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ foreach ($items_info as $id => $field_values) { $object->Load($id); // record created for using with selector (e.g. Reviews->Select User), and not validated => Delete it if ( $object->isLoaded() && !$object->Validate() && ($id <= 0) ) { $delete_ids[] = $id; } } if ( $delete_ids ) { $temp_handler->DeleteItems($event->Prefix, $event->Special, $delete_ids); } } $event->SetRedirectParam('opener', 'u'); } /** * Deletes all selected items. * Automatically recurse into sub-items using temp handler, and deletes sub-items * by calling its Delete method if sub-item has AutoDelete set to true in its config file * * @param kEvent $event * @return void * @access protected */ protected function OnMassDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return ; } $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $ids = $this->StoreSelectedIDs($event); $event->setEventParam('ids', $ids); $this->customProcessing($event, 'before'); $ids = $event->getEventParam('ids'); if ( $ids ) { $temp_handler->DeleteItems($event->Prefix, $event->Special, $ids); } $this->clearSelectedIDs($event); } /** * Sets window id (of first opened edit window) to temp mark in uls * * @param kEvent $event * @return void * @access protected */ protected function setTempWindowID(kEvent $event) { $prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true)); foreach ($prefixes as $prefix) { $mode = $this->Application->GetVar($prefix . '_mode'); if ($mode == 't') { $wid = $this->Application->GetVar('m_wid'); $this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid); break; } } } /** * Prepare temp tables and populate it * with items selected in the grid * * @param kEvent $event * @return void * @access protected */ protected function OnEdit(kEvent $event) { $this->setTempWindowID($event); $ids = $this->StoreSelectedIDs($event); $object = $event->getObject(Array('skip_autoload' => true)); /* @var $object kDBItem */ $object->setPendingActions(null, true); $changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $this->Application->RemoveVar($changes_var_name); $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->PrepareEdit(); $event->SetRedirectParam('m_lang', $this->Application->GetDefaultLanguageId()); $event->SetRedirectParam($event->getPrefixSpecial() . '_id', array_shift($ids)); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); $simultaneous_edit_message = $this->Application->GetVar('_simultaneous_edit_message'); if ( $simultaneous_edit_message ) { $event->SetRedirectParam('_simultaneous_edit_message', $simultaneous_edit_message); } } /** * Saves content of temp table into live and * redirects to event' default redirect (normally grid template) * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { $event->CallSubEvent('OnPreSave'); if ( $event->status != kEvent::erSUCCESS ) { return; } $skip_master = false; $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); if ( !$this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $live_ids = $temp_handler->SaveEdit($event->getEventParam('master_ids') ? $event->getEventParam('master_ids') : Array ()); if ( $live_ids === false ) { // coping from table failed, because we have another coping process to same table, that wasn't finished $event->status = kEvent::erFAIL; return; } if ( $live_ids ) { // ensure, that newly created item ids are available as if they were selected from grid // NOTE: only works if main item has sub-items !!! $this->StoreSelectedIDs($event, $live_ids); } $object = $event->getObject(); /* @var $object kDBItem */ $this->SaveLoggedChanges($changes_var_name, $object->ShouldLogChanges()); } else { $event->status = kEvent::erFAIL; } $this->clearSelectedIDs($event); $event->SetRedirectParam('opener', 'u'); $this->Application->RemoveVar($event->getPrefixSpecial() . '_modified'); // all temp tables are deleted here => all after hooks should think, that it's live mode now $this->Application->SetVar($event->Prefix . '_mode', ''); } /** * Saves changes made in temporary table to log * * @param string $changes_var_name * @param bool $save * @return void * @access public */ public function SaveLoggedChanges($changes_var_name, $save = true) { // 1. get changes, that were made $changes = $this->Application->RecallVar($changes_var_name); $changes = $changes ? unserialize($changes) : Array (); $this->Application->RemoveVar($changes_var_name); if (!$changes) { // no changes, skip processing return ; } // TODO: 2. optimize change log records (replace multiple changes to same record with one change record) $to_increment = Array (); // 3. collect serials to reset based on foreign keys foreach ($changes as $index => $rec) { if (array_key_exists('DependentFields', $rec)) { foreach ($rec['DependentFields'] as $field_name => $field_value) { // will be "ci|ItemResourceId:345" $to_increment[] = $rec['Prefix'] . '|' . $field_name . ':' . $field_value; // also reset sub-item prefix general serial $to_increment[] = $rec['Prefix']; } unset($changes[$index]['DependentFields']); } unset($changes[$index]['ParentId'], $changes[$index]['ParentPrefix']); } // 4. collect serials to reset based on changed ids foreach ($changes as $change) { $to_increment[] = $change['MasterPrefix'] . '|' . $change['MasterId']; if ($change['MasterPrefix'] != $change['Prefix']) { // also reset sub-item prefix general serial $to_increment[] = $change['Prefix']; // will be "ci|ItemResourceId" $to_increment[] = $change['Prefix'] . '|' . $change['ItemId']; } } // 5. reset serials collected before $to_increment = array_unique($to_increment); $this->Application->incrementCacheSerial($this->Prefix); foreach ($to_increment as $to_increment_mixed) { if (strpos($to_increment_mixed, '|') !== false) { list ($to_increment_prefix, $to_increment_id) = explode('|', $to_increment_mixed, 2); $this->Application->incrementCacheSerial($to_increment_prefix, $to_increment_id); } else { $this->Application->incrementCacheSerial($to_increment_mixed); } } // save changes to database $session_log_id = $this->Application->RecallVar('_SessionLogId_'); if (!$save || !$session_log_id) { // saving changes to database disabled OR related session log missing return ; } $add_fields = Array ( 'PortalUserId' => $this->Application->RecallVar('user_id'), 'SessionLogId' => $session_log_id, ); $change_log_table = $this->Application->getUnitConfig('change-log')->getTableName(); foreach ($changes as $rec) { $this->Conn->doInsert(array_merge($rec, $add_fields), $change_log_table); } $this->Application->incrementCacheSerial('change-log'); $sql = 'UPDATE ' . $this->Application->getUnitConfig('session-log')->getTableName() . ' SET AffectedItems = AffectedItems + ' . count($changes) . ' WHERE SessionLogId = ' . $session_log_id; $this->Conn->Query($sql); $this->Application->incrementCacheSerial('session-log'); } /** * Cancels edit * Removes all temp tables and clears selected ids * * @param kEvent $event * @return void * @access protected */ protected function OnCancelEdit(kEvent $event) { $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->CancelEdit(); $this->clearSelectedIDs($event); $this->Application->RemoveVar($event->getPrefixSpecial() . '_modified'); $changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $this->Application->RemoveVar($changes_var_name); $event->SetRedirectParam('opener', 'u'); } /** * Allows to determine if we are creating new item or editing already created item * * @param kEvent $event * @return bool * @access public */ public function isNewItemCreate(kEvent $event) { $object = $event->getObject( Array ('raise_warnings' => 0) ); /* @var $object kDBItem */ return !$object->isLoaded(); } /** * Saves edited item into temp table * If there is no id, new item is created in temp table * * @param kEvent $event * @return void * @access protected */ protected function OnPreSave(kEvent $event) { // if there is no id - it means we need to create an item if ( is_object($event->MasterEvent) ) { $event->MasterEvent->setEventParam('IsNew', false); } if ( $this->isNewItemCreate($event) ) { $event->CallSubEvent('OnPreSaveCreated'); if ( is_object($event->MasterEvent) ) { $event->MasterEvent->setEventParam('IsNew', true); } return ; } // don't just call OnUpdate event here, since it maybe overwritten to Front-End specific behavior $this->_update($event); } /** * Analog of OnPreSave event for usage in AJAX request * * @param kEvent $event * * @return void */ protected function OnPreSaveAjax(kEvent $event) { $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); /* @var $ajax_form_helper AjaxFormHelper */ $ajax_form_helper->transitEvent($event, 'OnPreSave'); } /** * [HOOK] Saves sub-item * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveSubItem(kEvent $event) { $not_created = $this->isNewItemCreate($event); $event->CallSubEvent($not_created ? 'OnCreate' : 'OnUpdate'); if ( $event->status == kEvent::erSUCCESS ) { $object = $event->getObject(); /* @var $object kDBItem */ $this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $event->MasterEvent->status = $event->status; } $event->SetRedirectParam('opener', 's'); } /** * Saves edited item in temp table and loads * item with passed id in current template * Used in Prev/Next buttons * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndGo(kEvent $event) { $event->CallSubEvent('OnPreSave'); if ( $event->status == kEvent::erSUCCESS ) { $id = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId'); $event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id); } } /** * Saves edited item in temp table and goes * to passed tabs, by redirecting to it with OnPreSave event * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndGoToTab(kEvent $event) { $event->CallSubEvent('OnPreSave'); if ( $event->status == kEvent::erSUCCESS ) { $event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab'); } } /** * Saves editable list and goes to passed tab, * by redirecting to it with empty event * * @param kEvent $event * @return void * @access protected */ protected function OnUpdateAndGoToTab(kEvent $event) { $event->setPseudoClass('_List'); $event->CallSubEvent('OnUpdate'); if ( $event->status == kEvent::erSUCCESS ) { $event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab'); } } /** * Prepare temp tables for creating new item * but does not create it. Actual create is * done in OnPreSaveCreated * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { $this->setTempWindowID($event); $this->clearSelectedIDs($event); $this->Application->SetVar('m_lang', $this->Application->GetDefaultLanguageId()); $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $temp_handler = $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->PrepareEdit(); $object->setID(0); $this->Application->SetVar($event->getPrefixSpecial() . '_id', 0); $this->Application->SetVar($event->getPrefixSpecial() . '_PreCreate', 1); $changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $this->Application->RemoveVar($changes_var_name); $event->redirect = false; } /** * Creates a new item in temp table and * stores item id in App vars and Session on success * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveCreated(kEvent $event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ + $object->setID(0); $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); if ( $object->Create() ) { $this->customProcessing($event, 'after'); $event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID()); } else { $event->status = kEvent::erFAIL; $event->redirect = false; - $object->setID(0); } } /** * Reloads form to loose all changes made during item editing * * @param kEvent $event * @return void * @access protected */ protected function OnReset(kEvent $event) { //do nothing - should reset :) if ( $this->isNewItemCreate($event) ) { // just reset id to 0 in case it was create $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ $object->setID(0); $this->Application->SetVar($event->getPrefixSpecial() . '_id', 0); } } /** * Apply same processing to each item being selected in grid * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return ; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $config = $event->getUnitConfig(); $status_field = $config->getStatusField(true); $order_field = $config->getOrderField(); if ( !$order_field ) { $order_field = 'Priority'; } foreach ($ids as $id) { $object->Load($id); switch ( $event->Name ) { case 'OnMassApprove': $object->SetDBField($status_field, 1); break; case 'OnMassDecline': $object->SetDBField($status_field, 0); break; case 'OnMassMoveUp': $object->SetDBField($order_field, $object->GetDBField($order_field) + 1); break; case 'OnMassMoveDown': $object->SetDBField($order_field, $object->GetDBField($order_field) - 1); break; } if ( $object->Update() ) { $event->status = kEvent::erSUCCESS; } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } $this->clearSelectedIDs($event); } /** * Clones selected items in list * * @param kEvent $event * @return void * @access protected */ protected function OnMassClone(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $temp_handler->CloneItems($event->Prefix, $event->Special, $ids); } $this->clearSelectedIDs($event); } /** * Checks if given value is present in given array * * @param Array $records * @param string $field * @param mixed $value * @return bool * @access protected */ protected function check_array($records, $field, $value) { foreach ($records as $record) { if ($record[$field] == $value) { return true; } } return false; } /** * Saves data from editing form to database without checking required fields * * @param kEvent $event * @return void * @access protected */ protected function OnPreSavePopup(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $this->RemoveRequiredFields($object); $event->CallSubEvent('OnPreSave'); $event->SetRedirectParam('opener', 'u'); } /* End of Edit events */ // III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item /** * Occurs before loading item, 'id' parameter * allows to get id of item being loaded * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemLoad(kEvent $event) { } /** * Occurs after loading item, 'id' parameter * allows to get id of item that was loaded * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { } /** * Occurs before creating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->IsTempTable() ) { $this->_processPendingActions($event); } } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { } /** * Occurs after updating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->IsTempTable() ) { $this->_processPendingActions($event); } } /** * Occurs before deleting item, id of item being * deleted is stored as 'id' event param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemDelete(kEvent $event) { } /** * Occurs after deleting item, id of deleted item * is stored as 'id' param of event * * Also deletes subscriptions to that particual item once it's deleted * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemDelete(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ // 1. delete direct subscriptions to item, that was deleted $this->_deleteSubscriptions($event->Prefix, 'ItemId', $object->GetID()); // 2. delete this item sub-items subscriptions, that reference item, that was deleted foreach ($event->getUnitConfig()->getSubItems(Array ()) as $sub_prefix) { $this->_deleteSubscriptions($sub_prefix, 'ParentItemId', $object->GetID()); } } /** * Deletes all subscriptions, associated with given item * * @param string $prefix * @param string $field * @param int $value * @return void * @access protected */ protected function _deleteSubscriptions($prefix, $field, $value) { $sql = 'SELECT TemplateId FROM ' . $this->Application->getUnitConfig('email-template')->getTableName() . ' WHERE BindToSystemEvent REGEXP "' . $this->Conn->escape($prefix) . '(\\\\.[^:]*:.*|:.*)"'; $email_template_ids = $this->Conn->GetCol($sql); if ( !$email_template_ids ) { return; } // e-mail events, connected to that unit prefix are found $sql = 'SELECT SubscriptionId FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions WHERE ' . $field . ' = ' . $value . ' AND EmailTemplateId IN (' . implode(',', $email_template_ids) . ')'; $ids = $this->Conn->GetCol($sql); if ( !$ids ) { return; } $temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('system-event-subscription', '', $ids); } /** * Occurs before validation attempt * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemValidate(kEvent $event) { } /** * Occurs after successful item validation * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemValidate(kEvent $event) { } /** * Occurs after an item has been copied to temp * Id of copied item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCopyToTemp(kEvent $event) { } /** * Occurs before an item is deleted from live table when copying from temp * (temp handler deleted all items from live and then copy over all items from temp) * Id of item being deleted is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { } /** * Occurs before an item is copied to live table (after all foreign keys have been updated) * Id of item being copied is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeCopyToLive(kEvent $event) { } /** * Occurs after an item has been copied to live table * Id of copied item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCopyToLive(kEvent $event) { $object = $event->getObject(array('skip_autoload' => true)); /* @var $object kDBItem */ $object->SwitchToLive(); $object->Load($event->getEventParam('id')); $this->_processPendingActions($event); } /** * Processing file pending actions (e.g. delete scheduled files) * * @param kEvent $event * @return void * @access protected */ protected function _processPendingActions(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $update_required = false; $temp_id = $event->getEventParam('temp_id'); $id = $temp_id !== false ? $temp_id : $object->GetID(); foreach ($object->getPendingActions($id) as $data) { switch ( $data['action'] ) { case 'delete': unlink($data['file']); break; case 'make_live': $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ + if ( !file_exists($data['file']) ) { + // file removal was requested too + continue; + } + $old_name = basename($data['file']); $new_name = $file_helper->ensureUniqueFilename(dirname($data['file']), kUtil::removeTempExtension($old_name)); rename($data['file'], dirname($data['file']) . '/' . $new_name); $db_value = $object->GetDBField($data['field']); $object->SetDBField($data['field'], str_replace($old_name, $new_name, $db_value)); $update_required = true; break; default: trigger_error('Unsupported pending action "' . $data['action'] . '" for "' . $event->getPrefixSpecial() . '" unit', E_USER_WARNING); break; } } // remove pending actions before updating to prevent recursion $object->setPendingActions(); if ( $update_required ) { $object->Update(); } } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { } /** * Occurs after an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnAfterClone(kEvent $event) { } /** * Occurs after list is queried * * @param kEvent $event * @return void * @access protected */ protected function OnAfterListQuery(kEvent $event) { } /** * Ensures that popup will be closed automatically * and parent window will be refreshed with template * passed * * @param kEvent $event * @return void * @access protected * @deprecated */ protected function finalizePopup(kEvent $event) { $event->SetRedirectParam('opener', 'u'); } /** * Create search filters based on search query * * @param kEvent $event * @return void * @access protected */ protected function OnSearch(kEvent $event) { $event->setPseudoClass('_List'); $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $search_helper->performSearch($event); } /** * Clear search keywords * * @param kEvent $event * @return void * @access protected */ protected function OnSearchReset(kEvent $event) { $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $search_helper->resetSearch($event); } /** * Set's new filter value (filter_id meaning from config) * * @param kEvent $event * @return void * @access protected * @deprecated */ protected function OnSetFilter(kEvent $event) { $filter_id = $this->Application->GetVar('filter_id'); $filter_value = $this->Application->GetVar('filter_value'); $view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter'); $view_filter = $view_filter ? unserialize($view_filter) : Array (); $view_filter[$filter_id] = $filter_value; $this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter)); } /** * Sets view filter based on request * * @param kEvent $event * @return void * @access protected */ protected function OnSetFilterPattern(kEvent $event) { $filters = $this->Application->GetVar($event->getPrefixSpecial(true) . '_filters'); if ( !$filters ) { return; } $view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter'); $view_filter = $view_filter ? unserialize($view_filter) : Array (); $filters = explode(',', $filters); foreach ($filters as $a_filter) { list($id, $value) = explode('=', $a_filter); $view_filter[$id] = $value; } $this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter)); $event->redirect = false; } /** * Add/Remove all filters applied to list from "View" menu * * @param kEvent $event * @return void * @access protected */ protected function FilterAction(kEvent $event) { $view_filter = Array (); $filter_menu = $event->getUnitConfig()->getFilterMenu(); switch ($event->Name) { case 'OnRemoveFilters': $filter_value = 1; break; case 'OnApplyFilters': $filter_value = 0; break; default: $filter_value = 0; break; } foreach ($filter_menu['Filters'] as $filter_key => $filter_params) { if ( !$filter_params ) { continue; } $view_filter[$filter_key] = $filter_value; } $this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter)); } /** * Enter description here... * * @param kEvent $event * @access protected */ protected function OnPreSaveAndOpenTranslator(kEvent $event) { $this->Application->SetVar('allow_translation', true); $object = $event->getObject(); /* @var $object kDBItem */ $this->RemoveRequiredFields($object); $event->CallSubEvent('OnPreSave'); if ( $event->status == kEvent::erSUCCESS ) { $resource_id = $this->Application->GetVar('translator_resource_id'); if ( $resource_id ) { $t_prefixes = explode(',', $this->Application->GetVar('translator_prefixes')); $cdata = $this->Application->recallObject($t_prefixes[1], NULL, Array ('skip_autoload' => true)); /* @var $cdata kDBItem */ $cdata->Load($resource_id, 'ResourceId'); if ( !$cdata->isLoaded() ) { $cdata->SetDBField('ResourceId', $resource_id); $cdata->Create(); } $this->Application->SetVar($cdata->getPrefixSpecial() . '_id', $cdata->GetID()); } $event->redirect = $this->Application->GetVar('translator_t'); $redirect_params = Array ( 'pass' => 'all,trans,' . $this->Application->GetVar('translator_prefixes'), 'opener' => 's', $event->getPrefixSpecial(true) . '_id' => $object->GetID(), 'trans_event' => 'OnLoad', 'trans_prefix' => $this->Application->GetVar('translator_prefixes'), 'trans_field' => $this->Application->GetVar('translator_field'), 'trans_multi_line' => $this->Application->GetVar('translator_multi_line'), ); $event->setRedirectParams($redirect_params); // 1. SAVE LAST TEMPLATE TO SESSION (really needed here, because of tweaky redirect) $last_template = $this->Application->RecallVar('last_template'); preg_match('/index4\.php\|' . $this->Application->GetSID() . '-(.*):/U', $last_template, $rets); $this->Application->StoreVar('return_template', $this->Application->GetVar('t')); } } /** * Makes all fields non-required * * @param kDBItem $object * @return void * @access protected */ protected function RemoveRequiredFields(&$object) { // making all field non-required to achieve successful presave $fields = array_keys( $object->getFields() ); foreach ($fields as $field) { if ( $object->isRequired($field) ) { $object->setRequired($field, false); } } } /** * Saves selected user in needed field * * @param kEvent $event * @return void * @access protected */ protected function OnSelectUser(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $items_info = $this->Application->GetVar('u'); if ( $items_info ) { list ($user_id, ) = each($items_info); $this->RemoveRequiredFields($object); $is_new = !$object->isLoaded(); $is_main = substr($this->Application->GetVar($event->Prefix . '_mode'), 0, 1) == 't'; if ( $is_new ) { $new_event = $is_main ? 'OnPreCreate' : 'OnNew'; $event->CallSubEvent($new_event); $event->redirect = true; } $object->SetDBField($this->Application->RecallVar('dst_field'), $user_id); if ( $is_new ) { $object->Create(); } else { $object->Update(); } } $event->SetRedirectParam($event->getPrefixSpecial() . '_id', $object->GetID()); $event->SetRedirectParam('opener', 'u'); } /** EXPORT RELATED **/ /** * Shows export dialog * * @param kEvent $event * @return void * @access protected */ protected function OnExport(kEvent $event) { $selected_ids = $this->StoreSelectedIDs($event); if ( implode(',', $selected_ids) == '' ) { // K4 fix when no ids found bad selected ids array is formed $selected_ids = false; } $this->Application->StoreVar($event->Prefix . '_export_ids', $selected_ids ? implode(',', $selected_ids) : ''); $this->Application->LinkVar('export_finish_t'); $this->Application->LinkVar('export_progress_t'); $this->Application->StoreVar('export_oroginal_special', $event->Special); $export_helper = $this->Application->recallObject('CatItemExportHelper'); /*list ($index_file, $env) = explode('|', $this->Application->RecallVar('last_template')); $finish_url = $this->Application->BaseURL() . 'admin/' . $index_file . '?' . ENV_VAR_NAME . '=' . $env; $this->Application->StoreVar('export_finish_url', $finish_url);*/ $redirect_params = Array ( $this->Prefix . '.export_event' => 'OnNew', 'pass' => 'all,' . $this->Prefix . '.export' ); $event->setRedirectParams($redirect_params); } /** * Apply some special processing to object being * recalled before using it in other events that * call prepareObject * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { if ( $event->Special == 'export' || $event->Special == 'import' ) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->prepareExportColumns($event); } } /** * Returns specific to each item type columns only * * @param kEvent $event * @return Array * @access public */ public function getCustomExportColumns(kEvent $event) { return Array (); } /** * Export form validation & processing * * @param kEvent $event * @return void * @access protected */ protected function OnExportBegin(kEvent $event) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->OnExportBegin($event); } /** * Enter description here... * * @param kEvent $event * @return void * @access protected */ protected function OnExportCancel(kEvent $event) { $this->OnGoBack($event); } /** * Allows configuring export options * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeExportBegin(kEvent $event) { } /** * Deletes export preset * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteExportPreset(kEvent $event) { $field_values = $this->getSubmittedFields($event); if ( !$field_values ) { return ; } $preset_key = $field_values['ExportPresets']; $export_settings = $this->Application->RecallPersistentVar('export_settings'); if ( !$export_settings ) { return ; } $export_settings = unserialize($export_settings); if ( !isset($export_settings[$event->Prefix]) ) { return ; } $to_delete = ''; foreach ($export_settings[$event->Prefix] as $key => $val) { if ( implode('|', $val['ExportColumns']) == $preset_key ) { $to_delete = $key; break; } } if ( $to_delete ) { unset($export_settings[$event->Prefix][$to_delete]); $this->Application->StorePersistentVar('export_settings', serialize($export_settings)); } } /** * Saves changes & changes language * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndChangeLanguage(kEvent $event) { if ( $this->UseTempTables($event) ) { $event->CallSubEvent('OnPreSave'); } if ( $event->status == kEvent::erSUCCESS ) { $this->Application->SetVar('m_lang', $this->Application->GetVar('language')); $data = $this->Application->GetVar('st_id'); if ( $data ) { $event->SetRedirectParam('st_id', $data); } } } /** * Used to save files uploaded via Plupload * * @param kEvent $event * @return void * @access protected */ protected function OnUploadFile(kEvent $event) { $event->status = kEvent::erSTOP; /** @var kUploadHelper $upload_helper */ $upload_helper = $this->Application->recallObject('kUploadHelper'); try { $filename = $upload_helper->handle($event); $response = array( 'jsonrpc' => '2.0', 'status' => 'success', 'result' => $filename, ); } catch ( kUploaderException $e ) { $response = array( 'jsonrpc' => '2.0', 'status' => 'error', 'error' => array('code' => $e->getCode(), 'message' => $e->getMessage()), ); } echo json_encode($response); } /** * Remembers, that file should be deleted on item's save from temp table * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteFile(kEvent $event) { $event->status = kEvent::erSTOP; $field_id = $this->Application->GetVar('field_id'); if ( !preg_match_all('/\[([^\[\]]*)\]/', $field_id, $regs) ) { return; } $field = $regs[1][1]; $record_id = $regs[1][0]; /** @var kUploadHelper $upload_helper */ $upload_helper = $this->Application->recallObject('kUploadHelper'); $object = $upload_helper->prepareUploadedFile($event, $field); if ( !$object->GetDBField($field) ) { return; } $pending_actions = $object->getPendingActions($record_id); $pending_actions[] = Array ( 'action' => 'delete', 'id' => $record_id, 'field' => $field, 'file' => $object->GetField($field, 'full_path'), ); $object->setPendingActions($pending_actions, $record_id); } /** * Returns url for viewing uploaded file * * @param kEvent $event * @return void * @access protected */ protected function OnViewFile(kEvent $event) { $event->status = kEvent::erSTOP; $field = $this->Application->GetVar('field'); /** @var kUploadHelper $upload_helper */ $upload_helper = $this->Application->recallObject('kUploadHelper'); $object = $upload_helper->prepareUploadedFile($event, $field); if ( !$object->GetDBField($field) ) { return; } // get url to uploaded file if ( $this->Application->GetVar('thumb') ) { $url = $object->GetField($field, $object->GetFieldOption($field, 'thumb_format')); } else { $url = $object->GetField($field, 'raw_url'); } /** @var FileHelper $file_helper */ $file_helper = $this->Application->recallObject('FileHelper'); $path = $file_helper->urlToPath($url); if ( !file_exists($path) ) { exit; } header('Content-Length: ' . filesize($path)); $this->Application->setContentType(kUtil::mimeContentType($path), false); header('Content-Disposition: inline; filename="' . kUtil::removeTempExtension($object->GetDBField($field)) . '"'); readfile($path); } /** * Validates MInput control fields * * @param kEvent $event * @return void * @access protected */ protected function OnValidateMInputFields(kEvent $event) { $minput_helper = $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ $minput_helper->OnValidateMInputFields($event); } /** * Validates individual object field and returns the result * * @param kEvent $event * @return void * @access protected */ protected function OnValidateField(kEvent $event) { $event->status = kEvent::erSTOP; $field = $this->Application->GetVar('field'); if ( ($this->Application->GetVar('ajax') != 'yes') || !$field ) { return; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { return; } list ($id, $field_values) = each($items_info); $object->Load($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $object->setID($id); $response = Array ('status' => 'OK'); $event->CallSubEvent($object->isLoaded() ? 'OnBeforeItemUpdate' : 'OnBeforeItemCreate'); // validate all fields, since "Password_plain" field sets error to "Password" field, which is passed here $error_field = $object->GetFieldOption($field, 'error_field', false, $field); if ( !$object->Validate() && $object->GetErrorPseudo($error_field) ) { $response['status'] = $object->GetErrorMsg($error_field, false); } $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); /* @var $ajax_form_helper AjaxFormHelper */ $response['other_errors'] = $ajax_form_helper->getErrorMessages($object); $response['uploader_info'] = $ajax_form_helper->getUploaderInfo($object, array_keys($field_values)); $event->status = kEvent::erSTOP; // since event's OnBefore... events can change this event status echo json_encode($response); } /** * Returns auto-complete values for ajax-dropdown * * @param kEvent $event * @return void * @access protected */ protected function OnSuggestValues(kEvent $event) { $event->status = kEvent::erSTOP; $this->Application->XMLHeader(); $data = $this->getAutoCompleteSuggestions($event, $this->Application->GetVar('cur_value')); echo ''; if ( kUtil::isAssoc($data) ) { foreach ($data as $key => $title) { echo '' . kUtil::escape($title, kUtil::ESCAPE_HTML) . ''; } } else { foreach ($data as $title) { echo '' . kUtil::escape($title, kUtil::ESCAPE_HTML) . ''; } } echo ''; } /** * Returns auto-complete values for jQueryUI.AutoComplete * * @param kEvent $event * @return void * @access protected */ protected function OnSuggestValuesJSON(kEvent $event) { $event->status = kEvent::erSTOP; $data = $this->getAutoCompleteSuggestions($event, $this->Application->GetVar('term')); if ( kUtil::isAssoc($data) ) { $transformed_data = array(); foreach ($data as $key => $title) { $transformed_data[] = array('value' => $key, 'label' => $title); } $data = $transformed_data; } echo json_encode($data); } /** * Prepares a suggestion list based on a given term. * * @param kEvent $event Event. * @param string $term Term. * * @return Array * @access protected */ protected function getAutoCompleteSuggestions(kEvent $event, $term) { $object = $event->getObject(); /* @var $object kDBItem */ $field = $this->Application->GetVar('field'); if ( !$field || !$term || !$object->isField($field) ) { return array(); } $limit = $this->Application->GetVar('limit'); if ( !$limit ) { $limit = 20; } $sql = 'SELECT DISTINCT ' . $field . ' FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE ' . $field . ' LIKE ' . $this->Conn->qstr($term . '%') . ' ORDER BY ' . $field . ' LIMIT 0,' . $limit; return $this->Conn->GetCol($sql); } /** * Enter description here... * * @param kEvent $event * @return void * @access protected */ protected function OnSaveWidths(kEvent $event) { $event->status = kEvent::erSTOP; // $this->Application->setContentType('text/xml'); $picker_helper = new kColumnPickerHelper( $event->getPrefixSpecial(), $this->Application->GetVar('grid_name') ); $picker_helper->saveWidths($this->Application->GetVar('widths')); echo 'OK'; } /** * Called from CSV import script after item fields * are set and validated, but before actual item create/update. * If event status is kEvent::erSUCCESS, line will be imported, * else it will not be imported but added to skipped lines * and displayed in the end of import. * Event status is preset from import script. * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeCSVLineImport(kEvent $event) { // abstract, for hooking } /** * [HOOK] Allows to add cloned subitem to given prefix * * @param kEvent $event * @return void * @access protected */ protected function OnCloneSubItem(kEvent $event) { $sub_item_prefix = $event->Prefix . '-' . preg_replace('/^#/', '', $event->MasterEvent->Prefix); $event->MasterEvent->getUnitConfig()->addClones(Array ( $sub_item_prefix => Array ('ParentPrefix' => $event->Prefix), )); } /** * Returns constrain for priority calculations * * @param kEvent $event * @return void * @see PriorityEventHandler * @access protected */ protected function OnGetConstrainInfo(kEvent $event) { $event->setEventParam('constrain_info', Array ('', '')); } - } \ No newline at end of file + } Index: branches/5.3.x/core/kernel/db/dbitem.php =================================================================== --- branches/5.3.x/core/kernel/db/dbitem.php (revision 16110) +++ branches/5.3.x/core/kernel/db/dbitem.php (revision 16111) @@ -1,1572 +1,1609 @@ validator) ) { $validator_class = $this->getUnitConfig()->getValidatorClass('kValidator'); $this->validator = $this->Application->makeClass($validator_class); } $this->validator->setDataSource($this); } public function SetDirtyField($field_name, $field_value) { $this->DirtyFieldValues[$field_name] = $field_value; } public function GetDirtyField($field_name) { return $this->DirtyFieldValues[$field_name]; } public function GetOriginalField($field_name, $formatted = false, $format=null) { if (array_key_exists($field_name, $this->OriginalFieldValues)) { // item was loaded before $value = $this->OriginalFieldValues[$field_name]; } else { // no original fields -> use default field value $value = $this->Fields[$field_name]['default']; } if (!$formatted) { return $value; } $res = $value; $formatter = $this->GetFieldOption($field_name, 'formatter'); if ( $formatter ) { $formatter = $this->Application->recallObject($formatter); /* @var $formatter kFormatter */ $res = $formatter->Format($value, $field_name, $this, $format); } return $res; } /** * Sets original field value (useful for custom virtual fields) * * @param string $field_name * @param string $field_value */ public function SetOriginalField($field_name, $field_value) { $this->OriginalFieldValues[$field_name] = $field_value; } /** * Set's default values for all fields * * @access public */ public function SetDefaultValues() { parent::SetDefaultValues(); if ($this->populateMultiLangFields) { $this->PopulateMultiLangFields(); } foreach ($this->Fields as $field => $field_options) { $default_value = isset($field_options['default']) ? $field_options['default'] : NULL; $this->SetDBField($field, $default_value); } } /** * Sets current item field value * (applies formatting) * * @access public * @param string $name Name of the field * @param mixed $value Value to set the field to * @return void */ public function SetField($name,$value) { $options = $this->GetFieldOptions($name); $parsed = $value; if ($value == '') { $parsed = NULL; } // kFormatter is always used, to make sure, that numeric value is converted to normal representation // according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why) $formatter = $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter'); /* @var $formatter kFormatter */ $parsed = $formatter->Parse($value, $name, $this); $this->SetDBField($name,$parsed); } /** * Sets current item field value * (doesn't apply formatting) * * @access public * @param string $name Name of the field * @param mixed $value Value to set the field to * @return void */ public function SetDBField($name,$value) { $this->FieldValues[$name] = $value; } /** * Set's field error, if pseudo passed not found then create it with message text supplied. * Don't overwrite existing pseudo translation. * * @param string $field * @param string $pseudo * @param string $error_label * @param Array $error_params * * @return bool * @access public */ public function SetError($field, $pseudo, $error_label = null, $error_params = null) { $this->initValidator(); return $this->validator->SetError($field, $pseudo, $error_label, $error_params); } /** * Removes error on field * * @param string $field * @access public */ public function RemoveError($field) { if ( !is_object($this->validator) ) { return ; } $this->validator->RemoveError($field); } /** * Returns error pseudo * * @param string $field * @return string */ public function GetErrorPseudo($field) { if ( !is_object($this->validator) ) { return ''; } return $this->validator->GetErrorPseudo($field); } /** * Return current item' field value by field name * (doesn't apply formatter) * * @param string $name field name to return * @return mixed * @access public */ public function GetDBField($name) { /*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) { $this->Application->Debugger->appendTrace(); }*/ return $this->FieldValues[$name]; } public function HasField($name) { return array_key_exists($name, $this->FieldValues); } public function GetFieldValues() { return $this->FieldValues; } /** * Sets item' fields corresponding to elements in passed $hash values. - * * The function sets current item fields to values passed in $hash, by matching $hash keys with field names * of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before actually setting the fields * - * @param Array $hash - * @param Array $skip_fields Optional param, field names in target object not to set, other fields will be set + * @param Array $hash Fields 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, $skip_fields = Array (), $set_fields = Array ()) + public function SetFieldsFromHash($hash, $set_fields = Array ()) { if ( !$set_fields ) { $set_fields = array_keys($hash); } + $skip_fields = $this->getRequestProtectedFields($hash); + if ( $skip_fields ) { $set_fields = array_diff($set_fields, $skip_fields); } $set_fields = array_intersect($set_fields, array_keys($this->Fields)); // used in formatter which work with multiple fields together foreach ($set_fields as $field_name) { $this->SetDirtyField($field_name, $hash[$field_name]); } // formats all fields using associated formatters foreach ($set_fields as $field_name) { $this->SetField($field_name, $hash[$field_name]); } } /** + * Returns fields, that are not allowed to be changed from request. + * + * @param array $fields_hash Fields hash. + * + * @return array + */ + protected function getRequestProtectedFields(array $fields_hash) + { + // by default don't allow changing ID or foreign key from request + $config = $this->getUnitConfig(); + + $fields = Array (); + $fields[] = $config->getIDField(); + + $parent_prefix = $config->getParentPrefix(); + + if ( $parent_prefix && $this->isLoaded() && !$this->Application->isAdmin ) { + // don't allow changing foreign key of existing item from request + $fields[] = $config->getForeignKey($parent_prefix); + } + + return $fields; + } + + /** * Sets object fields from $hash array * @param Array $hash - * @param Array|null $skip_fields * @param Array|null $set_fields * @return void * @access public */ - public function SetDBFieldsFromHash($hash, $skip_fields = Array (), $set_fields = Array ()) + public function SetDBFieldsFromHash($hash, $set_fields = Array ()) { if ( !$set_fields ) { $set_fields = array_keys($hash); } - if ( $skip_fields ) { - $set_fields = array_diff($set_fields, $skip_fields); - } - $set_fields = array_intersect($set_fields, array_keys($this->Fields)); foreach ($set_fields as $field_name) { $this->SetDBField($field_name, $hash[$field_name]); } } /** * Returns part of SQL WHERE clause identifying the record, ex. id = 25 * * @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method * @param Array $keys_hash alternative, then item id, keys hash to load item by * @see kDBItem::Load() * @see kDBItem::Update() * @see kDBItem::Delete() * @return string * @access protected */ protected function GetKeyClause($method = null, $keys_hash = null) { if ( !isset($keys_hash) ) { $keys_hash = Array ($this->IDField => $this->ID); } $ret = ''; foreach ($keys_hash as $field => $value) { $value_part = is_null($value) ? ' IS NULL' : ' = ' . $this->Conn->qstr($value); $ret .= '(' . (strpos($field, '.') === false ? '`' . $this->TableName . '`.' : '') . $field . $value_part . ') AND '; } return substr($ret, 0, -5); } /** * Loads item from the database by given id * * @access public * @param mixed $id item id of keys->values hash to load item by * @param string $id_field_name Optional parameter to load item by given Id field * @param bool $cachable cache this query result based on it's prefix serial * @return bool True if item has been loaded, false otherwise */ 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->getUnitConfig()->getIDField(); } 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; + $this->Loaded = true; } 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); } } } /** * Returns variable name, used to store pending file actions * * @return string * @access protected */ protected function _getPendingActionVariableName() { $window_id = $this->Application->GetTopmostWid($this->Prefix); return $this->Prefix . '_file_pending_actions' . $window_id; } /** * Returns pending actions * * @param mixed $id * @return Array * @access public */ public function getPendingActions($id = null) { if ( !isset($id) ) { $id = $this->GetID(); } $pending_actions = $this->Application->RecallVar($this->_getPendingActionVariableName()); $pending_actions = $pending_actions ? unserialize($pending_actions) : Array (); if ( is_numeric($id) ) { // filter by given/current id $ret = Array (); foreach ($pending_actions as $pending_action) { if ( $pending_action['id'] == $id ) { $ret[] = $pending_action; } } return $ret; } return $pending_actions; } /** * Sets new pending actions * * @param Array|null $new_pending_actions * @param mixed $id * @return void * @access public */ public function setPendingActions($new_pending_actions = null, $id = null) { if ( !isset($new_pending_actions) ) { $new_pending_actions = Array (); } if ( !isset($id) ) { $id = $this->GetID(); } $pending_actions = Array (); $old_pending_actions = $this->getPendingActions(true); if ( is_numeric($id) ) { // remove old actions for this id foreach ($old_pending_actions as $pending_action) { if ( $pending_action['id'] != $id ) { $pending_actions[] = $pending_action; } } // add new actions for this id $pending_actions = array_merge($pending_actions, $new_pending_actions); } else { $pending_actions = $new_pending_actions; } // save changes $var_name = $this->_getPendingActionVariableName(); if ( !$pending_actions ) { $this->Application->RemoveVar($var_name); } else { - $this->Application->StoreVar($var_name, serialize($pending_actions)); + $this->Application->StoreVar($var_name, serialize($this->sortPendingActions($pending_actions))); } } /** + * Sorts pending actions the way, that `delete` action will come before other actions. + * + * @param array $pending_actions Pending actions. + * + * @return array + */ + protected function sortPendingActions(array $pending_actions) + { + usort($pending_actions, array($this, 'comparePendingActions')); + + return $pending_actions; + } + + protected function comparePendingActions($pending_action_a, $pending_action_b) + { + if ( $pending_action_a['action'] == $pending_action_b['action'] ) { + return 0; + } + + return $pending_action_a['action'] == 'delete' ? -1 : 1; + } + + /** * 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 Array $update_fields * @param bool $system_update * @return bool * @access public */ public function Update($id = null, $update_fields = 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 = ''; $set_fields = isset($update_fields) ? $update_fields : array_keys($this->FieldValues); foreach ($set_fields as $field_name) { if ( $this->skipField($field_name) ) { continue; } $field_value = $this->FieldValues[$field_name]; if ( is_null($field_value) ) { if ( array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null'] ) { // "kFormatter::Parse" methods converts empty values to NULL and for // not-null fields they are replaced with default value here $field_value = $this->Fields[$field_name]['default']; } } $sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', '; } $sql = 'UPDATE ' . $this->TableName . ' SET ' . substr($sql, 0, -2) . ' WHERE ' . $this->GetKeyClause('update'); if ( $this->Conn->ChangeQuery($sql) === false ) { // there was and sql error $this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg()); return false; } $affected_rows = $this->Conn->getAffectedRows(); if ( !$system_update && ($affected_rows > 0) ) { $this->setModifiedFlag(ChangeLog::UPDATE); } $this->saveCustomFields(); $this->raiseEvent('OnAfterItemUpdate'); - if ( !isset($update_fields) ) { - $this->OriginalFieldValues = $this->FieldValues; - } - else { - foreach ($update_fields as $update_field) { - $this->OriginalFieldValues[$update_field] = $this->FieldValues[$update_field]; - } - } - + // Preserve OriginalFieldValues during recursive Update() method calls. $this->Loaded = true; if ( !$this->IsTempTable() ) { $this->Application->resetCounters($this->TableName); } return true; } /** * Validates given field * * @param string $field * @return bool * @access public */ public function ValidateField($field) { $this->initValidator(); return $this->validator->ValidateField($field); } /** * Validate all item fields based on * constraints set in each field options * in config * * @return bool * @access private */ public function Validate() { if ( $this->IgnoreValidation ) { return true; } $this->initValidator(); // will apply any custom validation to the item $this->raiseEvent('OnBeforeItemValidate'); if ( $this->validator->Validate() ) { // no validation errors $this->raiseEvent('OnAfterItemValidate'); return true; } return false; } /** * Check if item has errors * * @param Array $skip_fields fields to skip during error checking * @return bool */ public function HasErrors($skip_fields = Array ()) { if ( !is_object($this->validator) ) { return false; } return $this->validator->HasErrors($skip_fields); } /** * Check if value is set for required field * * @param string $field field name * @param Array $params field options from config * @return bool * @access public * @todo Find a way to get rid of direct call from kMultiLanguage::UpdateMasterFields method */ public function ValidateRequired($field, $params) { return $this->validator->ValidateRequired($field, $params); } /** * Return error message for field * * @param string $field * @param bool $force_escape * @return string * @access public */ public function GetErrorMsg($field, $force_escape = null) { if ( !is_object($this->validator) ) { return ''; } return $this->validator->GetErrorMsg($field, $force_escape); } /** * Returns field errors * * @return Array * @access public */ public function GetFieldErrors() { if ( !is_object($this->validator) ) { return Array (); } return $this->validator->GetFieldErrors(); } /** * Creates a record in the database table with current item' values * * @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE! * @param bool $system_create * @return bool * @access public */ public function Create($force_id = false, $system_create = false) { if (!$this->raiseEvent('OnBeforeItemCreate')) { return false; } // Validating fields before attempting to create record if (!$this->Validate()) { return false; } if (is_int($force_id)) { $this->FieldValues[$this->IDField] = $force_id; } elseif (!$force_id || !is_bool($force_id)) { $this->FieldValues[$this->IDField] = $this->generateID(); } $fields_sql = ''; $values_sql = ''; foreach ($this->FieldValues as $field_name => $field_value) { if ($this->skipField($field_name, $force_id)) { continue; } if (is_null($field_value)) { if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) { // "kFormatter::Parse" methods converts empty values to NULL and for // not-null fields they are replaced with default value here $values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']); } else { $values_sql .= $this->Conn->qstr($field_value); } } else { if (($field_name == $this->IDField) && ($field_value == 0) && !is_int($force_id)) { // don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value $values_sql .= 'DEFAULT'; } else { $values_sql .= $this->Conn->qstr($field_value); } } $fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement $values_sql .= ', '; } $sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ') VALUES (' . substr($values_sql, 0, -2) . ')'; //Executing the query and checking the result if ($this->Conn->ChangeQuery($sql) === false) { $this->SetError($this->IDField, 'sql_error', '#' . $this->Conn->getErrorCode() . ': ' . $this->Conn->getErrorMsg()); return false; } $insert_id = $this->Conn->getInsertID(); if ($insert_id == 0) { // insert into temp table (id is not auto-increment field) $insert_id = $this->FieldValues[$this->IDField]; } + + $temp_id = $this->GetID(); $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->raiseEvent('OnAfterItemCreate', null, array('temp_id' => $temp_id)); $this->Loaded = true; return true; } /** * Deletes the record from database * * @param int $id * @return bool * @access public */ public function Delete($id = null) { if ( isset($id) ) { $this->setID($id); } if ( !$this->raiseEvent('OnBeforeItemDelete') ) { return false; } $sql = 'DELETE FROM ' . $this->TableName . ' WHERE ' . $this->GetKeyClause('Delete'); $ret = $this->Conn->ChangeQuery($sql); $affected_rows = $this->Conn->getAffectedRows(); if ( $affected_rows > 0 ) { $this->setModifiedFlag(ChangeLog::DELETE); // will change affected rows, so get it before this line // something was actually deleted $this->raiseEvent('OnAfterItemDelete'); } if ( !$this->IsTempTable() ) { $this->Application->resetCounters($this->TableName); } return $ret; } public function PopulateMultiLangFields() { foreach ($this->Fields as $field => $options) { // master field is set only for CURRENT language $formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false; if ( ($formatter == 'kMultiLanguage') && isset($options['master_field']) && isset($options['error_field']) ) { // MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode, // we display ML fields directly so we set it back to itself, otherwise error won't be displayed unset( $this->Fields[$field]['error_field'] ); } } } /** * Sets new name for item in case if it is being copied in same table * * @param array $master Table data from TempHandler * @param int $foreign_key ForeignKey value to filter name check query by * @param string $title_field FieldName to alter, by default - TitleField of the prefix * @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name * @access public */ public function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s') { if ( !isset($title_field) ) { $title_field = $this->getUnitConfig()->getTitleField(); if ( !$title_field || isset($this->CalculatedFields[$title_field]) ) { return; } } $original_checked = false; $new_name = $this->GetDBField($title_field); 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 $sql = '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) ) { $sql .= ' AND ' . $foreign_key_field . ' = ' . $foreign_key; } $res = $this->Conn->GetOne($sql); // if not found in live table, check in temp table if applicable /*if ( $res === false && $this->Special == 'temp' ) { $sql = 'SELECT ' . $name_field . ' FROM ' . $this->Application->GetTempName($master['TableName']) . ' WHERE ' . $name_field . ' = ' . $this->Conn->qstr($new_name); $res = $this->Conn->GetOne($sql); }*/ $original_checked = true; } while ( $res !== false ); $this->SetDBField($title_field, $new_name); } protected function raiseEvent($name, $id = null, $additional_params = Array()) { $additional_params['id'] = isset($id) ? $id : $this->GetID(); $event = new kEvent($this->getPrefixSpecial() . ':' . $name, $additional_params); if ( is_object($this->parentEvent) ) { $event->MasterEvent = $this->parentEvent; } $this->Application->HandleEvent($event); return $event->status == kEvent::erSUCCESS; } /** * Set's new ID for item * * @param int $new_id * @access public */ public function setID($new_id) { $this->ID = $new_id; $this->SetDBField($this->IDField, $new_id); } /** * Generate and set new temporary id * * @access private */ public function setTempID() { $new_id = (int)$this->Conn->GetOne('SELECT MIN(' . $this->IDField . ') FROM ' . $this->TableName); if ( $new_id > 0 ) { $new_id = 0; } --$new_id; $this->Conn->Query('UPDATE ' . $this->TableName . ' SET `' . $this->IDField . '` = ' . $new_id . ' WHERE `' . $this->IDField . '` = ' . $this->GetID()); if ( $this->ShouldLogChanges(true) ) { // Updating TempId in ChangesLog, if changes are disabled $ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix) . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $changes = $this->Application->RecallVar($ses_var_name); $changes = $changes ? unserialize($changes) : Array (); if ( $changes ) { foreach ($changes as $key => $rec) { if ( $rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID() ) { // change log for record, that's ID was just updated -> update in change log record too $changes[$key]['ItemId'] = $new_id; } if ( $rec['MasterPrefix'] == $this->Prefix && $rec['MasterId'] == $this->GetID() ) { // master item id was changed $changes[$key]['MasterId'] = $new_id; } if ( in_array($this->Prefix, $rec['ParentPrefix']) && $rec['ParentId'][$this->Prefix] == $this->GetID() ) { // change log record of given item's sub item -> update changed id's in dependent fields $changes[$key]['ParentId'][$this->Prefix] = $new_id; if ( array_key_exists('DependentFields', $rec) ) { // these are fields from table of $rec['Prefix'] table! // when one of dependent fields goes into id field of it's parent item, that was changed $config = $this->Application->getUnitConfig($rec['Prefix']); $parent_table_key = $config->getParentTableKey($this->Prefix); if ( $parent_table_key == $this->IDField ) { $foreign_key = $config->getForeignKey($this->Prefix); $changes[$key]['DependentFields'][$foreign_key] = $new_id; } } } } } $this->Application->StoreVar($ses_var_name, serialize($changes)); } $this->SetID($new_id); } /** * Set's modification flag for main prefix of current prefix to true * * @param int $mode * @access private */ public function setModifiedFlag($mode = null) { $main_prefix = $this->Application->GetTopmostPrefix($this->Prefix); $this->Application->StoreVar($main_prefix . '_modified', '1', true); // true for optional if ($this->ShouldLogChanges(true)) { $this->LogChanges($main_prefix, $mode); if (!$this->IsTempTable()) { $handler = $this->Application->recallObject($this->Prefix . '_EventHandler'); /* @var $handler kDBEventHandler */ $ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $handler->SaveLoggedChanges($ses_var_name, $this->ShouldLogChanges()); } } } /** * Determines, that changes made to this item should be written to change log * * @param bool $log_changes * @return bool */ public function ShouldLogChanges($log_changes = null) { $config = $this->getUnitConfig(); if ( !isset($log_changes) ) { // specific logging mode no forced -> use global logging settings $log_changes = $config->getLogChanges() || $this->Application->ConfigValue('UseChangeLog'); } return $log_changes && !$config->getForceDontLogChanges(); } 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' => time(), '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) $config = $this->getUnitConfig(); $foreign_keys = $config->getForeignKey(null, 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 ( $config->getParentPrefix() ); $fields_hash['ParentId'][ $fields_hash['ParentPrefix'][0] ] = $this->getParentId('auto'); } $fields_hash['DependentFields'] = $dependent_fields; // works only, when main item is present in url, when sub-item is changed $master_id = $this->Application->GetVar($main_prefix . '_id'); if ( $master_id === false ) { // works in case of we are not editing topmost item, when sub-item is created/updated/deleted $master_id = $this->getParentId('auto', true); } $fields_hash['MasterId'] = $master_id; } switch ( $mode ) { case ChangeLog::UPDATE: $to_save = array_merge($this->GetTitleField(), $this->GetChangedFields()); break; case ChangeLog::CREATE: $to_save = $this->GetTitleField(); break; case ChangeLog::DELETE: $to_save = array_merge($this->GetTitleField(), $this->GetRealFields()); break; default: $to_save = Array (); break; } $fields_hash['Changes'] = serialize($to_save); $changes[] = $fields_hash; $this->Application->StoreVar($ses_var_name, serialize($changes)); } /** * Returns current item parent's ID * * @param string $parent_prefix * @param bool $top_most return topmost parent, when used * @return int * @access public */ public function getParentId($parent_prefix, $top_most = false) { $current_id = $this->GetID(); $current_prefix = $this->Prefix; $current_config = $this->Application->getUnitConfig($current_prefix); if ( $parent_prefix == 'auto' ) { $parent_prefix = $current_config->getParentPrefix(); } if ( !$parent_prefix ) { return $current_id; } do { // field in this table $foreign_key = $current_config->getForeignKey($parent_prefix); // get foreign key value for $current_prefix if ( $current_prefix == $this->Prefix ) { $foreign_key_value = $this->GetDBField($foreign_key); } else { $table_name = $current_config->getTableName(); if ( $this->IsTempTable() ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $current_prefix); } $sql = 'SELECT ' . $foreign_key . ' FROM ' . $table_name . ' WHERE ' . $current_config->getIDField() . ' = ' . $current_id; $foreign_key_value = $this->Conn->GetOne($sql); } // field in parent table $parent_table_key = $current_config->getParentTableKey($parent_prefix); $parent_config = $this->Application->getUnitConfig($parent_prefix); $parent_id_field = $parent_config->getIDField(); $parent_table_name = $parent_config->getTableName(); 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 id field $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; $current_config = $this->Application->getUnitConfig($current_prefix); if ( !$top_most ) { break; } } while ( $parent_prefix = $current_config->getParentPrefix() ); return $current_id; } /** * Returns title field (if any) * * @return Array */ public function GetTitleField() { $title_field = $this->getUnitConfig()->getTitleField(); 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('Query method is called in class ' . get_class($this) . ' for prefix ' . $this->getPrefixSpecial() . ''); } protected function saveCustomFields() { if ( !$this->customFields || $this->inCloning ) { return true; } $cdata_key = rtrim($this->Prefix . '-cdata.' . $this->Special, '.'); $cdata = $this->Application->recallObject($cdata_key, null, Array ('skip_autoload' => true)); /* @var $cdata kDBItem */ $resource_id = $this->GetDBField('ResourceId'); $cdata->Load($resource_id, 'ResourceId'); $cdata->SetDBField('ResourceId', $resource_id); $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); foreach ($this->customFields as $custom_id => $custom_name) { $force_primary = $cdata->GetFieldOption('cust_' . $custom_id, 'force_primary'); if ( $force_primary ) { $cdata->SetDBField($ml_formatter->LangFieldName('cust_' . $custom_id, true), $this->GetDBField('cust_' . $custom_name)); } else { foreach ($languages as $language_id) { $cdata->SetDBField('l' . $language_id . '_cust_' . $custom_id, $this->GetDBField('l' . $language_id . '_cust_' . $custom_name)); } } } return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create(); } /** * Returns specified field value from all selected rows. * Don't affect current record index * * @param string $field * @param bool $formatted * @param string $format * @return Array */ public function GetCol($field, $formatted = false, $format = null) { if ($formatted) { return Array (0 => $this->GetField($field, $format)); } return Array (0 => $this->GetDBField($field)); } /** * Set's loaded status of object * * @param bool $is_loaded * @access public * @todo remove this method, since item can't be marked as loaded externally */ public function setLoaded($is_loaded = true) { $this->Loaded = $is_loaded; } /** * Returns parser parameters, used to identify this object in the e-mail template. * * @param array $merge_with Original send params to merge with. * * @return array */ public function getEmailParams(array $merge_with = array()) { $merge_with['item_id'] = $this->GetID(); $merge_with['PrefixSpecial'] = $this->getPrefixSpecial(); return $merge_with; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/db/cat_event_handler.php =================================================================== --- branches/5.3.x/core/kernel/db/cat_event_handler.php (revision 16110) +++ branches/5.3.x/core/kernel/db/cat_event_handler.php (revision 16111) @@ -1,3105 +1,3101 @@ Array ('self' => 'add|edit|advanced:import'), 'OnResetSettings' => Array ('self' => 'add|edit|advanced:import'), 'OnBeforeDeleteOriginal' => Array ('self' => 'edit|advanced:approve'), 'OnAfterDeleteOriginal' => Array ('self' => 'edit|advanced:approve'), 'OnCopy' => Array ('self' => true), 'OnDownloadFile' => Array ('self' => 'view'), 'OnCancelAction' => Array ('self' => true), 'OnItemBuild' => Array ('self' => true), 'OnMakeVote' => Array ('self' => true), 'OnReviewHelpful' => Array ('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Load item if id is available * * @param kEvent $event * @return void * @access protected */ protected function LoadItem(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $id = $this->getPassedID($event); if ( $object->Load($id) ) { $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_id', $object->GetID()); $use_pending_editing = $event->getUnitConfig()->getUsePendingEditing(); if ( $use_pending_editing && $event->Special != 'original' ) { $this->Application->SetVar($event->Prefix . '.original_id', $object->GetDBField('OrgId')); } } else { $object->setID($id); } } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( !$this->Application->isAdmin ) { if ( $event->Name == 'OnSetSortingDirect' ) { // allow sorting on front event without view permission return true; } } if ( $event->Name == 'OnExport' ) { // save category_id before doing export $this->Application->LinkVar('m_cat_id'); } if ( in_array($event->Name, $this->_getMassPermissionEvents()) ) { $items = $this->_getPermissionCheckInfo($event); $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ if ( ($event->Name == 'OnSave') && array_key_exists(0, $items) ) { // adding new item (ID = 0) $perm_value = $perm_helper->AddCheckPermission($items[0]['CategoryId'], $event->Prefix) > 0; } else { // leave only items, that can be edited $ids = Array (); $check_method = in_array($event->Name, Array ('OnMassDelete', 'OnCut')) ? 'DeleteCheckPermission' : 'ModifyCheckPermission'; foreach ($items as $item_id => $item_data) { if ( $perm_helper->$check_method($item_data['CreatedById'], $item_data['CategoryId'], $event->Prefix) > 0 ) { $ids[] = $item_id; } } if ( !$ids ) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } $perm_value = true; $event->setEventParam('ids', $ids); // will be used later by "kDBEventHandler::StoreSelectedIDs" method } return $perm_helper->finalizePermissionCheck($event, $perm_value); } $export_events = Array ('OnSaveSettings', 'OnResetSettings', 'OnExportBegin'); if ( in_array($event->Name, $export_events) ) { // when import settings before selecting target import category return $this->Application->CheckPermission('in-portal:main_import.view'); } if ( $event->Name == 'OnProcessSelected' ) { if ( $this->Application->RecallVar('dst_field') == 'ImportCategory' ) { // when selecting target import category return $this->Application->CheckPermission('in-portal:main_import.view'); } } return parent::CheckPermission($event); } /** * Returns events, that require item-based (not just event-name based) permission check * * @return Array */ function _getMassPermissionEvents() { return Array ( 'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove', 'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown', 'OnCut', ); } /** * Returns category item IDs, that require permission checking * * @param kEvent $event * @return string */ function _getPermissionCheckIDs($event) { if ($event->Name == 'OnSave') { $selected_ids = implode(',', $this->getSelectedIDs($event, true)); if (!$selected_ids) { $selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave) } } else { // OnEdit, OnMassDelete events, when items are checked in grid $selected_ids = implode(',', $this->StoreSelectedIDs($event)); } return $selected_ids; } /** * Returns information used in permission checking * * @param kEvent $event * @return Array */ function _getPermissionCheckInfo($event) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ // when saving data from temp table to live table check by data from temp table $item_ids = $this->_getPermissionCheckIDs($event); $items = $perm_helper->GetCategoryItemData($event->Prefix, $item_ids, $event->Name == 'OnSave'); if (!$items) { // when item not present in temp table, then permission is not checked, because there are no data in db to check $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); list ($id, $fields_hash) = each($items_info); if (array_key_exists('CategoryId', $fields_hash)) { $item_category = $fields_hash['CategoryId']; } else { $item_category = $this->Application->GetVar('m_cat_id'); } $items[$id] = Array ( 'CreatedById' => $this->Application->RecallVar('use_id'), 'CategoryId' => $item_category, ); } return $items; } /** * Add selected items to clipboard with mode = COPY (CLONE) * * @param kEvent $event * @return void * @access protected */ protected function OnCopy($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'copy', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Add selected items to clipboard with mode = CUT * * @param kEvent $event * @return void * @access protected */ protected function OnCut($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Checks permission for OnPaste event * * @param kEvent $event * @return bool */ function _checkPastePermission($event) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $category_id = $this->Application->GetVar('m_cat_id'); if ($perm_helper->AddCheckPermission($category_id, $event->Prefix) == 0) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } return true; } /** * Performs category item paste * * @param kEvent $event * @return void * @access protected */ protected function OnPaste($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) || !$this->_checkPastePermission($event) ) { $event->status = kEvent::erFAIL; return; } $clipboard_data = $event->getEventParam('clipboard_data'); if ( !$clipboard_data['cut'] && !$clipboard_data['copy'] ) { return; } if ( $clipboard_data['copy'] ) { $temp = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp kTempTablesHandler */ $this->Application->SetVar('ResetCatBeforeClone', 1); // used in "kCatDBEventHandler::OnBeforeClone" $temp->CloneItems($event->Prefix, $event->Special, $clipboard_data['copy']); } if ( $clipboard_data['cut'] ) { $object = $this->Application->recallObject($event->getPrefixSpecial() . '.item', $event->Prefix, Array ('skip_autoload' => true)); /* @var $object kCatDBItem */ foreach ($clipboard_data['cut'] as $id) { $object->Load($id); $object->MoveToCat(); } } } /** * Deletes all selected items. * Automatically recurse into sub-items using temp handler, and deletes sub-items * by calling its Delete method if sub-item has AutoDelete set to true in its config file * * @param kEvent $event * @return void * @access protected */ protected function OnMassDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $ids = $this->StoreSelectedIDs($event); $to_delete = Array (); $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ( $recycle_bin ) { $rb = $this->Application->recallObject('c.recycle', NULL, array ('skip_autoload' => true)); /* @var $rb CategoriesItem */ $rb->Load($recycle_bin); $object = $this->Application->recallObject($event->Prefix . '.recycleitem', NULL, Array ('skip_autoload' => true)); /* @var $object kCatDBItem */ foreach ($ids as $id) { $object->Load($id); if ( preg_match('/^' . preg_quote($rb->GetDBField('ParentPath'), '/') . '/', $object->GetDBField('ParentPath')) ) { $to_delete[] = $id; continue; } $object->MoveToCat($recycle_bin); } $ids = $to_delete; } $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $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); } /** * Return type clauses for list bulding on front * * @param kEvent $event * @return Array */ function getTypeClauses($event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); $except_types = $event->getEventParam('except'); $except_types = $except_types ? explode(',', $except_types) : Array (); $type_clauses = Array(); $user_id = $this->Application->RecallVar('user_id'); $owner_field = $this->getOwnerField($event->Prefix); $type_clauses['my_items']['include'] = '%1$s.'.$owner_field.' = '.$user_id; $type_clauses['my_items']['except'] = '%1$s.'.$owner_field.' <> '.$user_id; $type_clauses['my_items']['having_filter'] = false; $type_clauses['pick']['include'] = '%1$s.EditorsPick = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['pick']['except'] = '%1$s.EditorsPick! = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['pick']['having_filter'] = false; $type_clauses['hot']['include'] = '`IsHot` = 1 AND PrimaryCat = 1'; $type_clauses['hot']['except'] = '`IsHot`! = 1 AND PrimaryCat = 1'; $type_clauses['hot']['having_filter'] = true; $type_clauses['pop']['include'] = '`IsPop` = 1 AND PrimaryCat = 1'; $type_clauses['pop']['except'] = '`IsPop`! = 1 AND PrimaryCat = 1'; $type_clauses['pop']['having_filter'] = true; $type_clauses['new']['include'] = '`IsNew` = 1 AND PrimaryCat = 1'; $type_clauses['new']['except'] = '`IsNew`! = 1 AND PrimaryCat = 1'; $type_clauses['new']['having_filter'] = true; $type_clauses['displayed']['include'] = ''; $displayed = $this->Application->GetVar($event->Prefix.'_displayed_ids'); if ($displayed) { $id_field = $event->getUnitConfig()->getIDField(); $type_clauses['displayed']['except'] = '%1$s.'.$id_field.' NOT IN ('.$displayed.')'; } else { $type_clauses['displayed']['except'] = ''; } $type_clauses['displayed']['having_filter'] = false; if (in_array('search', $types) || in_array('search', $except_types)) { $event_mapping = Array ( 'simple' => 'OnSimpleSearch', 'subsearch' => 'OnSubSearch', 'advanced' => 'OnAdvancedSearch' ); $keywords = $event->getEventParam('keyword_string'); $type = $this->Application->GetVar('search_type', 'simple'); if ( $keywords ) { // processing keyword_string param of ListProducts tag $this->Application->SetVar('keywords', $keywords); $type = 'simple'; } $search_event = $event_mapping[$type]; $this->$search_event($event); $object = $event->getObject(); /* @var $object kDBList */ $search_sql = ' FROM ' . TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search search_result JOIN %1$s ON %1$s.ResourceId = search_result.ResourceId'; $sql = str_replace('FROM %1$s', $search_sql, $object->GetPlainSelectSQL()); $object->SetSelectSQL($sql); $object->addCalculatedField('Relevance', 'search_result.Relevance'); $type_clauses['search']['include'] = 'PrimaryCat = 1 AND ('.TABLE_PREFIX.'Categories.Status = '.STATUS_ACTIVE.')'; $type_clauses['search']['except'] = 'PrimaryCat = 1 AND ('.TABLE_PREFIX.'Categories.Status = '.STATUS_ACTIVE.')'; $type_clauses['search']['having_filter'] = false; } if (in_array('related', $types) || in_array('related', $except_types)) { $related_to = $event->getEventParam('related_to'); if (!$related_to) { $related_prefix = $event->Prefix; } else { $sql = 'SELECT Prefix FROM '.TABLE_PREFIX.'ItemTypes WHERE ItemName = '.$this->Conn->qstr($related_to); $related_prefix = $this->Conn->GetOne($sql); } $rel_table = $this->Application->getUnitConfig('rel')->getTableName(); $item_type = (int)$event->getUnitConfig()->getItemType(); if ($item_type == 0) { trigger_error('ItemType not defined for prefix ' . $event->Prefix . '', E_USER_WARNING); } // process case, then this list is called inside another list $prefix_special = $event->getEventParam('PrefixSpecial'); if (!$prefix_special) { $prefix_special = $this->Application->Parser->GetParam('PrefixSpecial'); } $id = false; if ($prefix_special !== false) { $processed_prefix = $this->Application->processPrefix($prefix_special); if ($processed_prefix['prefix'] == $related_prefix) { // printing related categories within list of items (not on details page) $list = $this->Application->recallObject($prefix_special); /* @var $list kDBList */ $id = $list->GetID(); } } if ($id === false) { // printing related categories for single item (possibly on details page) if ($related_prefix == 'c') { $id = $this->Application->GetVar('m_cat_id'); } else { $id = $this->Application->GetVar($related_prefix . '_id'); } } $p_item = $this->Application->recallObject($related_prefix.'.current', NULL, Array('skip_autoload' => true)); /* @var $p_item kCatDBItem */ $p_item->Load( (int)$id ); $p_resource_id = $p_item->GetDBField('ResourceId'); $sql = 'SELECT SourceId, TargetId FROM '.$rel_table.' WHERE (Enabled = 1) AND ( (Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (Type = 1 AND ( (SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.') ) ) )'; $related_ids_array = $this->Conn->Query($sql); $related_ids = Array(); foreach ($related_ids_array as $record) { $related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ]; } if (count($related_ids) > 0) { $type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).') AND PrimaryCat = 1'; $type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).') AND PrimaryCat = 1'; } else { $type_clauses['related']['include'] = '0'; $type_clauses['related']['except'] = '1'; } $type_clauses['related']['having_filter'] = false; } if (in_array('favorites', $types) || in_array('favorites', $except_types)) { $sql = 'SELECT ResourceId FROM ' . $this->Application->getUnitConfig('fav')->getTableName() . ' WHERE PortalUserId = '.$this->Application->RecallVar('user_id'); $favorite_ids = $this->Conn->GetCol($sql); if ($favorite_ids) { $type_clauses['favorites']['include'] = '%1$s.ResourceId IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1'; $type_clauses['favorites']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $favorite_ids).') AND PrimaryCat = 1'; } else { $type_clauses['favorites']['include'] = 0; $type_clauses['favorites']['except'] = 1; } $type_clauses['favorites']['having_filter'] = false; } return $type_clauses; } /** * Returns SQL clause, that will help to select only data from specified category & it's children * * @param int $category_id * @return string */ function getCategoryLimitClause($category_id) { if (!$category_id) { return false; } $tree_indexes = $this->Application->getTreeIndex($category_id); if (!$tree_indexes) { // id of non-existing category was given return 'FALSE'; } return TABLE_PREFIX.'Categories.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight']; } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kCatDBList */ // add category filter if needed if ($event->Special != 'showall' && $event->Special != 'user') { if ( (string)$event->getEventParam('parent_cat_id') !== '' ) { $parent_cat_id = $event->getEventParam('parent_cat_id'); } else { $parent_cat_id = $this->Application->GetVar('c_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } if ("$parent_cat_id" == '0') { // replace "0" category with "Content" category id (this way template $parent_cat_id = $this->Application->getBaseCategory(); } if ((string)$parent_cat_id != 'any') { if ($event->getEventParam('recursive')) { $filter_clause = $this->getCategoryLimitClause($parent_cat_id); if ($filter_clause !== false) { $object->addFilter('category_filter', $filter_clause); } $object->addFilter('primary_filter', 'PrimaryCat = 1'); } else { $object->addFilter('category_filter', TABLE_PREFIX.'CategoryItems.CategoryId = '.$parent_cat_id ); } } else { $object->addFilter('primary_filter', 'PrimaryCat = 1'); } } else { $object->addFilter('primary_filter', 'PrimaryCat = 1'); // if using recycle bin don't show items from there $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ($recycle_bin) { $object->addFilter('recyclebin_filter', TABLE_PREFIX.'CategoryItems.CategoryId <> '.$recycle_bin); } } if ($event->Special == 'user') { $editable_user = $this->Application->GetVar('u_id'); $object->addFilter('owner_filter', '%1$s.'.$this->getOwnerField($event->Prefix).' = '.$editable_user); } $this->applyViewPermissionFilter($object); $types = $event->getEventParam('types'); $this->applyItemStatusFilter($object, $types); $except_types = $event->getEventParam('except'); $type_clauses = $this->getTypeClauses($event); $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $search_helper->SetComplexFilter($event, $type_clauses, $types, $except_types); } /** * Adds filter, that uses *.VIEW permissions to determine if an item should be shown to a user. * * @param kCatDBList $object Object. * * @return void * @access protected */ protected function applyViewPermissionFilter(kCatDBList $object) { if ( !$this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) { return; } if ( $this->Application->RecallVar('user_id') == USER_ROOT ) { // for "root" CATEGORY.VIEW permission is checked for items lists too $view_perm = 1; } else { // for any real user item list view permission is checked instead of CATEGORY.VIEW $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ list ($view_perm, $view_filter) = $count_helper->GetPermissionClause($object->Prefix, 'perm'); $object->addFilter('perm_filter2', $view_filter); } $object->addFilter('perm_filter', 'perm.PermId = ' . $view_perm); } /** * Adds filter that filters out items with non-required statuses * * @param kDBList $object * @param string $types */ function applyItemStatusFilter(&$object, $types) { // Link1 (before modifications) [Status = 1, OrgId = NULL], Link2 (after modifications) [Status = -2, OrgId = Link1_ID] $pending_editing = $object->getUnitConfig()->getUsePendingEditing(); if (!$this->Application->isAdminUser) { $types = explode(',', $types); if (in_array('my_items', $types)) { $allow_statuses = Array (STATUS_ACTIVE, STATUS_PENDING, STATUS_PENDING_EDITING); $object->addFilter('status_filter', '%1$s.Status IN ('.implode(',', $allow_statuses).')'); if ($pending_editing) { $user_id = $this->Application->RecallVar('user_id'); $this->applyPendingEditingFilter($object, $user_id); } } else { $object->addFilter('status_filter', '(%1$s.Status = ' . STATUS_ACTIVE . ') AND (' . TABLE_PREFIX . 'Categories.Status = ' . STATUS_ACTIVE . ')'); if ($pending_editing) { // if category item uses pending editing abilities, then in no cases show pending copies on front $object->addFilter('original_filter', '%1$s.OrgId = 0 OR %1$s.OrgId IS NULL'); } } } else { if ($pending_editing) { $this->applyPendingEditingFilter($object); } } } /** * Adds filter, that removes live items if they have pending editing copies * * @param kDBList $object * @param int $user_id */ function applyPendingEditingFilter(&$object, $user_id = NULL) { $sql = 'SELECT OrgId FROM '.$object->TableName.' WHERE Status = '.STATUS_PENDING_EDITING.' AND OrgId IS NOT NULL'; if (isset($user_id)) { $owner_field = $this->getOwnerField($object->Prefix); $sql .= ' AND '.$owner_field.' = '.$user_id; } $pending_ids = $this->Conn->GetCol($sql); if ($pending_ids) { $object->addFilter('no_original_filter', '%1$s.'.$object->IDField.' NOT IN ('.implode(',', $pending_ids).')'); } } /** * Adds calculates fields for item statuses * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { $this->prepareItemStatuses($event); $object->addCalculatedField('CachedNavbar', 'l' . $this->Application->GetVar('m_lang') . '_CachedNavbar'); if ( $event->Special == 'export' || $event->Special == 'import' ) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->prepareExportColumns($event); } } /** * Creates calculated fields for all item statuses based on config settings * * @param kEvent $event */ function prepareItemStatuses($event) { $object = $event->getObject( Array('skip_autoload' => true) ); $property_map = $event->getUnitConfig()->getItemPropertyMappings(); if (!$property_map) { return ; } // new items $object->addCalculatedField('IsNew', ' IF(%1$s.NewItem = 2, IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - ' . $this->Application->ConfigValue($property_map['NewDays']) . '*3600*24), 1, 0), %1$s.NewItem )'); // hot items (cache updated every hour) if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $serial_name = $this->Application->incrementCacheSerial($event->Prefix, NULL, false); $hot_limit = $this->Application->getCache($property_map['HotLimit'] . '[%' . $serial_name . '%]'); } else { $hot_limit = $this->Application->getDBCache($property_map['HotLimit']); } if ($hot_limit === false) { $hot_limit = $this->CalculateHotLimit($event); } $object->addCalculatedField('IsHot', ' IF(%1$s.HotItem = 2, IF(%1$s.'.$property_map['ClickField'].' >= '.$hot_limit.', 1, 0), %1$s.HotItem )'); // popular items $object->addCalculatedField('IsPop', ' IF(%1$s.PopItem = 2, IF(%1$s.CachedVotesQty >= ' . $this->Application->ConfigValue($property_map['MinPopVotes']) . ' AND %1$s.CachedRating >= ' . $this->Application->ConfigValue($property_map['MinPopRating']) . ', 1, 0), %1$s.PopItem)'); } /** * Calculates hot limit for current item's table * * @param kEvent $event * @return float * @access protected */ protected function CalculateHotLimit($event) { $config = $event->getUnitConfig(); $property_map = $config->getItemPropertyMappings(); if ( !$property_map ) { return 0.00; } $click_field = $property_map['ClickField']; $last_hot = $this->Application->ConfigValue($property_map['MaxHotNumber']) - 1; $sql = 'SELECT ' . $click_field . ' FROM ' . $config->getTableName() . ' ORDER BY ' . $click_field . ' DESC LIMIT ' . $last_hot . ', 1'; $res = $this->Conn->GetCol($sql); $hot_limit = (double)array_shift($res); if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $serial_name = $this->Application->incrementCacheSerial($event->Prefix, NULL, false); $this->Application->setCache($property_map['HotLimit'] . '[%' . $serial_name . '%]', $hot_limit); } else { $this->Application->setDBCache($property_map['HotLimit'], $hot_limit, 3600); } return $hot_limit; } /** * Moves item to preferred category, updates item hits * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $object = $event->getObject(); /* @var $object kCatDBItem */ // update hits field $config = $event->getUnitConfig(); $property_map = $config->getUserProfileMapping(); if ( $property_map ) { $click_field = $property_map['ClickField']; if ( $this->Application->isAdminUser && ($this->Application->GetVar($click_field . '_original') !== false) && floor($this->Application->GetVar($click_field . '_original')) != $object->GetDBField($click_field) ) { $sql = 'SELECT MAX(' . $click_field . ') FROM ' . $config->getTableName() . ' WHERE FLOOR(' . $click_field . ') = ' . $object->GetDBField($click_field); $hits = ($res = $this->Conn->GetOne($sql)) ? $res + 0.000001 : $object->GetDBField($click_field); $object->SetDBField($click_field, $hits); } } // change category $target_category = $object->GetDBField('CategoryId'); if ( $object->GetOriginalField('CategoryId') != $target_category ) { $object->MoveToCat($target_category); } } /** * Occurs after loading item, 'id' parameter * allows to get id of item that was loaded * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $special = substr($event->Special, -6); $object = $event->getObject(); /* @var $object kCatDBItem */ if ( $special == 'import' || $special == 'export' ) { $image_data = $object->getPrimaryImageData(); if ( $image_data ) { $thumbnail_image = $image_data[$image_data['LocalThumb'] ? 'ThumbPath' : 'ThumbUrl']; if ( $image_data['SameImages'] ) { $full_image = ''; } else { $full_image = $image_data[$image_data['LocalImage'] ? 'LocalPath' : 'Url']; } $object->SetDBField('ThumbnailImage', $thumbnail_image); $object->SetDBField('FullImage', $full_image); $object->SetDBField('ImageAlt', $image_data['AltName']); } } // substituting pending status value for pending editing if ( $object->HasField('OrgId') && $object->GetDBField('OrgId') > 0 && $object->GetDBField('Status') == -2 ) { $new_options = Array (); $options = $object->GetFieldOption('Status', 'options', false, Array ()); foreach ($options as $key => $val) { if ( $key == 2 ) { $key = -2; } $new_options[$key] = $val; } $object->SetFieldOption('Status', 'options', $new_options); } if ( !$this->Application->isAdmin ) { // linking existing images for item with virtual fields $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $image_helper->LoadItemImages($object); // linking existing files for item with virtual fields $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->LoadItemFiles($object); } if ( $object->isVirtualField('MoreCategories') ) { // set item's additional categories to virtual field (used in editing) $item_categories = $this->getItemCategories($object->GetDBField('ResourceId')); $object->SetDBField('MoreCategories', $item_categories ? '|' . implode('|', $item_categories) . '|' : ''); } } /** * Occurs after updating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { parent::OnAfterItemUpdate($event); $this->CalculateHotLimit($event); if ( substr($event->Special, -6) == 'import' ) { $this->setCustomExportColumns($event); } $object = $event->getObject(); /* @var $object kCatDBItem */ if ( !$this->Application->isAdmin ) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ // process image upload in virtual fields $image_helper->SaveItemImages($object); $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ // process file upload in virtual fields $file_helper->SaveItemFiles($object); if ( $event->Special != '-item' ) { // don't touch categories during cloning $this->processAdditionalCategories($object, 'update'); } } $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ( $this->Application->isAdminUser && $recycle_bin ) { $sql = 'SELECT CategoryId FROM ' . $this->Application->getUnitConfig('ci')->getTableName() . ' WHERE ItemResourceId = ' . $object->GetDBField('ResourceId') . ' AND PrimaryCat = 1'; $primary_category = $this->Conn->GetOne($sql); if ( $primary_category == $recycle_bin ) { $event->CallSubEvent('OnAfterItemDelete'); } } if ( $object->GetChangedFields() ) { $now = time(); $object->SetDBField('Modified_date', $now); $object->SetDBField('Modified_time', $now); $object->SetDBField('ModifiedById', $this->Application->RecallVar('user_id')); } } /** * Sets values for import process * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $object = $event->getObject(); /* @var $object kCatDBItem */ if ( substr($event->Special, -6) == 'import' ) { $this->setCustomExportColumns($event); } $object->assignPrimaryCategory(); if ( !$this->Application->isAdmin ) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ // process image upload in virtual fields $image_helper->SaveItemImages($object); $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ // process file upload in virtual fields $file_helper->SaveItemFiles($object); if ( $event->Special != '-item' ) { // don't touch categories during cloning $this->processAdditionalCategories($object, 'create'); } } } /** * Make record to search log * * @param string $keywords * @param int $search_type 0 - simple search, 1 - advanced search */ function saveToSearchLog($keywords, $search_type = 0) { // don't save keywords for each module separately, just one time // static variable can't help here, because each module uses it's own class instance ! if (!$this->Application->GetVar('search_logged')) { $sql = 'UPDATE '.TABLE_PREFIX.'SearchLogs SET Indices = Indices + 1 WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search $this->Conn->Query($sql); if ($this->Conn->getAffectedRows() == 0) { $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLogs'); } $this->Application->SetVar('search_logged', 1); } } /** * Makes simple search for category items * based on keywords string * * @param kEvent $event */ function OnSimpleSearch($event) { $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('HTTPQuery'); /* @var $query_object kHTTPQuery */ $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if(!isset($query_object->Get['keywords']) && !isset($query_object->Post['keywords']) && $this->Conn->Query($sql)) { return; // used when navigating by pages or changing sorting in search results } if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $this->Application->SetVar('keywords_too_short', 1); return; // if no or too short keyword entered, doing nothing } $this->Application->StoreVar('keywords', $keywords); $this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ $config = $event->getUnitConfig(); $this->Application->SetVar($event->getPrefixSpecial().'_Page', 1); $lang = $this->Application->GetVar('m_lang'); $items_table = $config->getTableName(); $module_name = $this->Application->findModule('Var', $event->Prefix, 'Name'); $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('confs')->getTableName() . ' WHERE ModuleName = ' . $this->Conn->qstr($module_name) . ' AND SimpleSearch = 1'; $search_config = $this->Conn->Query($sql, 'FieldName'); $field_list = array_keys($search_config); $join_clauses = Array(); // field processing $weight_sum = 0; $alias_counter = 0; $custom_fields = $config->getCustomFields(); if ($custom_fields) { $custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; } // what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table)) $search_config_map = Array(); foreach ($field_list as $key => $field) { $local_table = TABLE_PREFIX.$search_config[$field]['TableName']; $weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if ( !$search_config[$field]['CustomFieldId'] && $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) { $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $field_list[$key] = 'l'.$lang.'_'.$field; if (!isset($search_config[$field]['ForeignField'])) { $field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary']; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } } // processing fields from other tables $foreign_field = $search_config[$field]['ForeignField']; if ( $foreign_field ) { $exploded = explode(':', $foreign_field, 2); if ($exploded[0] == 'CALC') { // ignoring having type clauses in simple search unset($field_list[$key]); continue; } else { $multi_lingual = false; if ($exploded[0] == 'MULTI') { $multi_lingual = true; $foreign_field = $exploded[1]; } $exploded = explode('.', $foreign_field); // format: table.field_name $foreign_table = TABLE_PREFIX.$exploded[0]; $alias_counter++; $alias = 't'.$alias_counter; if ($multi_lingual) { $field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1]; $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $search_config_map[ $field_list[$key] ] = $field; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } else { $field_list[$key] = $alias.'.'.$exploded[1]; $search_config_map[ $field_list[$key] ] = $field; } $join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']); $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); $join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($search_config[$field]['CustomFieldId']) { $local_table = 'custom_data'; // search by custom field value on current language $custom_field_id = array_search($field_list[$key], $custom_fields); $field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id; // search by custom field value on primary language $field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } $field_list[$key] = $local_table.'.'.$field_list[$key]; $search_config_map[ $field_list[$key] ] = $field; } } // keyword string processing $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $where_clause = Array (); foreach ($field_list as $field) { if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) { // local real field $filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false); if ($filter_data) { $where_clause[] = $filter_data['value']; } } elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) { $custom_field_name = 'cust_' . $search_config_map[$field]; $filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false); if ($filter_data) { $where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']); } } else { $where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field)); } } $where_clause = '((' . implode(') OR (', $where_clause) . '))'; // 2 braces for next clauses, see below! $search_scope = $this->Application->GetVar('search_scope'); if ($search_scope == 'category') { $category_id = $this->Application->GetVar('m_cat_id'); $category_filter = $this->getCategoryLimitClause($category_id); if ($category_filter !== false) { $join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'CategoryItems ON '.TABLE_PREFIX.'CategoryItems.ItemResourceId = '.$items_table.'.ResourceId'; $join_clauses[] = ' LEFT JOIN '.TABLE_PREFIX.'Categories ON '.TABLE_PREFIX.'Categories.CategoryId = '.TABLE_PREFIX.'CategoryItems.CategoryId'; $where_clause = '('.$this->getCategoryLimitClause($category_id).') AND '.$where_clause; } } $where_clause = $where_clause . ' AND (' . $items_table . '.Status = ' . STATUS_ACTIVE . ')'; if ($event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild') { $sub_search_ids = $event->MasterEvent->getEventParam('ResultIds'); if ( $sub_search_ids !== false ) { if ( $sub_search_ids ) { $where_clause .= 'AND (' . $items_table . '.ResourceId IN (' . implode(',', $sub_search_ids) . '))'; } else { $where_clause .= 'AND FALSE'; } } } // making relevance clause $positive_words = $search_helper->getPositiveKeywords($keywords); $this->Application->StoreVar('highlight_keywords', serialize($positive_words)); $relevance_parts = Array(); reset($search_config); foreach ($positive_words as $keyword_index => $positive_word) { $positive_word = $search_helper->transformWildcards($positive_word); $positive_words[$keyword_index] = $this->Conn->escape($positive_word); } foreach ($field_list as $field) { if (!array_key_exists($field, $search_config_map)) { $map_key = $search_config_map[$items_table . '.' . $field]; } else { $map_key = $search_config_map[$field]; } $config_elem = $search_config[ $map_key ]; $weight = $config_elem['Priority']; // search by whole words only ([[:<:]] - word boundary) /*$relevance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.implode(' ', $positive_words).')[[:>:]]", '.$weight.', 0)'; foreach ($positive_words as $keyword) { $relevance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.$keyword.')[[:>:]]", '.$weight.', 0)'; }*/ // search by partial word matches too $relevance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; foreach ($positive_words as $keyword) { $relevance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)'; } } $relevance_parts = array_unique($relevance_parts); $conf_postfix = $config->getSearchConfigPostfix(); $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100; $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100; $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100; $relevance_clause = '('.implode(' + ', $relevance_parts).') / '.$weight_sum.' * '.$rel_keywords; if ($rel_pop && $object->isField('Hits')) { $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; } if ($rel_rating && $object->isField('CachedRating')) { $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; } // building final search query if (!$this->Application->GetVar('do_not_drop_search_table')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event $this->Application->SetVar('do_not_drop_search_table', true); } $search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"'); if ($search_table_exists) { $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) '; } else { $select_intro = 'CREATE TABLE '.$search_table.' AS '; } $edpick_clause = $config->getFieldByName('EditorsPick') ? $items_table.'.EditorsPick' : '0'; $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance, '.$items_table.'.'.$config->getIDField().' AS ItemId, '.$items_table.'.ResourceId, '.$config->getItemType().' AS ItemType, '.$edpick_clause.' AS EdPick FROM '.$object->TableName.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$items_table.'.'.$config->getIDField().' ORDER BY Relevance DESC'; $this->Conn->Query($sql); if ( !$search_table_exists ) { $sql = 'ALTER TABLE ' . $search_table . ' ADD INDEX (ResourceId), ADD INDEX (Relevance)'; $this->Conn->Query($sql); } } /** * Enter description here... * * @param kEvent $event */ function OnSubSearch($event) { // keep search results from other items after doing a sub-search on current item type $this->Application->SetVar('do_not_drop_search_table', true); $ids = Array (); $search_table = TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search'; $sql = 'SHOW TABLES LIKE "' . $search_table . '"'; if ( $this->Conn->Query($sql) ) { $item_type = $event->getUnitConfig()->getItemType(); // 1. get ids to be used as search bounds $sql = 'SELECT DISTINCT ResourceId FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $ids = $this->Conn->GetCol($sql); // 2. delete previously found ids $sql = 'DELETE FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $this->Conn->Query($sql); } $event->setEventParam('ResultIds', $ids); $event->CallSubEvent('OnSimpleSearch'); } /** * Enter description here... * * @param kEvent $event * @todo Change all hardcoded Products table & In-Commerce module usage to dynamic usage from item config !!! */ function OnAdvancedSearch($event) { $query_object = $this->Application->recallObject('HTTPQuery'); /* @var $query_object kHTTPQuery */ if ( !isset($query_object->Post['andor']) ) { // used when navigating by pages or changing sorting in search results return; } $this->Application->RemoveVar('keywords'); $this->Application->RemoveVar('Search_Keywords'); $module_name = $this->Application->findModule('Var', $event->Prefix, 'Name'); $sql = 'SELECT * FROM '.$this->Application->getUnitConfig('confs')->getTableName().' WHERE (ModuleName = '.$this->Conn->qstr($module_name).') AND (AdvancedSearch = 1)'; $search_config = $this->Conn->Query($sql); $lang = $this->Application->GetVar('m_lang'); $object = $event->getObject(); /* @var $object kDBList */ $object->SetPage(1); $config = $event->getUnitConfig(); $items_table = $config->getTableName(); $search_keywords = $this->Application->GetVar('value'); // will not be changed $keywords = $this->Application->GetVar('value'); // will be changed down there $verbs = $this->Application->GetVar('verb'); $glues = $this->Application->GetVar('andor'); $and_conditions = Array(); $or_conditions = Array(); $and_having_conditions = Array(); $or_having_conditions = Array(); $join_clauses = Array(); $highlight_keywords = Array(); $relevance_parts = Array(); $alias_counter = 0; $custom_fields = $config->getCustomFields(); if ($custom_fields) { $custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; } $search_log = ''; $weight_sum = 0; // processing fields and preparing conditions foreach ($search_config as $record) { $field = $record['FieldName']; $join_clause = ''; $condition_mode = 'WHERE'; // field processing $local_table = TABLE_PREFIX.$record['TableName']; $weight_sum += $record['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if ( $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) { $field_name = 'l'.$lang.'_'.$field; } else { $field_name = $field; } // processing fields from other tables $foreign_field = $record['ForeignField']; if ( $foreign_field ) { $exploded = explode(':', $foreign_field, 2); if($exploded[0] == 'CALC') { $user_groups = $this->Application->RecallVar('UserGroups'); $field_name = str_replace('{PREFIX}', TABLE_PREFIX, $exploded[1]); $join_clause = str_replace('{PREFIX}', TABLE_PREFIX, $record['JoinClause']); $join_clause = str_replace('{USER_GROUPS}', $user_groups, $join_clause); $join_clause = ' LEFT JOIN '.$join_clause; $condition_mode = 'HAVING'; } else { $exploded = explode('.', $foreign_field); $foreign_table = TABLE_PREFIX.$exploded[0]; if($record['CustomFieldId']) { $exploded[1] = 'l'.$lang.'_'.$exploded[1]; } $alias_counter++; $alias = 't'.$alias_counter; $field_name = $alias.'.'.$exploded[1]; $join_clause = str_replace('{ForeignTable}', $alias, $record['JoinClause']); $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); if($record['CustomFieldId']) { $join_clause .= ' AND '.$alias.'.CustomFieldId='.$record['CustomFieldId']; } $join_clause = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($record['CustomFieldId']) { $local_table = 'custom_data'; $field_name = 'l'.$lang.'_cust_'.array_search($field_name, $custom_fields); } $field_name = $local_table.'.'.$field_name; } $condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords); if ($record['CustomFieldId'] && strlen($condition)) { // search in primary value of custom field + value in current language $field_name = $local_table.'.'.'l'.$this->Application->GetDefaultLanguageId().'_cust_'.array_search($field, $custom_fields); $primary_condition = $this->getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, $highlight_keywords); $condition = '('.$condition.' OR '.$primary_condition.')'; } if ($condition) { if ($join_clause) { $join_clauses[] = $join_clause; } $relevance_parts[] = 'IF('.$condition.', '.$record['Priority'].', 0)'; if ($glues[$field] == 1) { // and if ($condition_mode == 'WHERE') { $and_conditions[] = $condition; } else { $and_having_conditions[] = $condition; } } else { // or if ($condition_mode == 'WHERE') { $or_conditions[] = $condition; } else { $or_having_conditions[] = $condition; } } // create search log record $search_log_data = Array('search_config' => $record, 'verb' => getArrayValue($verbs, $field), 'value' => ($record['FieldType'] == 'range') ? $search_keywords[$field.'_from'].'|'.$search_keywords[$field.'_to'] : $search_keywords[$field]); $search_log[] = $this->Application->Phrase('la_Field').' "'.$this->getHuman('Field', $search_log_data).'" '.$this->getHuman('Verb', $search_log_data).' '.$this->Application->Phrase('la_Value').' '.$this->getHuman('Value', $search_log_data).' '.$this->Application->Phrase($glues[$field] == 1 ? 'lu_And' : 'lu_Or'); } } if ($search_log) { $search_log = implode('
', $search_log); $search_log = preg_replace('/(.*) '.preg_quote($this->Application->Phrase('lu_and'), '/').'|'.preg_quote($this->Application->Phrase('lu_or'), '/').'$/is', '\\1', $search_log); $this->saveToSearchLog($search_log, 1); // advanced search } $this->Application->StoreVar('highlight_keywords', serialize($highlight_keywords)); // making relevance clause if($relevance_parts) { $conf_postfix = $config->getSearchConfigPostfix(); $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100; $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100; $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100; $relevance_clause = '('.implode(' + ', $relevance_parts).') / '.$weight_sum.' * '.$rel_keywords; $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; } else { $relevance_clause = '0'; } // building having clause if($or_having_conditions) { $and_having_conditions[] = '('.implode(' OR ', $or_having_conditions).')'; } $having_clause = implode(' AND ', $and_having_conditions); $having_clause = $having_clause ? ' HAVING '.$having_clause : ''; // building where clause if($or_conditions) { $and_conditions[] = '('.implode(' OR ', $or_conditions).')'; } // $and_conditions[] = $items_table.'.Status = 1'; $where_clause = implode(' AND ', $and_conditions); if(!$where_clause) { if($having_clause) { $where_clause = '1'; } else { $where_clause = '0'; $this->Application->SetVar('adv_search_error', 1); } } $where_clause .= ' AND '.$items_table.'.Status = 1'; // building final search query $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $id_field = $config->getIDField(); $pick_field = $config->getFieldByName('EditorsPick') ? $items_table.'.EditorsPick' : '0'; $sql = ' CREATE TABLE '.$search_table.' SELECT '.$relevance_clause.' AS Relevance, '.$items_table.'.'.$id_field.' AS ItemId, '.$items_table.'.ResourceId AS ResourceId, 11 AS ItemType, '.$pick_field.' AS EdPick FROM '.$items_table.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$items_table.'.'.$id_field. $having_clause; $res = $this->Conn->Query($sql); } function getAdvancedSearchCondition($field_name, $record, $keywords, $verbs, &$highlight_keywords) { $field = $record['FieldName']; $condition_patterns = Array ( 'any' => '%s LIKE %s', 'contains' => '%s LIKE %s', 'notcontains' => '(NOT (%1$s LIKE %2$s) OR %1$s IS NULL)', 'is' => '%s = %s', 'isnot' => '(%1$s != %2$s OR %1$s IS NULL)' ); $condition = ''; switch ($record['FieldType']) { case 'select': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if ($keywords[$field]) { $condition = sprintf($condition_patterns['is'], $field_name, $this->Conn->qstr( $keywords[$field] )); } break; case 'multiselect': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if ($keywords[$field]) { $condition = Array (); $values = explode('|', substr($keywords[$field], 1, -1)); foreach ($values as $selected_value) { $condition[] = sprintf($condition_patterns['contains'], $field_name, $this->Conn->qstr('%|'.$selected_value.'|%')); } $condition = '('.implode(' OR ', $condition).')'; } break; case 'text': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if (mb_strlen($keywords[$field]) >= $this->Application->ConfigValue('Search_MinKeyword_Length')) { $highlight_keywords[] = $keywords[$field]; if (in_array($verbs[$field], Array('any', 'contains', 'notcontains'))) { $keywords[$field] = '%'.strtr($keywords[$field], Array('%' => '\\%', '_' => '\\_')).'%'; } $condition = sprintf($condition_patterns[$verbs[$field]], $field_name, $this->Conn->qstr( $keywords[$field] )); } break; case 'boolean': if ($keywords[$field] != -1) { $config = $this->getUnitConfig(); $items_table = $config->getTableName(); $property_mappings = $config->getItemPropertyMappings(); switch ($field) { case 'HotItem': $hot_limit_var = getArrayValue($property_mappings, 'HotLimit'); if ($hot_limit_var) { $hot_limit = (int)$this->Application->getDBCache($hot_limit_var); $condition = 'IF('.$items_table.'.HotItem = 2, IF('.$items_table.'.Hits >= '. $hot_limit. ', 1, 0), '.$items_table.'.HotItem) = '.$keywords[$field]; } break; case 'PopItem': $votes2pop_var = getArrayValue($property_mappings, 'VotesToPop'); $rating2pop_var = getArrayValue($property_mappings, 'RatingToPop'); if ($votes2pop_var && $rating2pop_var) { $condition = 'IF('.$items_table.'.PopItem = 2, IF('.$items_table.'.CachedVotesQty >= '. $this->Application->ConfigValue($votes2pop_var). ' AND '.$items_table.'.CachedRating >= '. $this->Application->ConfigValue($rating2pop_var). ', 1, 0), '.$items_table.'.PopItem) = '.$keywords[$field]; } break; case 'NewItem': $new_days_var = getArrayValue($property_mappings, 'NewDays'); if ($new_days_var) { $condition = 'IF('.$items_table.'.NewItem = 2, IF('.$items_table.'.CreatedOn >= (UNIX_TIMESTAMP() - '. $this->Application->ConfigValue($new_days_var). '*3600*24), 1, 0), '.$items_table.'.NewItem) = '.$keywords[$field]; } break; case 'EditorsPick': $condition = $items_table.'.EditorsPick = '.$keywords[$field]; break; } } break; case 'range': $range_conditions = Array(); if ($keywords[$field.'_from'] && !preg_match("/[^0-9]/i", $keywords[$field.'_from'])) { $range_conditions[] = $field_name.' >= '.$keywords[$field.'_from']; } if ($keywords[$field.'_to'] && !preg_match("/[^0-9]/i", $keywords[$field.'_to'])) { $range_conditions[] = $field_name.' <= '.$keywords[$field.'_to']; } if ($range_conditions) { $condition = implode(' AND ', $range_conditions); } break; case 'date': if ($keywords[$field]) { if (in_array($keywords[$field], Array('today', 'yesterday'))) { $current_time = getdate(); $day_begin = mktime(0, 0, 0, $current_time['mon'], $current_time['mday'], $current_time['year']); $time_mapping = Array('today' => $day_begin, 'yesterday' => ($day_begin - 86400)); $min_time = $time_mapping[$keywords[$field]]; } else { $time_mapping = Array ( 'last_week' => 604800, 'last_month' => 2628000, 'last_3_months' => 7884000, 'last_6_months' => 15768000, 'last_year' => 31536000, ); $min_time = time() - $time_mapping[$keywords[$field]]; } $condition = $field_name.' > '.$min_time; } break; } return $condition; } /** * Returns human readable representation of searched data to be placed in search log * @param string $type * @param Array $search_data * @return string * @access protected */ protected function getHuman($type, $search_data) { // all 3 variables are retrieved from $search_data array /* @var $search_config Array */ /* @var $verb string */ /* @var $value string */ $type = ucfirst(strtolower($type)); extract($search_data); switch ($type) { case 'Field': return $this->Application->Phrase($search_config['DisplayName']); break; case 'Verb': return $verb ? $this->Application->Phrase('lu_advsearch_'.$verb) : ''; break; case 'Value': switch ($search_config['FieldType']) { case 'date': $values = Array(0 => 'lu_comm_Any', 'today' => 'lu_comm_Today', 'yesterday' => 'lu_comm_Yesterday', 'last_week' => 'lu_comm_LastWeek', 'last_month' => 'lu_comm_LastMonth', 'last_3_months' => 'lu_comm_Last3Months', 'last_6_months' => 'lu_comm_Last6Months', 'last_year' => 'lu_comm_LastYear'); $ret = $this->Application->Phrase($values[$value]); break; case 'range': $value = explode('|', $value); return $this->Application->Phrase('lu_comm_From').' "'.$value[0].'" '.$this->Application->Phrase('lu_comm_To').' "'.$value[1].'"'; break; case 'boolean': $values = Array(1 => 'lu_comm_Yes', 0 => 'lu_comm_No', -1 => 'lu_comm_Both'); $ret = $this->Application->Phrase($values[$value]); break; default: $ret = $value; break; } return '"'.$ret.'"'; break; } return ''; } /** * Set's correct page for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetPagination(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ // get PerPage (forced -> session -> config -> 10) $object->SetPerPage($this->getPerPage($event)); // main lists on Front-End have special get parameter for page $page = $object->isMainList() ? $this->Application->GetVar('page') : false; if ( !$page ) { // page is given in "env" variable for given prefix $page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page'); } if ( !$page && $event->Special ) { // when not part of env, then variables like "prefix.special_Page" are // replaced (by PHP) with "prefix_special_Page", so check for that too $page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page'); } if ( !$object->isMainList() ) { // main lists doesn't use session for page storing $this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional if ( !$page ) { if ( $this->Application->RewriteURLs() ) { // when page not found by prefix+special, then try to search it without special at all $page = $this->Application->GetVar($event->Prefix . '_Page'); if ( !$page ) { // page not found in request -> get from session $page = $this->Application->RecallVar($event->Prefix . '_Page'); } 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'); } } else { // page found in request -> store in session $this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional } 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); } /* === RELATED TO IMPORT/EXPORT: BEGIN === */ /** * Shows export dialog * * @param kEvent $event * @return void * @access protected */ protected function OnExport(kEvent $event) { $selected_ids = $this->StoreSelectedIDs($event); if ( implode(',', $selected_ids) == '' ) { // K4 fix when no ids found bad selected ids array is formed $selected_ids = false; } $selected_cats_ids = $this->Application->GetVar('export_categories'); $this->Application->StoreVar($event->Prefix . '_export_ids', $selected_ids ? implode(',', $selected_ids) : ''); $this->Application->StoreVar($event->Prefix . '_export_cats_ids', $selected_cats_ids); $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $redirect_params = Array ( $this->Prefix . '.export_event' => 'OnNew', 'pass' => 'all,' . $this->Prefix . '.export' ); $event->setRedirectParams($redirect_params); } /** * Performs each export step & displays progress percent * * @param kEvent $event */ function OnExportProgress($event) { $export_object = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_object kCatDBItemExportHelper */ - $event = new kEvent($event->getPrefixSpecial().':OnDummy'); - $action_method = 'perform'.ucfirst($event->Special); $field_values = $export_object->$action_method($event); // finish code is done from JS now if ($field_values['start_from'] == $field_values['total_records']) { if ($event->Special == 'import') { $this->Application->StoreVar('PermCache_UpdateRequired', 1); $event->SetRedirectParam('m_cat_id', $this->Application->RecallVar('ImportCategory')); $event->SetRedirectParam('anchor', 'tab-' . $event->Prefix); $event->redirect = 'catalog/catalog'; } elseif ($event->Special == 'export') { $event->redirect = $export_object->getModuleName($event) . '/' . $event->Special . '_finish'; $event->SetRedirectParam('pass', 'all'); } return ; } $export_options = $export_object->loadOptions($event); echo $export_options['start_from'] * 100 / $export_options['total_records']; $event->status = kEvent::erSTOP; } /** * Returns specific to each item type columns only * * @param kEvent $event * @return Array * @access protected */ public function getCustomExportColumns(kEvent $event) { return Array ( '__VIRTUAL__ThumbnailImage' => 'ThumbnailImage', '__VIRTUAL__FullImage' => 'FullImage', '__VIRTUAL__ImageAlt' => 'ImageAlt' ); } /** * Sets non standart virtual fields (e.g. to other tables) * * @param kEvent $event */ function setCustomExportColumns($event) { $this->restorePrimaryImage($event); } /** * Create/Update primary image record in info found in imported data * * @param kEvent $event * @return void * @access protected */ protected function restorePrimaryImage($event) { $object = $event->getObject(); /* @var $object kCatDBItem */ - $has_image_info = $object->GetDBField('ImageAlt') && ($object->GetDBField('ThumbnailImage') || $object->GetDBField('FullImage')); - if ( !$has_image_info ) { + if ( !$object->GetDBField('ThumbnailImage') && !$object->GetDBField('FullImage') ) { return ; } $image_data = $object->getPrimaryImageData(); $image = $this->Application->recallObject('img', NULL, Array ('skip_autoload' => true)); /* @var $image kDBItem */ if ( $image_data ) { $image->Load($image_data['ImageId']); } else { $image->Clear(); $image->SetDBField('Name', 'main'); $image->SetDBField('DefaultImg', 1); $image->SetDBField('ResourceId', $object->GetDBField('ResourceId')); } - $image->SetDBField('AltName', $object->GetDBField('ImageAlt')); + if ( $object->GetDBField('ImageAlt') ) { + $image->SetDBField('AltName', $object->GetDBField('ImageAlt')); + } if ( $object->GetDBField('ThumbnailImage') ) { $thumbnail_field = $this->isURL($object->GetDBField('ThumbnailImage')) ? 'ThumbUrl' : 'ThumbPath'; $image->SetDBField($thumbnail_field, $object->GetDBField('ThumbnailImage')); $image->SetDBField('LocalThumb', $thumbnail_field == 'ThumbPath' ? 1 : 0); } if ( !$object->GetDBField('FullImage') ) { $image->SetDBField('SameImages', 1); } else { $image->SetDBField('SameImages', 0); $full_field = $this->isURL($object->GetDBField('FullImage')) ? 'Url' : 'LocalPath'; $image->SetDBField($full_field, $object->GetDBField('FullImage')); $image->SetDBField('LocalImage', $full_field == 'LocalPath' ? 1 : 0); } if ( $image->isLoaded() ) { $image->Update(); } else { $image->Create(); } } /** * Detects if image url is specified in a given path (instead of path on disk) * * @param string $path * @return bool * @access protected */ protected function isURL($path) { return preg_match('#(http|https)://(.*)#', $path); } /** * Prepares item for import/export operations * * @param kEvent $event * @return void * @access protected */ protected function OnNew(kEvent $event) { parent::OnNew($event); if ( $event->Special == 'import' || $event->Special == 'export' ) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->setRequiredFields($event); } } /** * Process items selected in item_selector * * @param kEvent $event */ function OnProcessSelected($event) { $dst_field = $this->Application->RecallVar('dst_field'); $selected_ids = $this->Application->GetVar('selected_ids'); if ( $dst_field == 'ItemCategory' ) { // Item Edit -> Categories Tab -> New Categories $object = $event->getObject(); /* @var $object kCatDBItem */ $category_ids = explode(',', $selected_ids['c']); foreach ($category_ids as $category_id) { $object->assignToCategory($category_id); } } if ($dst_field == 'ImportCategory') { // Tools -> Import -> Item Import -> Select Import Category $this->Application->StoreVar('ImportCategory', $selected_ids['c']); $event->SetRedirectParam($event->getPrefixSpecial() . '_id', 0); $event->SetRedirectParam($event->getPrefixSpecial() . '_event', 'OnExportBegin'); } $event->SetRedirectParam('opener', 'u'); } /** * Saves Import/Export settings to session * * @param kEvent $event */ function OnSaveSettings($event) { $event->redirect = false; $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { list($id, $field_values) = each($items_info); $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->setID($id); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); + $field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!! $field_values['ImportSource'] = 2; $field_values['ImportLocalFilename'] = $object->GetDBField('ImportFilename'); $items_info[$id] = $field_values; $this->Application->StoreVar($event->getPrefixSpecial() . '_ItemsInfo', serialize($items_info)); } } /** * Saves Import/Export settings to session * * @param kEvent $event */ function OnResetSettings($event) { $this->Application->StoreVar('ImportCategory', $this->Application->getBaseCategory()); } /** * Cancels item editing * @param kEvent $event * @return void * @todo Used? */ function OnCancelAction($event) { $event->redirect = $this->Application->GetVar('cancel_template'); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); } /* === RELATED TO IMPORT/EXPORT: END === */ /** * Stores item's owner login into separate field together with id * * @param kEvent $event * @param string $id_field * @param string $cached_field */ function cacheItemOwner($event, $id_field, $cached_field) { $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField($cached_field, $object->GetField($id_field)); $options = $object->GetFieldOptions($id_field); if ( isset($options['options'][$user_id]) ) { $object->SetDBField($cached_field, $options['options'][$user_id]); } else { $user_config = $this->Application->getUnitConfig('u'); $id_field = $user_config->getIDField(); $table_name = $user_config->getTableName(); $sql = 'SELECT Username FROM ' . $table_name . ' WHERE ' . $id_field . ' = ' . $user_id; $object->SetDBField($cached_field, $this->Conn->GetOne($sql)); } } /** * Saves edited item into temp table * If there is no id, new item is created in temp table * * @param kEvent $event * @return void * @access protected */ protected function OnPreSave(kEvent $event) { parent::OnPreSave($event); $use_pending_editing = $event->getUnitConfig()->getUsePendingEditing(); if ( $event->status == kEvent::erSUCCESS && $use_pending_editing ) { // decision: clone or not clone $object = $event->getObject(); /* @var $object kCatDBItem */ if ( $object->GetID() == 0 || $object->GetDBField('OrgId') > 0 ) { // new items or cloned items shouldn't be cloned again return ; } $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $owner_field = $this->getOwnerField($event->Prefix); if ( $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix) == 2 ) { // 1. clone original item $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array ($object->GetID()), NULL, NULL, NULL, true); $ci_table = $this->Application->GetTempName(TABLE_PREFIX . 'CategoryItems'); // 2. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem $sql = 'SELECT ResourceId FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $cloned_ids[0]; $clone_resource_id = $this->Conn->GetOne($sql); $sql = 'DELETE FROM ' . $ci_table . ' WHERE ItemResourceId = ' . $clone_resource_id . ' AND PrimaryCat = 1'; $this->Conn->Query($sql); // 3. copy main item categoryitems to cloned item $sql = ' INSERT INTO ' . $ci_table . ' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename) SELECT CategoryId, ' . $clone_resource_id . ' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename FROM ' . $ci_table . ' WHERE ItemResourceId = ' . $object->GetDBField('ResourceId'); $this->Conn->Query($sql); // 4. put cloned id to OrgId field of item being cloned $sql = 'UPDATE ' . $object->TableName . ' SET OrgId = ' . $object->GetID() . ' WHERE ' . $object->IDField . ' = ' . $cloned_ids[0]; $this->Conn->Query($sql); // 5. substitute id of item being cloned with clone id $this->Application->SetVar($event->getPrefixSpecial() . '_id', $cloned_ids[0]); $selected_ids = $this->getSelectedIDs($event, true); $selected_ids[ array_search($object->GetID(), $selected_ids) ] = $cloned_ids[0]; $this->StoreSelectedIDs($event, $selected_ids); // 6. delete original item from temp table $temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($object->GetID())); } } } /** * Sets item's owner field * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); if ( $event->status != kEvent::erSUCCESS ) { return ; } $object = $event->getObject(); /* @var $object kDBItem */ $owner_field = $this->getOwnerField($event->Prefix); $object->SetDBField($owner_field, $this->Application->RecallVar('user_id')); } /** * Occurs before original item of item in pending editing got deleted (for hooking only) * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteOriginal(kEvent $event) { } /** * Occurs after original item of item in pending editing got deleted (for hooking only) * * @param kEvent $event * @return void * @access protected */ protected function OnAfterDeleteOriginal(kEvent $event) { } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { parent::OnBeforeClone($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('ResourceId', 0); // this will reset it if ( $this->Application->GetVar('ResetCatBeforeClone') ) { $object->SetDBField('CategoryId', NULL); } } /** * Set status for new category item based on user permission in category * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $object = $event->getObject(); /* @var $object kCatDBItem */ $is_admin = $this->Application->isAdminUser; $owner_field = $this->getOwnerField($event->Prefix); if ( (!$object->IsTempTable() && !$is_admin) || ($is_admin && !$object->GetDBField($owner_field)) ) { // Front-end OR owner not specified -> set to currently logged-in user $object->SetDBField($owner_field, $this->Application->RecallVar('user_id')); } if ( !$this->Application->isAdmin ) { $this->setItemStatusByPermission($event); } } /** * Sets category item status based on user permissions (only on Front-end) * * @param kEvent $event */ function setItemStatusByPermission($event) { $use_pending_editing = $event->getUnitConfig()->getUsePendingEditing(); if (!$use_pending_editing) { return ; } $object = $event->getObject(); /* @var $object kCatDBItem */ $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $primary_category = $object->GetDBField('CategoryId') > 0 ? $object->GetDBField('CategoryId') : $this->Application->GetVar('m_cat_id'); $item_status = $perm_helper->AddCheckPermission($primary_category, $event->Prefix); if ($item_status == STATUS_DISABLED) { $event->status = kEvent::erFAIL; } else { $object->SetDBField('Status', $item_status); } } /** * Creates category item & redirects to confirmation template (front-end only) * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { parent::OnCreate($event); $this->SetFrontRedirectTemplate($event, 'suggest'); } /** * Returns item's categories (allows to exclude primary category) * * @param int $resource_id * @param bool $with_primary * @return Array */ function getItemCategories($resource_id, $with_primary = false) { $sql = 'SELECT CategoryId FROM '.TABLE_PREFIX.'CategoryItems WHERE (ItemResourceId = '.$resource_id.')'; if (!$with_primary) { $sql .= ' AND (PrimaryCat = 0)'; } return $this->Conn->GetCol($sql); } /** * Adds new and removes old additional categories from category item * * @param kCatDBItem $object * @param int $mode */ function processAdditionalCategories(&$object, $mode) { if ( !$object->isVirtualField('MoreCategories') ) { // given category item doesn't require such type of processing return ; } $process_categories = $object->GetDBField('MoreCategories'); if ($process_categories === '') { // field was not in submit & have default value (when no categories submitted, then value is null) return ; } if ($mode == 'create') { // prevents first additional category to become primary $object->assignPrimaryCategory(); } $process_categories = $process_categories ? explode('|', substr($process_categories, 1, -1)) : Array (); $existing_categories = $this->getItemCategories($object->GetDBField('ResourceId')); $add_categories = array_diff($process_categories, $existing_categories); foreach ($add_categories as $category_id) { $object->assignToCategory($category_id); } $remove_categories = array_diff($existing_categories, $process_categories); foreach ($remove_categories as $category_id) { $object->removeFromCategory($category_id); } } /** * Creates category item & redirects to confirmation template (front-end only) * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { $use_pending = $event->getUnitConfig()->getUsePendingEditing(); if ($this->Application->isAdminUser || !$use_pending) { parent::OnUpdate($event); $this->SetFrontRedirectTemplate($event, 'modify'); return ; } $object = $event->getObject(Array('skip_autoload' => true)); /* @var $object kCatDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ($items_info) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $owner_field = $this->getOwnerField($event->Prefix); $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ foreach ($items_info as $id => $field_values) { $object->Load($id); $edit_perm = $perm_helper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $event->Prefix); if ($use_pending && !$object->GetDBField('OrgId') && ($edit_perm == STATUS_PENDING)) { // pending editing enabled + not pending copy -> get/create pending copy & save changes to it $original_id = $object->GetID(); $original_resource_id = $object->GetDBField('ResourceId'); $file_helper->PreserveItemFiles($field_values); $object->Load($original_id, 'OrgId'); if (!$object->isLoaded()) { // 1. user has no pending copy of live item -> clone live item $cloned_ids = $temp_handler->CloneItems($event->Prefix, $event->Special, Array($original_id), NULL, NULL, NULL, true); $object->Load($cloned_ids[0]); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); // 1a. delete record from CategoryItems (about cloned item) that was automatically created during call of Create method of kCatDBItem $ci_table = $this->Application->getUnitConfig('ci')->getTableName(); $sql = 'DELETE FROM '.$ci_table.' WHERE ItemResourceId = '.$object->GetDBField('ResourceId').' AND PrimaryCat = 1'; $this->Conn->Query($sql); // 1b. copy main item categoryitems to cloned item $sql = 'INSERT INTO '.$ci_table.' (CategoryId, ItemResourceId, PrimaryCat, ItemPrefix, Filename) SELECT CategoryId, '.$object->GetDBField('ResourceId').' AS ItemResourceId, PrimaryCat, ItemPrefix, Filename FROM '.$ci_table.' WHERE ItemResourceId = '.$original_resource_id; $this->Conn->Query($sql); // 1c. put cloned id to OrgId field of item being cloned $object->SetDBField('Status', STATUS_PENDING_EDITING); $object->SetDBField('OrgId', $original_id); } else { // 2. user has pending copy of live item -> just update field values - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); } // update id in request (used for redirect in mod-rewrite mode) $this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID()); } else { // 3. already editing pending copy -> just update field values - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); } if ($object->Update()) { $event->status = kEvent::erSUCCESS; } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } $this->SetFrontRedirectTemplate($event, 'modify'); } /** * Sets next template to one required for front-end after adding/modifying item * * @param kEvent $event * @param string $template_key - {suggest,modify} */ function SetFrontRedirectTemplate($event, $template_key) { if ( $this->Application->isAdmin || $event->status != kEvent::erSUCCESS ) { return; } // prepare redirect template $object = $event->getObject(); /* @var $object kDBItem */ $is_active = ($object->GetDBField('Status') == STATUS_ACTIVE); $next_template = $is_active ? 'confirm_template' : 'pending_confirm_template'; $event->redirect = $this->Application->GetVar($template_key . '_' . $next_template); $event->SetRedirectParam('opener', 's'); // send email events $perm_prefix = $event->getUnitConfig()->getPermItemPrefix(); $owner_field = $this->getOwnerField($event->Prefix); $owner_id = $object->GetDBField($owner_field); $send_params = $object->getEmailParams(); switch ( $event->Name ) { case 'OnCreate': $event_suffix = $is_active ? 'ADD' : 'ADD.PENDING'; $this->Application->emailUser($perm_prefix . '.' . $event_suffix, $owner_id, $send_params); $this->Application->emailAdmin($perm_prefix . '.' . $event_suffix, null, $send_params); // there are no ADD.PENDING event for admin :( break; case 'OnUpdate': $event_suffix = $is_active ? 'MODIFY' : 'MODIFY.PENDING'; $user_id = is_numeric($object->GetDBField('ModifiedById')) ? $object->GetDBField('ModifiedById') : $owner_id; $this->Application->emailUser($perm_prefix . '.' . $event_suffix, $user_id, $send_params); $this->Application->emailAdmin($perm_prefix . '.' . $event_suffix, null, $send_params); // there are no ADD.PENDING event for admin :( break; } } /** * Apply same processing to each item being selected in grid * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline' ) { parent::iterateItems($event); } if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return ; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kCatDBItem */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { foreach ($ids as $id) { $ret = true; $object->Load($id); switch ( $event->Name ) { case 'OnMassApprove': $ret = $object->ApproveChanges(); break; case 'OnMassDecline': $ret = $object->DeclineChanges(); break; } if ( !$ret ) { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } $this->clearSelectedIDs($event); } /** * Deletes items & preserves clean env * * @param kEvent $event * @return void * @access protected */ protected function OnDelete(kEvent $event) { parent::OnDelete($event); if ( $event->status == kEvent::erSUCCESS && !$this->Application->isAdmin ) { $event->SetRedirectParam('pass', 'm'); $event->SetRedirectParam('m_cat_id', 0); } } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool * @access protected */ protected function checkItemStatus(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->isLoaded() ) { if ( $event->Special != 'previous' && $event->Special != 'next' ) { $this->_errorNotFound($event); } return true; } $status = $object->GetDBField('Status'); $user_id = $this->Application->RecallVar('user_id'); $owner_field = $this->getOwnerField($event->Prefix); if ( ($status == STATUS_PENDING_EDITING || $status == STATUS_PENDING) && ($object->GetDBField($owner_field) == $user_id) ) { return true; } return $status == STATUS_ACTIVE; } /** * Set's correct sorting for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetSorting(kEvent $event) { if ( !$this->Application->isAdmin ) { $event->setEventParam('same_special', true); } $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); if ( in_array('search', $types) ) { $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ // 1. no user sorting - sort by relevance $default_sortings = parent::_getDefaultSorting($event); $default_sorting = key($default_sortings['Sorting']) . ',' . current($default_sortings['Sorting']); if ( $object->isMainList() ) { $sort_by = $this->Application->GetVar('sort_by', ''); if ( !$sort_by ) { $this->Application->SetVar('sort_by', 'Relevance,desc|' . $default_sorting); } - elseif ( strpos($sort_by, 'Relevance,') !== false ) { - $this->Application->SetVar('sort_by', $sort_by . '|' . $default_sorting); - } } else { $sorting_settings = $this->getListSetting($event, 'Sortings'); $sort_by = trim(getArrayValue($sorting_settings, 'Sort1') . ',' . getArrayValue($sorting_settings, 'Sort1_Dir'), ','); if ( !$sort_by ) { $event->setEventParam('sort_by', 'Relevance,desc|' . $default_sorting); } - elseif ( strpos($sort_by, 'Relevance,') !== false ) { - $event->setEventParam('sort_by', $sort_by . '|' . $default_sorting); - } } $this->_removeForcedSortings($event); } parent::SetSorting($event); } /** * Removes forced sortings * * @param kEvent $event */ protected function _removeForcedSortings(kEvent $event) { $config = $event->getUnitConfig(); foreach ($config->getListSortingSpecials() as $special) { $list_sortings = $config->getListSortingsBySpecial($special); unset($list_sortings['ForcedSorting']); $config->setListSortingsBySpecial('', $list_sortings); } } /** * Default sorting in search results only comes from relevance field * * @param kEvent $event * @return Array * @access protected */ protected function _getDefaultSorting(kEvent $event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); return in_array('search', $types) ? Array () : parent::_getDefaultSorting($event); } /** * Returns current per-page setting for list * * @param kEvent $event * @return int * @access protected */ protected function getPerPage(kEvent $event) { if ( !$this->Application->isAdmin ) { $event->setEventParam('same_special', true); } return parent::getPerPage($event); } /** * Returns owner field for given prefix * * @param $prefix * @return string * @access protected */ protected function getOwnerField($prefix) { return $this->Application->getUnitConfig($prefix)->getOwnerField('CreatedById'); } /** * Creates virtual image fields for item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); if (defined('IS_INSTALL') && IS_INSTALL) { $this->addViewPermissionJoin($event); return ; } if ( !$this->Application->isAdmin ) { $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->createItemFiles($event->Prefix, true); // create image fields $file_helper->createItemFiles($event->Prefix, false); // create file fields } $this->changeSortings($event)->addViewPermissionJoin($event); // add grids for advanced view (with primary category column) $config = $event->getUnitConfig(); foreach (Array ('Default', 'Radio') as $process_grid) { $grid_data = $config->getGridByName($process_grid); $grid_data['Fields']['CachedNavbar'] = Array ('title' => 'la_col_Path', 'data_block' => 'grid_primary_category_td', 'filter_block' => 'grid_like_filter'); $config->addGrids($grid_data, $process_grid . 'ShowAll'); } // add options for CategoryId field (quick way to select item's primary category) $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $virtual_fields = $config->getVirtualFields(); $virtual_fields['CategoryId']['default'] = (int)$this->Application->GetVar('m_cat_id'); $virtual_fields['CategoryId']['options'] = $category_helper->getStructureTreeAsOptions(); $config->setVirtualFields($virtual_fields); } /** * Changes default sorting according to system settings. * * @param kEvent $event Event. * * @return self * @access protected */ protected function changeSortings(kEvent $event) { $remove_sortings = Array (); $config = $event->getUnitConfig(); if ( !$this->Application->isAdmin ) { // remove Pick sorting on Front-end, when not required $config_mapping = $config->getConfigMapping(Array ()); if ( !isset($config_mapping['ForceEditorPick']) || !$this->Application->ConfigValue($config_mapping['ForceEditorPick']) ) { $remove_sortings[] = 'EditorsPick'; } } else { // remove all forced sortings in Admin Console $remove_sortings = array_merge($remove_sortings, Array ('Priority', 'EditorsPick')); } if ( !$remove_sortings ) { return $this; } foreach ($config->getListSortingSpecials() as $special) { $list_sortings = $config->getListSortingsBySpecial($special); foreach ($remove_sortings as $sorting_field) { unset($list_sortings['ForcedSorting'][$sorting_field]); } $config->setListSortingsBySpecial('', $list_sortings); } return $this; } /** * Adds permission table table JOIN clause only, when advanced catalog view permissions enabled. * * @param kEvent $event Event. * * @return self * @access protected */ protected function addViewPermissionJoin(kEvent $event) { if ( $this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) { $join_clause = 'LEFT JOIN ' . TABLE_PREFIX . 'CategoryPermissionsCache perm ON perm.CategoryId = ' . TABLE_PREFIX . '%3$sCategoryItems.CategoryId'; } else { $join_clause = ''; } $config = $event->getUnitConfig(); foreach ( $config->getListSQLSpecials() as $special ) { $list_sql = str_replace('{PERM_JOIN}', $join_clause, $config->getListSQLsBySpecial($special)); $config->setListSQLsBySpecial($special, $list_sql); } return $this; } /** * Returns file contents associated with item * * @param kEvent $event */ function OnDownloadFile($event) { $object = $event->getObject(); /* @var $object kCatDBItem */ $event->status = kEvent::erSTOP; $field = $this->Application->GetVar('field'); if (!preg_match('/^File([\d]+)/', $field)) { return ; } $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $filename = $object->GetField($field, 'full_path'); $file_helper->DownloadFile($filename); } /** * Saves user's vote * * @param kEvent $event */ function OnMakeVote($event) { $event->status = kEvent::erSTOP; if ($this->Application->GetVar('ajax') != 'yes') { // this is supposed to call from AJAX only return ; } $rating_helper = $this->Application->recallObject('RatingHelper'); /* @var $rating_helper RatingHelper */ $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kCatDBItem */ $object->Load( $this->Application->GetVar('id') ); echo $rating_helper->makeVote($object); } /** * Marks review as useful * * @param kEvent $event * @return void * @access protected */ protected function OnReviewHelpful($event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } $review_id = (int)$this->Application->GetVar('review_id'); if ( !$review_id ) { return; } $spam_helper = $this->Application->recallObject('SpamHelper'); /* @var $spam_helper SpamHelper */ $spam_helper->InitHelper($review_id, 'ReviewHelpful', strtotime('+1 month') - strtotime('now')); $field = (int)$this->Application->GetVar('helpful') ? 'HelpfulCount' : 'NotHelpfulCount'; $review_config = $this->Application->getUnitConfig('rev'); $sql = 'SELECT ' . $field . ' FROM ' . $review_config->getTableName() . ' WHERE ' . $review_config->getIDField() . ' = ' . $review_id; $count = $this->Conn->GetOne($sql); if ( $spam_helper->InSpamControl() ) { if ( $this->Application->GetVar('ajax') == 'yes' ) { echo $count; } return; } $sql = 'UPDATE ' . $review_config->getTableName() . ' SET ' . $field . ' = ' . $field . ' + 1 WHERE ' . $review_config->getIDField() . ' = ' . $review_id; $this->Conn->Query($sql); if ( $this->Conn->getAffectedRows() ) { // db was changed -> review with such ID exists $spam_helper->AddToSpamControl(); } if ( $this->Application->GetVar('ajax') == 'yes' ) { echo $count + 1; } } /** * [HOOK] Allows to add cloned subitem to given prefix * * @param kEvent $event * @return void * @access protected */ protected function OnCloneSubItem(kEvent $event) { parent::OnCloneSubItem($event); if ( $event->MasterEvent->Prefix == 'fav' ) { $master_config = $event->MasterEvent->getUnitConfig(); $clones = $master_config->getClones(); $sub_item_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix; $clones[$sub_item_prefix]['ParentTableKey'] = 'ResourceId'; $clones[$sub_item_prefix]['ForeignKey'] = 'ResourceId'; $master_config->setClones($clones); } } /** * Set's new unique resource id to user * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemValidate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $resource_id = $object->GetDBField('ResourceId'); if ( !$resource_id ) { $object->SetDBField('ResourceId', $this->Application->NextResourceId()); } } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/application.php =================================================================== --- branches/5.3.x/core/kernel/application.php (revision 16110) +++ branches/5.3.x/core/kernel/application.php (revision 16111) @@ -1,3060 +1,3076 @@ * The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.
*
* The class is a singleton, which means that there could be only one instance of kApplication in the script.
* This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method, * which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.
* See singleton pattern by GOF. */ class kApplication implements kiCacheable { /** * Location of module helper class (used in installator too) */ const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php'; /** * Is true, when Init method was called already, prevents double initialization * * @var bool */ public $InitDone = false; /** * Holds internal NParser object * * @var NParser * @access public */ public $Parser; /** * Holds parser output buffer * * @var string * @access protected */ protected $HTML = ''; /** * The main Factory used to create * almost any class of kernel and * modules * * @var kFactory * @access protected */ protected $Factory; /** * Template names, that will be used instead of regular templates * * @var Array * @access public */ public $ReplacementTemplates = Array (); /** * Mod-Rewrite listeners used during url building and parsing * * @var Array * @access public */ public $RewriteListeners = Array (); /** * Reference to debugger * * @var Debugger * @access public */ public $Debugger = null; /** * Holds all phrases used * in code and template * * @var PhrasesCache * @access public */ public $Phrases; /** * Modules table content, key - module name * * @var Array * @access public */ public $ModuleInfo = Array (); /** * Holds DBConnection * * @var IDBConnection * @access public */ public $Conn = null; /** * Reference to event log * * @var Array|kLogger * @access public */ protected $_logger = Array (); // performance needs: /** * Holds a reference to httpquery * * @var kHttpQuery * @access public */ public $HttpQuery = null; /** * Holds a reference to UnitConfigReader * * @var kUnitConfigReader * @access public */ public $UnitConfigReader = null; /** * Holds a reference to Session * * @var Session * @access public */ public $Session = null; /** * Holds a ref to kEventManager * * @var kEventManager * @access public */ public $EventManager = null; /** * Holds a ref to kUrlManager * * @var kUrlManager * @access public */ public $UrlManager = null; /** * Ref for TemplatesCache * * @var TemplatesCache * @access public */ public $TemplatesCache = null; /** * Holds current NParser tag while parsing, can be used in error messages to display template file and line * * @var _BlockTag * @access public */ public $CurrentNTag = null; /** * Object of unit caching class * * @var kCacheManager * @access public */ public $cacheManager = null; /** * Tells, that administrator has authenticated in administrative console * Should be used to manipulate data change OR data restrictions! * * @var bool * @access public */ public $isAdminUser = false; /** * Tells, that admin version of "index.php" was used, nothing more! * Should be used to manipulate data display! * * @var bool * @access public */ public $isAdmin = false; /** * Instance of site domain object * * @var kDBItem * @access public * @todo move away into separate module */ public $siteDomain = null; /** * Prevent kApplication class to be created directly, only via Instance method * * @access private */ private function __construct() { } final private function __clone() {} /** * Returns kApplication instance anywhere in the script. * * This method should be used to get single kApplication object instance anywhere in the * Kernel-based application. The method is guaranteed to return the SAME instance of kApplication. * Anywhere in the script you could write: * * $application =& kApplication::Instance(); * * or in an object: * * $this->Application =& kApplication::Instance(); * * to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class. * To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant * BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would * create and return default KernelApplication instance. * * Pattern: Singleton * * @static * @return kApplication * @access public */ public static function &Instance() { static $instance = false; if ( !$instance ) { $class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication'; $instance = new $class(); } return $instance; } /** * Initializes the Application * * @param string $factory_class * @return bool Was Init actually made now or before * @access public * @see kHTTPQuery * @see Session * @see TemplatesCache */ public function Init($factory_class = 'kFactory') { if ( $this->InitDone ) { return false; } if ( preg_match('/utf-8/i', CHARSET) ) { setlocale(LC_ALL, 'en_US.UTF-8'); mb_internal_encoding('UTF-8'); } $this->isAdmin = kUtil::constOn('ADMIN'); if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) { ob_start(); // collect any output from method (other then tags) into buffer } if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Init:'); } $this->_logger = new kLogger($this->_logger); $this->Factory = new $factory_class(); $this->registerDefaultClasses(); $system_config = new kSystemConfig(true); $vars = $system_config->getData(); $db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection'); $this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array ($this->_logger, 'handleSQLError'))); $this->Conn->setup($vars); $this->cacheManager = $this->makeClass('kCacheManager'); $this->cacheManager->InitCache(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Before UnitConfigReader'); } // init config reader and all managers $this->UnitConfigReader = $this->makeClass('kUnitConfigReader'); $this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read $this->registerModuleConstants(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('After UnitConfigReader'); } define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0); // start processing request $this->HttpQuery = $this->recallObject('HTTPQuery'); $this->HttpQuery->process(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed HTTPQuery initial'); } $this->Session = $this->recallObject('Session'); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed Session'); } $this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit'); } $this->cacheManager->LoadApplicationCache(); $site_timezone = $this->ConfigValue('Config_Site_Time'); if ( $site_timezone ) { date_default_timezone_set($site_timezone); } if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Loaded cache and phrases'); } $this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there $this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed AfterConfigRead'); } if ( $this->GetVar('m_cat_id') === false ) { $this->SetVar('m_cat_id', 0); } if ( !$this->RecallVar('curr_iso') ) { $this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional } $visit_id = $this->RecallVar('visit_id'); if ( $visit_id !== false ) { $this->SetVar('visits_id', $visit_id); } if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->profileFinish('kernel4_startup'); } $this->InitDone = true; $this->HandleEvent(new kEvent('adm:OnStartup')); return true; } /** * Performs initialization of manager classes, that can be overridden from unit configs * * @return void * @access public * @throws Exception */ public function InitManagers() { if ( $this->InitDone ) { throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR); } $this->UrlManager = $this->makeClass('kUrlManager'); $this->EventManager = $this->makeClass('EventManager'); $this->Phrases = $this->makeClass('kPhraseCache'); $this->RegisterDefaultBuildEvents(); } /** * Returns module information. Searches module by requested field * * @param string $field * @param mixed $value * @param string $return_field field value to returns, if not specified, then return all fields * @return Array */ public function findModule($field, $value, $return_field = null) { $found = $module_info = false; foreach ($this->ModuleInfo as $module_info) { if ( strtolower($module_info[$field]) == strtolower($value) ) { $found = true; break; } } if ( $found ) { return isset($return_field) ? $module_info[$return_field] : $module_info; } return false; } /** * Refreshes information about loaded modules * * @return void * @access public */ public function refreshModuleInfo() { if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) { $this->registerModuleConstants(); return; } // use makeClass over recallObject, since used before kApplication initialization during installation $modules_helper = $this->makeClass('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $this->Conn->nextQueryCachable = true; $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'Modules WHERE ' . $modules_helper->getWhereClause() . ' ORDER BY LoadOrder'; $this->ModuleInfo = $this->Conn->Query($sql, 'Name'); $this->registerModuleConstants(); } /** * Checks if passed language id if valid and sets it to primary otherwise * * @return void * @access public */ public function VerifyLanguageId() { $language_id = $this->GetVar('m_lang'); if ( !$language_id ) { $language_id = 'default'; } $this->SetVar('lang.current_id', $language_id); $this->SetVar('m_lang', $language_id); $lang_mode = $this->GetVar('lang_mode'); $this->SetVar('lang_mode', ''); $lang = $this->recallObject('lang.current'); /* @var $lang kDBItem */ if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) { if ( !defined('IS_INSTALL') ) { $this->ApplicationDie('Unknown or disabled language'); } } $this->SetVar('lang_mode', $lang_mode); } /** * Checks if passed theme id if valid and sets it to primary otherwise * * @return void * @access public */ public function VerifyThemeId() { if ( $this->isAdmin ) { kUtil::safeDefine('THEMES_PATH', '/core/admin_templates'); return; } $path = $this->GetFrontThemePath(); if ( $path === false ) { $this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled'); } kUtil::safeDefine('THEMES_PATH', $path); } /** * Returns relative path to current front-end theme * * @param bool $force * @return string * @access public */ public function GetFrontThemePath($force = false) { static $path = null; if ( !$force && isset($path) ) { return $path; } $theme_id = $this->GetVar('m_theme'); if ( !$theme_id ) { $theme_id = 'default'; // $this->GetDefaultThemeId(1); // 1 to force front-end mode! } $this->SetVar('m_theme', $theme_id); $this->SetVar('theme.current_id', $theme_id); // KOSTJA: this is to fool theme' getPassedID $theme = $this->recallObject('theme.current'); /* @var $theme ThemeItem */ if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) { return false; } // assign & then return, since it's static variable $path = '/themes/' . $theme->GetDBField('Name'); return $path; } /** * Returns primary front/admin language id * * @param bool $init * @return int * @access public */ public function GetDefaultLanguageId($init = false) { $cache_key = 'primary_language_info[%LangSerial%]'; $language_info = $this->getCache($cache_key); if ( $language_info === false ) { // cache primary language info first $language_config = $this->getUnitConfig('lang'); $table = $language_config->getTableName(); $id_field = $language_config->getIDField(); $this->Conn->nextQueryCachable = true; $sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey FROM ' . $table . ' WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)'; $language_info = $this->Conn->GetCol($sql, 'LanguageKey'); if ( $language_info !== false ) { $this->setCache($cache_key, $language_info); } } $language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front'; if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) { // get from cache return $language_info[$language_key]; } $language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false; if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) { $language_id = 1; } return $language_id; } /** * Returns front-end primary theme id (even, when called from admin console) * * @param bool $force_front * @return int * @access public */ public function GetDefaultThemeId($force_front = false) { static $theme_id = 0; if ( $theme_id > 0 ) { return $theme_id; } if ( kUtil::constOn('DBG_FORCE_THEME') ) { $theme_id = DBG_FORCE_THEME; } elseif ( !$force_front && $this->isAdmin ) { $theme_id = 999; } else { $cache_key = 'primary_theme[%ThemeSerial%]'; $theme_id = $this->getCache($cache_key); if ( $theme_id === false ) { $this->Conn->nextQueryCachable = true; $theme_config = $this->getUnitConfig('theme'); $sql = 'SELECT ' . $theme_config->getIDField() . ' FROM ' . $theme_config->getTableName() . ' WHERE (PrimaryTheme = 1) AND (Enabled = 1)'; $theme_id = $this->Conn->GetOne($sql); if ( $theme_id !== false ) { $this->setCache($cache_key, $theme_id); } } } return $theme_id; } /** * Returns site primary currency ISO code * * @return string * @access public * @todo Move into In-Commerce */ public function GetPrimaryCurrency() { $cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId'); $currency_iso = $this->getCache($cache_key); if ( $currency_iso === false ) { if ( $this->prefixRegistred('curr') ) { $this->Conn->nextQueryCachable = true; $currency_id = $this->siteDomainField('PrimaryCurrencyId'); $sql = 'SELECT ISO FROM ' . $this->getUnitConfig('curr')->getTableName() . ' WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1'); $currency_iso = $this->Conn->GetOne($sql); } else { $currency_iso = 'USD'; } $this->setCache($cache_key, $currency_iso); } return $currency_iso; } /** * Returns site domain field. When none of site domains are found false is returned. * * @param string $field * @param bool $formatted * @param string $format * @return mixed * @todo Move into separate module */ public function siteDomainField($field, $formatted = false, $format = null) { if ( $this->isAdmin ) { // don't apply any filtering in administrative console return false; } if ( !$this->siteDomain ) { $this->siteDomain = $this->recallObject('site-domain.current', null, Array ('live_table' => true)); /* @var $site_domain kDBItem */ } if ( $this->siteDomain->isLoaded() ) { return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field); } return false; } /** * Registers default classes such as kDBEventHandler, kUrlManager * * Called automatically while initializing kApplication * * @return void * @access public */ public function RegisterDefaultClasses() { $this->registerClass('kHelper', KERNEL_PATH . '/kbase.php'); $this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php'); $this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php'); $this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager'); $this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php'); $this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php'); $this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php'); $this->registerClass('kSubscriptionManager', KERNEL_PATH . '/managers/subscription_manager.php'); $this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php'); $this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php'); $this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php'); $this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php'); $this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php'); $this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache'); $this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php'); $this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php'); $this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php'); $this->registerClass('kLogger', KERNEL_PATH . '/utility/logger.php'); $this->registerClass('kUnitConfig', KERNEL_PATH . '/utility/unit_config.php'); $this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php'); $this->registerClass('kUnitConfigCloner', KERNEL_PATH . '/utility/unit_config_cloner.php'); $this->registerClass('PasswordHash', KERNEL_PATH . '/utility/php_pass.php'); // Params class descendants $this->registerClass('kArray', KERNEL_PATH . '/utility/params.php'); $this->registerClass('Params', KERNEL_PATH . '/utility/params.php'); $this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions'); $this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params'); $this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery'); // session $this->registerClass('kCookieHasher', KERNEL_PATH . '/utility/cookie_hasher.php'); $this->registerClass('Session', KERNEL_PATH . '/session/session.php'); $this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php'); $this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session'); $this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage'); // template parser $this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php'); $this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor'); $this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php'); $this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php'); $this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php'); $this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php'); // database $this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php'); $this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php'); $this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php'); $this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php'); $this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php'); $this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php'); $this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php'); $this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php'); $this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php'); // email sending $this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php'); $this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender'); $this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket'); // do not move to config - this helper is used before configs are read $this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper'); } /** * Registers default build events * * @return void * @access protected */ protected function RegisterDefaultBuildEvents() { $this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild'); } /** * Returns cached category information by given cache name. All given category * information is recached, when at least one of 4 caches is missing. * * @param int $category_id * @param string $name cache name = {filenames, category_designs, category_tree} * @return string * @access public */ public function getCategoryCache($category_id, $name) { return $this->cacheManager->getCategoryCache($category_id, $name); } /** * Returns caching type (none, memory, temporary) * * @param int $caching_type * @return bool * @access public */ public function isCachingType($caching_type) { return $this->cacheManager->isCachingType($caching_type); } /** * Increments serial based on prefix and it's ID (optional) * * @param string $prefix * @param int $id ID (value of IDField) or ForeignKeyField:ID * @param bool $increment * @return string * @access public */ public function incrementCacheSerial($prefix, $id = null, $increment = true) { return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment); } /** * Returns cached $key value from cache named $cache_name * * @param int $key key name from cache * @param bool $store_locally store data locally after retrieved * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0) { return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds); } /** * Stores new $value in cache with $key name * * @param int $key key name to add to cache * @param mixed $value value of cached record * @param int $expiration when value expires (0 - doesn't expire) * @return bool * @access public */ public function setCache($key, $value, $expiration = 0) { return $this->cacheManager->setCache($key, $value, $expiration); } /** * Stores new $value in cache with $key name (only if it's not there) * * @param int $key key name to add to cache * @param mixed $value value of cached record * @param int $expiration when value expires (0 - doesn't expire) * @return bool * @access public */ public function addCache($key, $value, $expiration = 0) { return $this->cacheManager->addCache($key, $value, $expiration); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @return bool * @access public */ public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0) { return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from cache * * @param string $key * @return void * @access public */ public function deleteCache($key) { $this->cacheManager->deleteCache($key); } /** * Reset's all memory cache at once * * @return void * @access public */ public function resetCache() { $this->cacheManager->resetCache(); } /** * Returns value from database cache * * @param string $name key name * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getDBCache($name, $max_rebuild_seconds = 0) { return $this->cacheManager->getDBCache($name, $max_rebuild_seconds); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @return void * @access public */ public function setDBCache($name, $value, $expiration = false) { $this->cacheManager->setDBCache($name, $value, $expiration); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @return bool * @access public */ public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0) { return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from database cache * * @param string $name * @return void * @access public */ public function deleteDBCache($name) { $this->cacheManager->deleteDBCache($name); } /** * Registers each module specific constants if any found * * @return bool * @access protected */ protected function registerModuleConstants() { if ( file_exists(KERNEL_PATH . '/constants.php') ) { kUtil::includeOnce(KERNEL_PATH . '/constants.php'); } if ( !$this->ModuleInfo ) { return false; } foreach ($this->ModuleInfo as $module_info) { $constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php'; if ( file_exists($constants_file) ) { kUtil::includeOnce($constants_file); } } return true; } /** * Performs redirect to hard maintenance template * * @return void * @access public */ public function redirectToMaintenance() { $maintenance_page = WRITEBALE_BASE . '/maintenance.html'; $query_string = ''; // $this->isAdmin ? '' : '?next_template=' . kUtil::escape($_SERVER['REQUEST_URI'], kUtil::ESCAPE_URL); if ( file_exists(FULL_PATH . $maintenance_page) ) { header('Location: ' . BASE_PATH . $maintenance_page . $query_string); exit; } } /** * Actually runs the parser against current template and stores parsing result * * This method gets 't' variable passed to the script, loads the template given in 't' variable and * parses it. The result is store in {@link $this->HTML} property. * * @return void * @access public */ public function Run() { // process maintenance mode redirect: begin $maintenance_mode = $this->getMaintenanceMode(); if ( $maintenance_mode == MaintenanceMode::HARD ) { $this->redirectToMaintenance(); } elseif ( $maintenance_mode == MaintenanceMode::SOFT ) { $maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate'); if ( $this->GetVar('t') != $maintenance_template ) { $redirect_params = Array (); if ( !$this->isAdmin ) { $redirect_params['next_template'] = $_SERVER['REQUEST_URI']; } $this->Redirect($maintenance_template, $redirect_params); } } // process maintenance mode redirect: end if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Run:'); } if ( $this->isAdminUser ) { // for permission checking in events & templates $this->LinkVar('module'); // for common configuration templates $this->LinkVar('module_key'); // for common search templates $this->LinkVar('section'); // for common configuration templates if ( $this->GetVar('m_opener') == 'p' ) { $this->LinkVar('main_prefix'); // window prefix, that opened selector $this->LinkVar('dst_field'); // field to set value choosed in selector } if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) { // hide debug output from ajax requests automatically kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it } } elseif ( $this->GetVar('admin') ) { $admin_session = $this->recallObject('Session.admin'); /* @var $admin_session Session */ // store Admin Console User's ID to Front-End's session for cross-session permission checks $this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id')); if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) { // user can edit cms blocks (when viewing front-end through admin's frame) $editing_mode = $this->GetVar('editing_mode'); define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE); } } kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything $this->Phrases->setPhraseEditing(); $this->EventManager->ProcessRequest(); $this->InitParser(); $t = $this->GetVar('render_template', $this->GetVar('t')); if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) { $cms_handler = $this->recallObject('st_EventHandler'); /* @var $cms_handler CategoriesEventHandler */ $t = ltrim($cms_handler->GetDesignTemplate(), '/'); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendHTML('Design Template: ' . $t . '; CategoryID: ' . $this->GetVar('m_cat_id')); } } /*else { $cms_handler->SetCatByTemplate(); }*/ if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Parsing:'); } $this->HTML = $this->Parser->Run($t); if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application after Parsing:'); } } /** * Only renders template * * @see kDBEventHandler::_errorNotFound() */ public function QuickRun() { // discard any half-parsed content ob_clean(); // replace current page content with 404 $this->InitParser(); $this->HTML = $this->Parser->Run($this->GetVar('t')); } /** * Performs template parser/cache initialization * * @param bool|string $theme_name * @return void * @access public */ public function InitParser($theme_name = false) { if ( !is_object($this->Parser) ) { $this->Parser = $this->recallObject('NParser'); $this->TemplatesCache = $this->recallObject('TemplatesCache'); } $this->TemplatesCache->forceThemeName = $theme_name; } /** * Send the parser results to browser * * Actually send everything stored in {@link $this->HTML}, to the browser by echoing it. * * @return void * @access public */ public function Done() { $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); $debug_mode = defined('DEBUG_MODE') && $this->isDebugMode(); if ( $debug_mode ) { if ( kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Done:'); } $this->Session->SaveData(); // adds session data to debugger report $this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true); } else { // send "Set-Cookie" header before any output is made $this->Session->SetSession(); $this->HTML = ob_get_clean() . $this->HTML; } $this->_outputPage(); $this->cacheManager->UpdateApplicationCache(); if ( !$debug_mode ) { $this->Session->SaveData(); } $this->EventManager->runScheduledTasks(); if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) { $this->_storeStatistics(); } } /** * Outputs generated page content to end-user * * @return void * @access protected */ protected function _outputPage() { $this->setContentType(); ob_start(); if ( $this->UseOutputCompression() ) { $compression_level = $this->ConfigValue('OutputCompressionLevel'); if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) { $compression_level = 7; } header('Content-Encoding: gzip'); echo gzencode($this->HTML, $compression_level); } else { // when gzip compression not used connection won't be closed early! echo $this->HTML; } // send headers to tell the browser to close the connection header('Content-Length: ' . ob_get_length()); header('Connection: close'); // flush all output ob_end_flush(); if ( ob_get_level() ) { ob_flush(); } flush(); // close current session if ( session_id() ) { session_write_close(); } } /** * Stores script execution statistics to database * * @return void * @access protected */ protected function _storeStatistics() { global $start; $script_time = microtime(true) - $start; $query_statistics = $this->Conn->getQueryStatistics(); // time & count $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'StatisticsCapture WHERE TemplateName = ' . $this->Conn->qstr($this->GetVar('t')); $data = $this->Conn->GetRow($sql); if ( $data ) { $this->_updateAverageStatistics($data, 'ScriptTime', $script_time); $this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']); $this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']); $data['Hits']++; $data['LastHit'] = time(); $this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']); } else { $data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time; $data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time']; $data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count']; $data['TemplateName'] = $this->GetVar('t'); $data['Hits'] = 1; $data['LastHit'] = time(); $this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture'); } } /** * Calculates average time for statistics * * @param Array $data * @param string $field_prefix * @param float $current_value * @return void * @access protected */ protected function _updateAverageStatistics(&$data, $field_prefix, $current_value) { $data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1); if ( $current_value < $data[$field_prefix . 'Min'] ) { $data[$field_prefix . 'Min'] = $current_value; } if ( $current_value > $data[$field_prefix . 'Max'] ) { $data[$field_prefix . 'Max'] = $current_value; } } /** * Remembers slow query SQL and execution time into log * * @param string $slow_sql * @param int $time * @return void * @access public */ public function logSlowQuery($slow_sql, $time) { $query_crc = kUtil::crc32($slow_sql); $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'SlowSqlCapture WHERE QueryCrc = ' . $query_crc; $data = $this->Conn->Query($sql, null, true); if ( $data ) { $this->_updateAverageStatistics($data, 'Time', $time); $template_names = explode(',', $data['TemplateNames']); array_push($template_names, $this->GetVar('t')); $data['TemplateNames'] = implode(',', array_unique($template_names)); $data['Hits']++; $data['LastHit'] = time(); $this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']); } else { $data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time; $data['SqlQuery'] = $slow_sql; $data['QueryCrc'] = $query_crc; $data['TemplateNames'] = $this->GetVar('t'); $data['Hits'] = 1; $data['LastHit'] = time(); $this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture'); } } /** * Checks if output compression options is available * * @return bool * @access protected */ protected function UseOutputCompression() { if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) { return false; } $accept_encoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : ''; return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($accept_encoding, 'gzip'); } // Facade /** * Returns current session id (SID) * * @return int * @access public */ public function GetSID() { $session = $this->recallObject('Session'); /* @var $session Session */ return $session->GetID(); } /** * Destroys current session * * @return void * @access public * @see UserHelper::logoutUser() */ public function DestroySession() { $session = $this->recallObject('Session'); /* @var $session Session */ $session->Destroy(); } /** * Returns variable passed to the script as GET/POST/COOKIE * * @param string $name Name of variable to retrieve * @param mixed $default default value returned in case if variable not present * @return mixed * @access public */ public function GetVar($name, $default = false) { return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default; } /** + * Removes forceful escaping done to the variable upon Front-End submission. + * + * @param string|array $value Value. + * + * @return string|array + * @see kHttpQuery::StripSlashes + * @todo Temporary method for marking problematic places to take care of, when forceful escaping will be removed. + */ + public function unescapeRequestVariable($value) + { + return $this->HttpQuery->unescapeRequestVariable($value); + } + + /** * Returns variable passed to the script as $type * * @param string $name Name of variable to retrieve * @param string $type Get/Post/Cookie * @param mixed $default default value returned in case if variable not present * @return mixed * @access public */ public function GetVarDirect($name, $type, $default = false) { // $type = ucfirst($type); $array = $this->HttpQuery->$type; return isset($array[$name]) ? $array[$name] : $default; } /** * Returns ALL variables passed to the script as GET/POST/COOKIE * * @return Array * @access public * @deprecated */ public function GetVars() { return $this->HttpQuery->GetParams(); } /** * Set the variable 'as it was passed to the script through GET/POST/COOKIE' * * This could be useful to set the variable when you know that * other objects would relay on variable passed from GET/POST/COOKIE * or you could use SetVar() / GetVar() pairs to pass the values between different objects.
* * @param string $var Variable name to set * @param mixed $val Variable value * @return void * @access public */ public function SetVar($var,$val) { $this->HttpQuery->Set($var, $val); } /** * Deletes kHTTPQuery variable * * @param string $var * @return void * @todo Think about method name */ public function DeleteVar($var) { $this->HttpQuery->Remove($var); } /** * Deletes Session variable * * @param string $var * @return void * @access public */ public function RemoveVar($var) { $this->Session->RemoveVar($var); } /** * Removes variable from persistent session * * @param string $var * @return void * @access public */ public function RemovePersistentVar($var) { $this->Session->RemovePersistentVar($var); } /** * Restores Session variable to it's db version * * @param string $var * @return void * @access public */ public function RestoreVar($var) { $this->Session->RestoreVar($var); } /** * Returns session variable value * * Return value of $var variable stored in Session. An optional default value could be passed as second parameter. * * @param string $var Variable name * @param mixed $default Default value to return if no $var variable found in session * @return mixed * @access public * @see Session::RecallVar() */ public function RecallVar($var,$default=false) { return $this->Session->RecallVar($var,$default); } /** * Returns variable value from persistent session * * @param string $var * @param mixed $default * @return mixed * @access public * @see Session::RecallPersistentVar() */ public function RecallPersistentVar($var, $default = false) { return $this->Session->RecallPersistentVar($var, $default); } /** * Stores variable $val in session under name $var * * Use this method to store variable in session. Later this variable could be recalled. * * @param string $var Variable name * @param mixed $val Variable value * @param bool $optional * @return void * @access public * @see kApplication::RecallVar() */ public function StoreVar($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVar($var, $val, $optional); } /** * Stores variable to persistent session * * @param string $var * @param mixed $val * @param bool $optional * @return void * @access public */ public function StorePersistentVar($var, $val, $optional = false) { $this->Session->StorePersistentVar($var, $val, $optional); } /** * Stores default value for session variable * * @param string $var * @param string $val * @param bool $optional * @return void * @access public * @see Session::RecallVar() * @see Session::StoreVar() */ public function StoreVarDefault($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVarDefault($var, $val, $optional); } /** * Links HTTP Query variable with session variable * * If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session. * This method could be used for making sure that GetVar will return query or session value for given * variable, when query variable should overwrite session (and be stored there for later use).
* This could be used for passing item's ID into popup with multiple tab - * in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id'). * After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @param bool $optional * @return void * @access public */ public function LinkVar($var, $ses_var = null, $default = '', $optional = false) { if ( !isset($ses_var) ) { $ses_var = $var; } if ( $this->GetVar($var) !== false ) { $this->StoreVar($ses_var, $this->GetVar($var), $optional); } else { $this->SetVar($var, $this->RecallVar($ses_var, $default)); } } /** * Returns variable from HTTP Query, or from session if not passed in HTTP Query * * The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed. * Returns the default value if variable does not exist in session and was not passed in HTTP Query * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @return mixed * @access public * @see LinkVar */ public function GetLinkedVar($var, $ses_var = null, $default = '') { $this->LinkVar($var, $ses_var, $default); return $this->GetVar($var); } /** * Renders given tag and returns it's output * * @param string $prefix * @param string $tag * @param Array $params * @return mixed * @access public * @see kApplication::InitParser() */ public function ProcessParsedTag($prefix, $tag, $params) { $processor = $this->Parser->GetProcessor($prefix); /* @var $processor kDBTagProcessor */ return $processor->ProcessParsedTag($tag, $params, $prefix); } /** * Return object of IDBConnection interface * * Return object of IDBConnection interface already connected to the project database, configurable in config.php * * @return IDBConnection * @access public */ public function &GetADODBConnection() { return $this->Conn; } /** * Allows to parse given block name or include template * * @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name. * @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter. * @param bool $as_template * @return string * @access public */ public function ParseBlock($params, $pass_params = false, $as_template = false) { if ( substr($params['name'], 0, 5) == 'html:' ) { return substr($params['name'], 5); } return $this->Parser->ParseBlock($params, $pass_params, $as_template); } /** * Checks, that we have given block defined * * @param string $name * @return bool * @access public */ public function ParserBlockFound($name) { return $this->Parser->blockFound($name); } /** * Allows to include template with a given name and given parameters * * @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name. * @return string * @access public */ public function IncludeTemplate($params) { return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0); } /** * Return href for template * * @param string $t Template path * @param string $prefix index.php prefix - could be blank, 'admin' * @param Array $params * @param string $index_file * @return string */ public function HREF($t, $prefix = '', $params = Array (), $index_file = null) { return $this->UrlManager->HREF($t, $prefix, $params, $index_file); } /** * Returns theme template filename and it's corresponding page_id based on given seo template * * @param string $seo_template * @return string * @access public */ public function getPhysicalTemplate($seo_template) { return $this->UrlManager->getPhysicalTemplate($seo_template); } /** * Returns seo template by physical template * * @param string $physical_template * @return string * @access public */ public function getSeoTemplate($physical_template) { return $this->UrlManager->getSeoTemplate($physical_template); } /** * Returns template name, that corresponds with given virtual (not physical) page id * * @param int $page_id * @return string|bool * @access public */ public function getVirtualPageTemplate($page_id) { return $this->UrlManager->getVirtualPageTemplate($page_id); } /** * Returns section template for given physical/virtual template * * @param string $template * @param int $theme_id * @return string * @access public */ public function getSectionTemplate($template, $theme_id = null) { return $this->UrlManager->getSectionTemplate($template, $theme_id); } /** * Returns variables with values that should be passed through with this link + variable list * * @param Array $params * @return Array * @access public */ public function getPassThroughVariables(&$params) { return $this->UrlManager->getPassThroughVariables($params); } /** * Builds url * * @param string $t * @param Array $params * @param string $pass * @param bool $pass_events * @param bool $env_var * @return string * @access public */ public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true) { return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var); } /** * Process QueryString only, create * events, ids, based on config * set template name and sid in * desired application variables. * * @param string $env_var environment string value * @param string $pass_name * @return Array * @access public */ public function processQueryString($env_var, $pass_name = 'passed') { return $this->UrlManager->plain->parse($env_var, $pass_name); } /** * Parses rewrite url and returns parsed variables * * @param string $url * @param string $pass_name * @return Array * @access public */ public function parseRewriteUrl($url, $pass_name = 'passed') { return $this->UrlManager->rewrite->parse($url, $pass_name); } /** * Returns base part of all urls, build on website * * @param string $domain Domain override. * @param boolean $ssl_redirect Redirect to/from SSL. * * @return string */ public function BaseURL($domain = '', $ssl_redirect = null) { if ( $ssl_redirect === null ) { // stay on same encryption level return PROTOCOL . ($domain ? $domain : SERVER_NAME) . (defined('PORT') ? ':' . PORT : '') . BASE_PATH . '/'; } if ( $ssl_redirect ) { // going from http:// to https:// $protocol = 'https://'; $domain = $this->getSecureDomain(); } else { // going from https:// to http:// $protocol = 'http://'; $domain = $this->siteDomainField('DomainName'); if ( $domain === false ) { $domain = DOMAIN; // not on site domain } } return $protocol . $domain . (defined('PORT') ? ':' . PORT : '') . BASE_PATH . '/'; } /** * Returns secure domain. * * @return string */ public function getSecureDomain() { $ret = $this->isAdmin ? $this->ConfigValue('AdminSSLDomain') : false; if ( !$ret ) { $ssl_domain = $this->siteDomainField('SSLDomainName'); return strlen($ssl_domain) ? $ssl_domain : $this->ConfigValue('SSLDomain'); } return $ret; } /** * Redirects user to url, that's build based on given parameters * * @param string $t * @param Array $params * @param string $prefix * @param string $index_file * @return void * @access public */ public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null) { $js_redirect = getArrayValue($params, 'js_redirect'); if ( $t == '' || $t === true ) { $t = $this->GetVar('t'); } // pass prefixes and special from previous url if ( array_key_exists('js_redirect', $params) ) { unset($params['js_redirect']); } // allows to send custom responce code along with redirect header if ( array_key_exists('response_code', $params) ) { $response_code = (int)$params['response_code']; unset($params['response_code']); } else { $response_code = 302; // Found } if ( !array_key_exists('pass', $params) ) { $params['pass'] = 'all'; } if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) { // redirects to the same template as current $params['ajax'] = 'yes'; } $location = $this->HREF($t, $prefix, $params, $index_file); if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) { $this->Debugger->appendTrace(); echo 'Debug output above !!!
' . "\n"; if ( array_key_exists('HTTP_REFERER', $_SERVER) ) { echo 'Referer: ' . $_SERVER['HTTP_REFERER'] . '
' . "\n"; } echo "Proceed to redirect:
{$location}
\n"; } else { if ( $js_redirect ) { // show "redirect" template instead of redirecting, // because "Set-Cookie" header won't work, when "Location" // header is used later $this->SetVar('t', 'redirect'); $this->SetVar('redirect_to', $location); // make all additional parameters available on "redirect" template too foreach ($params as $name => $value) { $this->SetVar($name, $value); } return; } else { if ( $this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t') ) { // redirection to other then current template during ajax request kUtil::safeDefine('DBG_SKIP_REPORTING', 1); echo '#redirect#' . $location; } elseif ( headers_sent() != '' ) { // some output occurred -> redirect using javascript echo ''; } else { // no output before -> redirect using HTTP header // header('HTTP/1.1 302 Found'); header('Location: ' . $location, true, $response_code); } } } // session expiration is called from session initialization, // that's why $this->Session may be not defined here $session = $this->recallObject('Session'); /* @var $session Session */ if ( $this->InitDone ) { // if redirect happened in the middle of application initialization don't call event, // that presumes that application was successfully initialized $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); } $session->SaveData(); ob_end_flush(); exit; } /** * Returns translation of given label * * @param string $label * @param bool $allow_editing return translation link, when translation is missing on current language * @param bool $use_admin use current Admin Console language to translate phrase * @return string * @access public */ public function Phrase($label, $allow_editing = true, $use_admin = false) { return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin); } /** * Replace language tags in exclamation marks found in text * * @param string $text * @param bool $force_escape force escaping, not escaping of resulting string * @return string * @access public */ public function ReplaceLanguageTags($text, $force_escape = null) { return $this->Phrases->ReplaceLanguageTags($text, $force_escape); } /** * Checks if user is logged in, and creates * user object if so. User object can be recalled * later using "u.current" prefix_special. Also you may * get user id by getting "u.current_id" variable. * * @return void * @access protected */ protected function ValidateLogin() { $session = $this->recallObject('Session'); /* @var $session Session */ $user_id = $session->GetField('PortalUserId'); if ( !$user_id && $user_id != USER_ROOT ) { $user_id = USER_GUEST; } $this->SetVar('u.current_id', $user_id); if ( !$this->isAdmin ) { // needed for "profile edit", "registration" forms ON FRONT ONLY $this->SetVar('u_id', $user_id); } $this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional $this->isAdminUser = $this->isAdmin && $this->LoggedIn(); if ( $this->GetVar('expired') == 1 ) { // this parameter is set only from admin $user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login')); /* @var $user UsersItem */ $user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired'); } $this->HandleEvent(new kEvent('adm:OnLogHttpRequest')); if ( $user_id != USER_GUEST ) { // normal users + root $this->LoadPersistentVars(); } $user_timezone = $this->Session->GetField('TimeZone'); if ( $user_timezone ) { date_default_timezone_set($user_timezone); } } /** * Loads current user persistent session data * * @return void * @access public */ public function LoadPersistentVars() { $this->Session->LoadPersistentVars(); } /** * Returns configuration option value by name * * @param string $name * @return string * @access public */ public function ConfigValue($name) { return $this->cacheManager->ConfigValue($name); } /** * Changes value of individual configuration variable (+resets cache, when needed) * * @param string $name * @param string $value * @param bool $local_cache_only * @return string * @access public */ public function SetConfigValue($name, $value, $local_cache_only = false) { return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only); } /** * Allows to process any type of event * * @param kEvent $event * @param Array $params * @param Array $specific_params * @return void * @access public */ public function HandleEvent($event, $params = null, $specific_params = null) { if ( isset($params) ) { $event = new kEvent($params, $specific_params); } $this->EventManager->HandleEvent($event); } /** * Notifies event subscribers, that event has occured * * @param kEvent $event * @return void */ public function notifyEventSubscribers(kEvent $event) { $this->EventManager->notifySubscribers($event); } /** * Allows to process any type of event * * @param kEvent $event * @return bool * @access public */ public function eventImplemented(kEvent $event) { return $this->EventManager->eventImplemented($event); } /** * Registers new class in the factory * * @param string $real_class Real name of class as in class declaration * @param string $file Filename in what $real_class is declared * @param string $pseudo_class Name under this class object will be accessed using getObject method * @return void * @access public */ public function registerClass($real_class, $file, $pseudo_class = null) { $this->Factory->registerClass($real_class, $file, $pseudo_class); } /** * Unregisters existing class from factory * * @param string $real_class Real name of class as in class declaration * @param string $pseudo_class Name under this class object is accessed using getObject method * @return void * @access public */ public function unregisterClass($real_class, $pseudo_class = null) { $this->Factory->unregisterClass($real_class, $pseudo_class); } /** * Add new scheduled task * * @param string $short_name name to be used to store last maintenance run info * @param string $event_string * @param int $run_schedule run schedule like for Cron * @param string $module * @param int $status * @access public */ public function registerScheduledTask($short_name, $event_string, $run_schedule, $module, $status = STATUS_ACTIVE) { $this->EventManager->registerScheduledTask($short_name, $event_string, $run_schedule, $module, $status); } /** * Registers Hook from subprefix event to master prefix event * * Pattern: Observer * * @param string $hook_event * @param string $do_event * @param int $mode * @param bool $conditional * @access public */ public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false) { $this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional); } /** * Registers build event for given pseudo class * * @param string $pseudo_class * @param string $event_name * @access public */ public function registerBuildEvent($pseudo_class, $event_name) { $this->EventManager->registerBuildEvent($pseudo_class, $event_name); } /** * Allows one TagProcessor tag act as other TagProcessor tag * * @param Array $tag_info * @return void * @access public */ public function registerAggregateTag($tag_info) { $aggregator = $this->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $tag_data = Array ( $tag_info['LocalPrefix'], $tag_info['LocalTagName'], getArrayValue($tag_info, 'LocalSpecial') ); $aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data); } /** * Returns object using params specified, creates it if is required * * @param string $name * @param string $pseudo_class * @param Array $event_params * @param Array $arguments * @return kBase */ public function recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ()) { /*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) { // first time, when object with "_prefix_here_" prefix is accessed $this->Debugger->appendTrace(); }*/ return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments); } /** * Returns tag processor for prefix specified * * @param string $prefix * @return kDBTagProcessor * @access public */ public function recallTagProcessor($prefix) { $this->InitParser(); // because kDBTagProcesor is in NParser dependencies return $this->recallObject($prefix . '_TagProcessor'); } /** * Checks if object with prefix passes was already created in factory * * @param string $name object pseudo_class, prefix * @return bool * @access public */ public function hasObject($name) { return $this->Factory->hasObject($name); } /** * Removes object from storage by given name * * @param string $name Object's name in the Storage * @return void * @access public */ public function removeObject($name) { $this->Factory->DestroyObject($name); } /** * Get's real class name for pseudo class, includes class file and creates class instance * * Pattern: Factory Method * * @param string $pseudo_class * @param Array $arguments * @return kBase * @access public */ public function makeClass($pseudo_class, $arguments = Array ()) { return $this->Factory->makeClass($pseudo_class, $arguments); } /** * Checks if application is in debug mode * * @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant * @return bool * @author Alex * @access public */ public function isDebugMode($check_debugger = true) { $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE; if ($check_debugger) { $debug_mode = $debug_mode && is_object($this->Debugger); } return $debug_mode; } /** * Apply url rewriting used by mod_rewrite or not * * @param bool|null $ssl Force ssl link to be build * @return bool * @access public */ public function RewriteURLs($ssl = false) { // case #1,#4: // we want to create https link from http mode // we want to create https link from https mode // conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL') // case #2,#3: // we want to create http link from https mode // we want to create http link from http mode // conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://') $allow_rewriting = (!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http || // or allow rewriting for redirect TO httpS or when already in httpS (($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config! return kUtil::constOn('MOD_REWRITE') && $allow_rewriting; } /** * Returns unit config for given prefix * * @param string $prefix * @return kUnitConfig * @access public */ public function getUnitConfig($prefix) { return $this->UnitConfigReader->getUnitConfig($prefix); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix * @return bool */ public function prefixRegistred($prefix) { return $this->UnitConfigReader->prefixRegistered($prefix); } /** * Splits any mixing of prefix and * special into correct ones * * @param string $prefix_special * @return Array * @access public */ public function processPrefix($prefix_special) { return $this->Factory->processPrefix($prefix_special); } /** * Set's new event for $prefix_special * passed * * @param string $prefix_special * @param string $event_name * @return void * @access public */ public function setEvent($prefix_special, $event_name) { $this->EventManager->setEvent($prefix_special, $event_name); } /** * SQL Error Handler * * @param int $code * @param string $msg * @param string $sql * @return bool * @access public * @throws Exception * @deprecated */ public function handleSQLError($code, $msg, $sql) { return $this->_logger->handleSQLError($code, $msg, $sql); } /** * Returns & blocks next ResourceId available in system * * @return int * @access public */ public function NextResourceId() { $table_name = TABLE_PREFIX . 'IdGenerator'; $this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE'); $this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1'); $id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name); if ( $id === false ) { $this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)'); $id = 2; } $this->Conn->Query('UNLOCK TABLES'); return $id - 1; } /** * Returns genealogical main prefix for sub-table prefix passes * OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix * * @param string $current_prefix * @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not * @return string * @access public */ public function GetTopmostPrefix($current_prefix, $real_top = false) { // 1. get genealogical tree of $current_prefix $prefixes = Array ($current_prefix); while ($parent_prefix = $this->getUnitConfig($current_prefix)->getParentPrefix()) { if ( !$this->prefixRegistred($parent_prefix) ) { // stop searching, when parent prefix is not registered break; } $current_prefix = $parent_prefix; array_unshift($prefixes, $current_prefix); } if ( $real_top ) { return $current_prefix; } // 2. find what if parent is passed $passed = explode(',', $this->GetVar('all_passed')); foreach ($prefixes as $a_prefix) { if ( in_array($a_prefix, $passed) ) { return $a_prefix; } } return $current_prefix; } /** * Triggers email event of type Admin * * @param string $email_template_name * @param int $to_user_id * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access public */ public function emailAdmin($email_template_name, $to_user_id = null, $send_params = Array ()) { return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_ADMIN, $to_user_id, $send_params); } /** * Triggers email event of type User * * @param string $email_template_name * @param int $to_user_id * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access public */ public function emailUser($email_template_name, $to_user_id = null, $send_params = Array ()) { return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_FRONTEND, $to_user_id, $send_params); } /** * Triggers general email event * * @param string $email_template_name * @param int $email_template_type (0 for User, 1 for Admin) * @param int $to_user_id * @param array $send_params associative array of direct send params, * possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access protected */ protected function _email($email_template_name, $email_template_type, $to_user_id = null, $send_params = Array ()) { $email = $this->makeClass('kEmail'); /* @var $email kEmail */ if ( !$email->findTemplate($email_template_name, $email_template_type) ) { return false; } $email->setParams($send_params); return $email->send($to_user_id); } /** * Allows to check if user in this session is logged in or not * * @return bool * @access public */ public function LoggedIn() { // no session during expiration process return is_null($this->Session) ? false : $this->Session->LoggedIn(); } /** * Check current user permissions based on it's group permissions in specified category * * @param string $name permission name * @param int $cat_id category id, current used if not specified * @param int $type permission type {1 - system, 0 - per category} * @return int * @access public */ public function CheckPermission($name, $type = 1, $cat_id = null) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->CheckPermission($name, $type, $cat_id); } /** * Check current admin permissions based on it's group permissions in specified category * * @param string $name permission name * @param int $cat_id category id, current used if not specified * @param int $type permission type {1 - system, 0 - per category} * @return int * @access public */ public function CheckAdminPermission($name, $type = 1, $cat_id = null) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->CheckAdminPermission($name, $type, $cat_id); } /** * Set's any field of current visit * * @param string $field * @param mixed $value * @return void * @access public * @todo move to separate module */ public function setVisitField($field, $value) { if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) { // admin logins are not registered in visits list return; } $visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0)); /* @var $visit kDBItem */ if ( $visit->isLoaded() ) { $visit->SetDBField($field, $value); $visit->Update(); } } /** * Allows to check if in-portal is installed * * @return bool * @access public */ public function isInstalled() { return $this->InitDone && (count($this->ModuleInfo) > 0); } /** * Allows to determine if module is installed & enabled * * @param string $module_name * @return bool * @access public */ public function isModuleEnabled($module_name) { return $this->findModule('Name', $module_name) !== false; } /** * Returns Window ID of passed prefix main prefix (in edit mode) * * @param string $prefix * @return int * @access public */ public function GetTopmostWid($prefix) { $top_prefix = $this->GetTopmostPrefix($prefix); $mode = $this->GetVar($top_prefix . '_mode'); return $mode != '' ? substr($mode, 1) : ''; } /** * Get temp table name * * @param string $table * @param mixed $wid * @return string * @access public */ public function GetTempName($table, $wid = '') { return $this->GetTempTablePrefix($wid) . $table; } /** * Builds temporary table prefix based on given window id * * @param string $wid * @return string * @access public */ public function GetTempTablePrefix($wid = '') { if ( preg_match('/prefix:(.*)/', $wid, $regs) ) { $wid = $this->GetTopmostWid($regs[1]); } return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_'; } /** * Checks if given table is a temporary table * * @param string $table * @return bool * @access public */ public function IsTempTable($table) { static $cache = Array (); if ( !array_key_exists($table, $cache) ) { $cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table); } return (bool)$cache[$table]; } /** * Checks, that given prefix is in temp mode * * @param string $prefix * @param string $special * @return bool * @access public */ public function IsTempMode($prefix, $special = '') { $top_prefix = $this->GetTopmostPrefix($prefix); $var_names = Array ( $top_prefix, rtrim($top_prefix . '_' . $special, '_'), // from post rtrim($top_prefix . '.' . $special, '.'), // assembled locally ); $var_names = array_unique($var_names); $temp_mode = false; foreach ($var_names as $var_name) { $value = $this->GetVar($var_name . '_mode'); if ( $value && (substr($value, 0, 1) == 't') ) { $temp_mode = true; break; } } return $temp_mode; } /** * Return live table name based on temp table name * * @param string $temp_table * @return string */ public function GetLiveName($temp_table) { if ( preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $temp_table, $rets) ) { // cut wid from table end if any return $rets[2]; } else { return $temp_table; } } /** * Stops processing of user request and displays given message * * @param string $message * @access public */ public function ApplicationDie($message = '') { - $message = ob_get_clean() . $message; + while ( ob_get_level() ) { + ob_end_clean(); + } if ( $this->isDebugMode() ) { $message .= $this->Debugger->printReport(true); } - echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message; - exit; + $this->HTML = $message; + $this->_outputPage(); } /** * Returns comma-separated list of groups from given user * * @param int $user_id * @return string */ public function getUserGroups($user_id) { switch ($user_id) { case USER_ROOT: $user_groups = $this->ConfigValue('User_LoggedInGroup'); break; case USER_GUEST: $user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup'); break; default: $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroupRelations WHERE PortalUserId = ' . (int)$user_id; $res = $this->Conn->GetCol($sql); $user_groups = Array ($this->ConfigValue('User_LoggedInGroup')); if ( $res ) { $user_groups = array_merge($user_groups, $res); } $user_groups = implode(',', $user_groups); } return $user_groups; } /** * Allows to detect if page is browsed by spider (293 scheduled_tasks supported) * * @return bool * @access public */ /*public function IsSpider() { static $is_spider = null; if ( !isset($is_spider) ) { $user_agent = trim($_SERVER['HTTP_USER_AGENT']); $robots = file(FULL_PATH . '/core/robots_list.txt'); foreach ($robots as $robot_info) { $robot_info = explode("\t", $robot_info, 3); if ( $user_agent == trim($robot_info[2]) ) { $is_spider = true; break; } } } return $is_spider; }*/ /** * Allows to detect table's presence in database * * @param string $table_name * @param bool $force * @return bool * @access public */ public function TableFound($table_name, $force = false) { return $this->Conn->TableFound($table_name, $force); } /** * Returns counter value * * @param string $name counter name * @param Array $params counter parameters * @param string $query_name specify query name directly (don't generate from parameters) * @param bool $multiple_results * @return mixed * @access public */ public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false) { $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ return $count_helper->getCounter($name, $params, $query_name, $multiple_results); } /** * Resets counter, which are affected by one of specified tables * * @param string $tables comma separated tables list used in counting sqls * @return void * @access public */ public function resetCounters($tables) { if ( kUtil::constOn('IS_INSTALL') ) { return; } $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $count_helper->resetCounters($tables); } /** * Sends XML header + optionally displays xml heading * * @param string|bool $xml_version * @return string * @access public * @author Alex */ public function XMLHeader($xml_version = false) { $this->setContentType('text/xml'); return $xml_version ? '' : ''; } /** * Returns category tree * * @param int $category_id * @return Array * @access public */ public function getTreeIndex($category_id) { $tree_index = $this->getCategoryCache($category_id, 'category_tree'); if ( $tree_index ) { $ret = Array (); list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index); return $ret; } return false; } /** * Base category of all categories * Usually replaced category, with ID = 0 in category-related operations. * * @return int * @access public */ public function getBaseCategory() { // same, what $this->findModule('Name', 'Core', 'RootCat') does // don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade return $this->ModuleInfo['Core']['RootCat']; } /** * Deletes all data, that was cached during unit config parsing (excluding unit config locations) * * @param Array $config_variables * @access public */ public function DeleteUnitCache($config_variables = null) { $this->cacheManager->DeleteUnitCache($config_variables); } /** * Deletes cached section tree, used during permission checking and admin console tree display * * @return void * @access public */ public function DeleteSectionCache() { $this->cacheManager->DeleteSectionCache(); } /** * Sets data from cache to object * * @param Array $data * @access public */ public function setFromCache(&$data) { $this->Factory->setFromCache($data); $this->UnitConfigReader->setFromCache($data); $this->EventManager->setFromCache($data); $this->ReplacementTemplates = $data['Application.ReplacementTemplates']; $this->RewriteListeners = $data['Application.RewriteListeners']; $this->ModuleInfo = $data['Application.ModuleInfo']; } /** * Gets object data for caching * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc) * * @access public * @return Array */ public function getToCache() { return array_merge( $this->Factory->getToCache(), $this->UnitConfigReader->getToCache(), $this->EventManager->getToCache(), Array ( 'Application.ReplacementTemplates' => $this->ReplacementTemplates, 'Application.RewriteListeners' => $this->RewriteListeners, 'Application.ModuleInfo' => $this->ModuleInfo, ) ); } public function delayUnitProcessing($method, $params) { $this->cacheManager->delayUnitProcessing($method, $params); } /** * Returns current maintenance mode state * * @param bool $check_ips * @return int * @access public */ public function getMaintenanceMode($check_ips = true) { $exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : ''; $setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT'; if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) { $exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false; if ( !$exception_ip ) { return constant($setting_name); } } return MaintenanceMode::NONE; } /** * Sets content type of the page * * @param string $content_type * @param bool $include_charset * @return void * @access public */ public function setContentType($content_type = 'text/html', $include_charset = null) { static $already_set = false; if ( $already_set ) { return; } $header = 'Content-type: ' . $content_type; if ( !isset($include_charset) ) { $include_charset = $content_type = 'text/html' || $content_type == 'text/plain' || $content_type = 'text/xml'; } if ( $include_charset ) { $header .= '; charset=' . CHARSET; } $already_set = true; header($header); } /** * Posts message to event log * * @param string $message * @param int $code * @param bool $write_now Allows further customization of log record by returning kLog object * @return bool|int|kLogger * @access public */ public function log($message, $code = null, $write_now = false) { $log = $this->_logger->prepare($message, $code)->addSource($this->_logger->createTrace(null, 1)); if ( $write_now ) { return $log->write(); } return $log; } /** * Deletes log with given id from database or disk, when database isn't available * * @param int $unique_id * @param int $storage_medium * @return void * @access public * @throws InvalidArgumentException */ public function deleteLog($unique_id, $storage_medium = kLogger::LS_AUTOMATIC) { $this->_logger->delete($unique_id, $storage_medium); } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public function getClientIp() { return $this->HttpQuery->getClientIp(); } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/processors/main_processor.php =================================================================== --- branches/5.3.x/core/kernel/processors/main_processor.php (revision 16110) +++ branches/5.3.x/core/kernel/processors/main_processor.php (revision 16111) @@ -1,1292 +1,1292 @@ Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set('t', $this->Application->GetVar('t')); $actions->Set('sid', $this->Application->GetSID()); $actions->Set('m_opener', $this->Application->GetVar('m_opener') ); } /** * Base folder for all template includes * * @param Array $params * @return string */ function TemplatesBase($params) { static $cached = Array (); $cache_key = crc32( serialize($params) ); if (!array_key_exists($cache_key, $cached)) { $module = array_key_exists('module', $params) ? $params['module'] : 'core'; if ($this->Application->isAdmin) { if ($module == 'in-portal') { $module = 'kernel'; } // remove leading slash + substitute module $module_path = $this->Application->findModule('Name', $module, 'Path'); if ($module_path !== false) { $path = $module_path . 'admin_templates'; } else { // remove leading slash + substitute module $path = preg_replace('/\/(.*?)\/(.*)/', $module . '/\\2', THEMES_PATH); } } else { $path = mb_substr(THEMES_PATH, 1); if (mb_strtolower($module) == 'in-portal') { $module_folder = 'platform'; } else { $module_folder = $this->Application->findModule('Name', $module, 'TemplatePath'); } $path .= rtrim('/' . trim($module_folder, '/'), '/') . '/'; } $cached[$cache_key] = $this->Application->BaseURL() . $path; } return $cached[$cache_key]; } /** * Creates HTML tag for all templates * affects future css, js files and href params of links * * @param Array $params * @return string * @access protected * @see kMainTagProcessor::TemplatesBase */ protected function Base_Ref($params) { // tag TemplatesBase adds trailing "/" but only on Front-End $base_href = rtrim($this->TemplatesBase($params), '/'); return ''; } /** * Returns base url for web-site * * @return string * @access public */ function BaseURL() { return $this->Application->BaseURL(); } //for compatability with K3 tags function Base($params) { return $this->TemplatesBase($params).'/'; } function ProjectBase($params) { return $this->Application->BaseURL(); } /*function Base($params) { return $this->Application->BaseURL().$params['add']; }*/ /** * Used to create link to any template. * use "pass" paramter if "t" tag to specify * prefix & special of object to be represented * in resulting url * * @param Array $params * @return string * @access public */ function T($params) { // by default link to current template $template = $this->SelectParam($params, 't,template'); $prefix = array_key_exists('prefix', $params) ? $params['prefix'] : ''; unset($params['t'], $params['template'], $params['prefix']); $no_html_escape = false; if ( isset($params['no_amp']) ) { $no_html_escape = $params['no_amp']; unset($params['no_amp']); } $ret = $this->Application->HREF($template, $prefix, $params); if ( !$no_html_escape ) { // most of the time links are placed into HTML document // TODO: in future always do escaping according to current "escape context" $ret = kUtil::escape($ret, kUtil::ESCAPE_HTML); } return $ret; } function Link($params) { // pass "m" prefix, instead of "all", that is by default on Front-End if (!array_key_exists('pass', $params)) { $params['pass'] = 'm'; } return $this->T($params); } /** * Performs redirect to provided template/url * * @param Array $params * @return string */ function Redirect($params) { $this->Application->Redirect('external:' . $this->Link($params)); return ''; } /*function Env($params) { $t = $params['template']; unset($params['template']); return $this->Application->BuildEnv($t, $params, 'm', false, false); }*/ function FormAction($params) { if (!array_key_exists('pass', $params)) { $params['pass'] = 'all,m'; } $params['pass_category'] = 1; return $this->Application->HREF('', '', $params); } /*// NEEDS TEST function Config($params) { return $this->Application->ConfigOption($params['var']); } function Object($params) { $name = $params['name']; $method = $params['method']; $tmp = $this->Application->recallObject($name); if ($tmp != null) { if (method_exists($tmp, $method)) return $tmp->$method($params); else echo "Method $method does not exist in object ".get_class($tmp)." named $name
"; } else echo "Object $name does not exist in the appliaction
"; }*/ /** * Tag, that always returns true. * For parser testing purposes * * @param Array $params * @return bool * @access public */ function True($params) { return true; } /** * Tag, that always returns false. * For parser testing purposes * * @param Array $params * @return bool * @access public */ function False($params) { return false; } /** * Returns block parameter by name (used only as "check" parameter value for "m_if" tag!) * * @param Array $params * @return stirng * @access public */ function Param($params) { $name = $params['name']; if (array_key_exists($name, $this->Application->Parser->Captures)) { $capture_params = $params; $capture_params['name'] = '__capture_' . $name; $this->Application->Parser->SetParam($name, $this->Application->ParseBlock($capture_params)); } $res = $this->Application->Parser->GetParam($name); if ($res === false) { $res = ''; } if (array_key_exists('plus', $params)) { $res += $params['plus']; } return $res; } /** * Compares block parameter with value specified * * @param Array $params * @return bool * @access public */ function ParamEquals($params) { $name = $this->SelectParam($params, 'name,var,param'); $value = $params['value']; return ($this->Application->Parser->GetParam($name) == $value); } /*function PHP_Self($params) { return $HTTP_SERVER_VARS['PHP_SELF']; } */ /** * Returns session variable value by name * * @param Array $params * @return string * @access public */ function Recall($params) { $var_name = $this->SelectParam($params,'name,var,param'); if (isset($params['persistent']) && $params['persistent']) { $ret = $this->Application->RecallPersistentVar($var_name); } else { $ret = $this->Application->RecallVar($var_name); } $ret = ($ret === false && isset($params['no_null'])) ? '' : $ret; if (getArrayValue($params, 'special') || getArrayValue($params, 'htmlchars')) { $ret = kUtil::escape($ret, kUtil::ESCAPE_HTML); } if (getArrayValue($params, 'urlencode')) { $ret = kUtil::escape($ret, kUtil::ESCAPE_URL); } return $ret; } function RemoveVar($params) { $this->Application->RemoveVar( $this->SelectParam($params,'name,var,param') ); } // bad style to store something from template to session !!! (by Alex) // Used here only to test how session works, nothing more function Store($params) { //echo"Store $params[name]
"; $name = $params['name']; $value = $params['value']; $this->Application->StoreVar($name,$value); } /** * Links variable from request with variable from session * * @param Array $params * @return string * @access protected */ protected function LinkVar($params) { $var_name = $params['name']; $session_var_name = isset($params['session_name']) ? $params['session_name'] : $var_name; $default_value = isset($params['default']) ? $params['default'] : ''; $this->Application->LinkVar($var_name, $session_var_name, $default_value); return ''; } /** * Links variable from request with variable from session and returns it's value * * @param Array $params * @return string * @access protected */ protected function GetLinkedVar($params) { $this->LinkVar($params); return $this->Application->GetVar( $params['name'] ); } /** * Sets application variable value(-s) * * @param Array $params * @access public */ function Set($params) { foreach ($params as $param => $value) { $this->Application->SetVar($param, $value); } } /** * Increment application variable * specified by number specified * * @param Array $params * @access public */ function Inc($params) { $this->Application->SetVar($params['param'], $this->Application->GetVar($params['param']) + $params['by']); } /** * Retrieves application variable * value by name * * @param Array $params * @return string * @access public */ function Get($params) { $name = $this->SelectParam($params, 'name,var,param'); if ( strpos($name, '[') !== false ) { preg_match('/([^\[\]]+)\[(.*)\]/', $name, $regs); $function_params = explode('][', $regs[2]); - $ret = $this->Application->GetVar($regs[1], Array ()); - array_unshift_ref($function_params, $ret); + $ret = $this->Application->GetVar($regs[1], array()); + kUtil::array_unshift_ref($function_params, $ret); - return call_user_func_array('getArrayValue', $function_params); + $ret = call_user_func_array('getArrayValue', $function_params); } else { $ret = $this->Application->GetVar($name, ''); } - if (array_key_exists('no_html_escape', $params) && $params['no_html_escape']) { - return htmlspecialchars_decode($ret); + if ( array_key_exists('no_html_escape', $params) && $params['no_html_escape'] ) { + return $this->Application->isAdmin ? $ret : kUtil::unescape($ret, kUtil::ESCAPE_HTML); } - return $ret; + return kUtil::escape($ret, kUtil::ESCAPE_HTML); } /** * Retrieves application constant * value by name * * @param Array $params * @return string * @access public */ function GetConst($params) { $constant_name = $this->SelectParam($params, 'name,const'); return defined($constant_name) ? constant($constant_name) : ''; } /** * Retrieves configuration variable value by name * * @param Array $params * @return string * @access public */ function GetConfig($params) { $config_name = $this->SelectParam($params, 'name,var'); $ret = $this->Application->ConfigValue($config_name); if ( isset($params['formatted']) && $params['formatted'] ) { $sql = 'SELECT ValueList FROM ' . TABLE_PREFIX . 'SystemSettings WHERE VariableName = ' . $this->Conn->qstr($config_name) . ' AND ElementType IN ("select", "radio")'; $value_list = $this->Conn->GetOne($sql); if ( $value_list ) { $helper = $this->Application->recallObject('InpCustomFieldsHelper'); /* @var $helper InpCustomFieldsHelper */ $options = $helper->GetValuesHash($value_list); $ret = isset($options[$ret]) ? $options[$ret] : $ret; } } if ( isset($params['as_label']) && $params['as_label'] ) { $ret = $this->Application->Phrase($ret); } return $ret; } /** * Compares configuration variable to a given value * * @param Array $params * @return bool * @deprecated * @access protected */ protected function ConfigEquals($params) { $option = $this->SelectParam($params, 'name,option,var'); return $this->Application->ConfigValue($option) == $params['value']; } /** * Creates all hidden fields * needed for kernel_form * * @param Array $params * @return string * @access protected */ protected function DumpSystemInfo($params) { $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set('t', $this->Application->GetVar('t')); $o = ''; $params = $actions->GetParams(); foreach ($params AS $name => $val) { $o .= "\n"; } return $o; } /** * Used for search sidebox on front-end only * * @param Array $params * @return string * @access protected */ protected function GetFormHiddens($params) { $t = $this->SelectParam($params, 'template,t'); unset($params['template']); $form_fields = Array (); if ( $this->Application->RewriteURLs() ) { $session = $this->Application->recallObject('Session'); /* @var $session Session */ if ( $session->NeedQueryString() ) { $form_fields['sid'] = $this->Application->GetSID(); } } else { $form_fields['env'] = $this->Application->BuildEnv($t, $params, 'm', false, false); } if ( $this->Application->GetVar('admin') == 1 ) { $form_fields['admin'] = 1; } $ret = ''; $field_tpl = '' . "\n"; foreach ($form_fields as $form_field => $field_value) { $ret .= sprintf($field_tpl, $form_field, $field_value); } return $ret; } function Odd_Even($params) { $odd = $params['odd']; $even = $params['even']; if (!isset($params['var'])) { $var = 'odd_even'; } else { $var = $params['var']; } if ($this->Application->GetVar($var) == 'even') { if (!isset($params['readonly']) || !$params['readonly']) { $this->Application->SetVar($var, 'odd'); } return $even; } else { if (!isset($params['readonly']) || !$params['readonly']) { $this->Application->SetVar($var, 'even'); } return $odd; } } /** * Returns phrase translation by name * * @param Array $params * @return string * @access public */ function Phrase($params) { $phrase_name = $this->SelectParam($params, 'label,name,title'); $default_translation = $this->SelectParam($params, 'default'); $no_editing = isset($params['no_editing']) && $params['no_editing']; $translation = $this->Application->Phrase($phrase_name, !$no_editing); $phrase_key = mb_strtoupper($phrase_name); if ( $default_translation && strpos($translation, '!' . $phrase_key . '!') !== false ) { $phrase = $this->Application->recallObject('phrases.autocreate', null, Array ('skip_autoload' => true)); /* @var $phrase kDBItem */ if ( !$phrase->Load($phrase_key, 'PhraseKey') ) { $phrase->SetDBField('Phrase', $phrase_name); $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); foreach ($languages AS $language_id) { $phrase->SetDBField('l' . $language_id . '_Translation', $default_translation); } if ( $phrase->Create() ) { $translation = $default_translation; } } } if ( isset($params['escape']) && $params['escape'] ) { // html escaping here is redundant $translation = kUtil::escape($translation, kUtil::ESCAPE_JS); } return $translation; } // for tabs function is_active($params) { $test_templ = $this->SelectParam($params, 'templ,template,t'); if ( !getArrayValue($params, 'allow_empty') ) { $if_true = getArrayValue($params, 'true') ? $params['true'] : 1; $if_false = getArrayValue($params, 'false') ? $params['false'] : 0; } else { $if_true = $params['true']; $if_false = $params['false']; } $physical_template = $this->Application->getPhysicalTemplate($this->Application->GetVar('t')); return preg_match('/^' . str_replace('/', '\/', $test_templ) . '/i', $physical_template) ? $if_true : $if_false; } function IsNotActive($params) { return !$this->is_active($params); } function IsActive($params) { return $this->is_active($params); } function is_t_active($params) { return $this->is_active($params); } function CurrentTemplate($params) { return $this->is_active($params); } /** * Checks if session variable * specified by name value match * value passed as parameter * * @param Array $params * @return string * @access public */ function RecallEquals($params) { $name = $this->SelectParam($params, 'name,var'); $value = $params['value']; if (isset($params['persistent']) && $params['persistent']) { return $this->Application->RecallPersistentVar($name) == $value; } return ($this->Application->RecallVar($name) == $value); } /** * Checks if application variable specified by name value match value passed as parameter * * @param Array $params * @return bool * @access protected * @deprecated */ protected function GetEquals($params) { $name = $this->SelectParam($params, 'var,name,param'); return $this->Application->GetVar($name) == $params['value']; } function ModuleInclude($params) { $ret = ''; $included = Array (); $block_params = array_merge($params, Array ('is_silent' => 2)); // don't make fatal errors in case if template is missing $current_template = $this->Application->GetVar('t'); $replace_main = isset($params['replace_m']) && $params['replace_m']; $skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array (); $cms_mode = $this->Application->GetVar('admin'); foreach ($this->Application->ModuleInfo as $module_name => $module_data) { $module_key = mb_strtolower($module_name); if ( $module_name == 'In-Portal' ) { if ( !$cms_mode && $this->Application->isAdmin ) { // don't process In-Portal templates in admin continue; } // Front-End still relies on In-Portal module $module_prefix = $module_data['TemplatePath']; } elseif ( $this->Application->isAdmin && $module_data['Path'] != 'core/' ) { $module_prefix = $module_key . '/'; // was $module_data['Path']; } else { $module_prefix = $module_data['TemplatePath']; // always have trailing "/" } if ( in_array($module_prefix, $included) ) { // template by this path was already included by other module (e.g. in-portal used core's template) continue; } $block_params['t'] = $module_prefix . $this->SelectParam($params, $module_key . '_template,' . $module_key . '_t,template,t'); $check_prefix = $module_data['Var']; if ( $check_prefix == 'adm' && $replace_main ) { $check_prefix = 'c'; } if ( $block_params['t'] == $current_template || in_array($check_prefix, $skip_prefixes) ) { continue; } $no_data = $this->SelectParam($params, $module_key . '_block_no_data,block_no_data'); if ( $no_data ) { $block_params['block_no_data'] = $module_prefix . '/' . $no_data; } $ret .= $this->Application->IncludeTemplate($block_params); $included[] = $module_prefix; } return $ret; } function ModuleEnabled($params) { return $this->Application->isModuleEnabled( $params['module'] ); } /** * Checks if debug mode is on * * @param Array $params * @return bool * @access public */ function IsDebugMode($params) { return defined('DEBUG_MODE') && $this->Application->isDebugMode(); } /*function MassParse($params) { $qty = $params['qty']; $block = $params['block']; $mode = $params['mode']; $o = ''; if ($mode == 'func') { $func = create_function('$params', ' $o = \'\'; $o.= \'a\'.$params[\'param1\'].\'\'; $o.= \'a\'.$params[\'param2\'].\'\'; $o.= \'a\'.$params[\'param3\'].\'\'; $o.= \'a\'.$params[\'param4\'].\'\'; $o.= \'\'; return $o; '); for ($i=1; $i<$qty; $i++) { $block_params['param1'] = rand(1, 10000); $block_params['param2'] = rand(1, 10000); $block_params['param3'] = rand(1, 10000); $block_params['param4'] = rand(1, 10000); $o .= $func($block_params); } return $o; } $block_params['name'] = $block; for ($i=0; $i<$qty; $i++) { $block_params['param1'] = rand(1, 10000); $block_params['param2'] = rand(1, 10000); $block_params['param3'] = rand(1, 10000); $block_params['param4'] = rand(1, 10000); $block_params['passed'] = $params['passed']; $block_params['prefix'] = 'm'; $o.= $this->Application->ParseBlock($block_params); } return $o; }*/ function LoggedIn($params) { return $this->Application->LoggedIn(); } /** * Allows to check if permission exists directly in template and perform additional actions if required * * @param Array $params * @return bool */ function CheckPermission($params) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->TagPermissionCheck($params); } /** * Checks if user is logged in and if not redirects it to template passed * * @param Array $params */ function RequireLogin($params) { $t = $this->Application->GetVar('t'); $next_t = getArrayValue($params, 'next_template'); if ( $next_t ) { $t = $next_t; } // check by permissions: begin if ((isset($params['perm_event']) && $params['perm_event']) || (isset($params['perm_prefix']) && $params['perm_prefix']) || (isset($params['permissions']) && $params['permissions'])) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $perm_status = $perm_helper->TagPermissionCheck($params); if (!$perm_status) { list($redirect_template, $redirect_params) = $perm_helper->getPermissionTemplate($params); $this->Application->Redirect($redirect_template, $redirect_params); } else { return ; } } // check by permissions: end // check by configuration value: begin $condition = getArrayValue($params, 'condition'); if (!$condition) { $condition = true; } else { if (substr($condition, 0, 1) == '!') { $condition = !$this->Application->ConfigValue(substr($condition, 1)); } else { $condition = $this->Application->ConfigValue($condition); } } // check by configuration value: end // check by belonging to group: begin $group = $this->SelectParam($params, 'group'); $group_access = true; if ($group) { $sql = 'SELECT GroupId FROM '.TABLE_PREFIX.'UserGroups WHERE Name = '.$this->Conn->qstr($group); $group_id = $this->Conn->GetOne($sql); if ($group_id) { $groups = explode(',', $this->Application->RecallVar('UserGroups')); $group_access = in_array($group_id, $groups); } } // check by belonging to group: end if ((!$this->Application->LoggedIn() || !$group_access) && $condition) { $redirect_params = $this->Application->HttpQuery->getRedirectParams(true); if (MOD_REWRITE) { // TODO: $next_t variable is ignored !!! (is anyone using m_RequireLogin tag with "next_template" parameter?) $redirect_params = Array ( 'm_cat_id' => 0, 'next_template' => 'external:' . $_SERVER['REQUEST_URI'], ); } else { $redirect_params['next_template'] = $t; } if (array_key_exists('pass_category', $params)) { $redirect_params['pass_category'] = $params['pass_category']; } if (array_key_exists('use_section', $params)) { $redirect_params['use_section'] = $params['use_section']; } if ( $this->Application->LoggedIn() && !$group_access) { $this->Application->Redirect($params['no_group_perm_template'], $redirect_params); } $this->Application->Redirect($params['login_template'], $redirect_params); } } /** * Checks, that user belongs to a group with a given name * * @param Array $params * @return bool */ protected function IsMember($params) { $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroups WHERE Name = ' . $this->Conn->qstr($params['group']); $group_id = $this->Conn->GetOne($sql); if ( $group_id ) { $groups = explode(',', $this->Application->RecallVar('UserGroups')); return in_array($group_id, $groups); } return false; } /** * Checks if SSL is enabled and redirects to SSL Domain if needed * If SSLDomain is not defined in config - the tag does not do anything * If for_logged_in_only="1" exits if user is not logged in. * If called without params forces https right away. If called with by_config="1" checks the * Require SSL setting from General Config and if it is ON forces https * * @param Array $params */ protected function CheckSSL($params) { $ssl_domain = $this->Application->getSecureDomain(); if ( !$ssl_domain || ($this->Application->TemplatesCache->forceThemeName !== false) ) { // SSL URL is not set - no way to require SSL // internal parsing (e.g. "TemplateParser::_parseTemplate") -> don't redirect return; } $require = false; if ( isset($params['mode']) && $params['mode'] == 'required' ) { $require = true; if ( isset($params['for_logged_in_only']) && $params['for_logged_in_only'] && !$this->Application->LoggedIn() ) { $require = false; } if ( isset($params['condition']) ) { if ( !$this->Application->ConfigValue($params['condition']) ) { $require = false; } } } if ( EDITING_MODE ) { // match SSL mode on front-end to one in administrative console, when browse modes are used $require = $this->Application->ConfigValue('Require_AdminSSL'); } $http_query = $this->Application->recallObject('HTTPQuery'); /* @var $http_query kHTTPQuery */ $pass = $http_query->getRedirectParams(); $pass['pass_events'] = 1; // to make sure all events are passed when redirect happens if ( $require ) { if ( PROTOCOL == 'https://' ) { $this->Application->SetVar('__KEEP_SSL__', 1); return; } $pass['__SSL__'] = 1; $this->Application->Redirect('', $pass); } else { if ( PROTOCOL == 'https://' && $this->Application->ConfigValue('Force_HTTP_When_SSL_Not_Required') ) { if ( $this->Application->GetVar('__KEEP_SSL__') ) { return; } // $pass_more = Array ('pass' => 'm', 'm_cat_id' => 0, '__SSL__' => 0); $pass['__SSL__'] = 0; $this->Application->Redirect('', $pass); // $pass_more } } } function ConstOn($params) { $name = $this->SelectParam($params,'name,const'); return kUtil::constOn($name); } function SetDefaultCategory($params) { $category_id = $this->Application->findModule('Name', $params['module'], 'RootCat'); $this->Application->SetVar('m_cat_id', $category_id); } function XMLTemplate($params) { $this->NoDebug($params); if ( isset($params['cache']) && $params['cache'] ) { $nextyear = intval(date('Y') + 1); $format = "D, d M Y H:i:s"; $expiration = gmdate($format, time() + $params['cache']) . ' GMT'; $last_modified = time(); header('Cache-Control: public, cache, max-age=' . $params['cache']); header("Expires: $expiration"); header('Pragma: public'); // Getting headers sent by the client. $headers = $this->Application->HttpQuery->getHeaders(); // Checking if the client is validating his cache and if it is current. if ( isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) > $last_modified - $params['cache']) ) { // Client's cache IS current, so we just respond '304 Not Modified'. header('Last-Modified: ' . date($format, strtotime($headers['If-Modified-Since'])) . ' GMT', true, 304); exit; } else { // Image not cached or cache outdated, we respond '200 OK' and output the image. header('Last-Modified: ' . gmdate($format, $last_modified) . ' GMT', true, 200); } } // xml documents are usually long kUtil::setResourceLimit(); if ( !$this->Application->GetVar('debug') ) { return $this->Application->XMLHeader(getArrayValue($params, 'xml_version')); } return ''; } function Header($params) { header($params['data']); } function NoDebug($params) { if ( !$this->Application->GetVar('debug') ) { kUtil::safeDefine('DBG_SKIP_REPORTING', 1); } } /** * Returns Home category name * * @param Array $params * @return string * @deprecated */ function RootCategoryName($params) { $no_editing = array_key_exists('no_editing', $params) && $params['no_editing']; return $this->Application->Phrase('la_rootcategory_name', !$no_editing); } /** * Allows to attach file directly from email event template * * @param Array $params */ function AttachFile($params) { $path = FULL_PATH . '/' . $params['path']; $pseudo = isset($params['special']) ? 'EmailSender.' . $params['special'] : 'EmailSender'; $esender = $this->Application->recallObject($pseudo); /* @var $esender kEmailSendingHelper */ if ( file_exists($path) ) { $esender->AddAttachment($path); } } function CaptchaImage($params) { $this->NoDebug($params); $this->Application->SetVar('skip_last_template', 1); $captcha_helper = $this->Application->recallObject('CaptchaHelper'); /* @var $captcha_helper kCaptchaHelper */ // generate captcha code $code = $captcha_helper->prepareCode( $this->Application->GetVar('var') ); $captcha_helper->GenerateCaptchaImage($code, $this->Application->GetVar('w'), $this->Application->GetVar('h'), true); } function SID($params) { return $this->Application->GetSID(); } function ModuleInfo($params) { return $this->Application->findModule($params['key'], $params['value'], $params['return']); } function Random($params) { return rand(1, 100000000); } /** * Prints parser params, available at current deep level * * @param Array $params * @return string */ function PrintCurrentParams($params) { $current_params = $this->Application->Parser->Params; foreach ($current_params as $param_name => $param_value) { $current_params[$param_name] = $param_name . ' = "' . $param_value . '"'; } return '
' . implode("\n", $current_params) . '
'; } /** * Gets previously defined counter result * * @param Array $params * @return int */ function GetCounter($params) { return $this->Application->getCounter($params['name'], $params); } /** * Increments PageHit counter * * @param Array $params * @return int */ function RegisterPageHit($params) { if ($this->Application->ConfigValue('UsePageHitCounter')) { // get current counte $sql = 'SELECT VariableValue FROM '.TABLE_PREFIX.'SystemSettings WHERE VariableName = "PageHitCounter"'; $page_counter = (int)$this->Conn->GetOne($sql); $sql = 'UPDATE LOW_PRIORITY '.TABLE_PREFIX.'SystemSettings SET VariableValue = '.($page_counter + 1).' WHERE VariableName = "PageHitCounter"'; $this->Conn->Query($sql); } } function Timestamp($params) { $format = isset($params['format']) ? $params['format'] : 'd.m.Y H:i:s'; return date($format); } function GetUrlHiddenFileds($params) { $vars = Array ('page', 'per_page', 'sort_by'); $ret = ''; if (array_key_exists('skip', $params)) { $vars = array_diff($vars, $params['skip']); } foreach ($vars as $var_name) { $var_value = $this->Application->GetVar($var_name); if ($var_value) { $ret .= ''; } } return $ret; } /** * Returns current Page URL (without re-assembling it). * "skip_query" param is optional and will remove the ?QUERY part from the result. * * @param Array $params * @return string * @access protected */ protected function CurrentPageLink($params) { if ( isset($params['skip_query']) && $params['skip_query'] ) { return preg_replace('/\?' . preg_quote($_SERVER['QUERY_STRING'], '/') . '$/', '', $_SERVER['REQUEST_URI']); } return $_SERVER['REQUEST_URI']; } /** * Returns current maintenance mode state * * @param Array $params * @return int * @access protected */ protected function MaintenanceMode($params) { $check_ips = isset($params['check_ips']) ? $params['check_ips'] : true; return $this->Application->getMaintenanceMode($check_ips); } /** * Checks if element with given name is defined * * @param Array $params * @return int * @access protected */ protected function ElementDefined($params) { return $this->Application->Parser->blockFound($params['name']); } /** * Detects mobile browser. * * @param array $params * * @return string * @link http://detectmobilebrowsers.com/ */ protected function IsMobileBrowser($params) { $user_agent = $_SERVER['HTTP_USER_AGENT']; if ( preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $user_agent) || preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', substr($user_agent, 0, 4)) ) { return true; } return false; } } Index: branches/5.3.x/core/kernel/utility/debugger/debugger.js =================================================================== --- branches/5.3.x/core/kernel/utility/debugger/debugger.js (revision 16110) +++ branches/5.3.x/core/kernel/utility/debugger/debugger.js (revision 16111) @@ -1,643 +1,643 @@ function DebugReq() {} DebugReq.timeout = 5 * 60 * 1000; // 5 minutes DebugReq.makeRequest = function(p_url, p_busyReq, p_progId, p_successCallBack, p_errorCallBack, p_pass, p_object) { //p_url: the web service url //p_busyReq: is a request for this object currently in progress? //p_progId: element id where progress HTML should be shown //p_successCallBack: callback function for successful response //p_errorCallBack: callback function for erroneous response //p_pass: string of params to pass to callback functions //p_object: object of params to pass to callback functions if (p_busyReq) { return; } var req = DebugReq.getRequest(); if (req != null) { p_busyReq = true; DebugReq.showProgress(p_progId); req.onreadystatechange = function() { if (req.readyState == 4) { p_busyReq = false; window.clearTimeout(toId); if (req.status == 200) { p_successCallBack(req,p_pass,p_object); } else { p_errorCallBack(req,p_pass,p_object); } } } var $ajax_mark = (p_url.indexOf('?') ? '&' : '?') + 'ajax=yes'; req.open('GET', p_url + $ajax_mark, true); req.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT'); req.send(null); var toId = window.setTimeout( function() {if (p_busyReq) req.abort();}, DebugReq.timeout ); } } DebugReq.getRequest = function() { var xmlHttp; try { xmlHttp = new ActiveXObject('MSXML2.XMLHTTP'); return xmlHttp; } catch (e) {} try { xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); return xmlHttp; } catch (e) {} try { xmlHttp = new XMLHttpRequest(); return xmlHttp; } catch(e) {} return null; } DebugReq.showProgress = function(p_id) { $Debugger.AppendRow(DebugReq.getProgressHtml()); } DebugReq.getProgressHtml = function() { return 'Loading ...'; } DebugReq.getErrorHtml = function(p_req) { //TODO: implement accepted way to handle request error return "

" + "(" + p_req.status + ") " + p_req.statusText + "

" } // Debugger function Debugger($params) { this.RowSeparator = ''; this.FilterTypes = $params['FilterTypes']; this.IsFatalError = false; this.ErrorsCount = 0; this.SQLCount = 0; this.SQLTime = 0; this.ScriptTime = 0; this.ScriptMemory = 0; this.Shortcut = 'F12'; for (var $param_name in $params) { this[$param_name] = $params[$param_name]; } this.IsQueried = false; this.IsVisible = false; this.DebuggerDIV = document.getElementById('debug_layer'); this.DebuggerTable = document.getElementById('debug_table'); this.RowCount = 0; this.busyRequest = false; this.DragObject = null; this.LastDragObject = null; this.MouseOffset = [0,0]; this.ResizeHappening = false; this.ResizeTimer = null; this.InitialPos = null; // window.$Debugger = this; // this should be uncommented in case if debugger variable is not $Debugger this.AddEvent(window, 'scroll', function (ev) { window.$Debugger.Resize(ev); }); this.AddEvent(window, 'resize', function (ev) { window.$Debugger.Resize(ev); }); this.AddEvent(document, 'keydown', function (ev) { window.$Debugger.KeyDown(ev); }); // don't work in IE } Debugger.prototype.createEnvironment = function($outer_width, $inner_width) { if (!this.DebuggerDIV) { // when debugger wasn't added already var $container = document.createElement('DIV'); $container.id = 'debug_layer'; $container.className = 'debug_layer_container'; $container.style.display = 'none'; $container.style.width = $outer_width + 'px'; var $debug_layer = document.createElement('DIV'); $debug_layer.className = 'debug_layer'; $debug_layer.style.width = $inner_width + 'px'; $container.insertBefore($debug_layer, $container.firstChild); var $table = document.createElement('TABLE'); $table.style.width = '100%'; $table.className = 'debug_layer_table'; $table.style.width = $inner_width + 'px'; $debug_layer.insertBefore($table, $debug_layer.firstChild); var $tbody = document.createElement('TBODY'); $tbody.id = 'debug_table'; $table.insertBefore($tbody, $table.firstChild); var $body = document.getElementsByTagName('BODY')[0]; $body.insertBefore($container, $body.lastChild); this.DebuggerDIV = document.getElementById('debug_layer'); this.DebuggerTable = document.getElementById('debug_table'); } else { this.Clear(); } } Debugger.prototype.SetOpacity = function(opacity) { this.DebuggerToolbar.style.opacity = (opacity / 100); this.DebuggerToolbar.style.MozOpacity = (opacity / 100); this.DebuggerToolbar.style.KhtmlOpacity = (opacity / 100); this.DebuggerToolbar.style.filter = "alpha(opacity=" + opacity + ")"; } Debugger.prototype.ToolbarClick = function ($button) { switch ($button.id) { case 'dbg_ReloadFrame': self.location.reload(); break; case 'dbg_ShowDebugger': this.Toggle(); break; case 'dbg_TurnOff': var $exdate = new Date(); $exdate.setDate( $exdate.getDate() + 365 ); document.cookie = 'debug_off=1; expires=' + $exdate.toUTCString() + '; path=' + this.BasePath + '/'; self.location.reload(); break; } } Debugger.prototype.jQueryFound = function () { return typeof jQuery == 'function'; } Debugger.prototype.AddToolbar = function($var_name) { if (document.getElementById('debug_toolbar_span')) { // toolbar was already created before if (this.jQueryFound()) { $('#debug_toolbar_span').remove(); } else { return ; } } var $span = document.createElement('SPAN'); $span.style.position = 'absolute'; - $span.style.zIndex= 99; + $span.style.zIndex = 1499999; $span.style.top = '0px'; $span.style.left = '0px'; $span.id = 'debug_toolbar_span'; document.body.style.textAlign = 'left'; var $toolbar_content = 'Reload Frame'; if (this.ErrorsCount > 0) { $toolbar_content += 'Show Debugger (' + this.ErrorsCount + ' errors)'; } else { $toolbar_content += 'Show Debugger'; } $toolbar_content += 'Turn Off'; if (this.SQLCount > 0) { $toolbar_content += '' + this.SQLCount + ' sqls (' + this.SQLTime + ' s)'; } if (this.ScriptTime > 0) { $toolbar_content += '' + this.ScriptTime + ' s (' + this.ScriptMemory + ')'; } $span.innerHTML = '' + $toolbar_content + '
'; this.DebuggerToolbar = $span; this.SetOpacity(20); $span.onmouseover = function() { $Debugger.SetOpacity(100); } $span.onmouseout = function() { $Debugger.SetOpacity(20); } var $body = document.getElementsByTagName('BODY')[0]; $body.insertBefore($span, $body.firstChild); // alert($span.offsetWidth) this.MakeDragable('debug_toolbar_span', function() {}, function() {}, function() {}); } Debugger.prototype.AppendRow = function($html, $row_type) { var $message_type = $row_type || 'other'; if ( $message_type == 'warning' || $message_type == 'notice' ) { $row_type = 'error'; } else { $row_type = $message_type; } this.RowCount++; var $tr = document.createElement('TR'); this.DebuggerTable.appendChild($tr); $tr.className = 'debug_row_' + (this.RowCount % 2 ? 'odd' : 'even'); if ( this.RowCount > 2 ) { $tr.setAttribute('data-row-type', $row_type); if ( $message_type == 'error' ) { document.getElementById('debug_row_' + (this.RowCount -1 )).setAttribute('data-row-type', 'error'); } } $tr.id = 'debug_row_' + this.RowCount; var $td = document.createElement('TD'); $td.className = 'debug_cell'; $td.innerHTML = $html; $tr.appendChild($td); } Debugger.prototype.RemoveRow = function($row_index) { this.DebuggerTable.deleteRow($row_index); this.RowCount--; } Debugger.prototype.Clear = function() { if (!this.IsQueried) return false; while (this.DebuggerTable.rows.length) { this.RemoveRow(0); } this.Toggle(27); this.IsQueried = false; } Debugger.prototype.KeyDown = function($e) { var $matched_parts = 0, $KeyCode = this.GetKeyCode($e), $shortcut_parts = this.Shortcut.toLowerCase().split('+'); $e = ($e) ? $e : event; var $modifier_map = { 'ctrl': $e.ctrlKey, 'shift': $e.shiftKey, 'alt': $e.altKey, 'meta': $e.metaKey }; for (var $i = 0; $i < $shortcut_parts.length; $i++) { for (var $modifier in $modifier_map) { if ( $shortcut_parts[$i] == $modifier && $modifier_map[$modifier] ) { $matched_parts++; break; } } if ( this.getKeyCodeFromString($shortcut_parts[$i]) == $KeyCode ) { $matched_parts++; } } if ( $matched_parts == $shortcut_parts.length || $KeyCode == 27 ) {// F12 or ESC this.Toggle($KeyCode); this.StopEvent($e); } } Debugger.prototype.getKeyCodeFromString = function($string) { var $special_keys = { 'esc': 27, 'escape': 27, 'tab': 9, 'space': 32, 'return': 13, 'enter': 13, 'backspace': 8, 'scrolllock': 145, 'scroll_lock': 145, 'scroll': 145, 'capslock': 20, 'caps_lock': 20, 'caps': 20, 'numlock': 144, 'num_lock': 144, 'num': 144, 'pause': 19, 'break': 19, 'insert': 45, 'home': 36, 'delete': 46, 'end': 35, 'pageup': 33, 'page_up': 33, 'pu': 33, 'pagedown': 34, 'page_down': 34, 'pd': 34, 'left': 37, 'up': 38, 'right': 39, 'down': 40, 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118, 'f8': 119, 'f9': 120, 'f10': 121, 'f11': 122, 'f12': 123 }; $string = $string.toLowerCase(); if ( $special_keys[$string] !== undefined ) { return $special_keys[$string]; } return $string.charCodeAt(0); } Debugger.prototype.OpenDOMViewer = function() { var $value = document.getElementById('dbg_domviewer').value; DOMViewerObj = ($value.indexOf('"') != -1) ? document.getElementById( $value.substring(1,$value.length-1) ) : eval($value); window.open(this.DOMViewerURL); return false; } Debugger.prototype.GetKeyCode = function($e) { $e = ($e) ? $e : event; var charCode = ($e.charCode) ? $e.charCode : (($e.which) ? $e.which : $e.keyCode); if ( charCode == 27 ) { // don't lowercase ESC return charCode; } return String.fromCharCode(charCode).toLowerCase().charCodeAt(0); } Debugger.prototype.StopEvent = function($e) { $e = ($e) ? $e : event; - $e.cancelBubble = true; - if ($e.stopPropagation) $e.stopPropagation(); + $e.returnValue = false; + if ($e.preventDefault) $e.preventDefault(); } Debugger.prototype.Filter = function() { var $new_filter = document.getElementById('dbg_filter').value; var $container_class_name = 'debug_layer_container'; if ( $new_filter != '' ) { for (var index = 0; index < this.FilterTypes.length; ++index) { if ( this.FilterTypes[index] != $new_filter ) { $container_class_name += ' dbg-' + this.FilterTypes[index] + '-row-hidden'; } } } this.DebuggerDIV.className = $container_class_name; } Debugger.prototype.Toggle = function($KeyCode) { if(!this.DebuggerDIV) return false; this.IsVisible = this.DebuggerDIV.style.display == 'none' ? false : true; if (!this.IsVisible && $KeyCode == 27) { return false; } this.Resize(null); if (!this.IsQueried) { this.Query(); } this.DebuggerDIV.style.display = this.IsVisible ? 'none' : 'block'; } Debugger.prototype.Query = function() { DebugReq.makeRequest(this.DebugURL, this.busyRequest, '', this.successCallback, this.errorCallback, '', this); } Debugger.prototype.successCallback = function(p_req, p_pass, p_object) { if (p_pass == 'resetCache') { alert('Requested action performed.'); return ; } var contents = p_req.responseText; contents = contents.split(p_object.RowSeparator); if (contents.length == 1) { alert('error: '+p_req.responseText); p_object.IsQueried = true; return ; } for (var $i = 0; $i < contents.length - 1; $i++) { $json = eval('(' + contents[$i] + ')'); p_object.AppendRow($json['html'], $json['row_type']); } if ( p_object.jQueryFound() ) { var $stats_table = $('.dbg_stats_table:first').clone(); var $statistics_html = $( $('
').html($stats_table) ).html(); if ($statistics_html) { $('.dbg_stats_table:first').remove(); p_object.AppendRow($statistics_html); } } p_object.Refresh(); } Debugger.prototype.errorCallback = function(p_req, p_pass, p_object) { alert('AJAX ERROR: '+DebugReq.getErrorHtml(p_req)); p_object.Refresh(); } Debugger.prototype.Refresh = function() { // progress meter row this.RemoveRow(0); this.IsQueried = true; this.DebuggerDIV.scrollTop = this.IsFatalError ? 10000000 : 0; this.DebuggerDIV.scrollLeft = 0; } Debugger.prototype.Resize = function($e) { if (!this.DebuggerDIV) return false; var $pageTop = document.all ? document.body.offsetTop + document.body.scrollTop : window.scrollY; this.DebuggerDIV.style.top = $pageTop + 'px'; this.DebuggerDIV.style.height = GetWindowHeight() + 'px'; return true; } function GetWindowHeight() { var currWinHeight; // if (document.body.clientHeight) { // currWinHeight = document.body.clientHeight; if (window.innerHeight) {//FireFox with correction for status bar at bottom of window currWinHeight = window.innerHeight; } else if (document.documentElement.clientHeight) {//IE 7 with correction for address bar currWinHeight = document.documentElement.clientHeight; } else if (document.body.offsetHeight) {//IE 4+ currWinHeight = document.body.offsetHeight + 10; } return currWinHeight - 10; // 10 - horizontal scrollbar height } /*function GetWinHeight() { if (window.innerHeight) return window.innerHeight; else if (document.documentElement.clientHeight) return document.documentElement.clientHeight; else if (document.body.offsetHeight) return document.body.offsetHeight; else return _winHeight; }*/ Debugger.prototype.SetClipboard = function(copyText) { if (window.clipboardData) { // IE send-to-clipboard method. window.clipboardData.setData('Text', copyText); } else if (window.netscape) { // You have to sign the code to enable this or allow the action in about:config by changing user_pref("signed.applets.codebase_principal_support", true); netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); // Store support string in an object. var str = Components.classes['@mozilla.org/supports-string;1'].createInstance(Components.interfaces.nsISupportsString); if (!str) { return false; } str.data = copyText; // Make transferable. var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable); if (!trans) { return false; } // Specify what datatypes we want to obtain, which is text in this case. trans.addDataFlavor('text/unicode'); trans.setTransferData('text/unicode', str, copyText.length * 2); var clipid = Components.interfaces.nsIClipboard; var clip = Components.classes['@mozilla.org/widget/clipboard;1'].getService(clipid); if (!clip) { return false; } clip.setData(trans, null, clipid.kGlobalClipboard); } } Debugger.prototype.ShowProps = function($Obj, $Name) { var $ret = ''; for ($Prop in $Obj) { $ret += $Name + '.' + $Prop + ' = ' + $Obj[$Prop] + "\n"; } return alert($ret); } Debugger.prototype.ToggleTraceArgs = function($arguments_layer_id) { var $arguments_layer = document.getElementById($arguments_layer_id); $arguments_layer.style.display = ($arguments_layer.style.display == 'none') ? 'block' : 'none'; } Debugger.prototype.AddEvent = function (el, evname, func) { var $status = false; if (document.all) { $status = el.attachEvent('on' + evname, func); } else { $status = el.addEventListener(evname, func, true); } } Debugger.prototype.resetCache = function ($event_source) { var $events = document.getElementById($event_source); var $event = $events.options[$events.selectedIndex].value; if (!$event) { alert('Please select action to perform first!'); } else if (confirm('Really perform "' + $events.options[$events.selectedIndex].innerHTML + '"?')) { DebugReq.makeRequest(this.EventURL + '&' + $event, this.busyRequest, '', this.successCallback, this.errorCallback, 'resetCache', this); } } Debugger.prototype.mouseCoords = function(ev) { if(ev.pageX || ev.pageY){ var res = {x:ev.pageX, y:ev.pageY}; } else { var res = { x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, y:ev.clientY + document.body.scrollTop - document.body.clientTop }; } return res; } Debugger.prototype.MakeDragable = function(object_id, startCallback, moveCallback, endCallback, options) { var drag_object = document.getElementById(object_id); var cur_options = {'VerticalDrag': 1, 'HorizontalDrag': 1}; if (options) { for(var i in options) { cur_options[i] = options[i]; } } var the_debugger = this; this.AddEvent(drag_object, 'mousedown', function(ev){ ev = ev || window.event; the_debugger.InitialPos = dbg_findPos(drag_object); var coords = the_debugger.mouseCoords(ev); var pos = dbg_findPos(drag_object); the_debugger.MouseOffset = [coords.x - pos[0], coords.y - pos[1]]; the_debugger.DragObject = drag_object; the_debugger.LastDragObject = drag_object; the_debugger.DragObject.style.position = 'absolute'; the_debugger.Options = cur_options; startCallback(drag_object); }); this.AddEvent(document, 'mousemove', function(ev) { // window.status = 'mouse at: '+coords.x+','+coords.y; if(the_debugger.DragObject){ ev = ev || window.event; var coords = the_debugger.mouseCoords(ev); if (the_debugger.Options.VerticalDrag) { the_debugger.DragObject.style.top = (coords.y - the_debugger.MouseOffset[1] ) + 'px' // ; } if (the_debugger.Options.HorizontalDrag) { the_debugger.DragObject.style.left = (coords.x - the_debugger.MouseOffset[0] ) + 'px' // ; } moveCallback(drag_object, coords) return false; } }); this.AddEvent(document, 'mouseup', function(ev){ var tmp = the_debugger.DragObject; the_debugger.DragObject = null; if(tmp){ endCallback(tmp); } var pos = dbg_findPos(drag_object); }); } function dbg_findPos(obj) { var curleft = curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft curtop = obj.offsetTop while (obj = obj.offsetParent) { curleft += obj.offsetLeft curtop += obj.offsetTop } } return [curleft,curtop]; -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/utility/formatters/formatter.php =================================================================== --- branches/5.3.x/core/kernel/utility/formatters/formatter.php (revision 16110) +++ branches/5.3.x/core/kernel/utility/formatters/formatter.php (revision 16111) @@ -1,303 +1,302 @@ _categoryHelper = $this->Application->recallObject('CategoryHelper'); } /** * Replace FCK links like "@@ID@@" to real page urls, when "using_fck" option is set. * * @param string $value * @param Array $options * @param string $format * @return string */ function _replaceFCKLinks(&$value, $options, $format = null) { if ((isset($format) && strpos($format, 'fck_ready') !== false) || (!array_key_exists('using_fck', $options) || !$options['using_fck'])) { // in textarea, where fck will be used OR not using fck return $value; } return $this->_categoryHelper->replacePageIds($value); } /** * Convert's value to match type from config * * @param mixed $value * @param Array $options * @return mixed * @access protected */ function TypeCast($value, $options) { $ret = true; if ( isset($options['type']) ) { $field_type = $options['type']; if ($field_type == 'numeric') { trigger_error('Invalid field type ' . $field_type . ' (in TypeCast method), please use float instead', E_USER_NOTICE); $field_type = 'float'; } elseif ( $field_type == 'string' ) { - if ( !$this->Application->isAdmin && isset($options['allow_html']) && $options['allow_html'] ) { - // this allows to revert kUtil::escape() call for each field submitted on Front-End - $value = htmlspecialchars_decode($value); + if ( isset($options['allow_html']) && $options['allow_html'] ) { + $value = $this->Application->unescapeRequestVariable($value); } return $value; } $value = $this->formatNumber($value); $type_ok = preg_match('#int|integer|double|float|real|numeric|string#', $field_type); if ( $value != '' && $type_ok ) { $ret = is_numeric($value); if ($ret) { $f = 'is_' . $field_type; settype($value, $field_type); $ret = $f($value); } } } return $ret ? $value : false; } /** * Formats number, according to regional settings * * @param string $number * @return float */ function formatNumber($number) { static $comma = null, $thousands = null; if ( !isset($comma) || !isset($thousands) ) { $lang = $this->Application->recallObject('lang.current'); /* @var $lang LanguagesItem */ $comma = $lang->GetDBField('DecimalPoint'); $thousands = $lang->GetDBField('ThousandSep'); } $number = str_replace($thousands, '', $number); $number = str_replace($comma, '.', $number); return $number; } /** * Applies type casting on each array element * @param Array $src * @param kDBItem|kDBList|kDBBase $object * @return Array * @access public */ public function TypeCastArray($src, &$object) { $dst = array (); foreach ($src as $id => $row) { $tmp_row = array (); foreach ($row as $fld => $value) { $field_options = $object->GetFieldOptions($fld); $tmp_row[$fld] = $this->TypeCast($value, $field_options); } $dst[$id] = $tmp_row; } return $dst; } /** * Formats value of a given field * * @param string $value * @param string $field_name * @param kDBItem|kDBList|kDBBase $object * @param string $format * @return string */ function Format($value, $field_name, &$object, $format = null) { if ( is_null($value) ) { return ''; } $options = $object->GetFieldOptions($field_name); if (!isset($format) && array_key_exists('format', $options)) { $format = $options['format']; } if ($value === false) { // used ? return $value; // for leaving badly formatted date on the form } $original_format = $format; if (isset($format)) { if (strpos($format, 'fck_ready') !== false) { $format = trim(str_replace('fck_ready', '', $format), ';'); } } if (isset($format) && $format) { $value = sprintf($format, $value); if ( isset($options['cut_zeros']) && $options['cut_zeros'] ) { // converts 5.00 to 5, but doesn't change 5.340 or 5.34 $value = preg_replace('/\.[0]+$/', '', $value); } } if (preg_match('#int|integer|double|float|real|numeric#', $options['type'])) { $lang = $this->Application->recallObject('lang.current'); /* @var $lang LanguagesItem */ return $lang->formatNumber($value); } elseif ($options['type'] == 'string') { $value = $this->_replaceFCKLinks($value, $options, $original_format); } return $value; } /** * Performs basic type validation on form field value * * @param mixed $value * @param string $field_name * @param kDBItem|kDBList|kDBBase $object * @return mixed * @access public */ public function Parse($value, $field_name, &$object) { if ($value == '') { return NULL; } $options = $object->GetFieldOptions($field_name); $tc_value = $this->TypeCast($value, $options); if ($tc_value === false) { return $value; // for leaving badly formatted date on the form } if(isset($options['type'])) { if (preg_match('#double|float|real|numeric#', $options['type'])) { $tc_value = str_replace(',', '.', $tc_value); } } if (isset($options['regexp'])) { if (!preg_match($options['regexp'], $value)) { $object->SetError($field_name, 'invalid_format'); } } return $tc_value; } function HumanFormat($format) { return $format; } /** * The method is supposed to alter config options or cofigure object in some way based on its usage of formatters * The methods is called for every field with formatter defined when configuring item. * Could be used for adding additional VirtualFields to an object required by some special Formatter * * @param string $field_name * @param array $field_options * @param kDBBase $object */ function PrepareOptions($field_name, &$field_options, &$object) { } /** * Used for split fields like timestamp -> date, time * Called from DBItem to update sub fields values after loading item * * @param string $field * @param string $value * @param Array $options * @param kDBItem|kDBList|kDBBase $object * @return void * @access public */ public function UpdateSubFields($field, $value, &$options, &$object) { } /** * Used for split fields like timestamp -> date, time * Called from DBItem Validate (before validation) to get back master field value from its sub_fields * * @param string $field * @param mixed $value * @param Array $options * @param kDBItem|kDBList|kDBBase $object */ function UpdateMasterFields($field, $value, &$options, &$object) { } /** * Return sample value, that can be entered in this field * * @param string $field * @param Array $options * @param kDBItem|kDBList|kDBBase $object * @return string * @access public */ public function GetSample($field, &$options, &$object) { return isset($options['sample_value']) ? $options['sample_value'] : ''; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/utility/formatters/password_formatter.php =================================================================== --- branches/5.3.x/core/kernel/utility/formatters/password_formatter.php (revision 16110) +++ branches/5.3.x/core/kernel/utility/formatters/password_formatter.php (revision 16111) @@ -1,337 +1,337 @@ _phpPass = $this->Application->makeClass('PasswordHash', Array (8, false)); } /** * The method is supposed to alter config options or configure object in some way based on its usage of formatters * The methods is called for every field with formatter defined when configuring item. * Could be used for adding additional VirtualFields to an object required by some special Formatter * * @param string $field_name * @param array $field_options * @param kDBBase $object */ function PrepareOptions($field_name, &$field_options, &$object) { if ( !isset($field_options['verify_field']) ) { return; } $add_fields = Array (); $options = Array ( 'master_field' => $field_name, // 'error_field' => $field_name, 'formatter' => 'kPasswordFormatter' ); $copy_options = Array ('hashing_method', 'hashing_method_field', 'salt', 'required', 'skip_empty'); foreach ($copy_options as $copy_option) { if ( array_key_exists($copy_option, $field_options) ) { $options[$copy_option] = $field_options[$copy_option]; } } $add_fields[$field_options['verify_field']] = $options; $add_fields[$field_name . '_plain'] = Array ('type' => 'string', 'error_field' => $field_name); $add_fields[$field_options['verify_field'] . '_plain'] = Array ('type' => 'string', 'error_field' => $field_options['verify_field']); $virtual_fields = $object->getVirtualFields(); $add_fields = kUtil::array_merge_recursive($add_fields, $virtual_fields); $object->setVirtualFields($add_fields); } /** * Formats value of a given field * * @param string $value * @param string $field_name * @param kDBItem|kDBList $object * @param string $format * @return string */ function Format($value, $field_name, &$object, $format = null) { return $value; } /** * Performs password & verify password field validation * * @param mixed $value * @param string $field_name * @param kDBItem $object * @return mixed * @access public */ public function Parse($value, $field_name, &$object) { list ($password_field, $verify_field) = $this->_getPasswordFields($value, $field_name, $object); $options = $object->GetFieldOptions($field_name); $salt = $object->GetFieldOption($password_field, 'salt', false, ''); $hashing_method = isset($options['hashing_method']) ? $options['hashing_method'] : $object->GetDBField($options['hashing_method_field']); if ( $object->GetFieldOption($password_field, 'verify_field_set') && $object->GetFieldOption($verify_field, 'master_field_set') ) { $new_password = $object->GetDBField($password_field . '_plain'); $verify_password = $object->GetDBField($verify_field . '_plain'); if ( $new_password == '' && $verify_password == '' ) { $stored_hash = $object->GetDBField($password_field); if ( !$this->checkPassword('', $stored_hash, $hashing_method) ) { // return empty string causing password from database to stay return $value; } else { return $this->hashPassword($value, $salt, $hashing_method); } } // determine admin or front $phrase_error_prefix = $this->Application->isAdmin ? 'la' : 'lu'; if ( $new_password != $verify_password ) { // passwords don't match (no matter what is their length) $object->SetError($verify_field, 'passwords_do_not_match', $phrase_error_prefix . '_passwords_do_not_match'); } $min_length = $this->Application->ConfigValue('Min_Password'); // for error message too $min_length = $object->GetFieldOption($password_field, 'min_length', false, $min_length); if ( mb_strlen($new_password) < $min_length ) { - $error_msg = '+' . sprintf($this->Application->Phrase($phrase_error_prefix . '_passwords_too_short'), $min_length); // + -> not phrase + $error_msg = '+' . sprintf($this->Application->Phrase($phrase_error_prefix . '_passwords_too_short', false), $min_length); // + -> not phrase $object->SetError($password_field, 'passwords_min_length', $error_msg); } } if ( $value == '' ) { // new value is empty - return hash from database return $object->GetDBField($field_name); } return $this->hashPassword($value, $salt, $hashing_method); } /** * Finds out names of password and verify password fields and updates "_plain" virtual field * * @param string $value * @param string $field_name * @param kDBItem $object * @return Array * @access protected */ protected function _getPasswordFields($value, $field_name, &$object) { $options = $object->GetFieldOptions($field_name); $flip_count = 0; $password_field = $verify_field = ''; $fields = Array ('master_field', 'verify_field'); // 1. collect values from both Password and VerifyPassword fields while ($flip_count < 2) { if ( getArrayValue($options, $fields[0]) ) { $tmp_field = $options[$fields[0]]; $object->SetDBField($field_name . '_plain', $value); if ( !$object->GetFieldOption($tmp_field, $fields[1] . '_set') ) { $object->SetFieldOption($tmp_field, $fields[1] . '_set', true); } $password_field = $options[$fields[0]]; $verify_field = $field_name; } $fields = array_reverse($fields); $flip_count++; } return Array ($password_field, $verify_field); } /** * Creates hash from given password and salt * * @param string $password * @param string $salt * @param int $hashing_method * @return string * @throws InvalidArgumentException * @access public */ public function hashPassword($password, $salt = null, $hashing_method = PasswordHashingMethod::PHPPASS) { switch ( $hashing_method ) { case PasswordHashingMethod::NONE: return $password; break; case PasswordHashingMethod::MD5: return $this->_md5hash($password, $salt, false); break; case PasswordHashingMethod::MD5_AND_PHPPASS: return $this->_phpPass->hashPassword($this->_md5hash($password, $salt, true)); break; case PasswordHashingMethod::PHPPASS: return $this->_phpPass->hashPassword($password); break; default: throw new InvalidArgumentException('Unknown password hashing method "' . $hashing_method . '"'); break; } } /** * Checks, that user password is valid * * @param string $password Non-hashed password provided by user * @param string $stored_hash Hash, calculated before from correct user password (must have salt inside) * @param int $hashing_method Hash generation method * @return bool * @access public * @throws InvalidArgumentException */ public function checkPassword($password, $stored_hash = null, $hashing_method = PasswordHashingMethod::PHPPASS) { $salt = ''; if ( $hashing_method != PasswordHashingMethod::PHPPASS && strpos($stored_hash, ':') !== false ) { list ($salt, $stored_hash) = explode(':', $stored_hash, 2); } switch ( $hashing_method ) { case PasswordHashingMethod::NONE: return $password == $stored_hash; break; case PasswordHashingMethod::MD5: return $this->_md5hash($password, $salt, false) == $stored_hash; break; case PasswordHashingMethod::MD5_AND_PHPPASS: $password_hashed = preg_match('/^[a-f0-9]{32}$/', $password); return $this->_phpPass->checkPassword($this->_md5hash($password, $salt, $password_hashed), $stored_hash); break; case PasswordHashingMethod::PHPPASS: return $this->_phpPass->checkPassword($password, $stored_hash); break; default: throw new InvalidArgumentException('Unknown password hashing method "' . $hashing_method . '"'); break; } } /** * Checks a password stored as system setting using phppass with fallback to salted md5 * * @param string $setting_name * @param string $password * @param int $hashing_method * @return bool * @access public */ public function checkPasswordFromSetting($setting_name, $password, $hashing_method = PasswordHashingMethod::PHPPASS) { $stored_hash = $this->Application->ConfigValue($setting_name); $stored_hash = $this->prepareHash($stored_hash, 'b38', $hashing_method); if ( $this->checkPassword($password, $stored_hash, $hashing_method) ) { return true; } if ( $hashing_method != PasswordHashingMethod::MD5 ) { if ( $this->checkPasswordFromSetting($setting_name, $password, PasswordHashingMethod::MD5) ) { // rehash password on the go using more secure algorithm $this->Application->SetConfigValue($setting_name, $this->hashPassword($password)); return true; } } return false; } /** * Ensures, that salt is always present in the hash * * @param string $stored_hash * @param string $salt * @param int $hashing_method * @return string * @access public */ public function prepareHash($stored_hash, $salt = '', $hashing_method = PasswordHashingMethod::PHPPASS) { if ( $hashing_method == PasswordHashingMethod::PHPPASS ) { return $stored_hash; } // embed salt into hash generated not by phppass return $salt . ':' . $stored_hash; } /** * Hashes password using MD5 algorithm * * @param string $password * @param string $salt * @param bool $password_hashed * @return string * @access protected */ protected function _md5hash($password, $salt = null, $password_hashed = false) { if ( !$password_hashed ) { $password = md5($password); } if ( isset($salt) && $salt ) { return md5($password . $salt); } // if empty salt, assume, that it's not passed at all return $password; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/utility/formatters/upload_formatter.php =================================================================== --- branches/5.3.x/core/kernel/utility/formatters/upload_formatter.php (revision 16110) +++ branches/5.3.x/core/kernel/utility/formatters/upload_formatter.php (revision 16111) @@ -1,631 +1,627 @@ fileHelper = $this->Application->recallObject('FileHelper'); if ( $this->DestinationPath ) { $this->FullPath = FULL_PATH . $this->DestinationPath; } } /** * Processes file uploads from form * * @param mixed $value * @param string $field_name * @param kDBItem $object * @return mixed * @access public */ public function Parse($value, $field_name, &$object) { - if ( !$this->Application->isAdmin ) { - // this allows to revert kUtil::escape() call for each field submitted on front-end - $value = is_array($value) ? array_map('htmlspecialchars_decode', $value) : htmlspecialchars_decode($value); - } - + $value = $this->Application->HttpQuery->unescapeRequestVariable($value); $options = $object->GetFieldOptions($field_name); if ( getArrayValue($options, 'upload_dir') ) { $this->DestinationPath = $options['upload_dir']; $this->FullPath = FULL_PATH . $this->DestinationPath; } if ( is_array($value) && isset($value['tmp_ids']) ) { // SWF Uploader return $this->_processFlashUploader($value, $field_name, $object); } return $this->_processRegularUploader($value, $field_name, $object); } /** * Handles uploaded files, provided by Flash uploader * * @param Array|string $value * @param string $field_name * @param kDBItem $object * @return string * @access protected */ protected function _processFlashUploader($value, $field_name, $object) { $options = $object->GetFieldOptions($field_name); $this->sorting = isset($value['order']) ? explode('|', $value['order']) : Array (); if ( $value['tmp_deleted'] ) { $n_upload = Array (); $deleted = explode('|', $value['tmp_deleted']); $upload = explode('|', $value['upload']); foreach ($upload as $name) { if ( in_array($name, $deleted) ) { continue; } $n_upload[] = $name; } $value['upload'] = implode('|', $n_upload); } if ( !$value['tmp_ids'] ) { // no pending files -> return already uploaded files return $this->_sortFiles($value['upload']); } $swf_uploaded_ids = explode('|', $value['tmp_ids']); $swf_uploaded_names = explode('|', $value['tmp_names']); $existing = $value['upload'] ? explode('|', $value['upload']) : Array (); $fret = Array (); $max_files = $this->_getMaxFiles($options); $pending_actions = $object->getPendingActions(); $files_to_delete = $this->_getFilesToDelete($object); for ($i = 0; $i < min($max_files, count($swf_uploaded_ids)); $i++) { // don't delete uploaded file, when it's name matches delete file name $real_name = $this->_getRealFilename($swf_uploaded_names[$i], $options, $object, $files_to_delete); $file_name = $this->FullPath . $real_name; $tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i] . '_' . $swf_uploaded_names[$i]; rename($tmp_file, $file_name); @chmod($file_name, 0666); $fret[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name; $pending_actions[] = Array ( 'action' => 'make_live', 'id' => $object->GetID(), 'field' => $field_name, 'file' => $file_name ); $this->_renameFileInSorting($swf_uploaded_names[$i], $real_name); } $object->setPendingActions($pending_actions); return $this->_sortFiles(array_merge($existing, $fret)); } /** * Returns files, scheduled for deleting * * @param kDBItem $object * @return Array * @access protected */ protected function _getFilesToDelete($object) { $ret = Array (); foreach ($object->getPendingActions() as $data) { if ( $data['action'] == 'delete' ) { $ret[] = $data['file']; } } return $ret; } /** * Handles regular file upload * * @param string|Array $value * @param string $field_name * @param kDBItem $object * @return string * @access protected */ protected function _processRegularUploader($value, $field_name, $object) { $ret = !is_array($value) ? $value : ''; $options = $object->GetFieldOptions($field_name); if ( getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE ) { // file was not uploaded this time, but was uploaded before, then use previously uploaded file (from db) return getArrayValue($value, 'upload'); } if ( is_array($value) && count($value) > 1 && $value['size'] ) { if ( is_array($value) && (int)$value['error'] === UPLOAD_ERR_OK ) { $max_file_size = isset($options['max_size']) ? $options['max_size'] : MAX_UPLOAD_SIZE; // we can get mime type based on file content and don't use one, provided by the client // $value['type'] = kUtil::mimeContentType($value['tmp_name']); if ( getArrayValue($options, 'file_types') && !$this->extensionMatch($value['name'], $options['file_types']) ) { // match by file extensions $error_params = Array ( 'file_name' => $value['name'], 'file_types' => $options['file_types'], ); $object->SetError($field_name, 'bad_file_format', 'la_error_InvalidFileFormat', $error_params); } elseif ( getArrayValue($options, 'allowed_types') && !in_array($value['type'], $options['allowed_types']) ) { // match by mime type provided by web-browser $error_params = Array ( 'file_type' => $value['type'], 'allowed_types' => $options['allowed_types'], ); $object->SetError($field_name, 'bad_file_format', 'la_error_InvalidFileFormat', $error_params); } elseif ( $value['size'] > $max_file_size ) { $object->SetError($field_name, 'bad_file_size', 'la_error_FileTooLarge'); } elseif ( !is_writable($this->FullPath) ) { $object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file'); } else { $real_name = $this->_getRealFilename($value['name'], $options, $object); $file_name = $this->FullPath . $real_name; $storage_format = isset($options['storage_format']) ? $options['storage_format'] : false; 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); $moved = rename($tmp_name, $file_name); } else { $moved = move_uploaded_file($value['tmp_name'], $file_name); } if ( !$moved ) { $object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file'); } else { @chmod($file_name, 0666); if ( getArrayValue($options, 'size_field') ) { $object->SetDBField($options['size_field'], $value['size']); } if ( getArrayValue($options, 'orig_name_field') ) { $object->SetDBField($options['orig_name_field'], $value['name']); } if ( getArrayValue($options, 'content_type_field') ) { $object->SetDBField($options['content_type_field'], $value['type']); } $ret = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name; // delete previous file, when new file is uploaded under same field /*$previous_file = isset($value['upload']) ? $value['upload'] : false; if ( $previous_file && file_exists($this->FullPath . $previous_file) ) { unlink($this->FullPath . $previous_file); }*/ } } } else { $object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file'); } } if ( (count($value) > 1) && $value['error'] && ($value['error'] != UPLOAD_ERR_NO_FILE) ) { $object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file', $value); } return $ret; } /** * Checks, that given file name has on of provided file extensions * * @param string $filename * @param string $file_types * @return bool * @access protected */ protected function extensionMatch($filename, $file_types) { if ( preg_match_all('/\*\.(.*?)(;|$)/', $file_types, $regs) ) { $file_extension = mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $file_extensions = array_map('mb_strtolower', $regs[1]); return in_array($file_extension, $file_extensions); } return true; } /** * Resorts uploaded files according to given file order * * @param Array|string $files * @return string * @access protected */ protected function _sortFiles($files) { if ( !is_array($files) ) { $files = explode('|', $files); } $sorted_files = array_intersect($this->sorting, $files); // removes deleted files from sorting $new_files = array_diff($files, $sorted_files); // files, that weren't sorted - add to the end return implode('|', array_merge($sorted_files, $new_files)); } /** * Returns maximal allowed file count per field * * @param Array $options * @return int * @access protected */ protected function _getMaxFiles($options) { if ( !isset($options['multiple']) ) { return 1; } return $options['multiple'] == false ? 1 : $options['multiple']; } /** * Returns final filename after applying storage-engine specific naming * * @param string $file_name * @param Array $options * @param kDBItem $object * @param Array $files_to_delete * @return string * @access protected */ protected function _getRealFilename($file_name, $options, $object, $files_to_delete = Array ()) { $real_name = $this->getStorageEngineFile($file_name, $options, $object->Prefix); $real_name = $this->getStorageEngineFolder($real_name, $options) . $real_name; return $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name, $files_to_delete); } /** * Renames file in sorting list * * @param string $old_name * @param string $new_name * @return void * @access protected */ protected function _renameFileInSorting($old_name, $new_name) { $index = array_search($old_name, $this->sorting); if ( $index !== false ) { $this->sorting[$index] = $new_name; } } function getSingleFormat($format) { $single_mapping = Array ( 'file_raw_urls' => 'raw_url', 'file_display_names' => 'display_name', 'file_urls' => 'full_url', 'file_paths' => 'full_path', 'file_sizes' => 'file_size', 'files_resized' => 'resize', 'img_sizes' => 'img_size', 'wms' => 'wm', ); return $single_mapping[$format]; } /** * Return formatted file url,path or size (or same for multiple files) * * @param string $value * @param string $field_name * @param kDBItem|kDBList $object * @param string $format * @return string */ function Format($value, $field_name, &$object, $format = NULL) { if ( is_null($value) ) { return ''; } $options = $object->GetFieldOptions($field_name); if ( !isset($format) ) { $format = isset($options['format']) ? $options['format'] : false; } if ( $format && preg_match('/(file_raw_urls|file_display_names|file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|wms)(.*)/', $format, $regs) ) { if ( !$value || $format == 'file_names' ) { // storage format matches display format OR no value return $value; } $ret = Array (); $files = explode('|', $value); $format = $this->getSingleFormat($regs[1]) . $regs[2]; foreach ($files as $a_file) { $ret[] = $this->GetFormatted($a_file, $field_name, $object, $format); } return implode('|', $ret); } $tc_value = $this->TypeCast($value, $options); if ( ($tc_value === false) || ($tc_value != $value) ) { // for leaving badly formatted date on the form return $value; } return $this->GetFormatted($tc_value, $field_name, $object, $format); } /** * Return formatted file url,path or size * * @param string $value * @param string $field_name * @param kDBItem $object * @param string $format * @return string */ function GetFormatted($value, $field_name, &$object, $format = NULL) { if ( !$format ) { return $value; } $options = $object->GetFieldOptions($field_name); $upload_dir = isset($options['include_path']) && $options['include_path'] ? '' : $this->getUploadDir($options); $file_path = strlen($value) ? FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value : ''; if ( preg_match('/resize:([\d]*)x([\d]*)/', $format, $regs) ) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ try { return $image_helper->ResizeImage($file_path, $format); } - catch ( \RuntimeException $e ) { + catch ( RuntimeException $e ) { // error, during image resize -> return empty string return ''; } } elseif ( !strlen($file_path) || !file_exists($file_path) ) { // file doesn't exist OR not uploaded return ''; } switch ($format) { case 'display_name': return kUtil::removeTempExtension($value); break; case 'raw_url': return $this->fileHelper->pathToUrl($file_path); break; case 'full_url': $direct_links = isset($options['direct_links']) ? $options['direct_links'] : true; if ( $direct_links ) { return $this->fileHelper->pathToUrl($file_path); } else { $url_params = Array ( 'pass' => 'm,'.$object->Prefix, $object->Prefix . '_event' => 'OnViewFile', 'file' => $value, 'field' => $field_name ); return $this->Application->HREF('', '', $url_params); } break; case 'full_path': return $file_path; break; case 'file_size': return filesize($file_path); break; case 'img_size': $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $image_info = $image_helper->getImageInfo($file_path); return $image_info ? $image_info[3] : ''; break; } return sprintf($format, $value); } /** * Creates & returns folder, based on storage engine specified in field options * * @param string $file_name * @param array $options * @return string * @access protected * @throws Exception */ protected function getStorageEngineFolder($file_name, $options) { $storage_engine = (string)getArrayValue($options, 'storage_engine'); if ( !$storage_engine ) { return ''; } switch ($storage_engine) { case StorageEngine::HASH: $folder_path = kUtil::getHashPathForLevel($file_name); break; case StorageEngine::TIMESTAMP: $folder_path = date('Y-m/d/'); break; default: throw new Exception('Unknown storage engine "' . $storage_engine . '".'); break; } return $folder_path; } /** * Applies prefix & suffix to uploaded filename, based on storage engine in field options * * @param string $name * @param array $options * @param string $unit_prefix * @return string * @access protected */ protected function getStorageEngineFile($name, $options, $unit_prefix) { $prefix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_prefix'), $unit_prefix); $suffix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_suffix'), $unit_prefix); $parts = pathinfo($name); return ($prefix ? $prefix . '_' : '') . $parts['filename'] . ($suffix ? '_' . $suffix : '') . '.' . $parts['extension']; } /** * Creates prefix/suffix to join with uploaded file * * Added "u" before user_id to keep this value after FileHelper::ensureUniqueFilename method call * * @param string $option * @param string $unit_prefix * @return string * @access protected */ protected function getStorageEngineFilePart($option, $unit_prefix) { $replace_from = Array ( StorageEngine::PS_DATE_TIME, StorageEngine::PS_PREFIX, StorageEngine::PS_USER ); $replace_to = Array ( date('Ymd-His'), $unit_prefix, 'u' . $this->Application->RecallVar('user_id') ); return str_replace($replace_from, $replace_to, $option); } public function getUploadDir($options) { return isset($options['upload_dir']) ? $options['upload_dir'] : $this->DestinationPath; } } class kPictureFormatter extends kUploadFormatter { public function __construct() { $this->NakeLookupPath = IMAGES_PATH; // used ? $this->DestinationPath = kUtil::constOn('ADMIN') ? IMAGES_PENDING_PATH : IMAGES_PATH; parent::__construct(); } /** * Return formatted file url,path or size * * @param string $value * @param string $field_name * @param kDBItem $object * @param string $format * @return string */ function GetFormatted($value, $field_name, &$object, $format = NULL) { if ( $format == 'img_size' ) { $options = $object->GetFieldOptions($field_name); $img_path = FULL_PATH . '/' . $this->getUploadDir($options) . $value; $image_info = getimagesize($img_path); return ' ' . $image_info[3]; } return parent::GetFormatted($value, $field_name, $object, $format); } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/utility/http_query.php =================================================================== --- branches/5.3.x/core/kernel/utility/http_query.php (revision 16110) +++ branches/5.3.x/core/kernel/utility/http_query.php (revision 16111) @@ -1,761 +1,787 @@ Order = $order; if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { // when AJAX request is made from jQuery, then create ajax variable, // so any logic based in it (like redirects) will not break down $_GET['ajax'] = 'yes'; } $this->_trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } /** * Discovers unit form request and returns it's QueryString option on success * * @param string $prefix_special * * @return Array|bool * @access public */ public function discoverUnit($prefix_special) { list($prefix) = explode('.', $prefix_special); $query_string = $this->getQueryString($prefix); if ($query_string) { // only units with QueryString option can be discovered $this->discoveredUnits[$prefix_special] = $query_string; return $query_string; } unset( $this->discoveredUnits[$prefix] ); return false; } /** * Returns units, passed in request * * @param bool $prefix_special_only * @return Array * @access protected */ public function getDiscoveredUnits($prefix_special_only = true) { return $prefix_special_only ? array_keys( $this->discoveredUnits ) : $this->discoveredUnits; } /** * Returns QueryMap for requested unit config. * In case if unit config is a clone, then get parent item's (from prefix) config to create clone * * @param string $prefix * @return Array * @access protected */ protected function getQueryString($prefix) { return $this->Application->getUnitConfig($prefix)->getQueryString(Array ()); } /** * Removes specials from request * * @param Array $array * @return Array * @access protected */ protected function _removeSpecials($array) { $ret = Array (); $removed = false; foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag ) { $removed = true; list ($prefix, $special) = explode('.', $prefix_special, 2); foreach ($array as $key => $val) { $new_key = preg_match("/^" . $prefix . "[._]{1}" . $special . "(.*)/", $key, $regs) ? $prefix . $regs[1] : $key; $ret[$new_key] = is_array($val) ? $this->_removeSpecials($val) : $val; } } } return $removed ? $ret : $array; } public function process() { $this->AddAllVars(); $this->removeSpecials(); ini_set('magic_quotes_gpc', 0); $this->Application->UrlManager->LoadStructureTemplateMapping(); $this->AfterInit(); } /** * All all requested vars to * common storage place * * @return void * @access protected */ protected function AddAllVars() { for ($i = 0; $i < strlen($this->Order); $i++) { switch ($this->Order[$i]) { case 'G': $this->Get = $this->AddVars($_GET); if ( array_key_exists('sid', $_GET) ) { $this->_sidInQueryString = true; } $vars = $this->Application->processQueryString($this->Get(ENV_VAR_NAME)); if ( array_key_exists('sid', $vars) ) { // used by Session::GetPassedSIDValue $this->Get['sid'] = $vars['sid']; } $this->AddParams($vars); break; case 'P': $this->Post = $this->AddVars($_POST); $this->convertPostEvents(); $this->_processPostEnvVariables(); break; case 'C': $cookie_hasher = $this->Application->makeClass('kCookieHasher'); /* @var $cookie_hasher kCookieHasher */ $parsed_cookies = Array (); foreach ($_COOKIE as $cookie_name => $encrypted_value) { $parsed_cookies[$cookie_name] = $cookie_hasher->decrypt($cookie_name, $encrypted_value); } $this->Cookie = $this->AddVars($parsed_cookies); break; /*case 'E'; $this->Env = $this->AddVars($_ENV, false); //do not strip slashes! break; case 'S'; $this->Server = $this->AddVars($_SERVER, false); //do not strip slashes! break;*/ case 'F'; $this->convertFiles(); $this->Files = $this->MergeVars($_FILES); // , false); //do not strip slashes! break; } } } /** * Allow POST variables, that names were transformed by PHP ("." replaced with "_") to * override variables, that were virtually created through environment variable parsing * */ function _processPostEnvVariables() { $passed = $this->Get('passed'); if ( !$passed ) { return; } $passed = explode(',', $passed); foreach ($passed as $prefix_special) { if ( strpos($prefix_special, '.') === false ) { continue; } list ($prefix, $special) = explode('.', $prefix_special); $query_map = $this->getQueryString($prefix); $post_prefix_special = $prefix . '_' . $special; foreach ($query_map as $var_name) { if ( array_key_exists($post_prefix_special . '_' . $var_name, $this->Post) ) { $this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]); } } } } /** * Removes requested specials from all request variables * * @return void * @access protected */ protected function removeSpecials() { $this->specialsToRemove = $this->Get('remove_specials'); if ( $this->specialsToRemove ) { foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag && strpos($prefix_special, '.') === false ) { unset($this->specialsToRemove[$prefix_special]); trigger_error('Incorrect usage of "remove_specials[' . $prefix_special . ']" field (no special found)', E_USER_NOTICE); } } $this->_Params = $this->_removeSpecials($this->_Params); } } /** * Finishes initialization of kHTTPQuery class * * @return void * @access protected * @todo: only uses build-in rewrite listeners, when cache is build for the first time */ protected function AfterInit() { $rewrite_url = $this->Get('_mod_rw_url_'); if ( $this->Application->RewriteURLs() || $rewrite_url ) { // maybe call onafterconfigread here $this->Application->UrlManager->initRewrite(); if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('url_parsing', 'Parsing MOD_REWRITE url'); $this->Application->UrlManager->rewrite->parseRewriteURL(); $description = 'Parsing MOD_REWRITE url (template: ' . $this->Get('t') . ')'; $this->Application->Debugger->profileFinish('url_parsing', $description); } else { $this->Application->UrlManager->rewrite->parseRewriteURL(); } if ( !$rewrite_url && $this->rewriteRedirectRequired() ) { // rewrite url is missing (e.g. not a script from tools folder) $url_params = $this->getRedirectParams(); // no idea about how to check, that given template require category to be passed with it, so pass anyway $url_params['pass_category'] = 1; $url_params['response_code'] = 301; // Moved Permanently trigger_error('Non mod-rewrite url "' . $_SERVER['REQUEST_URI'] . '" used', E_USER_NOTICE); $this->Application->Redirect('', $url_params); } } else { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); } if ( !$this->Application->isAdmin && $this->Application->ConfigValue('ForceCanonicalUrls') ) { $template = $this->Application->GetVar('t'); $seo_template = $this->Application->getSeoTemplate($template); if ( $seo_template && $seo_template != $template ) { $url_params = $this->getRedirectParams(); $url_params['response_code'] = 301; trigger_error('Request url "' . $_SERVER['REQUEST_URI'] . '" points directly to physical template', E_USER_NOTICE); $this->Application->Redirect($seo_template, $url_params); } } } /** * Checks, that non-rewrite url was visited and it's automatic rewrite is required * * @return bool */ function rewriteRedirectRequired() { $redirect_conditions = Array ( !$this->IsHTTPSRedirect(), // not https <-> http redirect !$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains) !defined('GW_NOTIFY'), // not in payment gateway notification script preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited $this->Get('t') != 'index', // not on index page ); $perform_redirect = true; foreach ($redirect_conditions as $redirect_condition) { $perform_redirect = $perform_redirect && $redirect_condition; if (!$perform_redirect) { return false; } } return true; } /** * This is redirect from https to http or via versa * * @return bool */ function IsHTTPSRedirect() { $http_referer = array_key_exists('HTTP_REFERER', $_SERVER) ? $_SERVER['HTTP_REFERER'] : false; return ( ( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) ) || ( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) ) ); } /** * Checks, that referer is out site * * @return bool */ function refererIsOurSite() { if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) { // no referer -> don't care what happens return false; } $site_helper = $this->Application->recallObject('SiteHelper'); /* @var $site_helper SiteHelper */ $parsed_url = parse_url($_SERVER['HTTP_REFERER']); if ( $parsed_url['scheme'] == 'https' ) { $found = $site_helper->compare($parsed_url['host'], 'SSLDomainName', $this->Application->ConfigValue('SSLDomain')); } else { $found = $site_helper->compare($parsed_url['host'], 'DomainName', DOMAIN); } return $found; } function convertFiles() { if ( !$_FILES ) { return ; } $tmp = Array (); $file_keys = Array ('error', 'name', 'size', 'tmp_name', 'type'); foreach ($_FILES as $file_name => $file_info) { if ( is_array($file_info['error']) ) { $tmp[$file_name] = $this->getArrayLevel($file_info['error'], $file_name); } else { $normal_files[$file_name] = $file_info; } } if ( !$tmp ) { return ; } $files = $_FILES; $_FILES = Array (); foreach ($tmp as $prefix => $prefix_files) { $anchor =& $_FILES; foreach ($prefix_files['keys'] as $key) { $anchor =& $anchor[$key]; } foreach ($prefix_files['value'] as $field_name) { unset($inner_anchor, $copy); $work_copy = $prefix_files['keys']; foreach ($file_keys as $file_key) { $inner_anchor =& $files[$prefix][$file_key]; if ( isset($copy) ) { $work_copy = $copy; } else { $copy = $work_copy; } array_shift($work_copy); foreach ($work_copy as $prefix_file_key) { $inner_anchor =& $inner_anchor[$prefix_file_key]; } $anchor[$field_name][$file_key] = $inner_anchor[$field_name]; } } } // keys: img_temp, 0, values: LocalPath, ThumbPath } function getArrayLevel(&$level, $prefix='') { $ret['keys'] = $prefix ? Array($prefix) : Array(); $ret['value'] = Array(); foreach($level as $level_key => $level_value) { if( is_array($level_value) ) { $ret['keys'][] = $level_key; $tmp = $this->getArrayLevel($level_value); $ret['keys'] = array_merge($ret['keys'], $tmp['keys']); $ret['value'] = array_merge($ret['value'], $tmp['value']); } else { $ret['value'][] = $level_key; } } return $ret; } /** * Overwrites GET events with POST events in case if they are set and not empty * * @return void * @access protected */ protected function convertPostEvents() { $events = $this->Get('events', Array ()); /* @var $events Array */ if ( is_array($events) ) { $events = array_filter($events); foreach ($events as $prefix_special => $event_name) { $this->Set($prefix_special . '_event', $event_name); } } } function finalizeParsing($passed = Array()) { if (!$passed) { return; } foreach ($passed as $passed_prefix) { $this->discoverUnit($passed_prefix); // from mod-rewrite url parsing } $this->Set('passed', implode(',', $this->getDiscoveredUnits())); } /** * Saves variables from array specified * into common variable storage place * * @param Array $array * @param bool $strip_slashes * @return Array * @access private */ function AddVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value) { $this->Set($key, $value); } return $array; } function MergeVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value_array) { // $value_array is an array too $this->_Params = kUtil::array_merge_recursive($this->_Params, Array ($key => $value_array)); } return $array; } function StripSlashes($array) { static $magic_quotes = null; if (!isset($magic_quotes)) { $magic_quotes = get_magic_quotes_gpc(); } foreach ($array as $key => $value) { if (is_array($value)) { $array[$key] = $this->StripSlashes($value); } else { if ($magic_quotes) { $value = stripslashes($value); } if (!$this->Application->isAdmin) { // TODO: always escape output instead of input $value = kUtil::escape($value, kUtil::ESCAPE_HTML); } $array[$key] = $value; } } return $array; } /** + * Removes forceful escaping done to the variable upon Front-End submission. + * + * @param string|array $value Value. + * + * @return string|array + * @see StripSlashes + */ + public function unescapeRequestVariable($value) + { + if ( $this->Application->isAdmin ) { + return $value; + } + + // This allows to revert kUtil::escape() call for each field submitted on front-end. + if ( is_array($value) ) { + foreach ( $value as $param_name => $param_value ) { + $value[$param_name] = $this->unescapeRequestVariable($param_value); + } + + return $value; + } + + return kUtil::unescape($value, kUtil::ESCAPE_HTML); + } + + /** * Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls * * @param bool $access_error Method is called during no_permission, require login, session expiration link preparation * @return Array */ function getRedirectParams($access_error = false) { $vars = $this->Get; $unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action'); if (!$this->_sidInQueryString) { $unset_vars[] = 'sid'; } // remove system variables foreach ($unset_vars as $var_name) { if (array_key_exists($var_name, $vars)) { unset($vars[$var_name]); } } if ($access_error) { // place 1 of 2 (also in UsersEventHandler::OnSessionExpire) $vars = $this->_removePassThroughVariables($vars); } return $vars; } /** * Removes all pass_though variables from redirect params * * @param Array $url_params * @return Array */ function _removePassThroughVariables($url_params) { $pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : ''; if (!$pass_through) { return $url_params; } $pass_through = explode(',', $pass_through . ',pass_through'); foreach ($pass_through as $pass_through_var) { unset($url_params[$pass_through_var]); } $url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again return $url_params; } /** * Checks, that url is empty * * @return bool * @access public */ public function isEmptyUrl() { if ( $this->Application->RewriteURLs() ) { return !$this->Get('_mod_rw_url_'); } return !count($this->Get); } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public function getClientIp() { if ( $this->_trustProxy ) { if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) { return $_SERVER['HTTP_CLIENT_IP']; } if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) { $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($client_ip as $ip_address) { $clean_ip_address = trim($ip_address); if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) { return $clean_ip_address; } } return ''; } } return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } /** * Returns headers * * @return array * @access public */ public function getHeaders() { if ( function_exists('apache_request_headers') ) { // If apache_request_headers() exists... $headers = apache_request_headers(); if ( $headers ) { return $headers; // And works... Use it } } $headers = array(); foreach ( array_keys($_SERVER) as $server_key ) { if ( substr($server_key, 0, 5) == 'HTTP_' ) { $header_name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($server_key, 0, 5))))); $headers[$header_name] = $_SERVER[$server_key]; } } return $headers; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/utility/email.php =================================================================== --- branches/5.3.x/core/kernel/utility/email.php (revision 16110) +++ branches/5.3.x/core/kernel/utility/email.php (revision 16111) @@ -1,927 +1,928 @@ Array (), EmailTemplate::RECIPIENT_TYPE_CC => Array (), EmailTemplate::RECIPIENT_TYPE_BCC => Array (), ); /** * Stores log data. * * @var array */ protected $logData = array(); /** * Creates e-mail instance */ public function __construct() { parent::__construct(); $this->sender = $this->Application->recallObject('EmailSender'); } /** * Resets state of e-mail * * @return void * @access protected */ protected function _resetState() { $this->logData = array(); $this->fromEmail = $this->fromName = ''; $this->Application->removeObject('u.email-from'); $this->recipients = Array ( EmailTemplate::RECIPIENT_TYPE_TO => Array (), EmailTemplate::RECIPIENT_TYPE_CC => Array (), EmailTemplate::RECIPIENT_TYPE_BCC => Array (), ); $this->toEmail = $this->toEmail = ''; $this->Application->removeObject('u.email-to'); } /** * Finds e-mail template matching user data * * @param string $name * @param int $type * @return bool * @throws InvalidArgumentException * @access public */ public function findTemplate($name, $type) { if ( !$name || !preg_match('/^[A-Z\.]+$/', $name) ) { throw new InvalidArgumentException('Invalid e-mail template name "' . $name . '". Only UPPERCASE characters and dots are allowed.'); } if ( $type != EmailTemplate::TEMPLATE_TYPE_ADMIN && $type != EmailTemplate::TEMPLATE_TYPE_FRONTEND ) { throw new InvalidArgumentException('Invalid e-mail template type'); } // use "-item" special prevent error, when e-mail sent out from e-mail templates list $this->emailTemplate = $this->Application->recallObject('email-template.-item', null, Array ('skip_autoload' => true)); if ( !$this->emailTemplate->isLoaded() || !$this->_sameTemplate($name, $type) ) { // get template parameters by name & type $this->emailTemplate->Load(Array ('TemplateName' => $name, 'Type' => $type)); } return $this->_templateUsable(); } /** * Detects, that given e-mail template data matches currently used e-mail template * * @param string $name * @param int $type * @return bool * @access protected */ protected function _sameTemplate($name, $type) { return $this->emailTemplate->GetDBField('TemplateName') == $name && $this->emailTemplate->GetDBField('Type') == $type; } /** * Determines if we can use e-mail template we've found based on user data * * @return bool * @access protected */ protected function _templateUsable() { if ( !$this->emailTemplate->isLoaded() || $this->emailTemplate->GetDBField('Enabled') == STATUS_DISABLED ) { return false; } if ( $this->emailTemplate->GetDBField('FrontEndOnly') && $this->Application->isAdmin ) { return false; } return true; } /** * Sets e-mail template params * * @param Array $params * @access public */ public function setParams($params) { $this->params = $params; } /** * Returns any custom parameters, that are passed when invoked e-mail template sending * * @return Array * @access protected */ protected function _getCustomParams() { $ret = $this->params; $send_keys = Array ( 'from_email', 'from_name', 'to_email', 'to_name', 'overwrite_to_email', 'language_id', 'use_custom_design', 'delivery', 'PrefixSpecial', 'item_id', ); foreach ($send_keys as $send_key) { unset($ret[$send_key]); } return $ret; } /** * Sends e-mail now or puts it in queue * * @param int $recipient_user_id * @return bool * @access public */ public function send($recipient_user_id = null) { $this->recipientUserId = $recipient_user_id; $this->_resetState(); $this->_processSender(); $this->_processRecipients(); $this->_changeLanguage(false); if ( $this->_storeEmailLog() ) { // 1. prepare log $this->logData = Array ( 'From' => $this->fromName . ' (' . $this->fromEmail . ')', 'To' => $this->toName . ' (' . $this->toEmail . ')', 'OtherRecipients' => serialize($this->recipients), 'Status' => EmailLogStatus::SENT, 'ErrorMessage' => '', 'SentOn' => TIMENOW, 'TemplateName' => $this->emailTemplate->GetDBField('TemplateName'), 'EventType' => $this->emailTemplate->GetDBField('Type'), 'EventParams' => serialize($this->_getCustomParams()), 'ToUserId' => $this->recipientUserId, 'ItemPrefix' => $this->getItemPrefix(), 'ItemId' => isset($this->params['item_id']) ? $this->params['item_id'] : null, ); $this->params['email_access_key'] = $this->_generateAccessKey(); } // 1. set headers try { $message_headers = $this->_getHeaders(); } catch ( Exception $e ) { return $this->setError('Error parsing e-mail message headers'); } $message_subject = isset($message_headers['Subject']) ? $message_headers['Subject'] : 'Mail message'; $this->sender->SetSubject($message_subject); foreach ($message_headers as $header_name => $header_value) { $this->sender->SetEncodedHeader($header_name, $header_value); } if ( $this->_storeEmailLog() ) { $this->logData['Subject'] = $message_subject; } // 3. set body try { $html_message_body = $this->_getMessageBody(true); $plain_message_body = $this->_getMessageBody(false); } catch ( Exception $e ) { return $this->setError('Error parsing e-mail message body'); } if ( $html_message_body === false && $plain_message_body === false ) { return $this->setError('Message template is empty (maybe after parsing).'); } if ( $html_message_body !== false ) { $this->sender->CreateTextHtmlPart($html_message_body, true); } if ( $plain_message_body !== false ) { $this->sender->CreateTextHtmlPart($plain_message_body, false); } $this->_changeLanguage(true); if ( $this->_storeEmailLog() ) { // 4. set log $this->logData['HtmlBody'] = $html_message_body; $this->logData['TextBody'] = $plain_message_body; $this->logData['AccessKey'] = $this->params['email_access_key']; $this->sender->setLogData($this->logData); } $delivery = isset($this->params['delivery']) ? $this->params['delivery'] : $this->Application->ConfigValue('EmailDelivery'); return $this->sender->Deliver(null, $delivery == EmailDelivery::IMMEDIATE); } /** * Extracts prefix from a given PrefixSpecial parameter. * * @return string */ protected function getItemPrefix() { $prefix_special = isset($this->params['PrefixSpecial']) ? $this->params['PrefixSpecial'] : ''; if ( !$prefix_special ) { return ''; } $prefix_info = $this->Application->processPrefix($prefix_special); return $prefix_info['prefix']; } /** * Determines whatever we should keep e-mail log or not * * @return bool * @access protected */ protected function _storeEmailLog() { return $this->Application->ConfigValue('EnableEmailLog'); } /** * Marks e-mail sending as failed. * * @param string $error_message Error message. * * @return boolean */ protected function setError($error_message) { if ( $this->_storeEmailLog() ) { $this->logData['Status'] = EmailLogStatus::ERROR; $this->logData['ErrorMessage'] = $error_message; $this->Conn->doInsert($this->logData, TABLE_PREFIX . 'EmailLog'); } return false; } /** * Generates access key for accessing e-mail later * * @return string * @access protected */ protected function _generateAccessKey() { $ret = ''; $use_fields = Array ('From', 'To', 'Subject'); foreach ($use_fields as $use_field) { $ret .= $this->logData[$use_field] . ':'; } return md5($ret . microtime(true)); } /** * Processes email sender * * @return void * @access protected */ protected function _processSender() { if ( $this->emailTemplate->GetDBField('CustomSender') ) { $this->_processCustomSender(); } // update with custom data given during event execution if ( isset($this->params['from_email']) ) { $this->fromEmail = $this->params['from_email']; } if ( isset($this->params['from_name']) ) { $this->fromName = $this->params['from_name']; } // still nothing, set defaults $this->_ensureDefaultSender(); $this->sender->SetFrom($this->fromEmail, $this->fromName); } /** * Processes custom e-mail sender * * @return void * @access protected */ protected function _processCustomSender() { $address = $this->emailTemplate->GetDBField('SenderAddress'); $address_type = $this->emailTemplate->GetDBField('SenderAddressType'); switch ($address_type) { case EmailTemplate::ADDRESS_TYPE_EMAIL: $this->fromEmail = $address; break; case EmailTemplate::ADDRESS_TYPE_USER: $sql = 'SELECT FirstName, LastName, Email, PortalUserId FROM ' . TABLE_PREFIX . 'Users WHERE Username = ' . $this->Conn->qstr($address); $user_info = $this->Conn->GetRow($sql); if ( $user_info ) { // user still exists $this->fromEmail = $user_info['Email']; $this->fromName = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $user = $this->Application->recallObject('u.email-from', null, Array ('skip_autoload' => true)); /* @var $user UsersItem */ $user->Load($user_info['PortalUserId']); } break; } if ( $this->emailTemplate->GetDBField('SenderName') ) { $this->fromName = $this->emailTemplate->GetDBField('SenderName'); } } /** * Ensures, that sender name & e-mail are not empty * * @return void * @access protected */ protected function _ensureDefaultSender() { if ( !$this->fromEmail ) { $this->fromEmail = $this->Application->ConfigValue('DefaultEmailSender'); } if ( !$this->fromName ) { $this->fromName = strip_tags($this->Application->ConfigValue('Site_Name')); } } /** * Processes email recipients * * @return void * @access protected */ protected function _processRecipients() { $this->_collectRecipients(); $header_mapping = Array ( EmailTemplate::RECIPIENT_TYPE_TO => 'To', EmailTemplate::RECIPIENT_TYPE_CC => 'Cc', EmailTemplate::RECIPIENT_TYPE_BCC => 'Bcc', ); $default_email = $this->Application->ConfigValue('DefaultEmailSender'); $this->recipients = array_map(Array ($this, '_transformRecipientsIntoPairs'), $this->recipients); foreach ($this->recipients as $recipient_type => $recipients) { // add recipients to email if ( !$recipients ) { continue; } if ( $recipient_type == EmailTemplate::RECIPIENT_TYPE_TO ) { $this->toEmail = $recipients[0]['email'] ? $recipients[0]['email'] : $default_email; $this->toName = $recipients[0]['name'] ? $recipients[0]['name'] : $this->toEmail; } $header_name = $header_mapping[$recipient_type]; foreach ($recipients as $recipient) { $email = $recipient['email'] ? $recipient['email'] : $default_email; $name = $recipient['name'] ? $recipient['name'] : $email; $this->sender->AddRecipient($header_name, $email, $name); } } } /** * Collects e-mail recipients from various sources * * @return void * @access protected */ protected function _collectRecipients() { $this->_addRecipientsFromXml($this->emailTemplate->GetDBField('Recipients')); $this->_overwriteToRecipient(); $this->_addRecipientByUserId(); $this->_addRecipientFromParams(); if ( ($this->emailTemplate->GetDBField('Type') == EmailTemplate::TEMPLATE_TYPE_ADMIN) && !$this->recipients[EmailTemplate::RECIPIENT_TYPE_TO] ) { // admin email template without direct recipient -> send to admin $this->_addDefaultRecipient(); } } /** * Adds multiple recipients from an XML * * @param string $xml * @return bool * @access protected */ protected function _addRecipientsFromXml($xml) { if ( !$xml ) { return false; } $minput_helper = $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ // group recipients by type $records = $minput_helper->parseMInputXML($xml); foreach ($records as $record) { $this->recipients[$record['RecipientType']][] = $record; } return true; } /** * Remove all "To" recipients, when not allowed * * @return void * @access protected */ protected function _overwriteToRecipient() { $overwrite_to_email = isset($this->params['overwrite_to_email']) ? $this->params['overwrite_to_email'] : false; if ( !$this->emailTemplate->GetDBField('CustomRecipient') || $overwrite_to_email ) { $this->recipients[EmailTemplate::RECIPIENT_TYPE_TO] = Array (); } } /** * Update with custom data given during event execution (user_id) * * @return void * @access protected */ protected function _addRecipientByUserId() { if ( !is_numeric($this->recipientUserId) ) { return; } if ( $this->recipientUserId <= 0 ) { // recipient is system user with negative ID (root, guest, etc.) -> send to admin $this->_addDefaultRecipient(); return; } $language_field = $this->emailTemplate->GetDBField('Type') == EmailTemplate::TEMPLATE_TYPE_FRONTEND ? 'FrontLanguage' : 'AdminLanguage'; $sql = 'SELECT FirstName, LastName, Email, ' . $language_field . ' AS Language FROM ' . TABLE_PREFIX . 'Users WHERE PortalUserId = ' . $this->recipientUserId; $user_info = $this->Conn->GetRow($sql); if ( !$user_info ) { return; } $add_recipient = Array ( 'RecipientAddressType' => EmailTemplate::ADDRESS_TYPE_EMAIL, 'RecipientAddress' => $user_info['Email'], 'RecipientName' => trim($user_info['FirstName'] . ' ' . $user_info['LastName']), ); if ( $user_info['Language'] && !isset($this->params['language_id']) ) { $this->params['language_id'] = $user_info['Language']; } array_unshift($this->recipients[EmailTemplate::RECIPIENT_TYPE_TO], $add_recipient); $user = $this->Application->recallObject('u.email-to', null, Array('skip_autoload' => true)); /* @var $user UsersItem */ $user->Load($this->recipientUserId); } /** * Update with custom data given during event execution (email + name) * * @return void * @access protected */ protected function _addRecipientFromParams() { $add_recipient = Array (); if ( isset($this->params['to_email']) && $this->params['to_email'] ) { $add_recipient['RecipientName'] = ''; $add_recipient['RecipientAddressType'] = EmailTemplate::ADDRESS_TYPE_EMAIL; $add_recipient['RecipientAddress'] = $this->params['to_email']; } if ( isset($this->params['to_name']) && $this->params['to_name'] ) { $add_recipient['RecipientName'] = $this->params['to_name']; } if ( $add_recipient ) { array_unshift($this->recipients[EmailTemplate::RECIPIENT_TYPE_TO], $add_recipient); } } /** * This is default recipient, when we can't determine actual one * * @return void * @access protected */ protected function _addDefaultRecipient() { $xml = $this->Application->ConfigValue('DefaultEmailRecipients'); if ( !$this->_addRecipientsFromXml($xml) ) { $recipient = Array ( 'RecipientName' => $this->Application->ConfigValue('DefaultEmailSender'), 'RecipientAddressType' => EmailTemplate::ADDRESS_TYPE_EMAIL, 'RecipientAddress' => $this->Application->ConfigValue('DefaultEmailSender'), ); array_unshift($this->recipients[EmailTemplate::RECIPIENT_TYPE_TO], $recipient); } } /** * Transforms recipients into name/e-mail pairs * * @param Array $recipients * @return Array * @access protected */ protected function _transformRecipientsIntoPairs($recipients) { if ( !$recipients ) { return Array (); } $pairs = Array (); foreach ($recipients as $recipient) { $address = $recipient['RecipientAddress']; $address_type = $recipient['RecipientAddressType']; $recipient_name = $recipient['RecipientName']; switch ($address_type) { case EmailTemplate::ADDRESS_TYPE_EMAIL: $pairs[] = Array ('email' => $address, 'name' => $recipient_name); break; case EmailTemplate::ADDRESS_TYPE_USER: $sql = 'SELECT FirstName, LastName, Email FROM ' . TABLE_PREFIX . 'Users WHERE Username = ' . $this->Conn->qstr($address); $user_info = $this->Conn->GetRow($sql); if ( $user_info ) { // user still exists $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $pairs[] = Array ( 'email' => $user_info['Email'], 'name' => $name ? $name : $recipient_name, ); } break; case EmailTemplate::ADDRESS_TYPE_GROUP: $sql = 'SELECT u.FirstName, u.LastName, u.Email FROM ' . TABLE_PREFIX . 'UserGroups g JOIN ' . TABLE_PREFIX . 'UserGroupRelations ug ON ug.GroupId = g.GroupId JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = ug.PortalUserId WHERE g.Name = ' . $this->Conn->qstr($address); $users = $this->Conn->Query($sql); foreach ($users as $user_info) { $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); $pairs[] = Array ( 'email' => $user_info['Email'], 'name' => $name ? $name : $recipient_name, ); } break; } } return $pairs; } /** * Change system language temporarily to send e-mail on user language * * @param bool $restore * @return void * @access protected */ protected function _changeLanguage($restore = false) { static $prev_language_id = null; if ( !isset($prev_language_id) ) { $prev_language_id = $this->Application->GetVar('m_lang'); } // ensure that language is set if ( !isset($this->params['language_id']) ) { $this->params['language_id'] = $this->Application->GetVar('m_lang'); } $language_id = $restore ? $prev_language_id : $this->params['language_id']; $this->Application->SetVar('m_lang', $language_id); $language = $this->Application->recallObject('lang.current'); /* @var $language LanguagesItem */ $language->Load($language_id); $this->Application->Phrases->LanguageId = $language_id; $this->Application->Phrases->Phrases = Array (); } /** * Parses message headers into array * * @return Array * @access protected */ protected function _getHeaders() { $headers = $this->emailTemplate->GetDBField('Headers'); $headers = 'Subject: ' . $this->emailTemplate->GetField('Subject') . ($headers ? "\n" . $headers : ''); $headers = explode("\n", $this->_parseText($headers)); $ret = Array (); foreach ($headers as $header) { $header = explode(':', $header, 2); $ret[ trim($header[0]) ] = trim($header[1]); } if ( $this->Application->isDebugMode() ) { // set special header with template name, so it will be easier to determine what's actually was received $template_type = $this->emailTemplate->GetDBField('Type') == EmailTemplate::TEMPLATE_TYPE_ADMIN ? 'ADMIN' : 'USER'; $ret['X-Template-Name'] = $this->emailTemplate->GetDBField('TemplateName') . ' - ' . $template_type; } return $ret; } /** * Applies design to given e-mail text * * @param string $text * @param bool $is_html * @return string * @access protected */ protected function _applyMessageDesign($text, $is_html = true) { static $design_templates = Array(); $design_key = 'L' . $this->params['language_id'] . ':' . ($is_html ? 'html' : 'text'); if ( !isset($design_templates[$design_key]) ) { $language = $this->Application->recallObject('lang.current'); /* @var $language LanguagesItem */ $design_template = $language->GetDBField($is_html ? 'HtmlEmailTemplate' : 'TextEmailTemplate'); if ( !$is_html && !$design_template ) { $design_template = $this->sender->ConvertToText($language->GetDBField('HtmlEmailTemplate'), true); } $design_templates[$design_key] = $design_template; } return $this->_parseText(str_replace('$body', $text, $design_templates[$design_key]), $is_html); } /** * Returns message body * * @param bool $is_html * @return bool|string * @access protected */ protected function _getMessageBody($is_html = false) { $message_body = $this->emailTemplate->GetField($is_html ? 'HtmlBody' : 'PlainTextBody'); if ( !trim($message_body) && !$is_html ) { // no plain text part available -> make it from html part then $message_body = $this->sender->ConvertToText($this->emailTemplate->GetField('HtmlBody'), true); } if ( !trim($message_body) ) { return false; } if ( isset($this->params['use_custom_design']) && $this->params['use_custom_design'] ) { $message_body = $this->_parseText($message_body, $is_html); } else { $message_body = $this->_applyMessageDesign($message_body, $is_html); } return trim($message_body) ? $message_body : false; } /** * Parse message template and return headers (as array) and message body part * * @param string $text * @param bool $is_html * @return string * @access protected */ protected function _parseText($text, $is_html = true) { $text = $this->_substituteReplacementTags($text); if ( !$text ) { return ''; } // init for cases, when e-mail is sent from event before page template rendering $this->Application->InitParser(); $parser_params = $this->Application->Parser->Params; // backup parser params $this->Application->Parser->SetParams($this->params); - $text = $this->Application->Parser->Parse($this->_normalizeLineEndings($text), 'email_template'); + $template_name = 'et_' . $this->emailTemplate->GetID() . '_' . crc32($text); + $text = $this->Application->Parser->Parse($this->_normalizeLineEndings($text), $template_name); $this->Application->Parser->SetParams($parser_params); // restore parser params $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ return $category_helper->replacePageIds($is_html ? $this->_removeTrailingLineEndings($text) : $text); } /** * Substitutes replacement tags in given text * * @param string $text * @return string * @access protected */ protected function _substituteReplacementTags($text) { $default_replacement_tags = Array ( ' ' ' 'emailTemplate->GetDBField('ReplacementTags'); $replacement_tags = $replacement_tags ? unserialize($replacement_tags) : Array (); $replacement_tags = array_merge($default_replacement_tags, $replacement_tags); foreach ($replacement_tags as $replace_from => $replace_to) { $text = str_replace($replace_from, $replace_to, $text); } return $text; } /** * Convert Unix/Windows/Mac line ending into Unix line endings * * @param string $text * @return string * @access protected */ protected function _normalizeLineEndings($text) { return str_replace(Array ("\r\n", "\r"), "\n", $text); } /** * Remove trailing line endings * * @param $text * @return string * @access protected */ protected function _removeTrailingLineEndings($text) { return preg_replace('/(\n|\r)+/', "\\1", $text); } } Index: branches/5.3.x/core/kernel/globals.php =================================================================== --- branches/5.3.x/core/kernel/globals.php (revision 16110) +++ branches/5.3.x/core/kernel/globals.php (revision 16111) @@ -1,1011 +1,1071 @@ $sValue2) { $paArray1[$sKey2] = isset($paArray1[$sKey2]) ? self::array_merge_recursive($paArray1[$sKey2], $sValue2) : $sValue2; } return $paArray1; } /** * Prepend a reference to an element to the beginning of an array. * Renumbers numeric keys, so $value is always inserted to $array[0] * * @param $array array * @param $value mixed * @return int * @access public */ public static function array_unshift_ref(&$array, &$value) { $return = array_unshift($array,''); $array[0] =& $value; return $return; } /** * Rename key in associative array, maintaining keys order * * @param Array $array Associative Array * @param mixed $old Old key name * @param mixed $new New key name * @access public */ public static function array_rename_key(&$array, $old, $new) { $new_array = Array (); foreach ($array as $key => $val) { $new_array[ $key == $old ? $new : $key] = $val; } $array = $new_array; } /** * Same as print_r, but outputs result on screen or in debugger report (when in debug mode) * * @param Array $data * @param string $label * @param bool $on_screen * @access public */ public static function print_r($data, $label = '', $on_screen = false) { $is_debug = false; if ( class_exists('kApplication') && !$on_screen ) { $application =& kApplication::Instance(); $is_debug = $application->isDebugMode(); } if ( $is_debug && isset($application) ) { if ( $label ) { $application->Debugger->appendHTML('' . $label . ''); } $application->Debugger->dumpVars($data); } else { if ( $label ) { echo '' . $label . '
'; } echo '
', print_r($data, true), '
'; } } /** * Define constant if it was not already defined before * * @param string $const_name * @param string $const_value * @access public */ public static function safeDefine($const_name, $const_value) { if ( !defined($const_name) ) { define($const_name, $const_value); } } /** * Instantiate kSystemConfig class once and store locally * * @access public */ public static function getSystemConfig() { static $system_config; if ( !isset($system_config) ) { $system_config = new kSystemConfig(); } return $system_config; } /** * Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set * * @param string $file * @access public */ public static function includeOnce($file) { global $debugger; if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) { if ( in_array($file, get_included_files()) ) { return ; } global $debugger; /*$debugger->IncludeLevel++; $before_mem = memory_get_usage();*/ $debugger->ProfileStart('inc_'.crc32($file), $file); include_once($file); $debugger->ProfileFinish('inc_'.crc32($file)); $debugger->profilerAddTotal('includes', 'inc_'.crc32($file)); /*$used_mem = memory_get_usage() - $before_mem; $debugger->IncludeLevel--; $debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file); $debugger->IncludesData['mem'][] = $used_mem; $debugger->IncludesData['time'][] = $used_time; $debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/ } else { include_once($file); } } /** * Checks if given string is a serialized array * * @param string $string * @return bool * @access public */ public static function IsSerialized($string) { if ( is_array($string) ) { return false; } return preg_match('/a:([\d]+):{/', $string); } /** * Generates password of given length * * @param int $length * @return string * @access public */ public static function generatePassword($length = 10) { $pass_length = $length; $p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'); $p2 = Array ('a','e','i','o','u'); $p3 = Array ('1','2','3','4','5','6','7','8','9'); $p4 = Array ('(','&',')',';','%'); // if you need real strong stuff // how much elements in the array // can be done with a array count but counting once here is faster $s1 = 21;// this is the count of $p1 $s2 = 5; // this is the count of $p2 $s3 = 9; // this is the count of $p3 $s4 = 5; // this is the count of $p4 // possible readable combinations $c1 = '121'; // will be like 'bab' $c2 = '212'; // will be like 'aba' $c3 = '12'; // will be like 'ab' $c4 = '3'; // will be just a number '1 to 9' if you dont like number delete the 3 //$c5 = '4'; // uncomment to active the strong stuff $comb = '4'; // the amount of combinations you made above (and did not comment out) for ($p = 0; $p < $pass_length;) { mt_srand((double)microtime() * 1000000); $strpart = mt_rand(1, $comb); // checking if the stringpart is not the same as the previous one if ($strpart != $previous) { $pass_structure .= ${'c' . $strpart}; // shortcutting the loop a bit $p = $p + mb_strlen(${'c' . $strpart}); } $previous = $strpart; } // generating the password from the structure defined in $pass_structure for ($g = 0; $g < mb_strlen($pass_structure); $g++) { mt_srand((double)microtime() * 1000000); $sel = mb_substr($pass_structure, $g, 1); $pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ]; } return $pass; } /** * submits $url with $post as POST * * @param string $url * @param mixed $data * @param Array $headers * @param string $request_type * @param Array $curl_options * @return string * @access public * @deprecated */ public static function curl_post($url, $data, $headers = NULL, $request_type = 'POST', $curl_options = NULL) { $application =& kApplication::Instance(); $curl_helper = $application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ if ($request_type == 'POST') { $curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST); } $curl_helper->SetRequestData($data); if (!is_null($headers)) { // not an associative array, so don't use kCurlHelper::SetHeaders method $curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) ); } if (is_array($curl_options)) { $curl_helper->setOptions($curl_options); } $curl_helper->followLocation = false; $ret = $curl_helper->Send($url); $GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode; $GLOBALS['curl_error'] = $curl_helper->lastErrorMsg; return $ret; } /** * Checks if constant is defined and has positive value * * @param string $const_name * @return bool * @access public */ public static function constOn($const_name) { return defined($const_name) && constant($const_name); } /** * Converts KG to Pounds * * @param float $kg * @param bool $pounds_only * @return float * @access public */ public static function Kg2Pounds($kg, $pounds_only = false) { $major = floor( round($kg / self::POUND_TO_KG, 3) ); $minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2)); if ($pounds_only) { $major += round($minor * 0.0625, 2); $minor = 0; } return array($major, $minor); } /** * Converts Pounds to KG * * @param float $pounds * @param float $ounces * @return float * @access public */ public static function Pounds2Kg($pounds, $ounces = 0.00) { return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5); } /** * Formats file/memory size in nice way * * @param int $bytes * @return string * @access public */ public static function formatSize($bytes) { if ($bytes >= 1099511627776) { $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); $suffix = "TB"; } elseif ($bytes >= 1073741824) { $return = round($bytes / 1024 / 1024 / 1024, 2); $suffix = "GB"; } elseif ($bytes >= 1048576) { $return = round($bytes / 1024 / 1024, 2); $suffix = "MB"; } elseif ($bytes >= 1024) { $return = round($bytes / 1024, 2); $suffix = "KB"; } else { $return = $bytes; $suffix = "Byte"; } $return .= ' '.$suffix; return $return; } /** * Enter description here... * * @param resource $filePointer the file resource to write to * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @access public */ public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { fwrite($filePointer, self::getcsvline($data, $delimiter, $enclosure, $recordSeparator)); } /** * Enter description here... * * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @return string * @access public */ public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { - foreach($data as $field_index => $field_value) { - // replaces an enclosure with two enclosures - $data[$field_index] = str_replace($enclosure, $enclosure.$enclosure, $field_value); - } + ob_start(); + $fp = fopen('php://output', 'w'); + fputcsv($fp, $data, $delimiter, $enclosure); + fclose($fp); + $ret = ob_get_clean(); - $line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator; - $line = preg_replace('/'.preg_quote($enclosure, '/').'([0-9\.]+)'.preg_quote($enclosure, '/').'/', '$1', $line); + if ( $recordSeparator != "\n" ) { + return substr($ret, 0, -1) . $recordSeparator; + } - return $line; + return $ret; } /** * Allows to replace #section# within any string with current section * * @param string $string * @return string * @access public */ public static function replaceModuleSection($string) { $application =& kApplication::Instance(); $module_section = $application->RecallVar('section'); if ($module_section) { // substitute section instead of #section# parameter in title preset name $module_section = explode(':', $module_section); $section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]); $string = str_replace('#section#', mb_strtolower($section), $string); } return $string; } /** * Checks, that user IP address is within allowed range * * @param string $ip_list semi-column (by default) separated ip address list * @param string $separator ip address separator (default ";") * * @return bool * @access public */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $application =& kApplication::Instance(); $client_ip = $application->getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } if ($network == $ip) { // comparing two ip addresses directly return true; } $d = strpos($network, '-'); if ($d !== false) { // ip address range specified $from = ip2long(trim(substr($network, 0, $d))); $to = ip2long(trim(substr($network, $d + 1))); $ip = ip2long($ip); return ($ip >= $from && $ip <= $to); } elseif (strpos($network, '/') !== false) { // single subnet specified $ip_arr = explode('/', $network); if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) { $ip_arr[0] .= '.0'; // Alternate form 194.1.4/24 } $network_long = ip2long($ip_arr[0]); $x = ip2long($ip_arr[1]); $mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1])); $ip_long = ip2long($ip); return ($ip_long & $mask) == ($network_long & $mask); } return false; } /** * Returns mime type corresponding to given file * @param string $file * @return string * @access public */ public static function mimeContentType($file) { $ret = self::vendorMimeContentType($file); if ( $ret ) { // vendor-specific mime types override any automatic detection return $ret; } if ( function_exists('finfo_open') && function_exists('finfo_file') ) { $mime_magic_resource = finfo_open(FILEINFO_MIME_TYPE); if ( $mime_magic_resource ) { $ret = finfo_file($mime_magic_resource, $file); finfo_close($mime_magic_resource); } } elseif ( function_exists('mime_content_type') ) { $ret = mime_content_type($file); } return $ret ? $ret : self::mimeContentTypeByExtension($file); } /** * Determines vendor-specific mime type from a given file * * @param string $file * @return bool * @access public * @static */ public static function vendorMimeContentType($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = Array ( 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' ); return isset($mapping[$file_extension]) ? $mapping[$file_extension] : false; } /** * Detects mime type of the file purely based on it's extension * * @param string $file * @return string * @access public */ public static function mimeContentTypeByExtension($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf) (pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf) (tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript) (ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip) (mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap) (gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff) (wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml) (mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822) (sxw:application/vnd.sun.xml.writer)(sxc:application/vnd.sun.xml.calc)(sxi:application/vnd.sun.xml.impress) (sxd:application/vnd.sun.xml.draw)(sxm:application/vnd.sun.xml.math) (odt:application/vnd.oasis.opendocument.text)(oth:application/vnd.oasis.opendocument.text-web) (odm:application/vnd.oasis.opendocument.text-master)(odg:application/vnd.oasis.opendocument.graphics) (odp:application/vnd.oasis.opendocument.presentation)(ods:application/vnd.oasis.opendocument.spreadsheet) (odc:application/vnd.oasis.opendocument.chart)(odf:application/vnd.oasis.opendocument.formula) (odi:application/vnd.oasis.opendocument.image)'; if ( preg_match('/[\(,]' . $file_extension . '[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs) ) { return $regs[1]; } return 'application/octet-stream'; } /** * Strips ".tmp" suffix (added by flash uploader) from a filename * * @param string $file * @return string * @access public * @static */ public static function removeTempExtension($file) { return preg_replace('/(_[\d]+)?\.tmp$/', '', $file); } /** * Return param value and removes it from params array * * @param string $name * @param Array $params * @param bool $default * @return string */ public static function popParam($name, &$params, $default = false) { if ( isset($params[$name]) ) { $value = $params[$name]; unset($params[$name]); return $value; } return $default; } /** * Generate subpath from hashed value * * @param string $name * @param int $levels * @return string */ public static function getHashPathForLevel($name, $levels = 2) { if ( $levels == 0 ) { return ''; } else { $path = ''; $hash = md5($name); for ($i = 0; $i < $levels; $i++) { $path .= substr($hash, $i, 1) . '/'; } return $path; } } /** * Calculates the crc32 polynomial of a string (always positive number) * * @param string $str * @return int */ public static function crc32($str) { return sprintf('%u', crc32($str)); } /** * Returns instance of DateTime class with date set based on timestamp * * @static * @param int $timestamp * @return DateTime * @access public */ public static function dateFromTimestamp($timestamp) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { $date = new DateTime('@' . $timestamp); $date->setTimezone(new DateTimeZone(date_default_timezone_get())); } else { $date = new DateTime(); $date->setTimestamp($timestamp); } return $date; } /** * Returns timestamp from given DateTime class instance * * @static * @param DateTime $date_time * @return int|string * @access public */ public static function timestampFromDate(DateTime $date_time) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { return $date_time->format('U'); } return $date_time->getTimestamp(); } /** * Generates random numeric id * * @static * @return string * @access public */ public static function generateId() { list($usec, $sec) = explode(' ', microtime()); $id_part_1 = substr($usec, 4, 4); $id_part_2 = mt_rand(1, 9); $id_part_3 = substr($sec, 6, 4); $digit_one = substr($id_part_1, 0, 1); if ( $digit_one == 0 ) { $digit_one = mt_rand(1, 9); $id_part_1 = preg_replace('/^0/', '', $id_part_1); $id_part_1 = $digit_one . $id_part_1; } return $id_part_1 . $id_part_2 . $id_part_3; } /** * Changes script resource limits. Omitted argument results in limit removal. * * @static * @param string|int $memory_limit * @param int $time_limit * @return void * @access public */ public static function setResourceLimit($memory_limit = null, $time_limit = null) { set_time_limit(isset($time_limit) ? $time_limit : 0); ini_set('memory_limit', isset($memory_limit) ? $memory_limit : -1); } /** * Escapes a string. * * @param string $text Text to escape. * @param string $strategy Escape strategy. * * @return string * @throws InvalidArgumentException When unknown escape strategy is given. */ public static function escape($text, $strategy = null) { if ( !isset($strategy) ) { $strategy = self::$escapeStrategy; } if ( strpos($strategy, '+') !== false ) { $previous_strategy = ''; $strategies = explode('+', $strategy); foreach ($strategies as $current_strategy) { // apply default strategy if ( $current_strategy == '' ) { $current_strategy = self::$escapeStrategy; } // don't double-escape if ( $current_strategy != $previous_strategy ) { $text = self::escape($text, $current_strategy); $previous_strategy = $current_strategy; } } return $text; } if ( $strategy == self::ESCAPE_HTML ) { return htmlspecialchars($text, ENT_QUOTES, CHARSET); } if ( $strategy == self::ESCAPE_JS ) { // TODO: consider using "addcslashes", because "addslashes" isn't really for JavaScript escaping (according to docs) $text = addslashes($text); $text = str_replace(array("\r", "\n"), array('\r', '\n'), $text); $text = str_replace('', "", $text); return $text; } if ( $strategy == self::ESCAPE_URL ) { return rawurlencode($text); } if ( $strategy == self::ESCAPE_RAW ) { return $text; } throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } + /** + * Unescapes a string. + * + * @param string $text Text to unescape. + * @param string $strategy Escape strategy. + * + * @return string + * @throws InvalidArgumentException When unknown escape strategy is given. + */ + public static function unescape($text, $strategy = null) + { + if ( !isset($strategy) ) { + $strategy = self::$escapeStrategy; + } + + if ( strpos($strategy, '+') !== false ) { + $previous_strategy = ''; + $strategies = explode('+', $strategy); + + foreach ($strategies as $current_strategy) { + // apply default strategy + if ( $current_strategy == '' ) { + $current_strategy = self::$escapeStrategy; + } + + // don't double-unescape + if ( $current_strategy != $previous_strategy ) { + $text = self::unescape($text, $current_strategy); + $previous_strategy = $current_strategy; + } + } + + return $text; + } + + if ( $strategy == self::ESCAPE_HTML ) { + return htmlspecialchars_decode($text, ENT_QUOTES); + } + + if ( $strategy == self::ESCAPE_JS ) { + // TODO: consider using "stripcslashes", because "stripslashes" isn't really for JavaScript unescaping (according to docs) + $text = str_replace("", '', $text); + $text = str_replace(array('\r', '\n'), array("\r", "\n"), $text); + $text = stripslashes($text); + + return $text; + } + + if ( $strategy == self::ESCAPE_URL ) { + return rawurldecode($text); + } + + if ( $strategy == self::ESCAPE_RAW ) { + return $text; + } + + throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); + } } /** * Returns array value if key exists * Accepts infinite number of parameters * * @param Array $array searchable array * @param int $key array key * @return string */ function getArrayValue(&$array, $key) { $ret = isset($array[$key]) ? $array[$key] : false; if ( $ret && func_num_args() > 2 ) { for ($i = 2; $i < func_num_args(); $i++) { $cur_key = func_get_arg($i); $ret = getArrayValue($ret, $cur_key); if ( $ret === false ) { break; } } } return $ret; } if ( !function_exists('parse_ini_string') ) { /** * Equivalent for "parse_ini_string" function available since PHP 5.3.0 * * @param string $ini * @param bool $process_sections * @param int $scanner_mode * @return Array */ function parse_ini_string($ini, $process_sections = false, $scanner_mode = NULL) { # Generate a temporary file. $tempname = tempnam('/tmp', 'ini'); $fp = fopen($tempname, 'w'); fwrite($fp, $ini); $ini = parse_ini_file($tempname, !empty($process_sections)); fclose($fp); @unlink($tempname); return $ini; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !function_exists('imagecreatefrombmp') ) { // just in case if GD will add this function in future function imagecreatefrombmp($filename) { //Ouverture du fichier en mode binaire if (! $f1 = fopen($filename,"rb")) return FALSE; //1 : Chargement des ent�tes FICHIER $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14)); if ($FILE['file_type'] != 19778) return FALSE; //2 : Chargement des ent�tes BMP $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40)); $BMP['colors'] = pow(2,$BMP['bits_per_pixel']); if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] = 4-(4*$BMP['decal']); if ($BMP['decal'] == 4) $BMP['decal'] = 0; //3 : Chargement des couleurs de la palette $PALETTE = array(); if ($BMP['colors'] < 16777216) { $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4)); } //4 : Cr�ation de l'image $IMG = fread($f1,$BMP['size_bitmap']); $VIDE = chr(0); $res = imagecreatetruecolor($BMP['width'],$BMP['height']); $P = 0; $Y = $BMP['height']-1; while ($Y >= 0) { $X=0; while ($X < $BMP['width']) { if ($BMP['bits_per_pixel'] == 24) $COLOR = unpack("V",substr($IMG,$P,3).$VIDE); elseif ($BMP['bits_per_pixel'] == 16) { $COLOR = unpack("n",substr($IMG,$P,2)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 8) { $COLOR = unpack("n",$VIDE.substr($IMG,$P,1)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 4) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 1) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7; elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6; elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5; elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4; elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3; elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2; elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1; elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } else return FALSE; imagesetpixel($res,$X,$Y,$COLOR[1]); $X++; $P += $BMP['bytes_per_pixel']; } $Y--; $P+=$BMP['decal']; } //Fermeture du fichier fclose($f1); return $res; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/nparser/nparser.php =================================================================== --- branches/5.3.x/core/kernel/nparser/nparser.php (revision 16110) +++ branches/5.3.x/core/kernel/nparser/nparser.php (revision 16111) @@ -1,1192 +1,1188 @@ _btnPhrases['design'] = $this->Application->Phrase('la_btn_EditDesign', false, true); $this->_btnPhrases['block'] = $this->Application->Phrase('la_btn_EditBlock', false, true); } $this->RewriteUrls = $this->Application->RewriteURLs(); $this->UserLoggedIn = $this->Application->LoggedIn(); // cache only Front-End templated, when memory caching is available and template caching is enabled in configuration $this->CachingEnabled = !$this->Application->isAdmin && $this->Application->ConfigValue('SystemTagCache') && $this->Application->isCachingType(CACHING_TYPE_MEMORY); } function Compile($pre_parsed, $template_name = 'unknown') { $data = file_get_contents($pre_parsed['tname']); if (!$this->CompileRaw($data, $pre_parsed['tname'], $template_name)) { // compilation failed during errors in template // trigger_error('Template "' . $template_name . '" not compiled because of errors', E_USER_WARNING); return false; } // saving compiled version (only when compilation was successful) $this->Application->TemplatesCache->saveTemplate($pre_parsed['fname'], $this->Buffers[0]); return true; } function Parse($raw_template, $name = null) { $this->CompileRaw($raw_template, $name); ob_start(); $_parser =& $this; eval('?'.'>'.$this->Buffers[0]); return ob_get_clean(); } function CompileRaw($data, $t_name, $template_name = 'unknown') { $code = "extract (\$_parser->Params);\n"; $code .= "\$_parser->ElementLocations['{$template_name}'] = Array('template' => '{$template_name}', 'start_pos' => 0, 'end_pos' => " . strlen($data) . ");\n"; // $code .= "__@@__DefinitionsMarker__@@__\n"; // $code .= "if (!\$this->CacheStart('".abs(crc32($t_name))."_0')) {\n"; $this->Buffers[0] = '\n"; $this->Cacheable[0] = true; $this->Definitions = ''; // finding all the tags $reg = '(.*?)(<[\\/]?)' . TAG_NAMESPACE . '([^>]*?)([\\/]?>)(\r\n){0,1}'; preg_match_all('/'.$reg.'/s', $data, $results, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); $this->InsideComment = false; foreach ($results as $tag_data) { $tag = array( 'opening' => $tag_data[2][0], 'tag' => $tag_data[3][0], 'closing' => $tag_data[4][0], 'line' => substr_count(substr($data, 0, $tag_data[2][1]), "\n")+1, 'pos' => $tag_data[2][1], 'file' => $t_name, 'template' => $template_name, ); // the idea is to count number of comment openings and closings before current tag // if the numbers do not match we inverse the status of InsideComment if ($this->SkipComments && (substr_count($tag_data[1][0], ''))) { $this->InsideComment = !$this->InsideComment; } // appending any text/html data found before tag $this->Buffers[$this->Level] .= $tag_data[1][0]; if (!$this->InsideComment) { $tmp_tag = $this->Application->CurrentNTag; $this->Application->CurrentNTag = $tag; if ($this->ProcessTag($tag) === false) { $this->Application->CurrentNTag = $tmp_tag; return false; } $this->Application->CurrentNTag = $tmp_tag; } else { $this->Buffers[$this->Level] .= $tag_data[2][0] . TAG_NAMESPACE . $tag_data[3][0] . $tag_data[4][0]; } } if ($this->Level > 0) { $error_tag = Array ( 'file' => $this->Stack[$this->Level]->Tag['file'], 'line' => $this->Stack[$this->Level]->Tag['line'], ); throw new ParserException('Unclosed tag opened by ' . $this->TagInfo($this->Stack[$this->Level]->Tag), 0, null, $error_tag); } // appending text data after last tag (after its closing pos), // if no tag was found at all ($tag_data is not set) - append the whole $data $this->Buffers[$this->Level] .= isset($tag_data) ? substr($data, $tag_data[4][1]+strlen($tag_data[4][0])) : $data; $this->Buffers[$this->Level] = preg_replace('//s', '', $this->Buffers[$this->Level]); // remove hidden comments IB#23065 // $this->Buffers[$this->Level] .= 'CacheEnd();\n}\n"." ?".">\n"; // $this->Buffers[$this->Level] = str_replace('__@@__DefinitionsMarker__@@__', $this->Definitions, $this->Buffers[$this->Level]); return true; } function SplitParamsStr($params_str) { preg_match_all('/([\${}a-zA-Z0-9_.\\-\\\\#\\[\\]]+)=(["\']{1,1})(.*?)(? $val){ $values[$val[1]] = str_replace('\\' . $val[2], $val[2], $val[3]); } return $values; } function SplitTag($tag) { if (!preg_match('/([^_ \t\r\n]*)[_]?([^ \t\r\n]*)[ \t\r\n]*(.*)$$/s', $tag['tag'], $parts)) { // this is virtually impossible, but just in case throw new ParserException('Incorrect tag format: ' . $tag['tag'], 0, null, $tag); } $splited['prefix'] = $parts[2] ? $parts[1] : '__auto__'; $splited['name'] = $parts[2] ? $parts[2] : $parts[1]; $splited['attrs'] = $parts[3]; return $splited; } function ProcessTag($tag) { $splited = $this->SplitTag($tag); if ($splited === false) { return false; } $tag = array_merge($tag, $splited); $tag['processed'] = false; $tag['NP'] = $this->SplitParamsStr($tag['attrs']); $o = ''; $tag['is_closing'] = $tag['opening'] == ''; if (class_exists('_Tag_'.$tag['name'])) { // block tags should have special handling class if ($tag['opening'] == '<') { $class = '_Tag_'.$tag['name']; $instance = new $class($tag); $instance->Parser =& $this; /* @var $instance _BlockTag */ $this->Stack[++$this->Level] =& $instance; $this->Buffers[$this->Level] = ''; $this->Cachable[$this->Level] = true; $open_code = $instance->Open($tag); if ($open_code === false) { return false; } $o .= $open_code; } if ($tag['is_closing']) { // not ELSE here, because tag may be and still has a handler-class if ($this->Level == 0) { $dump = array(); foreach ($this->Stack as $instance) { $dump[] = $instance->Tag; } if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->dumpVars($dump); } $error_msg = 'Closing tag without an opening: ' . $this->TagInfo($tag) . ' - probably opening tag was removed or nested tags error'; throw new ParserException($error_msg, 0, null, $tag); } if ($this->Stack[$this->Level]->Tag['name'] != $tag['name']) { $opening_tag = $this->Stack[$this->Level]->Tag; $error_msg = ' Closing tag ' . $this->TagInfo($tag) . ' does not match opening tag at current nesting level (' . $this->TagInfo($opening_tag) . ' opened at line ' . $opening_tag['line'] . ')'; throw new ParserException($error_msg, 0, null, $tag); } $o .= $this->Stack[$this->Level]->Close($tag); // DO NOT use $this->Level-- here because it's used inside Close $this->Level--; } } else { // regular tags - just compile if (!$tag['is_closing']) { $error_msg = 'Tag without a handler: ' . $this->TagInfo($tag) . ' - probably missing <empty /> tag closing'; throw new ParserException($error_msg, 0, null, $tag); } if ($this->Level > 0) $o .= $this->Stack[$this->Level]->PassThrough($tag); if (!$tag['processed']) { $compiled = $this->CompileTag($tag); if ($compiled === false) return false; if (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) { $this->Cachable[$this->Level] = false; } $o .= '\n"; // $o .= 'BreakCache($compiled, $this->GetPointer($tag)) : $compiled; // $o .= " ?".">\n"; } } $this->Buffers[$this->Level] .= $o; return true; } function GetPointer($tag) { return abs(crc32($tag['file'])).'_'.$tag['line']; } function BreakCache($code, $pointer, $condition='') { return "\$_parser->CacheEnd();\n}\n" . $code."\nif ( !\$_parser->CacheStart('{$pointer}'" . ($condition ? ", {$condition}" : '') . ") ) {\n"; } function TagInfo($tag, $with_params=false) { return "{$tag['prefix']}_{$tag['name']}".($with_params ? ' '.$tag['attrs'] : '').""; } function CompileParamsArray($arr) { $to_pass = 'Array('; foreach ($arr as $name => $val) { $to_pass .= '"'.$name.'" => "'.str_replace('"', '\"', $val).'",'; } $to_pass .= ')'; return $to_pass; } function CompileTag($tag) { $code = ''; $to_pass = $this->CompileParamsArray($tag['NP']); if ($tag['prefix'] == '__auto__') { $prefix = $this->GetParam('PrefixSpecial'); $code .= '$_p_ =& $_parser->GetProcessor($PrefixSpecial);'."\n"; $code .= 'echo $_p_->ProcessParsedTag("'.$tag['name'].'", '.$to_pass.', "$PrefixSpecial", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } else { $prefix = $tag['prefix']; $code .= '$_p_ =& $_parser->GetProcessor("'.$tag['prefix'].'");'."\n"; $code .= 'echo $_p_->ProcessParsedTag("'.$tag['name'].'", '.$to_pass.', "'.$tag['prefix'].'", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } if (array_key_exists('result_to_var', $tag['NP']) && $tag['NP']['result_to_var']) { $code .= "\$params['{$tag['NP']['result_to_var']}'] = \$_parser->GetParam('{$tag['NP']['result_to_var']}');\n"; $code .= "\${$tag['NP']['result_to_var']} = \$params['{$tag['NP']['result_to_var']}'];\n"; } if ($prefix && strpos($prefix, '$') === false) { $p =& $this->GetProcessor($prefix); if (!is_object($p) || !$p->CheckTag($tag['name'], $tag['prefix'])) { $error_msg = 'Unknown tag: ' . $this->TagInfo($tag) . ' - incorrect tag name or prefix'; throw new ParserException($error_msg, 0, null, $tag); } } return $code; } function CheckTemplate($t, $silent = null) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($t); if (!$pre_parsed) { if (!$silent) { throw new ParserException('Cannot include "' . $t . '" - file does not exist'); } return false; } $force_compile = defined('DBG_NPARSER_FORCE_COMPILE') && DBG_NPARSER_FORCE_COMPILE; if (!$pre_parsed || !$pre_parsed['active'] || $force_compile) { $inc_parser = new NParser(); if ($force_compile) { // remove Front-End theme markings during total compilation $t = preg_replace('/^theme:.*?\//', '', $t); } if (!$inc_parser->Compile($pre_parsed, $t)) { return false; } } return $pre_parsed; } function Run($t, $silent = null) { if ((strpos($t, '../') !== false) || (trim($t) !== $t)) { // when relative paths or special chars are found template names from url, then it's hacking attempt return false; } $pre_parsed = $this->CheckTemplate($t, $silent); if (!$pre_parsed) { return false; } $backup_template = $this->TemplateName; $backup_fullpath = $this->TempalteFullPath; $this->TemplateName = $t; $this->TempalteFullPath = $pre_parsed['tname']; if (!isset($backup_template) && $this->CachingEnabled && !$this->UserLoggedIn && !EDITING_MODE) { // this is main page template -> check for page-based aggressive caching settings $output =& $this->RunMainPage($pre_parsed); } else { $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); } $this->TemplateName = $backup_template; $this->TempalteFullPath = $backup_fullpath; return $output; } function &RunMainPage($pre_parsed) { $page = $this->Application->recallObject('st.-virtual'); /* @var $page kDBItem */ if ($page->isLoaded()) { // page found in database $debug_mode = $this->Application->isDebugMode(); // don't cache debug output $template_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $this->TempalteFullPath, 1); $element = ($debug_mode ? 'DEBUG_MODE:' : '') . 'file=' . $template_path; $this->FullCachePage = $page->GetDBField('EnablePageCache'); if ($this->FullCachePage && $page->GetDBField('PageCacheKey')) { // page caching enabled -> try to get from cache $cache_key = $this->FormCacheKey($element, $page->GetDBField('PageCacheKey')); $output = $this->getCache($cache_key); if ($output !== false) { return $output; } } // page not cached OR cache expired $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); $this->generatePageCacheKey($page); if ($this->FullCachePage && $page->GetDBField('PageCacheKey')) { $cache_key = $this->FormCacheKey($element, $page->GetDBField('PageCacheKey')); $this->setCache($cache_key, $output, (int)$page->GetDBField('PageExpiration')); } } else { // page not found in database $output =& $this->Application->TemplatesCache->runTemplate($this, $pre_parsed); } return $output; } /** * Generate page caching key based on prefixes used on it + prefix IDs passed in url * * @param kDBItem $page */ function generatePageCacheKey(&$page) { if (!$page->isLoaded() || $page->GetDBField('OverridePageCacheKey')) { return ; } $page_cache_key = Array (); // nobody resets "m" prefix serial, don't count no user too unset($this->PrefixesInUse['m'], $this->PrefixesInUse['u']); if (array_key_exists('st', $this->PrefixesInUse)) { // prefix "st" serial will never be changed unset($this->PrefixesInUse['st']); $this->PrefixesInUse['c'] = 1; } $prefix_ids = Array (); $prefixes = array_keys($this->PrefixesInUse); asort($prefixes); foreach ($prefixes as $index => $prefix) { $id = $this->Application->GetVar($prefix . '_id'); if (is_numeric($id)) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Found: "' . $prefix . '_id" = ' . $id . ' during PageCacheKey forming.'); } $prefix_ids[] = $prefix; unset($prefixes[$index]); } } if ($prefix_ids) { $page_cache_key[] = 'prefix_id:' . implode(',', $prefix_ids); } if ($prefixes) { $page_cache_key[] = 'prefix:' . implode(',', $prefixes); } $page_cache_key = implode(';', $page_cache_key); if ($page_cache_key != $page->GetOriginalField('PageCacheKey')) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Canging PageCacheKey from "' . $page->GetOriginalField('PageCacheKey') . '" to "' . $page_cache_key . '".'); } $page->SetDBField('PageCacheKey', $page_cache_key); // don't use kDBItem::Update(), because it will change ModifiedById to current front-end user $sql = 'UPDATE ' . $page->TableName . ' SET PageCacheKey = ' . $this->Conn->qstr($page_cache_key) . ' WHERE ' . $page->IDField . ' = ' . $page->GetID(); $this->Conn->Query($sql); - - // increment serial, because we issue direct sql above! - $this->Application->incrementCacheSerial('c'); - $this->Application->incrementCacheSerial('c', $page->GetID()); } } /** * Creates tag processor and stores it in local cache + factory * * @param string $prefix * @return kTagProcessor */ function &GetProcessor($prefix) { static $processors = Array (); if ( !isset($processors[$prefix]) ) { $processors[$prefix] = $this->Application->recallObject($prefix . '_TagProcessor'); } return $processors[$prefix]; } /** * Not tag. Method for parameter selection from list in this TagProcessor * * @param Array $params * @param Array $possible_names * * @return string * @access protected */ protected function SelectParam($params, $possible_names) { if ( !is_array($params) ) { return ''; } if ( !is_array($possible_names) ) { $possible_names = explode(',', $possible_names); } foreach ($possible_names as $name) { if ( isset($params[$name]) ) { return $params[$name]; } } return ''; } function SetParams($params) { $this->Params = $params; $keys = array_keys($this->Params); } function GetParam($name) { return isset($this->Params[$name]) ? $this->Params[$name] : false; } function SetParam($name, $value) { $this->Params[$name] = $value; } function PushParams($params) { $this->ParamsStack[$this->ParamsLevel++] = $this->Params; $this->Params = $params; } function PopParams() { $this->Params = $this->ParamsStack[--$this->ParamsLevel]; } function ParseBlock($params, $pass_params=false) { if (array_key_exists('cache_timeout', $params) && $params['cache_timeout']) { $ret = $this->getCache( $this->FormCacheKey('element_' . $params['name']) ); if ($ret) { return $ret; } } if (substr($params['name'], 0, 5) == 'html:') { return substr($params['name'], 5); } if (!array_key_exists($params['name'], $this->Elements) && array_key_exists('default_element', $params)) { // when given element not found, but default element name given, then render it instead $params['name'] = $params['default_element']; unset($params['default_element']); return $this->ParseBlock($params, $pass_params); } $original_params = $params; if ($pass_params || isset($params['pass_params'])) $params = array_merge($this->Params, $params); $this->PushParams($params); $data_exists_bak = $this->DataExists; // if we are parsing design block and we have block_no_data - we need to wrap block_no_data into design, // so we should set DataExists to true manually, otherwise the design block will be skipped because of data_exists in params (by Kostja) // // keep_data_exists is used by block RenderElement (always added in ntags.php), to keep the DataExists value // from inside-content block, otherwise when parsing the design block DataExists will be reset to false resulting missing design block (by Kostja) // // Inside-content block parsing result is given to design block in "content" parameter (ntags.php) and "keep_data_exists" // is only passed, when parsing design block. In case, when $this->DataExists is set to true, but // zero-length content (in 2 cases: method NParser::CheckNoData set it OR really empty block content) // is returned from inside-content block, then design block also should not be shown (by Alex) $this->DataExists = (isset($params['keep_data_exists']) && isset($params['content']) && $params['content'] != '' && $this->DataExists) || (isset($params['design']) && isset($params['block_no_data']) && $params['name'] == $params['design']); if (!array_key_exists($params['name'], $this->Elements)) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($params['name']); if ($pre_parsed) { $ret = $this->IncludeTemplate($params); if (array_key_exists('no_editing', $params) && $params['no_editing']) { // when individual render element don't want to be edited return $ret; } return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params, true) : $ret; } $trace_results = debug_backtrace(); $error_tag = Array ( 'file' => $trace_results[0]['file'], 'line' => $trace_results[0]['line'], ); $error_msg = 'Rendering of undefined element ' . $params['name'] . ''; throw new ParserException($error_msg, 0, null, $error_tag); } $m_processor =& $this->GetProcessor('m'); $flag_values = $m_processor->PreparePostProcess($params); $f_name = $this->Elements[$params['name']]; /* @var $f_name Closure */ $ret = $f_name($this, $params); $ret = $m_processor->PostProcess($ret, $flag_values); $block_params = $this->Params; // input parameters, but modified inside rendered block $this->PopParams(); if (array_key_exists('result_to_var', $flag_values) && $flag_values['result_to_var']) { // when "result_to_var" used inside ParseBlock, then $$result_to_var parameter is set inside ParseBlock, // but not outside it as expected and got lost at all after PopParams is called, so make it work by // setting it's value on current parameter deep level (from where ParseBlock was called) $this->SetParam($flag_values['result_to_var'], $block_params[ $flag_values['result_to_var'] ]); } $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; if (array_key_exists('cache_timeout', $original_params) && $original_params['cache_timeout']) { $cache_key = $this->FormCacheKey('element_' . $original_params['name']); $this->setCache($cache_key, $ret, (int)$original_params['cache_timeout']); } if (array_key_exists('no_editing', $block_params) && $block_params['no_editing']) { // when individual render element don't want to be edited return $ret; } return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params) : $ret; } /** * Checks, that given block is defined * * @param string $name * @return bool */ function blockFound($name) { return array_key_exists($name, $this->Elements); } function DecorateBlock($block_content, $block_params, $is_template = false) { static $used_ids = Array (), $base_url = null; if (!isset($base_url)) { $base_url = $this->Application->BaseURL(); } // $prepend = '[name: ' . $block_params['name'] . '] [params: ' . implode(', ', array_keys($block_params)) . ']'; $decorate = false; $design = false; if (EDITING_MODE == EDITING_MODE_DESIGN) { $decorate = true; if ($is_template) { // content inside pair RenderElement tag } else { if (strpos($block_params['name'], '__capture_') === 0) { // capture tag (usually inside pair RenderElement) $decorate = false; } elseif (array_key_exists('content', $block_params)) { // pair RenderElement (on template, were it's used) $design = true; } } } if (!$decorate) { return $block_content; } /*else { $block_content = $prepend . $block_content; }*/ $block_name = $block_params['name']; $function_name = $is_template ? $block_name : $this->Elements[$block_name]; $block_title = ''; if (array_key_exists($function_name, $this->Application->Parser->ElementLocations)) { $element_location = $this->Application->Parser->ElementLocations[$function_name]; $block_title .= $element_location['template'] . '.tpl'; $block_title .= ' (' . $element_location['start_pos'] . ' - ' . $element_location['end_pos'] . ')'; } // ensure unique id for every div (used from print lists) $container_num = 1; $container_id = 'parser_block[' . $function_name . ']'; while (in_array($container_id . '_' . $container_num, $used_ids)) { $container_num++; } $container_id .= '_' . $container_num; $used_ids[] = $container_id; // prepare parameter string $param_string = $block_name . ':' . $function_name; if ($design) { $btn_text = $this->_btnPhrases['design']; $btn_class = 'cms-edit-design-btn'; $btn_container_class = 'block-edit-design-btn-container'; $btn_name = 'design'; } else { $btn_text = $this->_btnPhrases['block']; $btn_class = 'cms-edit-block-btn'; $btn_container_class = 'block-edit-block-btn-container'; $btn_name = 'content'; } $icon_url = $base_url . 'core/admin_templates/img/top_frame/icons/' . $btn_name . '_mode.png'; $block_editor = '
%s
'; // 1 - text before, 2 - open tag, 3 - open tag attributes, 4 - content inside tag, 5 - closing tag, 6 - text after closing tag if (preg_match('/^(\s*)<(td|span)(.*?)>(.*)<\/(td|span)>(.*)$/is', $block_content, $regs)) { // div inside span -> put div outside span return $regs[1] . '<' . $regs[2] . ' ' . $regs[3] . '>' . str_replace('%s', $regs[4], $block_editor) . '' . $regs[6]; } return str_replace('%s', $block_content, $block_editor); } function IncludeTemplate($params, $silent=null) { $t = is_array($params) ? $this->SelectParam($params, 't,template,block,name') : $params; $cache_timeout = array_key_exists('cache_timeout', $params) ? $params['cache_timeout'] : false; if ($cache_timeout) { $cache_key = $this->FormCacheKey('template:' . $t); $ret = $this->getCache($cache_key); if ($ret !== false) { return $ret; } } $t = preg_replace('/\.tpl$/', '', $t); $data_exists_bak = $this->DataExists; $this->DataExists = false; if (!isset($silent) && array_key_exists('is_silent', $params)) { $silent = $params['is_silent']; } if (isset($params['pass_params'])) { // ability to pass params from block to template $params = array_merge($this->Params, $params); } $m_processor =& $this->GetProcessor('m'); $flag_values = $m_processor->PreparePostProcess($params); $this->PushParams($params); $ret = $this->Run($t, $silent); $this->PopParams(); $ret = $m_processor->PostProcess($ret, $flag_values); $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; if ($cache_timeout) { $this->setCache($cache_key, $ret, (int)$cache_timeout); } return $ret; } function CheckNoData(&$ret, $params) { if (array_key_exists('data_exists', $params) && $params['data_exists'] && !$this->DataExists) { $block_no_data = isset($params['BlockNoData']) ? $params['BlockNoData'] : (isset($params['block_no_data']) ? $params['block_no_data'] : false); if ($block_no_data) { $ret = $this->ParseBlock(array('name'=>$block_no_data)); } else { $ret = ''; } } } function getCache($name) { if (!$this->CachingEnabled) { return false; } $ret = $this->Application->getCache($name, false); if (preg_match('/^\[DE_MARK:(.*?)\]$/', substr($ret, -11), $regs)) { $this->DataExists = $regs[1] ? true : false; $ret = substr($ret, 0, -11); } return $ret; } function setCache($name, $value, $expiration = 0) { if (!$this->CachingEnabled) { return false; } // remeber DataExists in cache, because after cache will be restored // it will not be available naturally (no tags, that set it will be called) $value .= '[DE_MARK:' . (int)$this->DataExists . ']'; return $this->Application->setCache($name, $value, $expiration); } function FormCacheKey($element, $key_string = '') { if (strpos($key_string, 'guest_only') !== false && $this->UserLoggedIn) { // don't cache, when user is logged-in "guest_only" is specified in key return ''; } $parts = Array (); // 1. replace INLINE variable (from request) into key parts if (preg_match_all('/\(%(.*?)\)/', $key_string, $regs)) { // parts in form "(%variable_name)" were found foreach ($regs[1] as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); $key_string = str_replace('(%' . $variable_name . ')', $variable_value, $key_string); } } // 2. replace INLINE serial numbers (they may not be related to any prefix at all) // Serial number also could be composed of inline variables! if (preg_match_all('/\[%(.*?)%\]/', $key_string, $regs)) { // format "[%LangSerial%]" - prefix-wide serial in case of any change in "lang" prefix // format "[%LangIDSerial:5%]" - one id-wide serial in case of data, associated with given id was changed // format "[%CiIDSerial:ItemResourceId:5%]" - foreign key-based serial in case of data, associated with given foreign key was changed foreach ($regs[1] as $serial_name) { $serial_value = $this->Application->getCache('[%' . $serial_name . '%]'); $key_string = str_replace('[%' . $serial_name . '%]', '[%' . $serial_name . '=' . $serial_value . '%]', $key_string); } } /* Always add: =========== * "var:m_lang" - show content on current language * "var:t" - template from url, used to differ multiple pages using same physical template (like as design) * "var:admin,editing_mode" - differ cached content when different editing modes are used * "var:m_cat_id,m_cat_page" - pass current category * "var:page,per_page,sort_by" - list pagination/sorting parameters * "prefix:theme-file" - to be able to reset all cached templated using "Rebuild Theme Files" function * "prefix:phrases" - use latest phrase translations * "prefix:conf" - output could slighly differ based on configuration settings */ $key_string = rtrim('var:m_lang,t,admin,editing_mode,m_cat_id,m_cat_page,page,per_page,sort_by;prefix:theme-file,phrases,conf;' . $key_string, ';'); $keys = explode(';', $key_string); /* Possible parts of a $key_string (all can have multiple occurencies): ==================================================================== * prefix:[,,] - include global serial for given prefix(-es) * skip_prefix:[,,] - exclude global serial for given prefix(-es) * prefix_id:[,,] - include id-based serial for given prefix(-es) * skip_prefix_id:[,,] - exclude id-based serial for given prefix(-es) * var:[,,] - include request variable value(-s) * skip_var:[,,] - exclude request variable value(-s) * (%variable_name) - include request variable value (only value without variable name ifself, like in "var:variable_name") * [%SerialName%] - use to retrieve serial value in free form */ // 3. get variable names, prefixes and prefix ids, that should be skipped $skip_prefixes = $skip_prefix_ids = $skip_variables = Array (); foreach ($keys as $index => $key) { if (preg_match('/^(skip_var|skip_prefix|skip_prefix_id):(.*?)$/i', $key, $regs)) { unset($keys[$index]); $tmp_parts = explode(',', $regs[2]); switch ($regs[1]) { case 'skip_var': $skip_variables = array_merge($skip_variables, $tmp_parts); break; case 'skip_prefix': $skip_prefixes = array_merge($skip_prefixes, $tmp_parts); break; case 'skip_prefix_id': $skip_prefix_ids = array_merge($skip_prefix_ids, $tmp_parts); break; } } } $skip_prefixes = array_unique($skip_prefixes); $skip_variables = array_unique($skip_variables); $skip_prefix_ids = array_unique($skip_prefix_ids); // 4. process keys foreach ($keys as $key) { if (preg_match('/^(var|prefix|prefix_id):(.*?)$/i', $key, $regs)) { $tmp_parts = explode(',', $regs[2]); switch ($regs[1]) { case 'var': // format: "var:country_id" will become "country_id=" $tmp_parts = array_diff($tmp_parts, $skip_variables); foreach ($tmp_parts as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); if ($variable_value !== false) { $parts[] = $variable_name . '=' . $variable_value; } } break; case 'prefix': // format: "prefix:country" will become "[%CountrySerial%]" $tmp_parts = array_diff($tmp_parts, $skip_prefixes); foreach ($tmp_parts as $prefix) { $serial_name = $this->Application->incrementCacheSerial($prefix, null, false); $parts[] = '[%' . $serial_name . '=' . $this->Application->getCache($serial_name) . '%]'; if (!$this->RewriteUrls) { // add env-style page and per-page variable, when mod-rewrite is off $prefix_variables = Array ($prefix . '_Page', $prefix . '_PerPage'); foreach ($prefix_variables as $variable_name) { $variable_value = $this->Application->GetVar($variable_name); if ($variable_value !== false) { $parts[] = $variable_name . '=' . $variable_value; } } } } break; case 'prefix_id': // format: "id:country" will become "[%CountryIDSerial:5%]" $tmp_parts = array_diff($tmp_parts, $skip_prefix_ids); foreach ($tmp_parts as $prefix_id) { $id = $this->Application->GetVar($prefix_id . '_id'); if ($id !== false) { $serial_name = $this->Application->incrementCacheSerial($prefix_id, $id, false); $parts[] = '[%' . $serial_name . '=' . $this->Application->getCache($serial_name) . '%]'; } } break; } } elseif ($key == 'currency') { // based on current currency $parts[] = 'curr_iso=' . $this->Application->RecallVar('curr_iso'); } elseif ($key == 'groups') { // based on logged-in user groups $parts[] = 'groups=' . $this->Application->RecallVar('UserGroups'); } elseif ($key == 'guest_only') { // we know this key, but process it at method beginning } else { throw new ParserException('Unknown key part "' . $key . '" used in "key" parameter of tag'); } } // 5. add unique given cache key identifier on this page $parts[] = $element; $key = implode(':', $parts); if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Parser Key: ' . $key); } return 'parser_' . crc32($key); } function PushPointer($pointer, $key) { $cache_key = $this->FullCachePage || !$this->CachingEnabled ? '' : $this->FormCacheKey('pointer:' . $pointer, $key); $this->CachePointers[++$this->CacheLevel] = $cache_key; return $this->CachePointers[$this->CacheLevel]; } function PopPointer() { return $this->CachePointers[$this->CacheLevel--]; } function CacheStart($pointer, $key) { $pointer = $this->PushPointer($pointer, $key); if ($pointer) { $ret = $this->getCache($pointer); $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode(); if ($ret !== false) { echo $debug_mode ? '' . $ret . '' : $ret; $this->PopPointer(); return true; } if ($debug_mode) { echo ''; } } ob_start(); return false; } function CacheEnd($expiration = 0) { $ret = ob_get_clean(); $pointer = $this->PopPointer(); if ($pointer) { $res = $this->setCache($pointer, $ret, $expiration); if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { echo ''; } } echo $ret; } /** * Performs compression of given files or text * * @param mixed $data * @param bool $raw_script * @param string $file_extension * @return string */ function CompressScript($data, $raw_script = false, $file_extension = '') { $minify_helper = $this->Application->recallObject('MinifyHelper'); /* @var $minify_helper MinifyHelper */ if ($raw_script) { $minify_helper->compressString($data, $file_extension); return $data; } return $minify_helper->CompressScriptTag($data); } } class ParserException extends Exception { public function __construct($message = null, $code = 0, $previous = null, $tag = null) { parent::__construct($message, $code, $previous); if ( isset($tag) ) { $this->file = $tag['file']; $this->line = $tag['line']; } } } Index: branches/5.3.x/core/kernel/nparser/template_cache.php =================================================================== --- branches/5.3.x/core/kernel/nparser/template_cache.php (revision 16110) +++ branches/5.3.x/core/kernel/nparser/template_cache.php (revision 16111) @@ -1,293 +1,293 @@ BasePath = FULL_PATH . THEMES_PATH; $this->_compileToDatabase = defined('SAFE_MODE') && SAFE_MODE; if ( $this->Application->isAdmin ) { // prepare module template paths for quick access $module_paths = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { $module_paths[mb_strtolower($module_name)] = rtrim($module_info['Path'], '/'); } $this->_modulePaths = $module_paths; } } /** * Based on template name gets it's location on disk and owner module * * @param string $filename * @return Array 0 - path on disk, 1 - template name */ function GetTemplatePaths($filename) { if ( $this->Application->isAdmin && array_key_exists($filename, $this->Application->ReplacementTemplates) ) { // process admin template replacement $filename = $this->Application->ReplacementTemplates[$filename]; } // allows to use non-replaced version of replaced template $filename = preg_replace('/^original:(.*)/', '\\1', $filename); if ( preg_match('#^[\/]{0,1}([^\/]*)\/(.*)#', $filename, $regs) ) { $first_dir = $regs[1]; $module_filename = $regs[2]; } else { $first_dir = ''; $module_filename = $filename; } if ( is_string($this->forceThemeName) ) { // when defined, then all templates are read from given theme name $first_dir = 'theme:' . $this->forceThemeName . ($first_dir ? '/' . $first_dir : ''); } if ( $this->Application->isAdmin && array_key_exists($first_dir, $this->_modulePaths) ) { // template belongs to one of the modules $path = FULL_PATH . '/' . $this->_modulePaths[$first_dir] . '/admin_templates'; } elseif ( $this->forceThemeName && preg_match('/^theme:(.*)/', $first_dir, $regs) ) { // ability to use Front-End templates in admin (only during mass compilation) $path = FULL_PATH . '/themes/' . $regs[1]; } else { // template from "core" module $path = $this->BasePath; $module_filename = $first_dir . '/' . $module_filename; } return Array ($path, $module_filename); } /** * Returns template filename by given template name * * @param string $filename * @return string */ function GetRealFilename($filename) { list ($path, $module_filename) = $this->GetTemplatePaths($filename); return $path . '/' . trim($module_filename, '/'); } /** * Checks, that given template exists on disk * * @param string $filename * @return bool */ function TemplateExists($filename) { if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) { // when relative paths or special chars are found template names from url, then it's hacking attempt return false; } - $real_file = $this->GetRealFilename(strtolower($filename)); + $real_file = $this->GetRealFilename($filename); if ( substr($real_file, -4) != '.tpl' ) { // add ".tpl" file extension, when not specified in template name $real_file .= '.tpl'; } return file_exists($real_file); } /** * Returns information about template compilation status * * @param string $template * @return Array */ function GetPreParsed($template) { $real_name = $this->GetRealFilename($template); $fname = str_replace(FULL_PATH, WRITEABLE . '/cache', $real_name . '.php'); $tname = $real_name . '.tpl'; if ( !file_exists($tname) ) { // given template doesn't exist return false; } if ( $this->_compileToDatabase ) { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = "' . $fname . '"'; $cached = $this->Conn->GetRow($sql); if ( $cached !== false && $cached['Cached'] > filemtime($tname) ) { return Array ('active' => 1, 'fname' => $fname, 'tname' => $tname, 'mode' => 'db', 'content' => $cached['Data']); } } else { if ( file_exists($fname) && file_exists($tname) && filemtime($fname) > filemtime($tname) ) { return Array ('active' => 1, 'fname' => $fname, 'tname' => $tname, 'mode' => 'file'); } if ( !file_exists($fname) ) { // make sure to create directory if pre-parsed file does not exist $this->CheckDir(dirname($fname), WRITEABLE . '/cache'); } } // when compiled template is expired or doesn't exist return Array ('active' => 0, 'fname' => $fname, 'tname' => $tname, 'mode' => 'file'); } /** * Saves compiled template version to database or disk * * @param string $filename * @param string $compiled_template * @return void * @throws ParserException */ function saveTemplate($filename, &$compiled_template) { if ( $this->_compileToDatabase ) { $fields_hash = Array ( 'VarName' => $filename, 'Data' => &$compiled_template, 'Cached' => time(), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SystemCache', 'REPLACE'); return ; } $fp = fopen($filename, 'w'); if ( !fwrite($fp, $compiled_template) ) { throw new ParserException('Saving compiled template failed'); } fclose($fp); } /** * Runs template and returns result (template already should be compiled by now) * * @param NParser $_parser * @param Array $pre_parsed * @return string */ function &runTemplate(&$_parser, &$pre_parsed) { ob_start(); if ( $this->_compileToDatabase ) { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = "' . $pre_parsed['fname'] . '"'; $cached = $this->Conn->GetRow($sql); if ( ($cached !== false) && ($cached['Cached'] > filemtime($pre_parsed['tname'])) ) { eval('?' . '>' . $cached['Data']); } } else { if ( $pre_parsed['mode'] == 'file' ) { include($pre_parsed['fname']); } else { eval('?' . '>' . $pre_parsed['content']); } } $output = ob_get_clean(); return $output; } /** * Recursive mkdir * * @param string $dir * @param string $base_path base path to directory where folders should be created in * @return bool * @access protected */ protected function CheckDir($dir, $base_path = '') { if ( file_exists($dir) ) { return true; } // remove $base_path from beginning because it is already created during install $dir = preg_replace('/^' . preg_quote($base_path . '/', '/') . '/', '', $dir, 1); $segments = explode('/', $dir); $cur_path = $base_path; foreach ($segments as $segment) { // do not add leading / for windows paths (c:\...) $cur_path .= preg_match('/^[a-zA-Z]{1}:/', $segment) ? $segment : '/' . $segment; if ( !file_exists($cur_path) ) { if ( !mkdir($cur_path) ) { return false; } } } return false; } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/thesaurus/thesaurus_eh.php =================================================================== --- branches/5.3.x/core/units/thesaurus/thesaurus_eh.php (revision 16110) +++ branches/5.3.x/core/units/thesaurus/thesaurus_eh.php (revision 16111) @@ -1,39 +1,39 @@ getObject(); /* @var $object kDBList */ if ( !$this->Application->isAdminUser ) { - $keywords = htmlspecialchars_decode(trim($this->Application->GetVar('keywords'))); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $object->addFilter('search_filter', '%1$s.SearchTerm LIKE ' . $this->Conn->qstr($keywords) . ' OR %1$s.SearchTerm LIKE ' . $this->Conn->qstr($keywords . '_')); } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/thesaurus/thesaurus_tp.php =================================================================== --- branches/5.3.x/core/units/thesaurus/thesaurus_tp.php (revision 16110) +++ branches/5.3.x/core/units/thesaurus/thesaurus_tp.php (revision 16111) @@ -1,96 +1,96 @@ getObject($params); /* @var $object kDBItem */ $params['search_type'] = 'subsearch'; $params['keywords'] = $object->GetDBField('ThesaurusTerm'); $params['narrow_search'] = 1; return $this->Application->ProcessParsedTag('m', 'Link', $params); } function _getThesaurusRecords() { - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $table_name = $this->getUnitConfig()->getTableName(); $sql = 'SELECT * FROM ' . $table_name . ' WHERE SearchTerm LIKE ' . $this->Conn->qstr($keywords).' OR SearchTerm LIKE ' . $this->Conn->qstr($keywords . '_'); $records = $this->Conn->Query($sql); $new_records = Array (); foreach ($records as $record) { if ($record['ThesaurusType'] == THESAURUS_TYPE_USE) { // expand in global scope $sql = 'SELECT * FROM ' . $table_name . ' WHERE SearchTerm = ' . $this->Conn->qstr($record['ThesaurusTerm']); $use_records = $this->Conn->Query($sql); if ($use_records) { $new_records = array_merge($new_records, $use_records); } } $new_records[] = $record; } usort($new_records, Array (&$this, '_sortThesaurus')); ksort($new_records); return $new_records; } function HasThesaurus($params) { $new_records = $this->_getThesaurusRecords(); return count($new_records) > 0; } function PrintThesaurus($params) { $new_records = $this->_getThesaurusRecords(); $ret = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($new_records as $record) { $block_params['term'] = $record['ThesaurusTerm']; $url_params = Array ( 'search_type' => 'subsearch', 'keywords' => $record['ThesaurusTerm'], 'narrow_search' => 1, ); $block_params['url'] = $this->Application->ProcessParsedTag('m', 'Link', $url_params); $ret .= $this->Application->ParseBlock($block_params); } return $ret; } function _sortThesaurus($record_a, $record_b) { return strcmp(strtolower($record_a['ThesaurusTerm']), strtolower($record_b['ThesaurusTerm'])); } } Index: branches/5.3.x/core/units/theme_files/theme_file_eh.php =================================================================== --- branches/5.3.x/core/units/theme_files/theme_file_eh.php (revision 16110) +++ branches/5.3.x/core/units/theme_files/theme_file_eh.php (revision 16111) @@ -1,233 +1,234 @@ Array ('subitem' => true), 'OnSaveBlock' => Array ('subitem' => true), 'OnSaveLayout' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( $event->Name == 'OnLoadBlock' || $event->Name == 'OnSaveBlock' ) { return $this->Application->isAdminUser; } return parent::CheckPermission($event); } /** * Loads template contents into virtual field * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ $filename = $this->_getTemplatePath($object); if ( file_exists($filename) ) { $object->SetDBField('FileContents', file_get_contents($filename)); } else { $object->SetError('FileContents', 'template_file_missing', 'la_error_TemplateFileMissing'); } } /** * Trim contents of edited template * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ $file_data = $object->GetDBField('FileContents'); $file_data = str_replace("\r\n", "\n", $file_data); $file_data = str_replace("\r", "\n", $file_data); $object->SetDBField('FileContents', trim($file_data)); } /** * Saves updated content to template * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { parent::OnAfterItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ $filename = $this->_getTemplatePath($object); if ( file_exists($filename) && is_writable($filename) ) { $fp = fopen($filename, 'w'); fwrite($fp, $object->GetDBField('FileContents')); fclose($fp); $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $meta_info = $themes_helper->parseTemplateMetaInfo($filename); $file_description = array_key_exists('desc', $meta_info) ? $meta_info['desc'] : ''; $object->SetDBField('Description', $file_description); $object->SetDBField('FileMetaInfo', serialize($meta_info)); $object->Update(); } } /** * Returns full path to template file * * @param kDBItem $object * @return string */ function _getTemplatePath(&$object) { $theme = $this->Application->recallObject('theme'); /* @var $theme kDBItem */ $path = FULL_PATH . '/themes/' . $theme->GetDBField('Name'); $path .= $object->GetDBField('FilePath') . '/' . $object->GetDBField('FileName'); return $path; } /** * Loads block data based on it's name in request * * @param kEvent $event */ function OnLoadBlock($event) { parent::OnNew($event); $object = $event->getObject(); /* @var $object kDBItem */ $template_helper = $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ $template_helper->InitHelper($object); $object->SetDBField('FileName', $template_helper->blockInfo('template_file')); $object->SetDBField('BlockPosition', $template_helper->blockInfo('start_pos') . ' - ' . $template_helper->blockInfo('end_pos')); $object->SetDBField('FileContents', $template_helper->blockInfo('content')); } /** * Saves changed template block * * @param kEvent $event */ function OnSaveBlock($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->getRequestProtectedFields($field_values)); $object->setID($id); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); } $status = $object->Validate(); $template_helper = $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ $template_helper->InitHelper($object); $status = $status && $template_helper->saveBlock($object); if ($status) { $event->SetRedirectParam('opener', 'u'); } else { $event->status = kEvent::erFAIL; } } /** * Saves layout on given template * * @param kEvent $event */ function OnSaveLayout($event) { $event->status = kEvent::erSTOP; if (($this->Application->GetVar('ajax') != 'yes') || (EDITING_MODE != EDITING_MODE_DESIGN)) { return ; } $target_order = $this->Application->GetVar('target_order'); $template_helper = $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ if ($template_helper->moveTemplateElements($target_order)) { echo 'OK'; return ; } echo 'FAILED'; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/categories/categories_tag_processor.php =================================================================== --- branches/5.3.x/core/units/categories/categories_tag_processor.php (revision 16110) +++ branches/5.3.x/core/units/categories/categories_tag_processor.php (revision 16111) @@ -1,2287 +1,2286 @@ getObject($params); /* @var $object kDBItem */ if ( isset($params['today']) && $params['today'] ) { $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' WHERE (ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%") AND (CreatedOn > ' . (time() - 86400) . ')'; return $this->Conn->GetOne($sql) - 1; } return $object->GetDBField('CachedDescendantCatsQty'); } /** * Returns category count in system * * @param Array $params * @return int */ function CategoryCount($params) { $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $today_only = isset($params['today']) && $params['today']; return $count_helper->CategoryCount($today_only); } function IsNew($params) { $object = $this->getObject($params); /* @var $object kDBItem */ return $object->GetDBField('IsNew') ? 1 : 0; } function IsPick($params) { return $this->IsEditorsPick($params); } /** * Returns item's editors pick status (using not formatted value) * * @param Array $params * @return bool */ function IsEditorsPick($params) { $object = $this->getObject($params); /* @var $object kDBItem */ return $object->GetDBField('EditorsPick') == 1; } function ItemIcon($params) { $grid = $this->getUnitConfig()->getGridByName($params['grid']); if ( !array_key_exists('Icons', $grid) ) { return ''; } $icons = $grid['Icons']; $icon_prefix = array_key_exists('icon_prefix', $params) ? $params['icon_prefix'] : 'icon16_'; if ( array_key_exists('name', $params) ) { $icon_name = $params['name']; return array_key_exists($icon_name, $icons) ? $icons[$icon_name] : ''; } $object = $this->getObject($params); /* @var $object kDBList */ if ( $object->GetDBField('ThemeId') > 0 ) { if ( !$object->GetDBField('IsMenu') ) { return $icon_prefix . 'section_menuhidden_system.png'; } return $icon_prefix . 'section_system.png'; } $status = $object->GetDBField('Status'); if ( $status == STATUS_DISABLED ) { return $icon_prefix . 'section_disabled.png'; } if ( !$object->GetDBField('IsMenu') ) { return $icon_prefix . 'section_menuhidden.png'; } if ( $status == STATUS_PENDING ) { return $icon_prefix . 'section_pending.png'; } if ( $object->GetDBField('IsNew') && ($icon_prefix == 'icon16_') ) { return $icon_prefix . 'section_new.png'; // show gris icon only in grids } return $icon_prefix . 'section.png'; } function ItemCount($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $ci_table = $this->Application->getUnitConfig('ci')->getTableName(); $module_prefixes = implode(',', $this->Conn->qstrArray($this->_getModulePrefixes())); $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' c JOIN ' . $ci_table . ' ci ON c.CategoryId = ci.CategoryId WHERE (c.TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight') . ') AND (ci.ItemPrefix IN (' . $module_prefixes . '))'; return $this->Conn->GetOne($sql); } function _getModulePrefixes() { $ret = Array (); foreach ($this->Application->ModuleInfo as $module_info) { $ret[] = $module_info['Var']; } return array_unique($ret); } function ListCategories($params) { return $this->PrintList2($params); } function RootCategoryName($params) { return $this->Application->ProcessParsedTag('m', 'RootCategoryName', $params); } function CheckModuleRoot($params) { $module_name = getArrayValue($params, 'module') ? $params['module'] : 'In-Commerce'; $module_root_cat = $this->Application->findModule('Name', $module_name, 'RootCat'); $additional_cats = $this->SelectParam($params, 'add_cats'); if ($additional_cats) { $additional_cats = explode(',', $additional_cats); } else { $additional_cats = array(); } if ($this->Application->GetVar('m_cat_id') == $module_root_cat || in_array($this->Application->GetVar('m_cat_id'), $additional_cats)) { $home_template = getArrayValue($params, 'home_template'); if ( !$home_template ) { return; } $this->Application->Redirect($home_template, Array('pass'=>'all')); }; } function CategoryPath($params) { $navigation_bar = $this->Application->recallObject('kNavigationBar'); /* @var $navigation_bar kNavigationBar */ return $navigation_bar->build($params); } /** * Shows category path to specified category * * @param Array $params * @return string */ function FieldCategoryPath($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $field = $this->SelectParam($params, 'name,field'); $category_id = $object->GetDBField($field); if ($category_id) { $params['cat_id'] = $category_id; $navigation_bar = $this->Application->recallObject('kNavigationBar'); /* @var $navigation_bar kNavigationBar */ return $navigation_bar->build($params); } return ''; } function CurrentCategoryName($params) { $cat_object = $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List'); /* @var $cat_object kDBList */ $sql = 'SELECT '.$this->getTitleField().' FROM '.$cat_object->TableName.' WHERE CategoryId = '.(int)$this->Application->GetVar('m_cat_id'); return $this->Conn->GetOne($sql); } /** * Returns current category name * * @param Array $params * @return string * @todo Find where it's used */ function CurrentCategory($params) { return $this->CurrentCategoryName($params); } function getTitleField() { $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ return $ml_formatter->LangFieldName('Name'); } /** * Returns symlinked category for given category * * @param int $category_id * @return int */ function getCategorySymLink($category_id) { if (!$category_id) { // don't bother to get symlink for "Home" category return $category_id; } $cache_key = 'category_symlinks[%CSerial%]'; $cache = $this->Application->getCache($cache_key); if ($cache === false) { $config = $this->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); // get symlinked categories, that are not yet deleted $this->Conn->nextQueryCachable = true; $sql = 'SELECT c1.SymLinkCategoryId, c1.' . $id_field . ' FROM ' . $table_name . ' c1 JOIN ' . $table_name . ' c2 ON c1.SymLinkCategoryId = c2.' . $id_field; $cache = $this->Conn->GetCol($sql, $id_field); $this->Application->setCache($cache_key, $cache); } return array_key_exists($category_id, $cache) ? $cache[$category_id] : $category_id; } function CategoryLink($params) { $category_id = getArrayValue($params, 'cat_id'); if ( $category_id === false ) { $category_id = $this->Application->GetVar($this->getPrefixSpecial() . '_id'); } if ( "$category_id" == 'Root' ) { $category_id = $this->Application->findModule('Name', $params['module'], 'RootCat'); } elseif ( "$category_id" == 'current' ) { $category_id = $this->Application->GetVar('m_cat_id'); } if ( !array_key_exists('direct_link', $params) || !$params['direct_link'] ) { $category_id = $this->getCategorySymLink((int)$category_id); } else { unset($params['direct_link']); } $virtual_template = $this->Application->getVirtualPageTemplate($category_id); if ( ($virtual_template !== false) && preg_match('/external:(.*)/', $virtual_template, $rets) ) { // external url (return here, instead of always replacing $params['t'] for kApplication::HREF to find it) return $rets[1]; } unset($params['cat_id'], $params['module']); $new_params = Array ('pass' => 'm', 'm_cat_id' => $category_id, 'pass_category' => 1); $params = array_merge($params, $new_params); return $this->Application->ProcessParsedTag('m', 't', $params); } function CategoryList($params) { //$object = $this->Application->recallObject( $this->getPrefixSpecial() , $this->Prefix.'_List', $params ); $object =& $this->GetList($params); if ($object->GetRecordsCount() == 0) { if (isset($params['block_no_cats'])) { $params['name'] = $params['block_no_cats']; return $this->Application->ParseBlock($params); } else { return ''; } } if (isset($params['block'])) { return $this->PrintList($params); } else { $params['block'] = $params['block_main']; if (isset($params['block_row_start'])) { $params['row_start_block'] = $params['block_row_start']; } if (isset($params['block_row_end'])) { $params['row_end_block'] = $params['block_row_end']; } return $this->PrintList2($params); } } function Meta($params) { $object = $this->Application->recallObject($this->Prefix); // .'.-item' /* @var $object CategoriesItem */ $meta_type = $params['name']; if ($object->isLoaded()) { // 1. get module prefix by current category $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $category_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $module_info = $category_helper->getCategoryModule($params, $category_path); // In-Edit & Proj-CMS module prefixes doesn't have custom field with item template if ($module_info && $module_info['Var'] != 'adm' && $module_info['Var'] != 'st') { // 2. get item template by current category & module prefix $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $category_params = Array ( 'CategoryId' => $object->GetID(), 'ParentPath' => $object->GetDBField('ParentPath'), ); $item_template = $rewrite_processor->GetItemTemplate($category_params, $module_info['Var']); if ($this->Application->GetVar('t') == $item_template) { // we are located on item's details page $item = $this->Application->recallObject($module_info['Var']); /* @var $item kCatDBItem */ // 3. get item's meta data $value = $item->GetField('Meta'.$meta_type); if ($value) { return $value; } } // 4. get category meta data $value = $object->GetField('Meta'.$meta_type); if ($value) { return $value; } } } // 5. get default meta data switch ($meta_type) { case 'Description': $config_name = 'Category_MetaDesc'; break; case 'Keywords': $config_name = 'Category_MetaKey'; break; } return $this->Application->ConfigValue($config_name); } function BuildListSpecial($params) { if (($this->Special != '') && !is_numeric($this->Special)) { // When recursive category list is printed (like in sitemap), then special // should be generated even if it's already present. Without it list on this // level will erase list on previous level, because it will be stored in same object. return $this->Special; } if ( isset($params['parent_cat_id']) ) { $parent_cat_id = $params['parent_cat_id']; } else { $parent_cat_id = $this->Application->GetVar($this->Prefix.'_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } $list_unique_key = $this->getUniqueListKey($params); // check for "admin" variable, because we are parsing front-end template from admin when using template editor feature if ($this->Application->GetVar('admin') || !$this->Application->isAdmin) { // add parent category to special, when on Front-End, // because there can be many category lists on same page $list_unique_key .= $parent_cat_id; } if ($list_unique_key == '') { return parent::BuildListSpecial($params); } return crc32($list_unique_key); } function IsCurrent($params) { $object = $this->getObject($params); if ($object->GetID() == $this->Application->GetVar('m_cat_id')) { return true; } else { return false; } } /** * Substitutes category in last template base on current category * This is required becasue when you navigate catalog using AJAX, last_template is not updated * but when you open item edit from catalog last_template is used to build opener_stack * So, if we don't substitute m_cat_id in last_template, after saving item we'll get redirected * to the first category we've opened, not the one we navigated to using AJAX * * @param Array $params */ function UpdateLastTemplate($params) { $category_id = $this->Application->GetVar('m_cat_id'); $wid = $this->Application->GetVar('m_wid'); list($index_file, $env) = explode('|', $this->Application->RecallVar(rtrim('last_template_'.$wid, '_')), 2); $vars_backup = Array (); $vars = $this->Application->processQueryString($env); foreach ($vars as $var_name => $var_value) { $vars_backup[$var_name] = $this->Application->GetVar($var_name); $this->Application->SetVar($var_name, $var_value); } // update required fields $this->Application->SetVar('m_cat_id', $category_id); $this->Application->Session->SaveLastTemplate($params['template']); foreach ($vars_backup as $var_name => $var_value) { $this->Application->SetVar($var_name, $var_value); } } function GetParentCategory($params) { $parent_id = $this->Application->getBaseCategory(); $category_id = $this->Application->GetVar('m_cat_id'); if ($category_id != $parent_id) { $config = $this->getUnitConfig(); $sql = 'SELECT ParentId FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' = ' . $category_id; $parent_id = $this->Conn->GetOne($sql); } return $parent_id; } function InitCacheUpdater($params) { kUtil::safeDefine('CACHE_PERM_CHUNK_SIZE', 30); $continue = $this->Application->GetVar('continue'); $total_cats = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'Categories'); if ( $continue === false ) { $rebuild_mode = $this->Application->ConfigValue('CategoryPermissionRebuildMode'); if ( $rebuild_mode == CategoryPermissionRebuild::AUTOMATIC && $total_cats > CACHE_PERM_CHUNK_SIZE ) { // first step, if category count > CACHE_PERM_CHUNK_SIZE, then ask for cache update return true; } // if we don't have to ask, then assume user selected "Yes" in permcache update dialog $continue = 1; } $updater = $this->Application->makeClass('kPermCacheUpdater', Array ($continue)); /* @var $updater kPermCacheUpdater */ if ( $continue === '0' ) { // No in dialog $updater->clearData(); $this->Application->Redirect($params['destination_template']); } $ret = false; // don't ask for update if ( $continue == 1 ) { // Initial run $updater->setData(); } if ( $continue == 2 ) { // Continuing // called from AJAX request => returns percent $needs_more = true; while ( $needs_more && $updater->iteration <= CACHE_PERM_CHUNK_SIZE ) { // until proceeded in this step category count exceeds category per step limit $needs_more = $updater->DoTheJob(); } if ( $needs_more ) { // still some categories are left for next step $updater->setData(); } else { // all done, update left tree and redirect $updater->SaveData(); $this->Application->HandleEvent(new kEvent('c:OnResetCMSMenuCache')); $this->Application->RemoveVar('PermCache_UpdateRequired'); $this->Application->StoreVar('RefreshStructureTree', 1); $this->Application->Redirect($params['destination_template']); } $ret = $updater->getDonePercent(); } return $ret; } /** * Parses warning block, but with style="display: none;". Used during permissions saving from AJAX * * @param Array $params * @return string * @access protected */ protected function SaveWarning($params) { if ( $this->Prefix == 'st' ) { // don't use this method for other prefixes then Categories, that use this tag processor return parent::SaveWarning($params); } $main_prefix = getArrayValue($params, 'main_prefix'); if ( $main_prefix && $main_prefix != '$main_prefix' ) { $top_prefix = $main_prefix; } else { $top_prefix = $this->Application->GetTopmostPrefix($this->Prefix); } $temp_tables = substr($this->Application->GetVar($top_prefix . '_mode'), 0, 1) == 't'; $modified = $this->Application->RecallVar($top_prefix . '_modified'); if ( !$temp_tables ) { $this->Application->RemoveVar($top_prefix . '_modified'); return ''; } $block_name = $this->SelectParam($params, 'render_as,name'); if ( $block_name ) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $block_name; $block_params['edit_mode'] = $temp_tables ? 1 : 0; $block_params['display'] = $temp_tables && $modified ? 1 : 0; return $this->Application->ParseBlock($block_params); } return $temp_tables && $modified ? 1 : 0; } /** * Allows to detect if this prefix has something in clipboard * * @param Array $params * @return bool */ function HasClipboard($params) { $clipboard = $this->Application->RecallVar('clipboard'); if ($clipboard) { $clipboard = unserialize($clipboard); foreach ($clipboard as $prefix => $clipboard_data) { foreach ($clipboard_data as $mode => $ids) { if ( count($ids) ) { return 1; } } } } return 0; } /** * Allows to detect if root category being edited * * @param Array $params */ function IsRootCategory($params) { $object = $this->getObject($params); /* @var $object CategoriesItem */ return $object->IsRoot(); } /** * Returns home category id * * @param Array $params * @return int */ function HomeCategory($params) { return $this->Application->getBaseCategory(); } /** * Used for disabling "Home" and "Up" buttons in category list * * @param Array $params * @return bool */ function ModuleRootCategory($params) { return $this->Application->GetVar('m_cat_id') == $this->Application->getBaseCategory(); } function CatalogItemCount($params) { $params['skip_quering'] = true; $object =& $this->GetList($params); return $object->GetRecordsCount(false) != $object->GetRecordsCount() ? $object->GetRecordsCount().' / '.$object->GetRecordsCount(false) : $object->GetRecordsCount(); } function InitCatalog($params) { $tab_prefixes = $this->Application->GetVar('tp'); // {all, , none} if ( $tab_prefixes === false ) { $tab_prefixes = 'all'; } $skip_prefixes = isset($params['skip_prefixes']) && $params['skip_prefixes'] ? explode(',', $params['skip_prefixes']) : Array(); $replace_main = isset($params['replace_m']) && $params['replace_m']; // get all prefixes available $prefixes = Array(); foreach ($this->Application->ModuleInfo as $module_name => $module_data) { $prefix = $module_data['Var']; if ( $prefix == 'adm' /* || $prefix == 'm'*/ ) { continue; } if ($prefix == 'm' && $replace_main) { $prefix = 'c'; } $prefixes[] = $prefix; } if ($tab_prefixes == 'none') { $skip_prefixes = array_unique(array_merge($skip_prefixes, $prefixes)); unset($skip_prefixes[ array_search($replace_main ? 'c' : 'm', $skip_prefixes) ]); } elseif ($tab_prefixes != 'all') { // prefix list here $tab_prefixes = explode(',', $tab_prefixes); // list of prefixes that should stay $skip_prefixes = array_unique(array_merge($skip_prefixes, array_diff($prefixes, $tab_prefixes))); } $params['name'] = $params['render_as']; $params['skip_prefixes'] = implode(',', $skip_prefixes); return $this->Application->ParseBlock($params); } /** * Determines, that printed category/menu item is currently active (will also match parent category) * * @param Array $params * @return bool */ function IsActive($params) { static $current_path = null; if ( !isset($current_path) ) { $sql = 'SELECT ParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . (int)$this->Application->GetVar('m_cat_id'); $current_path = $this->Conn->GetOne($sql); } if ( array_key_exists('parent_path', $params) ) { $test_path = $params['parent_path']; } else { $template = isset($params['template']) ? $params['template'] : ''; if ( $template ) { // when using from "c:CachedMenu" tag $sql = 'SELECT ParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE NamedParentPath = ' . $this->Conn->qstr('Content/' . $template); $test_path = $this->Conn->GetOne($sql); } else { // when using from "c:PrintList" tag $cat_id = array_key_exists('cat_id', $params) && $params['cat_id'] ? $params['cat_id'] : false; if ( $cat_id === false ) { // category not supplied -> get current from PrintList $category = $this->getObject($params); } else { if ( "$cat_id" == 'Root' ) { $cat_id = $this->Application->findModule('Name', $params['module'], 'RootCat'); } $category = $this->Application->recallObject($this->Prefix . '.-c' . $cat_id, $this->Prefix, Array ('skip_autoload' => true)); /* @var $category CategoriesItem */ $category->Load($cat_id); } $test_path = $category->GetDBField('ParentPath'); } } return strpos($current_path, $test_path) !== false; } /** * Checks if user have one of required permissions * * @param Array $params * @return bool */ function HasPermission($params) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $params['raise_warnings'] = 0; $object = $this->getObject($params); /* @var $object kDBItem */ $params['cat_id'] = $object->isLoaded() ? $object->GetDBField('ParentPath') : $this->Application->GetVar('m_cat_id'); return $perm_helper->TagPermissionCheck($params); } /** * Prepares name for field with event in it (used only on front-end) * * @param Array $params * @return string */ function SubmitName($params) { return 'events[' . $this->Prefix . '][' . $params['event'] . ']'; } /** * Returns last modification date of items in category / system * * @param Array $params * @return string */ function LastUpdated($params) { $category_id = (int)$this->Application->GetVar('m_cat_id'); $local = array_key_exists('local', $params) && ($category_id > 0) ? $params['local'] : false; $serial_name = $this->Application->incrementCacheSerial('c', $local ? $category_id : null, false); $cache_key = 'category_last_updated[%' . $serial_name . '%]'; $row_data = $this->Application->getCache($cache_key); if ( $row_data === false ) { if ( $local && ($category_id > 0) ) { // scan only current category & it's children list ($tree_left, $tree_right) = $this->Application->getTreeIndex($category_id); $sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate FROM ' . TABLE_PREFIX . 'Categories WHERE TreeLeft BETWEEN ' . $tree_left . ' AND ' . $tree_right; } else { // scan all categories in system $sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate FROM ' . TABLE_PREFIX . 'Categories'; } $this->Conn->nextQueryCachable = true; $row_data = $this->Conn->GetRow($sql); $this->Application->setCache($cache_key, $row_data); } if ( !$row_data ) { return ''; } $date = $row_data[$row_data['NewDate'] > $row_data['ModDate'] ? 'NewDate' : 'ModDate']; // format date $format = isset($params['format']) ? $params['format'] : '_regional_DateTimeFormat'; if ( preg_match("/_regional_(.*)/", $format, $regs) ) { $lang = $this->Application->recallObject('lang.current'); /* @var $lang LanguagesItem */ if ( $regs[1] == 'DateTimeFormat' ) { // combined format $format = $lang->GetDBField('DateFormat') . ' ' . $lang->GetDBField('TimeFormat'); } else { // simple format $format = $lang->GetDBField($regs[1]); } } return date($format, $date); } function CategoryItemCount($params) { $object = $this->getObject($params); /* @var $object kDBList */ $params['cat_id'] = $object->GetID(); $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ return $count_helper->CategoryItemCount($params['prefix'], $params); } /** * Returns prefix + any word (used for shared between categories per page settings) * * @param Array $params * @return string */ function VarName($params) { return $this->Prefix.'_'.$params['type']; } /** * Checks if current category is valid symbolic link to another category * * @param Array $params * @return string */ function IsCategorySymLink($params) { $object = $this->getObject($params); /* @var $object kDBList */ $sym_category_id = $object->GetDBField('SymLinkCategoryId'); if ( is_null($sym_category_id) ) { return false; } $config = $this->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' = ' . $sym_category_id; return $this->Conn->GetOne($sql) ? true : false; } /** * Returns module prefix based on root category for given * * @param Array $params * @return string */ function GetModulePrefix($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $parent_path = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $module_info = $category_helper->getCategoryModule($params, $parent_path); return $module_info['Var']; } function ImageSrc($params) { list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial()); return $tag_processed ? $ret : false; } function PageLink($params) { $params['m_cat_page'] = $this->Application->GetVar($this->getPrefixSpecial() . '_Page'); return parent::PageLink($params); } /** * Returns spelling suggestions against search keyword * * @param Array $params * @return string * @access protected */ protected function SpellingSuggestions($params) { - $keywords = htmlspecialchars_decode(trim($this->Application->GetVar('keywords'))); - + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); if ( !$keywords ) { return ''; } // 1. try to get already cached suggestion $cache_key = 'search.suggestion[%SpellingDictionarySerial%]:' . $keywords; $suggestion = $this->Application->getCache($cache_key); if ( $suggestion !== false ) { return $suggestion; } $table_name = $this->Application->getUnitConfig('spelling-dictionary')->getTableName(); // 2. search suggestion in database $this->Conn->nextQueryCachable = true; $sql = 'SELECT SuggestedCorrection FROM ' . $table_name . ' WHERE MisspelledWord = ' . $this->Conn->qstr($keywords); $suggestion = $this->Conn->GetOne($sql); if ( $suggestion !== false ) { $this->Application->setCache($cache_key, $suggestion); return $suggestion; } // 3. suggestion not found in database, ask webservice $curl_helper = $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $curl_helper->SetRequestData(array( 'appid' => $this->Application->ConfigValue('YahooApplicationId'), 'query' => $keywords, )); $xml_data = $curl_helper->Send('http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion'); $xml_helper = $this->Application->recallObject('kXMLHelper'); /* @var $xml_helper kXMLHelper */ $root_node =& $xml_helper->Parse($xml_data); /* @var $root_node kXMLNode */ $result = $root_node->FindChild('RESULT'); /* @var $result kXMLNode */ if ( is_object($result) ) { // webservice responded -> save in local database $fields_hash = Array ('MisspelledWord' => $keywords, 'SuggestedCorrection' => $result->Data); $this->Conn->doInsert($fields_hash, $table_name); $this->Application->setCache($cache_key, $result->Data); return $result->Data; } return ''; } /** * Shows link for searching by suggested word * * @param Array $params * @return string */ function SuggestionLink($params) { $params['keywords'] = $this->SpellingSuggestions($params); return $this->Application->ProcessParsedTag('m', 'Link', $params); } function InitCatalogTab($params) { $tab_params['mode'] = $this->Application->GetVar('tm'); // single/multi selection possible $tab_params['special'] = $this->Application->GetVar('ts'); // use special for this tab $tab_params['dependant'] = $this->Application->GetVar('td'); // is grid dependant on categories grid // set default params (same as in catalog) if ( $tab_params['mode'] === false ) { $tab_params['mode'] = 'multi'; } if ( $tab_params['special'] === false ) { $tab_params['special'] = ''; } if ( $tab_params['dependant'] === false ) { $tab_params['dependant'] = 'yes'; } // pass params to block with tab content $params['name'] = $params['render_as']; $special = $tab_params['special'] ? $tab_params['special'] : $this->Special; $params['prefix'] = trim($this->Prefix.'.'.$special, '.'); $prefix_append = $this->Application->GetVar('prefix_append'); if ($prefix_append) { $params['prefix'] .= $prefix_append; } $default_grid = array_key_exists('default_grid', $params) ? $params['default_grid'] : 'Default'; $radio_grid = array_key_exists('radio_grid', $params) ? $params['radio_grid'] : 'Radio'; $params['cat_prefix'] = trim('c.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.'); $params['tab_mode'] = $tab_params['mode']; $params['grid_name'] = ($tab_params['mode'] == 'multi') ? $default_grid : $radio_grid; $params['tab_dependant'] = $tab_params['dependant']; $params['show_category'] = $tab_params['special'] == 'showall' ? 1 : 0; // this is advanced view -> show category name if ($special == 'showall' || $special == 'user') { $params['grid_name'] .= 'ShowAll'; } // use $pass_params to be able to pass 'tab_init' parameter from m_ModuleInclude tag return $this->Application->ParseBlock($params, 1); } /** * Show CachedNavbar of current item primary category * * @param Array $params * @return string */ function CategoryName($params) { // show category cachednavbar of $object = $this->getObject($params); /* @var $object kDBItem */ $category_id = isset($params['cat_id']) ? $params['cat_id'] : $object->GetDBField('CategoryId'); $cache_key = 'category_paths[%CIDSerial:' . $category_id . '%][%PhrasesSerial%][Adm:' . (int)$this->Application->isAdmin . ']'; $category_path = $this->Application->getCache($cache_key); if ($category_path === false) { // not chached if ($category_id > 0) { $cached_navbar = $object->GetField('CachedNavbar'); if ($category_id == $object->GetDBField('ParentId')) { // parent category cached navbar is one element smaller, then current ones $cached_navbar = explode('&|&', $cached_navbar); array_pop($cached_navbar); $cached_navbar = implode('&|&', $cached_navbar); } else { // no relation with current category object -> query from db $language_id = (int)$this->Application->GetVar('m_lang'); if (!$language_id) { $language_id = 1; } $sql = 'SELECT l' . $language_id . '_CachedNavbar FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $category_id; $cached_navbar = $this->Conn->GetOne($sql); } $cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $cached_navbar); $category_path = trim($this->CategoryName( Array('cat_id' => 0) ).' > '.str_replace('&|&', ' > ', $cached_navbar), ' > '); } else { $category_path = $this->Application->Phrase(($this->Application->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name'); } $this->Application->setCache($cache_key, $category_path); } return $category_path; } // structure related /** * Returns page object based on requested params * * @param Array $params * @return CategoriesItem */ function &_getPage($params) { $page = $this->Application->recallObject($this->Prefix . '.' . $this->_getPageSpecial($params), null, $params); /* @var $page kDBItem */ // 1. load by given id $page_id = array_key_exists('page_id', $params) ? $params['page_id'] : 0; if ( $page_id ) { if ( $page_id != $page->GetID() ) { // load if different $page->Load($page_id); } return $page; } // 2. load by template $template = array_key_exists('page', $params) ? $params['page'] : ''; if ( !$template ) { $template = $this->Application->GetVar('t'); } // different path in structure AND design template differs from requested template $structure_path_match = mb_strtolower($page->GetDBField('NamedParentPath')) == mb_strtolower('Content/' . $template); $design_match = $page->GetDBField('CachedTemplate') == $template; if ( !$structure_path_match && !$design_match ) { // Same sql like in "c:getPassedID". Load, when current page object doesn't match requested page object $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $page_id = $themes_helper->getPageByTemplate($template); $page->Load($page_id); } return $page; } /** * Returns unique special for each used page * * @param Array $params * @return string * @access protected */ protected function _getPageSpecial($params) { $ret = Array (); $page_id = array_key_exists('page_id', $params) ? $params['page_id'] : 0; $template = array_key_exists('page', $params) ? $params['page'] : ''; if ( $page_id ) { $ret[] = 'page_id=' . $page_id; } if ( $template ) { $ret[] = 'page=' . $template; } return $ret ? '-virtual-' . kUtil::crc32(serialize($ret)) : '-virtual'; } /** * Returns requested content block content of current or specified page * * @param Array $params * @return string */ function ContentBlock($params) { $num = getArrayValue($params, 'num'); if ( !$num ) { $name = getArrayValue($params, 'name'); if ( $name ) { $num = kUtil::crc32($name); } } if ( !$num ) { return 'NO CONTENT NUM SPECIFIED'; } $page =& $this->_getPage($params); /* @var $page kDBItem */ if ( !$page->isLoaded() ) { // page is not created yet => all blocks are empty return ''; } $page_helper = $this->Application->recallObject('PageHelper'); /* @var $page_helper PageHelper */ $content = $this->Application->recallObject('content.-block', null, Array ('skip_autoload' => true)); /* @var $content kDBItem */ if ( !$page_helper->loadContentBlock($content, $page, $num) && EDITING_MODE ) { $page_helper->createNewContentBlock($page->GetID(), $num); $page_helper->loadContentBlock($content, $page, $num); } $edit_code_before = $edit_code_after = ''; $inline_editing = isset($params['mode']) && $params['mode'] == 'inline'; if ( EDITING_MODE == EDITING_MODE_CONTENT ) { if ( $inline_editing ) { $params['name'] = 'content_block_' . $num; $editing_hint = $this->Application->Phrase('la_hint_ClickToEdit', false, true); $edit_code_before = '
'; $edit_code_after = '
' . $this->FCKEditor($params); } else { $button_code = $this->Application->ProcessParsedTag($content->getPrefixSpecial(), 'AdminEditButton', $params); $edit_code_before = '
' . $button_code . '
'; $edit_code_after = '
'; } } if ( $this->Application->GetVar('_editor_preview_') == 1 ) { $data = $this->Application->RecallVar('_editor_preview_content_'); } elseif ( EDITING_MODE == EDITING_MODE_CONTENT && $inline_editing ) { // don't use formatter, that replaced "@@ID@@" links $content->SetFieldOption('Content', 'using_fck', false); $data = $content->GetField('Content'); $content->SetFieldOption('Content', 'using_fck', true); } else { // use formatter, that replaced "@@ID@@" links $data = $content->GetField('Content'); } $data = $edit_code_before . $this->_transformContentBlockData($data, $params) . $edit_code_after; if ( $data != '' ) { $this->Application->Parser->DataExists = true; } return $data; } /** * Apply all kinds of content block data transformations without rewriting ContentBlock tag * * @param string $data * @param Array $params * @return string */ function _transformContentBlockData(&$data, $params) { return $data; } /** * Returns current page name or page based on page/page_id parameters * * @param Array $params * @return string * @todo Used? */ function PageName($params) { $page =& $this->_getPage($params); return $page->GetDBField('Name'); } /** * Returns current/given page information * * @param Array $params * @return string */ function PageInfo($params) { $page =& $this->_getPage($params); switch ($params['type']) { case 'title': // TODO: rename column to SectionTitle $db_field = 'Name'; // "Section Title" - title to show on page (e.g. in

tag) break; case 'htmlhead_title': // TODO: rename column to HtmlTitle $db_field = 'Title'; // "Title (on Page)" - in html tag break; case 'meta_title': $db_field = 'MetaTitle'; break; case 'menu_title': $db_field = 'MenuTitle'; // "Title (Menu Item)" - in menu and navigation bar break; case 'meta_keywords': $db_field = 'MetaKeywords'; $cat_field = 'Keywords'; break; case 'meta_description': $db_field = 'MetaDescription'; $cat_field = 'Description'; break; case 'tracking': case 'index_tools': if (!EDITING_MODE) { $tracking = $page->GetDBField('IndexTools'); return $tracking ? $tracking : $this->Application->ConfigValue('cms_DefaultTrackingCode'); } // no break here on purpose default: return ''; } $default = isset($params['default']) ? $params['default'] : ''; $val = $page->GetField($db_field); if (!$default) { if ( $this->Application->prefixRegistred('c') ) { if (!$val && ($params['type'] == 'meta_keywords' || $params['type'] == 'meta_description')) { // take category meta if it's not set for the page return $this->Application->ProcessParsedTag('c', 'Meta', Array('name' => $cat_field)); } } } if (isset($params['force_default']) && $params['force_default']) { return $default; } if (preg_match('/^_Auto:/', $val)) { $val = $default; /*if ($db_field == 'Title') { $page->SetDBField($db_field, $default); $page->Update(); }*/ } elseif ($page->GetID() == false) { return $default; } return $val; } /** * Includes admin css and js, that are required for cms usage on Front-Edn * * @param Array $params * @return string * @access protected */ protected function EditingScripts($params) { if ( $this->Application->GetVar('admin_scripts_included') || !EDITING_MODE ) { return ''; } $this->Application->SetVar('admin_scripts_included', 1); $js_url = $this->Application->BaseURL() . 'core/admin_templates/js'; $minify_helper = $this->Application->recallObject('MinifyHelper'); /* @var $minify_helper MinifyHelper */ $to_compress = Array ( $js_url . '/jquery/thickbox/thickbox.css', $js_url . '/../incs/cms.css', $js_url . '/../img/toolbar/toolbar-sprite.css', ); $css_compressed = $minify_helper->CompressScriptTag(Array ('files' => implode('|', $to_compress), 'templates_base' => $js_url . '/../')); $ret = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\n"; $ret .= ' <!--[if IE]> <link rel="stylesheet" href="' . $js_url . '/../incs/cms_ie.css' . '" type="text/css" media="screen"/> <![endif]-->'; if ( EDITING_MODE == EDITING_MODE_DESIGN ) { $ret .= ' <style type="text/css" media="all"> div.movable-element .movable-header { cursor: move; } </style>'; } $ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-1.9.1.min.js"></script>' . "\n"; $ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-ui-1.10.3.custom.min.js"></script>' . "\n"; $to_compress = Array ( $js_url . '/is.js', $js_url . '/application.js', $js_url . '/script.js', $js_url . '/toolbar.js', $js_url . '/jquery/thickbox/thickbox.js', $js_url . '/template_manager.js', ); $js_compressed = $minify_helper->CompressScriptTag( Array ('files' => implode('|', $to_compress)) ); $ret .= '<script type="text/javascript" src="' . $js_compressed . '"></script>' . "\n"; $ret .= '<script language="javascript">' . "\n"; $ret .= "TB.pathToImage = '" . $js_url . "/jquery/thickbox/loadingAnimation.gif';" . "\n"; $template = $this->Application->GetVar('t'); $theme_id = $this->Application->GetVar('m_theme'); $url_params = Array ('block' => '#BLOCK#', 'theme-file_event' => '#EVENT#', 'theme_id' => $theme_id, 'source' => $template, 'pass' => 'all,theme-file', 'front' => 1, 'm_opener' => 'd', '__NO_REWRITE__' => 1); $edit_template_url = $this->Application->HREF('themes/template_edit', ADMIN_DIRECTORY, $url_params, 'index.php'); $url_params = Array ('theme-file_event' => 'OnSaveLayout', 'source' => $template, 'pass' => 'all,theme-file', '__NO_REWRITE__' => 1); $save_layout_url = $this->Application->HREF('index', '', $url_params); $url_params = Array ('content_event' => 'OnSaveContentBlock', 'pass' => 'all,content', '__NO_REWRITE__' => 1); $save_content_url = $this->Application->HREF('index', ADMIN_DIRECTORY, $url_params, 'index.php'); $page =& $this->_getPage($params); $page_helper = $this->Application->recallObject('PageHelper'); /* @var $page_helper PageHelper */ $class_params = Array ( 'languagePrefix' => 'l' . $this->Application->GetVar('m_lang') . '_', 'pageId' => $page->GetID(), 'pageInfo' => $page->isLoaded() ? $page_helper->getPageInfo( $page->GetID() ) : Array (), 'editUrl' => $edit_template_url, 'browseUrl' => $this->Application->HREF('', '', Array ('editing_mode' => '#EDITING_MODE#', '__NO_REWRITE__' => 1)), 'saveLayoutUrl' => $save_layout_url, 'saveContentUrl' => $save_content_url, 'editingMode' => (int)EDITING_MODE, ); $site_name = strip_tags($this->Application->ConfigValue('Site_Name')); $ret .= "var aTemplateManager = new TemplateManager(" . json_encode($class_params) . ");\n"; $ret .= "var main_title = '" . kUtil::escape($site_name, kUtil::ESCAPE_JS) . "';" . "\n"; $use_popups = (int)$this->Application->ConfigValue('UsePopups'); $ret .= "var \$use_popups = " . ($use_popups > 0 ? 'true' : 'false') . ";\n"; $ret .= "var \$modal_windows = " . ($use_popups == 2 ? 'true' : 'false') . ";\n"; if ( EDITING_MODE != EDITING_MODE_BROWSE ) { $ret .= 'var $visible_toolbar_buttons = true' . ";\n"; $ret .= 'var $use_toolbarlabels = ' . ($this->Application->ConfigValue('UseToolbarLabels') ? 'true' : 'false') . ";\n";; $ret .= "var base_url = '" . $this->Application->BaseURL() . "';" . "\n"; $ret .= 'TB.closeHtml = \'<img src="' . $js_url . '/../img/close_window15.gif" width="15" height="15" style="border-width: 0px;" alt="close"/><br/>\';' . "\n"; $url_params = Array ('m_theme' => '', 'pass' => 'm', 'm_opener' => 'r', '__NO_REWRITE__' => 1); $browse_url = $this->Application->HREF('catalog/catalog', ADMIN_DIRECTORY, $url_params, 'index.php'); $browse_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $browse_url); $admin_title = strip_tags($this->Application->Phrase('la_AdministrativeConsole', false)); $ret .= ' set_window_title(document.title + \' - ' . kUtil::escape($admin_title, kUtil::ESCAPE_JS) . '\'); t = \'' . $this->Application->GetVar('t') . '\'; if (window.parent.frames["menu"] != undefined) { if ( $.isFunction(window.parent.frames["menu"].SyncActive) ) { window.parent.frames["menu"].SyncActive("' . $browse_url . '"); } } '; } $ret .= '</script>' . "\n"; if ( EDITING_MODE != EDITING_MODE_BROWSE ) { // add form, so admin scripts could work $ret .= '<form id="kernel_form" name="kernel_form" enctype="multipart/form-data" method="post" action="' . $browse_url . '"> <input type="hidden" name="MAX_FILE_SIZE" id="MAX_FILE_SIZE" value="' . MAX_UPLOAD_SIZE . '" /> <input type="hidden" name="sid" id="sid" value="' . $this->Application->GetSID() . '" /> </form>'; } return $ret; } /** * Prints "Edit Page" button on cms page * * @param Array $params * @return string */ function EditPage($params) { if ( $this->Application->GetVar('preview') ) { // prevents draft preview function to replace last template in session and break page/content block editing process $this->Application->SetVar('skip_last_template', 1); } if (!EDITING_MODE) { return ''; } $display_mode = array_key_exists('mode', $params) ? $params['mode'] : false; unset($params['mode']); $edit_code = ''; $page =& $this->_getPage($params); if (!$page->isLoaded() || (($display_mode != 'end') && (EDITING_MODE == EDITING_MODE_BROWSE))) { // when "EditingScripts" tag is not used, make sure, that scripts are also included return $this->EditingScripts($params); } // show "EditPage" button only for pages, that exists in structure if ($display_mode != 'end') { $edit_btn = $edit_url = ''; if ( EDITING_MODE == EDITING_MODE_CONTENT ) { $item_prefix = isset($params['item_prefix']) ? $params['item_prefix'] : ''; unset($params['item_prefix']); if ( $item_prefix ) { $params['button_class'] = 'cms-section-properties-btn'; $edit_btn = $this->Application->ProcessParsedTag($item_prefix, 'AdminEditButton', $params) . "\n"; } else { $edit_btn = $this->AdminEditButton($params) . "\n"; // "st" object must be loaded before this } } elseif ( EDITING_MODE == EDITING_MODE_DESIGN ) { $url_params = Array( 'pass' => 'm,theme,theme-file', 'm_opener' => 'd', 'theme_id' => $this->Application->GetVar('m_theme'), 'theme_mode' => 't', 'theme_event' => 'OnEdit', 'theme-file_id' => $this->_getThemeFileId(), 'front' => 1, '__NO_REWRITE__'=> 1, 'index_file' => 'index.php', ); $edit_url = $this->Application->HREF('themes/file_edit', ADMIN_DIRECTORY, $url_params); $button1_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/save_button.gif'; $button1_title = $this->Application->Phrase('la_btn_SaveChanges', false, true); $button1_code = '<button style="background-image: url(' . $button1_icon . '); onclick="aTemplateManager.saveLayout(); return false;" class="cms-btn-new cms-save-layout-btn">' . $button1_title . '</button>'; $button2_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/cancel_button.gif'; $button2_title = $this->Application->Phrase('la_btn_Cancel', false, true); $button2_code = '<button style="background-image: url(' . $button2_icon . '); onclick="aTemplateManager.cancelLayout(); return false;" class="cms-btn-new cms-cancel-layout-btn">' . $button2_title . '</button>'; $button3_icon = $this->Application->BaseURL() . 'core/admin_templates/img/top_frame/icons/section_properties.png'; $button3_title = $this->Application->Phrase('la_btn_SectionTemplate', false, true); $button3_code = '<button style="background-image: url(' . $button3_icon . ');' . ($display_mode === false ? ' margin: 0px;' : '') . '" onclick="$form_name=\'kf_'.$page->GetID().'\'; std_edit_item(\'theme\', \'themes/file_edit\');" class="cms-btn-new cms-section-properties-btn">' . $button3_title . '</button>'; $edit_btn .= '<div class="cms-layout-btn-container"' . ($display_mode === false ? ' style="margin: 0px;"' : '') . '>' . $button1_code . $button2_code . '</div>' . $button3_code . "\n"; } if ( $display_mode == 'start' ) { // button with border around the page if ( EDITING_MODE == EDITING_MODE_CONTENT ) { $tabs = "\n" . str_repeat("\t", 9); $base_url = $this->Application->BaseURL(); $toolbar_hidden = $this->Application->GetVar('toolbar_hidden'); $edit_code .= ' <div> <div id="cms-editing-notice"> <div class="top"> <a href="#" id="cms-close-editing-notice"></a> <span prev_editors=""></span> </div> <div class="bottom"></div> </div> <div id="cms-revision-dropdown"> <div class="top"></div> <div class="bottom"></div> </div> </div>'; if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') ) { $edit_code .= '<div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '> <div id="cms-revision-toolbar"> <script type="text/javascript"> var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/"); ' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );'; if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) { $edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );'; } $edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs); if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) { $edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs); } $edit_code .= $tabs . 'a_toolbar.Render();' . "\n"; $revision = $this->Application->recallObject('page-revision.current'); /* @var $revision kDBItem */ $page_helper = $this->Application->recallObject('PageHelper'); /* @var $page_helper PageHelper */ foreach ( $page_helper->getToolbarButtonsState($revision) as $toolbar_button => $is_enabled ) { $edit_code .= $tabs . 'a_toolbar.SetEnabled("' . $toolbar_button . '", ' . json_encode($is_enabled) . ');'; } $publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true); $edit_code .= substr($tabs, 0, -1) . '</script> <div id="cms-current-revision-info"> <span class="revision-title"></span> <div class="draft-saved"></div> </div> <a href="#" id="cms-close-toolbar"></a> <div class="cms-clear"></div> </div> <a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a> </div>' . "\n"; } } $edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">'; } else { // button without border around the page $edit_code .= $edit_btn; } } if ($display_mode == 'end') { // draw border around the page $edit_code .= '</div></div>'; } if ($display_mode != 'end') { if ( EDITING_MODE == EDITING_MODE_CONTENT ) { $url_params = Array( 'pass' => 'm', 'm_opener' => 'd', 'm_cat_id' => $page->GetID(), '__NO_REWRITE__'=> 1, 'front' => 1, 'index_file' => 'index.php', ); $revision = $this->Application->GetVar('revision'); if ( $revision ) { $url_params['revision'] = $revision; } $page_admin_url = $this->Application->HREF('', ADMIN_DIRECTORY, $url_params); $edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_revisions_'.$page->GetID().'" id="kf_revisions_'.$page->GetID().'" action="' . $page_admin_url . '"> <input type="hidden" name="revision" value="' . $this->Application->GetVar('revision', 0) . '"/> </form>'; } if ( $edit_url ) { $edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_' . $page->GetID() . '" id="kf_' . $page->GetID() . '" action="' . $edit_url . '"></form>'; } // when "EditingScripts" tag is not used, make sure, that scripts are also included $edit_code .= $this->EditingScripts($params); } return $edit_code; } function toolbarButton($name, $title, $tabs) { $action = 'function() { aTemplateManager.revisionToolbarClick("' . $name . '"); }'; $phrase = kUtil::escape($this->Application->Phrase($title, false, true), kUtil::ESCAPE_HTML . '+' . kUtil::ESCAPE_JS); return $tabs . 'a_toolbar.AddButton(new ToolBarButton("' . $name . '", "' . $phrase . '", ' . $action . '));'; } function _getThemeFileId() { $template = $this->Application->GetVar('t'); if (!$this->Application->TemplatesCache->TemplateExists($template) && !$this->Application->isAdmin) { $cms_handler = $this->Application->recallObject($this->Prefix . '_EventHandler'); /* @var $cms_handler CategoriesEventHandler */ $template = ltrim($cms_handler->GetDesignTemplate(), '/'); } $file_path = dirname($template) == '.' ? '' : '/' . dirname($template); $file_name = basename($template); $sql = 'SELECT FileId FROM ' . TABLE_PREFIX . 'ThemeFiles WHERE (ThemeId = ' . (int)$this->Application->GetVar('m_theme') . ') AND (FilePath = ' . $this->Conn->qstr($file_path) . ') AND (FileName = ' . $this->Conn->qstr($file_name . '.tpl') . ')'; return $this->Conn->GetOne($sql); } /** * Creates a button for editing item in Admin Console * * @param Array $params * @return string * @access protected */ protected function AdminEditButton($params) { if ( EDITING_MODE != EDITING_MODE_CONTENT ) { return ''; } $object = $this->getObject($params); /* @var $object kDBItem */ $params['item_prefix'] = 'c'; if ( $this->Prefix == 'st' ) { $params['button_icon'] = 'section_properties.png'; $params['button_class'] = 'cms-section-properties-btn'; $params['button_title'] = 'la_btn_SectionProperties'; } return parent::AdminEditButton($params); } /** * Builds site menu * * @param Array $params * @return string */ function CachedMenu($params) { $menu_helper = $this->Application->recallObject('MenuHelper'); /* @var $menu_helper MenuHelper */ return $menu_helper->menuTag($this->getPrefixSpecial(), $params); } /** * Trick to allow some kind of output formatting when using CachedMenu tag * * @param Array $params * @return bool */ function SplitColumn($params) { return $this->Application->GetVar($params['i']) > ceil($params['total'] / $params['columns']); } /** * Returns direct children count of given category * * @param Array $params * @return int */ function HasSubCats($params) { $sql = 'SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'Categories WHERE ParentId = ' . $params['cat_id']; return $this->Conn->GetOne($sql); } /** * Prints sub-pages of given/current page. * * @param Array $params * @return string * @todo This could be reached by using "parent_cat_id" parameter. Only difference here is new block parameter "path". Need to rewrite. */ function PrintSubPages($params) { $list = $this->Application->recallObject($this->getPrefixSpecial(), $this->Prefix.'_List', $params); /* @var $list kDBList */ $category_id = array_key_exists('category_id', $params) ? $params['category_id'] : $this->Application->GetVar('m_cat_id'); $list->addFilter('current_pages', TABLE_PREFIX . 'CategoryItems.CategoryId = ' . $category_id); $list->Query(); $list->GoFirst(); $o = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; while (!$list->EOL()) { $block_params['path'] = $list->GetDBField('Path'); $o .= $this->Application->ParseBlock($block_params); $list->GoNext(); } return $o; } /** * Builds link for browsing current page on Front-End * * @param Array $params * @return string */ function PageBrowseLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $site_config_helper = $this->Application->recallObject('SiteConfigHelper'); /* @var $site_config_helper SiteConfigHelper */ $settings = $site_config_helper->getSettings(); $url_params = Array ( 'm_cat_id' => $object->GetID(), 'm_theme' => $themes_helper->getCurrentThemeId(), 'editing_mode' => $settings['default_editing_mode'], 'pass' => 'm', 'admin' => 1, ); if ($this->Application->ConfigValue('UseModRewrite')) { $url_params['__MOD_REWRITE__'] = 1; } else { $url_params['index_file'] = 'index.php'; } return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params); } /** * Builds a link for securely accessing a page later (even if it will not be publicly accessible) * * @param Array $params * @return string * @access protected */ protected function DirectLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $url_params = Array ( 'm_cat_id' => $object->GetID(), 'm_theme' => $themes_helper->getCurrentThemeId(), 'pass' => 'm', 'authkey' => $object->GetDBField('DirectLinkAuthKey'), '__SSL__' => 0, '__NO_SID__' => 0, ); if ($this->Application->ConfigValue('UseModRewrite')) { $url_params['__MOD_REWRITE__'] = 1; } else { $url_params['index_file'] = 'index.php'; } return $this->Application->HREF($object->GetDBField('NamedParentPath'), '_FRONT_END_', $url_params); } /** * Builds link to category as a cms page * * @param Array $params * @return string */ function ContentPageLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $params['t'] = mb_strtolower($object->GetDBField('NamedParentPath')); $params['m_cat_id'] = 0; return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Prepares cms page description for search result page * * @param Array $params * @return string */ function SearchDescription($params) { $object = $this->getObject($params); $desc = $object->GetField('MetaDescription'); if (!$desc) { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'PageContent WHERE PageId = ' . $object->GetID() . ' AND ContentNum = 1'; $content = $this->Conn->GetRow($sql); if ($content['l'.$this->Application->GetVar('m_lang').'_Content']) { $desc = $content['l'.$this->Application->GetVar('m_lang').'_Content']; } else { $desc = $content['l'.$this->Application->GetDefaultLanguageId().'_Content']; } } return mb_substr($desc, 0, 300).(mb_strlen($desc) > 300 ? '...' : ''); } /** * Simplified version of "c:CategoryLink" for "c:PrintList" * * @param Array $params * @return string * @todo Used? Needs refactoring. */ function EnterCatLink($params) { $object = $this->getObject($params); $url_params = Array ('pass' => 'm', 'm_cat_id' => $object->GetID()); return $this->Application->HREF($params['template'], '', $url_params); } /** * Simplified version of "c:CategoryPath", that do not use blocks for rendering * * @param Array $params * @return string * @todo Used? Maybe needs to be removed. */ function PagePath($params) { $object = $this->getObject($params); $path = $object->GetField('CachedNavbar'); if ($path) { $items = explode('&|&', $path); array_shift($items); return implode(' -> ', $items); } return ''; } /** * Returns configuration variable value * * @param Array $params * @return string * @todo Needs to be replaced with "m:GetConfig" tag; Not used now (were used on structure_edit.tpl). */ function AllowManualFilenames($params) { return $this->Application->ConfigValue('ProjCMSAllowManualFilenames'); } /** * Draws path to current page (each page can be link to it) * * @param Array $params * @return string */ function CurrentPath($params) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $block_params['render_as']; $object = $this->Application->recallObject($this->Prefix); /* @var $object kDBItem */ $category_ids = explode('|', substr($object->GetDBField('ParentPath'), 1, -1)); $config = $this->getUnitConfig(); $language = (int)$this->Application->GetVar('m_lang'); if ( !$language ) { $language = 1; } $sql = 'SELECT l' . $language . '_Name AS Name, NamedParentPath FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' IN (' . implode(',', $category_ids) . ')'; $categories_data = $this->Conn->Query($sql); $ret = ''; foreach ($categories_data as $index => $category_data) { if ( $category_data['Name'] == 'Content' ) { continue; } $block_params['title'] = $category_data['Name']; $block_params['template'] = preg_replace('/^Content\//i', '', $category_data['NamedParentPath']); $block_params['is_first'] = $index == 1; // because Content is 1st element $block_params['is_last'] = $index == count($categories_data) - 1; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Synonim to PrintList2 for "onlinestore" theme * * @param Array $params * @return string */ function ListPages($params) { return $this->PrintList2($params); } /** * Returns information about parser element locations in template * * @param Array $params * @return mixed */ function BlockInfo($params) { if (!EDITING_MODE) { return ''; } $template_helper = $this->Application->recallObject('TemplateHelper'); /* @var $template_helper TemplateHelper */ return $template_helper->blockInfo( $params['name'] ); } /** * Hide all editing tabs except permission tab, when editing "Home" (ID = 0) category * * @param Array $params */ function ModifyUnitConfig($params) { $root_category = $this->Application->RecallVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); if ( !$root_category ) { return; } $config = $this->getUnitConfig(); $edit_tab_preset = $config->getEditTabPresetByName('Default'); $config->addEditTabPresets(Array ( 'Default' => Array ('permissions' => $edit_tab_preset['permissions']) )); } /** * Prints catalog export templates * * @param Array $params * @return string */ function PrintCatalogExportTemplates($params) { $ret = Array (); $prefixes = explode(',', $params['prefixes']); foreach ($prefixes as $prefix) { if ( $this->Application->prefixRegistred($prefix) ) { $module_path = $this->Application->getUnitConfig($prefix)->getModuleFolder() . '/'; $module_name = $this->Application->findModule('Path', $module_path, 'Name'); $ret[$prefix] = mb_strtolower($module_name) . '/export'; } } return json_encode($ret); } /** * Checks, that "view in browse mode" functionality available * * @param Array $params * @return bool */ function BrowseModeAvailable($params) { $valid_special = $params['Special'] != 'user'; $not_selector = $this->Application->GetVar('type') != 'item_selector'; return $valid_special && $not_selector; } /** * Returns a link for editing product * * @param Array $params * @return string */ function ItemEditLink($params) { $object = $this->getObject($params); /* @var $object kDBList */ $config = $this->getUnitConfig(); $edit_template = $config->getAdminTemplatePath() . '/' . $config->getAdminTemplatePrefix() . 'edit'; $url_params = Array ( 'm_opener' => 'd', $this->Prefix.'_mode' => 't', $this->Prefix.'_event' => 'OnEdit', $this->Prefix.'_id' => $object->GetID(), 'm_cat_id' => $object->GetDBField('ParentId'), 'pass' => 'all,'.$this->Prefix, 'no_pass_through' => 1, ); return $this->Application->HREF($edit_template,'', $url_params); } function RelevanceIndicator($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; $sql = 'SELECT Relevance FROM '.$search_results_table.' WHERE ResourceId = '.$object->GetDBField('ResourceId'); $percents_off = (int)(100 - (100 * $this->Conn->GetOne($sql))); $percents_off = ($percents_off < 0) ? 0 : $percents_off; if ($percents_off) { $params['percent_off'] = $percents_off; $params['percent_on'] = 100 - $percents_off; $params['name'] = $this->SelectParam($params, 'relevance_normal_render_as,block_relevance_normal'); } else { $params['name'] = $this->SelectParam($params, 'relevance_full_render_as,block_relevance_full'); } return $this->Application->ParseBlock($params); } /** * Returns list of categories, that have category add/edit permission * * @param Array $params * @return string */ function AllowedCategoriesJSON($params) { if ($this->Application->RecallVar('user_id') == USER_ROOT) { $categories = true; } else { $object = $this->getObject($params); /* @var $object kDBItem */ $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $perm_prefix = $this->getUnitConfig()->getPermItemPrefix(); $categories = $perm_helper->getPermissionCategories($perm_prefix . '.' . ($object->IsNewItem() ? 'ADD' : 'MODIFY')); } $json_helper = $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ return $json_helper->encode($categories); } function PageEditable($params) { if ($this->Application->isDebugMode()) { return true; } $object = $this->getObject($params); /* @var $object kDBItem */ return !$object->GetDBField('Protected'); } /** * Returns element for "__item__" navigation bar part * * @param Array $params * @return string * @access protected */ protected function CategoryItemElement($params) { $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $navigation_bar = $this->Application->recallObject('kNavigationBar'); /* @var $navigation_bar kNavigationBar */ $category_id = isset($params['cat_id']) ? $params['cat_id'] : $this->Application->GetVar('m_cat_id'); $parent_path = explode('|', substr($navigation_bar->getParentPath($category_id), 1, -1)); array_shift($parent_path); // remove "Content" category $module_info = $category_helper->getCategoryModule($params, $parent_path); if ( !$module_info ) { return ''; } $module_prefix = $module_info['Var']; $object = $this->Application->recallObject($module_prefix); /* @var $object kCatDBItem */ $title_field = $this->Application->getUnitConfig($module_prefix)->getTitleField(); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $block_params['title'] = $object->GetField($title_field); $block_params['prefix'] = $module_prefix; return $this->Application->ParseBlock($block_params); } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/categories/categories_event_handler.php =================================================================== --- branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16111) @@ -1,3156 +1,3156 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class CategoriesEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnRebuildCache' => Array ('self' => 'add|edit'), 'OnCopy' => Array ('self' => true), 'OnCut' => Array ('self' => 'edit'), 'OnPasteClipboard' => Array ('self' => true), 'OnPaste' => Array ('self' => 'add|edit', 'subitem' => 'edit'), 'OnRecalculatePriorities' => Array ('self' => 'add|edit'), // category ordering 'OnItemBuild' => Array ('self' => true), // always allow to view individual categories (regardless of CATEGORY.VIEW right) 'OnUpdatePreviewBlock' => Array ('self' => true), // for FCKEditor integration ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Categories are sorted using special sorting event * */ function mapEvents() { parent::mapEvents(); $events_map = Array ( 'OnMassMoveUp' => 'OnChangePriority', 'OnMassMoveDown' => 'OnChangePriority', ); $this->eventMethods = array_merge($this->eventMethods, $events_map); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( $event->Name == 'OnResetCMSMenuCache' ) { // events from "Tools -> System Tools" section are controlled via that section "edit" permission $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $perm_value = $this->Application->CheckPermission('in-portal:service.edit'); return $perm_helper->finalizePermissionCheck($event, $perm_value); } if ( !$this->Application->isAdmin ) { if ( $event->Name == 'OnSetSortingDirect' ) { // allow sorting on front event without view permission return true; } if ( $event->Name == 'OnItemBuild' ) { $category_id = $this->getPassedID($event); if ( $category_id == 0 ) { return true; } } } if ( in_array($event->Name, $this->_getMassPermissionEvents()) ) { $items = $this->_getPermissionCheckInfo($event); $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ if ( ($event->Name == 'OnSave') && array_key_exists(0, $items) ) { // adding new item (ID = 0) $perm_value = $perm_helper->AddCheckPermission($items[0]['ParentId'], $event->Prefix) > 0; } else { // leave only items, that can be edited $ids = Array (); $check_method = in_array($event->Name, Array ('OnMassDelete', 'OnCut')) ? 'DeleteCheckPermission' : 'ModifyCheckPermission'; foreach ($items as $item_id => $item_data) { if ( $perm_helper->$check_method($item_data['CreatedById'], $item_data['ParentId'], $event->Prefix) > 0 ) { $ids[] = $item_id; } } if ( !$ids ) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } $perm_value = true; $event->setEventParam('ids', $ids); // will be used later by "kDBEventHandler::StoreSelectedIDs" method } return $perm_helper->finalizePermissionCheck($event, $perm_value); } if ( $event->Name == 'OnRecalculatePriorities' ) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $category_id = $this->Application->GetVar('m_cat_id'); return $perm_helper->AddCheckPermission($category_id, $event->Prefix) || $perm_helper->ModifyCheckPermission(0, $category_id, $event->Prefix); } if ( $event->Name == 'OnPasteClipboard' ) { // forces permission check to work by current category for "Paste In Category" operation $category_id = $this->Application->GetVar('m_cat_id'); $this->Application->SetVar('c_id', $category_id); } return parent::CheckPermission($event); } /** * Returns events, that require item-based (not just event-name based) permission check * * @return Array */ function _getMassPermissionEvents() { return Array ( 'OnEdit', 'OnSave', 'OnMassDelete', 'OnMassApprove', 'OnMassDecline', 'OnMassMoveUp', 'OnMassMoveDown', 'OnCut', ); } /** * Returns category item IDs, that require permission checking * * @param kEvent $event * @return string */ function _getPermissionCheckIDs($event) { if ($event->Name == 'OnSave') { $selected_ids = implode(',', $this->getSelectedIDs($event, true)); if (!$selected_ids) { $selected_ids = 0; // when saving newly created item (OnPreCreate -> OnPreSave -> OnSave) } } else { // OnEdit, OnMassDelete events, when items are checked in grid $selected_ids = implode(',', $this->StoreSelectedIDs($event)); } return $selected_ids; } /** * Returns information used in permission checking * * @param kEvent $event * @return Array */ function _getPermissionCheckInfo($event) { // when saving data from temp table to live table check by data from temp table $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ($event->Name == 'OnSave') { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $sql = 'SELECT ' . $id_field . ', CreatedById, ParentId FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . $this->_getPermissionCheckIDs($event) . ')'; $items = $this->Conn->Query($sql, $id_field); if (!$items) { // when creating new category, then no IDs are stored in session $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); list ($id, $fields_hash) = each($items_info); if (array_key_exists('ParentId', $fields_hash)) { $item_category = $fields_hash['ParentId']; } else { $item_category = $this->Application->RecallVar('m_cat_id'); // saved in c:OnPreCreate event permission checking } $items[$id] = Array ( 'CreatedById' => $this->Application->RecallVar('user_id'), 'ParentId' => $item_category, ); } return $items; } /** * Set's mark, that root category is edited * * @param kEvent $event * @return void * @access protected */ protected function OnEdit(kEvent $event) { $category_id = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); $home_category = $this->Application->getBaseCategory(); $this->Application->StoreVar('IsRootCategory_' . $this->Application->GetVar('m_wid'), ($category_id === '0') || ($category_id == $home_category)); parent::OnEdit($event); if ( $event->status == kEvent::erSUCCESS ) { // keep "Section Properties" link (in browse modes) clean $this->Application->DeleteVar('admin'); } } /** * Adds selected link to listing * * @param kEvent $event */ function OnProcessSelected($event) { $object = $event->getObject(); /* @var $object kDBItem */ $selected_ids = $this->Application->GetVar('selected_ids'); $this->RemoveRequiredFields($object); $object->SetDBField($this->Application->RecallVar('dst_field'), $selected_ids['c']); $object->Update(); $event->SetRedirectParam('opener', 'u'); } /** * Apply system filter to categories list * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ // don't show "Content" category in advanced view $object->addFilter('system_categories', '%1$s.Status <> 4'); // show system templates from current theme only + all virtual templates $object->addFilter('theme_filter', '%1$s.ThemeId = ' . $this->_getCurrentThemeId() . ' OR %1$s.ThemeId = 0'); if ($event->Special == 'showall') { // if using recycle bin don't show categories from there $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ($recycle_bin) { $sql = 'SELECT TreeLeft, TreeRight FROM '.TABLE_PREFIX.'Categories WHERE CategoryId = '.$recycle_bin; $tree_indexes = $this->Conn->GetRow($sql); $object->addFilter('recyclebin_filter', '%1$s.TreeLeft < '.$tree_indexes['TreeLeft'].' OR %1$s.TreeLeft > '.$tree_indexes['TreeRight']); } } if ( (string)$event->getEventParam('parent_cat_id') !== '' ) { $parent_cat_id = $event->getEventParam('parent_cat_id'); if ("$parent_cat_id" == 'Root') { $module_name = $event->getEventParam('module') ? $event->getEventParam('module') : 'In-Commerce'; $parent_cat_id = $this->Application->findModule('Name', $module_name, 'RootCat'); } } else { $parent_cat_id = $this->Application->GetVar('c_id'); if (!$parent_cat_id) { $parent_cat_id = $this->Application->GetVar('m_cat_id'); } if (!$parent_cat_id) { $parent_cat_id = 0; } } if ("$parent_cat_id" == '0') { // replace "0" category with "Content" category id (this way template $parent_cat_id = $this->Application->getBaseCategory(); } if ("$parent_cat_id" != 'any') { if ($event->getEventParam('recursive')) { if ($parent_cat_id > 0) { // not "Home" category $tree_indexes = $this->Application->getTreeIndex($parent_cat_id); $object->addFilter('parent_filter', '%1$s.TreeLeft BETWEEN '.$tree_indexes['TreeLeft'].' AND '.$tree_indexes['TreeRight']); } } else { $object->addFilter('parent_filter', '%1$s.ParentId = '.$parent_cat_id); } } $this->applyViewPermissionFilter($object); if (!$this->Application->isAdminUser) { // apply status filter only on front $object->addFilter('status_filter', $object->TableName.'.Status = 1'); } // process "types" and "except" parameters $type_clauses = Array(); $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); $except_types = $event->getEventParam('except'); $except_types = $except_types ? explode(',', $except_types) : Array (); $config = $event->getUnitConfig(); if (in_array('related', $types) || in_array('related', $except_types)) { $related_to = $event->getEventParam('related_to'); if (!$related_to) { $related_prefix = $event->Prefix; } else { $sql = 'SELECT Prefix FROM '.TABLE_PREFIX.'ItemTypes WHERE ItemName = '.$this->Conn->qstr($related_to); $related_prefix = $this->Conn->GetOne($sql); } $rel_table = $this->Application->getUnitConfig('rel')->getTableName(); $item_type = (int)$config->getItemType(); if ($item_type == 0) { trigger_error('<strong>ItemType</strong> not defined for prefix <strong>' . $event->Prefix . '</strong>', E_USER_WARNING); } // process case, then this list is called inside another list $prefix_special = $event->getEventParam('PrefixSpecial'); if (!$prefix_special) { $prefix_special = $this->Application->Parser->GetParam('PrefixSpecial'); } $id = false; if ($prefix_special !== false) { $processed_prefix = $this->Application->processPrefix($prefix_special); if ($processed_prefix['prefix'] == $related_prefix) { // printing related categories within list of items (not on details page) $list = $this->Application->recallObject($prefix_special); /* @var $list kDBList */ $id = $list->GetID(); } } if ($id === false) { // printing related categories for single item (possibly on details page) if ($related_prefix == 'c') { $id = $this->Application->GetVar('m_cat_id'); } else { $id = $this->Application->GetVar($related_prefix . '_id'); } } $p_item = $this->Application->recallObject($related_prefix . '.current', null, Array('skip_autoload' => true)); /* @var $p_item kCatDBItem */ $p_item->Load( (int)$id ); $p_resource_id = $p_item->GetDBField('ResourceId'); $sql = 'SELECT SourceId, TargetId FROM '.$rel_table.' WHERE (Enabled = 1) AND ( (Type = 0 AND SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (Type = 1 AND ( (SourceId = '.$p_resource_id.' AND TargetType = '.$item_type.') OR (TargetId = '.$p_resource_id.' AND SourceType = '.$item_type.') ) ) )'; $related_ids_array = $this->Conn->Query($sql); $related_ids = Array(); foreach ($related_ids_array as $key => $record) { $related_ids[] = $record[ $record['SourceId'] == $p_resource_id ? 'TargetId' : 'SourceId' ]; } if (count($related_ids) > 0) { $type_clauses['related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_ids).')'; $type_clauses['related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_ids).')'; } else { $type_clauses['related']['include'] = '0'; $type_clauses['related']['except'] = '1'; } $type_clauses['related']['having_filter'] = false; } if (in_array('category_related', $type_clauses)) { $object->removeFilter('parent_filter'); $resource_id = $this->Conn->GetOne(' SELECT ResourceId FROM '.$config->getTableName().' WHERE CategoryId = '.$parent_cat_id ); $sql = 'SELECT DISTINCT(TargetId) FROM '.TABLE_PREFIX.'CatalogRelationships WHERE SourceId = '.$resource_id.' AND SourceType = 1'; $related_cats = $this->Conn->GetCol($sql); $related_cats = is_array($related_cats) ? $related_cats : Array(); $sql = 'SELECT DISTINCT(SourceId) FROM '.TABLE_PREFIX.'CatalogRelationships WHERE TargetId = '.$resource_id.' AND TargetType = 1 AND Type = 1'; $related_cats2 = $this->Conn->GetCol($sql); $related_cats2 = is_array($related_cats2) ? $related_cats2 : Array(); $related_cats = array_unique( array_merge( $related_cats2, $related_cats ) ); if ($related_cats) { $type_clauses['category_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')'; $type_clauses['category_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')'; } else { $type_clauses['category_related']['include'] = '0'; $type_clauses['category_related']['except'] = '1'; } $type_clauses['category_related']['having_filter'] = false; } if (in_array('product_related', $types)) { $object->removeFilter('parent_filter'); $product_id = $event->getEventParam('product_id') ? $event->getEventParam('product_id') : $this->Application->GetVar('p_id'); $sql = 'SELECT ResourceId FROM ' . $this->Application->getUnitConfig('p')->getTableName() . ' WHERE ProductId = ' . $product_id; $resource_id = $this->Conn->GetOne($sql); $sql = 'SELECT DISTINCT(TargetId) FROM ' . TABLE_PREFIX . 'CatalogRelationships WHERE SourceId = '.$resource_id.' AND TargetType = 1'; $related_cats = $this->Conn->GetCol($sql); $related_cats = is_array($related_cats) ? $related_cats : Array(); $sql = 'SELECT DISTINCT(SourceId) FROM ' . TABLE_PREFIX . 'CatalogRelationships WHERE TargetId = '.$resource_id.' AND SourceType = 1 AND Type = 1'; $related_cats2 = $this->Conn->GetCol($sql); $related_cats2 = is_array($related_cats2) ? $related_cats2 : Array(); $related_cats = array_unique( array_merge( $related_cats2, $related_cats ) ); if ($related_cats) { $type_clauses['product_related']['include'] = '%1$s.ResourceId IN ('.implode(',', $related_cats).')'; $type_clauses['product_related']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $related_cats).')'; } else { $type_clauses['product_related']['include'] = '0'; $type_clauses['product_related']['except'] = '1'; } $type_clauses['product_related']['having_filter'] = false; } $type_clauses['menu']['include'] = '%1$s.IsMenu = 1'; $type_clauses['menu']['except'] = '%1$s.IsMenu = 0'; $type_clauses['menu']['having_filter'] = false; if (in_array('search', $types) || in_array('search', $except_types)) { $event_mapping = Array ( 'simple' => 'OnSimpleSearch', 'subsearch' => 'OnSubSearch', 'advanced' => 'OnAdvancedSearch' ); $keywords = $event->getEventParam('keyword_string'); $type = $this->Application->GetVar('search_type', 'simple'); if ( $keywords ) { // processing keyword_string param of ListProducts tag $this->Application->SetVar('keywords', $keywords); $type = 'simple'; } $search_event = $event_mapping[$type]; $this->$search_event($event); $object = $event->getObject(); /* @var $object kDBList */ $search_sql = ' FROM ' . TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search search_result JOIN %1$s ON %1$s.ResourceId = search_result.ResourceId'; $sql = str_replace('FROM %1$s', $search_sql, $object->GetPlainSelectSQL()); $object->SetSelectSQL($sql); $object->addCalculatedField('Relevance', 'search_result.Relevance'); $type_clauses['search']['include'] = '1'; $type_clauses['search']['except'] = '0'; $type_clauses['search']['having_filter'] = false; } $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $search_helper->SetComplexFilter($event, $type_clauses, implode(',', $types), implode(',', $except_types)); } /** * Adds filter, that uses *.VIEW permissions to determine if an item should be shown to a user. * * @param kDBList $object Object. * * @return void * @access protected */ protected function applyViewPermissionFilter(kDBList $object) { if ( !$this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) { return; } if ( $this->Application->RecallVar('user_id') == USER_ROOT ) { // for "root" CATEGORY.VIEW permission is checked for items lists too $view_perm = 1; } else { $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ list ($view_perm, $view_filter) = $count_helper->GetPermissionClause($object->Prefix, 'perm'); $object->addFilter('perm_filter2', $view_filter); } $object->addFilter('perm_filter', 'perm.PermId = ' . $view_perm); // check for CATEGORY.VIEW permission } /** * Returns current theme id * * @return int */ function _getCurrentThemeId() { $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ return (int)$themes_helper->getCurrentThemeId(); } /** * Returns ID of current item to be edited * by checking ID passed in get/post as prefix_id * or by looking at first from selected ids, stored. * Returned id is also stored in Session in case * it was explicitly passed as get/post * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { if ( ($event->Special == 'page') || $this->_isVirtual($event) || ($event->Prefix == 'st') ) { return $this->_getPassedStructureID($event); } if ( $this->Application->isAdmin ) { return parent::getPassedID($event); } return $this->Application->GetVar('m_cat_id'); } /** * Enter description here... * * @param kEvent $event * @return int */ function _getPassedStructureID($event) { static $page_by_template = Array (); if ( $event->Special == 'current' ) { return $this->Application->GetVar('m_cat_id'); } $event->setEventParam('raise_warnings', 0); $page_id = parent::getPassedID($event); if ( $page_id === false ) { $template = $event->getEventParam('page'); if ( !$template ) { $template = $this->Application->GetVar('t'); } // bug: when template contains "-" symbols (or others, that stripDisallowed will replace) it's not found if ( !array_key_exists($template, $page_by_template) ) { $config = $event->getUnitConfig(); $template_crc = kUtil::crc32(mb_strtolower($template)); $sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName() . ' WHERE ( (NamedParentPathHash = ' . $template_crc . ') OR (`Type` = ' . PAGE_TYPE_TEMPLATE . ' AND CachedTemplateHash = ' . $template_crc . ') ) AND (ThemeId = ' . $this->_getCurrentThemeId() . ' OR ThemeId = 0)'; $page_id = $this->Conn->GetOne($sql); } else { $page_id = $page_by_template[$template]; } if ( $page_id ) { $page_by_template[$template] = $page_id; } } if ( !$page_id && !$this->Application->isAdmin ) { $page_id = $this->Application->GetVar('m_cat_id'); } return $page_id; } function ParentGetPassedID($event) { return parent::getPassedID($event); } /** * Adds calculates fields for item statuses * * @param kCatDBItem $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { if ( $this->_isVirtual($event) ) { return; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->addCalculatedField( 'IsNew', ' IF(%1$s.NewItem = 2, IF(%1$s.CreatedOn >= (UNIX_TIMESTAMP() - '. $this->Application->ConfigValue('Category_DaysNew'). '*3600*24), 1, 0), %1$s.NewItem )'); } /** * Checks, that this is virtual page * * @param kEvent $event * @return int * @access protected */ protected function _isVirtual(kEvent $event) { return strpos($event->Special, '-virtual') !== false; } /** * Gets right special for configuring virtual page * * @param kEvent $event * @return string * @access protected */ protected function _getCategorySpecial(kEvent $event) { return $this->_isVirtual($event) ? '-virtual' : $event->Special; } /** * Set correct parent path for newly created categories * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCopyToLive(kEvent $event) { parent::OnAfterCopyToLive($event); $object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true, 'live_table' => true)); /* @var $object CategoriesItem */ $parent_path = false; $object->Load($event->getEventParam('id')); if ( $event->getEventParam('temp_id') == 0 ) { if ( $object->isLoaded() ) { // update path only for real categories (not including "Home" root category) $fields_hash = $object->buildParentBasedFields(); $this->Conn->doUpdate($fields_hash, $object->TableName, 'CategoryId = ' . $object->GetID()); $parent_path = $fields_hash['ParentPath']; } } else { $parent_path = $object->GetDBField('ParentPath'); } if ( $parent_path ) { $cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $parent_path)); /* @var $cache_updater kPermCacheUpdater */ $cache_updater->OneStepRun(); } } /** * Set cache modification mark if needed * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { parent::OnBeforeDeleteFromLive($event); $id = $event->getEventParam('id'); // loading anyway, because this object is needed by "c-perm:OnBeforeDeleteFromLive" event $temp_object = $event->getObject(Array ('skip_autoload' => true)); /* @var $temp_object CategoriesItem */ $temp_object->Load($id); if ( $id == 0 ) { if ( $temp_object->isLoaded() ) { // new category -> update cache (not loaded when "Home" category) $this->Application->StoreVar('PermCache_UpdateRequired', 1); } return ; } // existing category was edited, check if in-cache fields are modified $live_object = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('live_table' => true, 'skip_autoload' => true)); /* @var $live_object CategoriesItem */ $live_object->Load($id); $cached_fields = Array ('l' . $this->Application->GetDefaultLanguageId() . '_Name', 'Filename', 'Template', 'ParentId', 'Priority'); foreach ($cached_fields as $cached_field) { if ( $live_object->GetDBField($cached_field) != $temp_object->GetDBField($cached_field) ) { // use session instead of REQUEST because of permission editing in category can contain // multiple submits, that changes data before OnSave event occurs $this->Application->StoreVar('PermCache_UpdateRequired', 1); break; } } // remember category filename change between temp and live records if ( $temp_object->GetDBField('Filename') != $live_object->GetDBField('Filename') ) { $filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ()); $filename_changes[ $live_object->GetID() ] = Array ( 'from' => $live_object->GetDBField('Filename'), 'to' => $temp_object->GetDBField('Filename') ); $this->Application->SetVar($event->Prefix . '_filename_changes', $filename_changes); } } /** * Calls kDBEventHandler::OnSave original event * Used in proj-cms:StructureEventHandler->OnSave * * @param kEvent $event */ function parentOnSave($event) { parent::OnSave($event); } /** * Reset root-category flag when new category is created * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { // 1. for permission editing of Home category $this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); parent::OnPreCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ // 2. preset template $category_id = $this->Application->GetVar('m_cat_id'); $root_category = $this->Application->getBaseCategory(); if ( $category_id == $root_category ) { $object->SetDBField('Template', $this->_getDefaultDesign()); } // 3. set default owner $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } /** * Checks cache update mark and redirect to cache if needed * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { // get data from live table before it is overwritten by parent OnSave method call $ids = $this->getSelectedIDs($event, true); $is_editing = implode('', $ids); $old_statuses = $is_editing ? $this->_getCategoryStatus($ids) : Array (); $object = $event->getObject(); /* @var $object CategoriesItem */ parent::OnSave($event); if ( $event->status != kEvent::erSUCCESS ) { return; } if ( $this->Application->RecallVar('PermCache_UpdateRequired') ) { $this->Application->RemoveVar('IsRootCategory_' . $this->Application->GetVar('m_wid')); } $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); if ( $is_editing ) { // send email event to category owner, when it's status is changed (from admin) $object->SwitchToLive(); $new_statuses = $this->_getCategoryStatus($ids); $process_statuses = Array (STATUS_ACTIVE, STATUS_DISABLED); foreach ($new_statuses as $category_id => $new_status) { if ( $new_status != $old_statuses[$category_id] && in_array($new_status, $process_statuses) ) { $object->Load($category_id); $email_event = $new_status == STATUS_ACTIVE ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY'; $this->Application->emailUser($email_event, $object->GetDBField('CreatedById'), $object->getEmailParams()); } } } // change opener stack in case if edited category filename was changed $filename_changes = $this->Application->GetVar($event->Prefix . '_filename_changes', Array ()); if ( $filename_changes ) { $opener_stack = $this->Application->makeClass('kOpenerStack'); /* @var $opener_stack kOpenerStack */ list ($template, $params, $index_file) = $opener_stack->pop(); foreach ($filename_changes as $change_info) { $template = str_ireplace($change_info['from'], $change_info['to'], $template); } $opener_stack->push($template, $params, $index_file); $opener_stack->save(); } } /** * Returns statuses of given categories * * @param Array $category_ids * @return Array */ function _getCategoryStatus($category_ids) { $config = $this->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $sql = 'SELECT Status, ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $category_ids) . ')'; return $this->Conn->GetCol($sql, $id_field); } /** * Creates a new item in temp table and * stores item id in App vars and Session on success * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveCreated(kEvent $event) { $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object CategoriesItem */ if ( $object->IsRoot() ) { // don't create root category while saving permissions return; } parent::OnPreSaveCreated($event); } /** * Deletes sym link to other category * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemDelete(kEvent $event) { parent::OnAfterItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ $sql = 'UPDATE ' . $object->TableName . ' SET SymLinkCategoryId = NULL WHERE SymLinkCategoryId = ' . $object->GetID(); $this->Conn->Query($sql); // delete direct subscriptions to category, that was deleted $sql = 'SELECT SubscriptionId FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions WHERE CategoryId = ' . $object->GetID(); $ids = $this->Conn->GetCol($sql); if ( $ids ) { $temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event->MasterEvent)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('system-event-subscription', '', $ids); } } /** * Exclude root categories from deleting * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(kEvent $event, $type) { if ( $event->Name == 'OnMassDelete' && $type == 'before' ) { $ids = $event->getEventParam('ids'); if ( !$ids || $this->Application->ConfigValue('AllowDeleteRootCats') ) { return; } $root_categories = Array (); // get module root categories and exclude them foreach ($this->Application->ModuleInfo as $module_info) { $root_categories[] = $module_info['RootCat']; } $root_categories = array_unique($root_categories); if ( $root_categories && array_intersect($ids, $root_categories) ) { $event->setEventParam('ids', array_diff($ids, $root_categories)); $this->Application->StoreVar('root_delete_error', 1); } } } /** * Checks, that given template exists (physically) in given theme * * @param string $template * @param int $theme_id * @return bool */ function _templateFound($template, $theme_id = null) { static $init_made = false; if (!$init_made) { $this->Application->InitParser(true); $init_made = true; } if (!isset($theme_id)) { $theme_id = $this->_getCurrentThemeId(); } $theme_name = $this->_getThemeName($theme_id); return $this->Application->TemplatesCache->TemplateExists('theme:' . $theme_name . '/' . $template); } /** * Removes ".tpl" in template path * * @param string $template * @return string */ function _stripTemplateExtension($template) { // return preg_replace('/\.[^.\\\\\\/]*$/', '', $template); return preg_replace('/^[\\/]{0,1}(.*)\.tpl$/', "$1", $template); } /** * Deletes all selected items. * Automatically recourse into sub-items using temp handler, and deletes sub-items * by calling its Delete method if sub-item has AutoDelete set to true in its config file * * @param kEvent $event * @return void * @access protected */ protected function OnMassDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $to_delete = Array (); $ids = $this->StoreSelectedIDs($event); $recycle_bin = $this->Application->ConfigValue('RecycleBinFolder'); if ( $recycle_bin ) { $rb = $this->Application->recallObject('c.recycle', null, Array ('skip_autoload' => true)); /* @var $rb CategoriesItem */ $rb->Load($recycle_bin); $cat = $event->getObject(Array ('skip_autoload' => true)); /* @var $cat CategoriesItem */ foreach ($ids as $id) { $cat->Load($id); if ( preg_match('/^' . preg_quote($rb->GetDBField('ParentPath'), '/') . '/', $cat->GetDBField('ParentPath')) ) { // already in "Recycle Bin" -> delete for real $to_delete[] = $id; continue; } // just move into "Recycle Bin" category $cat->SetDBField('ParentId', $recycle_bin); $cat->Update(); } $ids = $to_delete; } $event->setEventParam('ids', $ids); $this->customProcessing($event, 'before'); $ids = $event->getEventParam('ids'); if ( $ids ) { $recursive_helper = $this->Application->recallObject('RecursiveHelper'); /* @var $recursive_helper kRecursiveHelper */ foreach ($ids as $id) { $recursive_helper->DeleteCategory($id, $event->Prefix); } } $this->clearSelectedIDs($event); $this->_ensurePermCacheRebuild($event); } /** * Add selected items to clipboard with mode = COPY (CLONE) * * @param kEvent $event */ function OnCopy($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'copy', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Add selected items to clipboard with mode = CUT * * @param kEvent $event */ function OnCut($event) { $this->Application->RemoveVar('clipboard'); $clipboard_helper = $this->Application->recallObject('ClipboardHelper'); /* @var $clipboard_helper kClipboardHelper */ $clipboard_helper->setClipboard($event, 'cut', $this->StoreSelectedIDs($event)); $this->clearSelectedIDs($event); } /** * Controls all item paste operations. Can occur only with filled clipboard. * * @param kEvent $event */ function OnPasteClipboard($event) { $clipboard = unserialize( $this->Application->RecallVar('clipboard') ); foreach ($clipboard as $prefix => $clipboard_data) { $paste_event = new kEvent($prefix.':OnPaste', Array('clipboard_data' => $clipboard_data)); $this->Application->HandleEvent($paste_event); $event->copyFrom($paste_event); } } /** * Checks permission for OnPaste event * * @param kEvent $event * @return bool */ function _checkPastePermission($event) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $category_id = $this->Application->GetVar('m_cat_id'); if ($perm_helper->AddCheckPermission($category_id, $event->Prefix) == 0) { // no items left for editing -> no permission return $perm_helper->finalizePermissionCheck($event, false); } return true; } /** * Paste categories with sub-items from clipboard * * @param kEvent $event * @return void * @access protected */ protected function OnPaste($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) || !$this->_checkPastePermission($event) ) { $event->status = kEvent::erFAIL; return; } $clipboard_data = $event->getEventParam('clipboard_data'); if ( !$clipboard_data['cut'] && !$clipboard_data['copy'] ) { return; } // 1. get ParentId of moved category(-es) before it gets updated!!!) $source_category_id = 0; $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $clipboard_data['cut'] ) { $sql = 'SELECT ParentId FROM ' . $table_name . ' WHERE ' . $id_field . ' = ' . $clipboard_data['cut'][0]; $source_category_id = $this->Conn->GetOne($sql); } $recursive_helper = $this->Application->recallObject('RecursiveHelper'); /* @var $recursive_helper kRecursiveHelper */ if ( $clipboard_data['cut'] ) { $recursive_helper->MoveCategories($clipboard_data['cut'], $this->Application->GetVar('m_cat_id')); } if ( $clipboard_data['copy'] ) { // don't allow to copy/paste system OR theme-linked virtual pages $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE ' . $id_field . ' IN (' . implode(',', $clipboard_data['copy']) . ') AND (`Type` = ' . PAGE_TYPE_VIRTUAL . ') AND (ThemeId = 0)'; $allowed_ids = $this->Conn->GetCol($sql); if ( !$allowed_ids ) { return; } foreach ($allowed_ids as $id) { $recursive_helper->PasteCategory($id, $event->Prefix); } } $priority_helper = $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ if ( $clipboard_data['cut'] ) { $ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $source_category_id); if ( $ids ) { $priority_helper->massUpdateChanged($event->Prefix, $ids); } } // recalculate priorities of newly pasted categories in destination category $parent_id = $this->Application->GetVar('m_cat_id'); $ids = $priority_helper->recalculatePriorities($event, 'ParentId = ' . $parent_id); if ( $ids ) { $priority_helper->massUpdateChanged($event->Prefix, $ids); } if ( $clipboard_data['cut'] || $clipboard_data['copy'] ) { $this->_ensurePermCacheRebuild($event); } } /** * Ensures, that category permission cache is rebuild when category is added/edited/deleted * * @param kEvent $event * @return void * @access protected */ protected function _ensurePermCacheRebuild(kEvent $event) { $this->Application->StoreVar('PermCache_UpdateRequired', 1); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Occurs when pasting category * * @param kEvent $event */ /*function OnCatPaste($event) { $inp_clipboard = $this->Application->RecallVar('ClipBoard'); $inp_clipboard = explode('-', $inp_clipboard, 2); if($inp_clipboard[0] == 'COPY') { $config = $event->getUnitConfig(); $cat_ids = $event->getEventParam('cat_ids'); $saved_cat_id = $this->Application->GetVar('m_cat_id'); $ids_sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName() . ' WHERE ResourceId IN (%s)'; $resource_ids_sql = 'SELECT ItemResourceId FROM '.TABLE_PREFIX.'CategoryItems WHERE CategoryId = %s AND PrimaryCat = 1'; $object = $this->Application->recallObject($event->Prefix.'.item', $event->Prefix, Array('skip_autoload' => true)); foreach($cat_ids as $source_cat => $dest_cat) { $item_resource_ids = $this->Conn->GetCol( sprintf($resource_ids_sql, $source_cat) ); if(!$item_resource_ids) continue; $this->Application->SetVar('m_cat_id', $dest_cat); $item_ids = $this->Conn->GetCol( sprintf($ids_sql, implode(',', $item_resource_ids) ) ); $temp = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); if($item_ids) $temp->CloneItems($event->Prefix, $event->Special, $item_ids); } $this->Application->SetVar('m_cat_id', $saved_cat_id); } }*/ /** * Clears clipboard content * * @param kEvent $event */ function OnClearClipboard($event) { $this->Application->RemoveVar('clipboard'); } /** * Sets correct status for new categories created on front-end * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $object = $event->getObject(); /* @var $object CategoriesItem */ if ( $object->GetDBField('ParentId') <= 0 ) { // no parent category - use current (happens during import) $object->SetDBField('ParentId', $this->Application->GetVar('m_cat_id')); } $this->_beforeItemChange($event); if ( $this->Application->isAdmin || $event->Prefix == 'st' ) { // don't check category permissions when auto-creating structure pages return ; } $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $new_status = false; $category_id = $this->Application->GetVar('m_cat_id'); if ( $perm_helper->CheckPermission('CATEGORY.ADD', 0, $category_id) ) { $new_status = STATUS_ACTIVE; } else { if ( $perm_helper->CheckPermission('CATEGORY.ADD.PENDING', 0, $category_id) ) { $new_status = STATUS_PENDING; } } if ( $new_status ) { $object->SetDBField('Status', $new_status); // don't forget to set Priority for suggested from Front-End categories $min_priority = $this->_getNextPriority($object->GetDBField('ParentId'), $object->TableName); $object->SetDBField('Priority', $min_priority); } else { $event->status = kEvent::erPERM_FAIL; return ; } } /** * Returns next available priority for given category from given table * * @param int $category_id * @param string $table_name * @return int */ function _getNextPriority($category_id, $table_name) { $sql = 'SELECT MIN(Priority) FROM ' . $table_name . ' WHERE ParentId = ' . $category_id; return (int)$this->Conn->GetOne($sql) - 1; } /** * Sets correct status for new categories created on front-end * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_beforeItemChange($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetChangedFields() ) { $object->SetDBField('ModifiedById', $this->Application->RecallVar('user_id')); } } /** * Creates needed sql query to load item, * if no query is defined in config for * special requested, then use list query * * @param kEvent $event * @return string * @access protected */ protected function ItemPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $sqls = $object->getFormOption('ItemSQLs', Array ()); $category_special = $this->_getCategorySpecial($event); $special = isset($sqls[$category_special]) ? $category_special : ''; // preferred special not found in ItemSQLs -> use analog from ListSQLs return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event); } /** * Creates needed sql query to load list, * if no query is defined in config for * special requested, then use default * query * * @param kEvent $event * @return string * @access protected */ protected function ListPrepareQuery(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $special = $this->_getCategorySpecial($event); $sqls = $object->getFormOption('ListSQLs', Array ()); return $sqls[array_key_exists($special, $sqls) ? $special : '']; } /** * Performs redirect to correct suggest confirmation template * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { parent::OnCreate($event); if ( $this->Application->isAdmin || $event->status != kEvent::erSUCCESS ) { // don't sent email or rebuild cache directly after category is created by admin return; } $object = $event->getObject(); /* @var $object kDBItem */ $cache_updater = $this->Application->makeClass('kPermCacheUpdater', Array (null, $object->GetDBField('ParentPath'))); /* @var $cache_updater kPermCacheUpdater */ $cache_updater->OneStepRun(); $is_active = ($object->GetDBField('Status') == STATUS_ACTIVE); $next_template = $is_active ? 'suggest_confirm_template' : 'suggest_pending_confirm_template'; $event->redirect = $this->Application->GetVar($next_template); $event->SetRedirectParam('opener', 's'); // send email events $send_params = $object->getEmailParams(); $event_suffix = $is_active ? 'ADD' : 'ADD.PENDING'; $perm_prefix = $event->getUnitConfig()->getPermItemPrefix(); $this->Application->emailUser($perm_prefix . '.' . $event_suffix, $object->GetDBField('CreatedById'), $send_params); $this->Application->emailAdmin($perm_prefix . '.' . $event_suffix, null, $send_params); } /** * Returns current per-page setting for list * * @param kEvent $event * @return int * @access protected */ protected function getPerPage(kEvent $event) { if ( !$this->Application->isAdmin ) { $same_special = $event->getEventParam('same_special'); $event->setEventParam('same_special', true); $per_page = parent::getPerPage($event); $event->setEventParam('same_special', $same_special); } return parent::getPerPage($event); } /** * Set's correct page for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetPagination(kEvent $event) { parent::SetPagination($event); if ( !$this->Application->isAdmin ) { $page_var = $event->getEventParam('page_var'); if ( $page_var !== false ) { $page = $this->Application->GetVar($page_var); if ( is_numeric($page) ) { $object = $event->getObject(); /* @var $object kDBList */ $object->SetPage($page); } } } } /** * Apply same processing to each item being selected in grid * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $event->Name != 'OnMassApprove' && $event->Name != 'OnMassDecline' ) { parent::iterateItems($event); } if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object CategoriesItem */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $propagate_category_status = $this->Application->GetVar('propagate_category_status'); $status_field = $event->getUnitConfig()->getStatusField(true); foreach ($ids as $id) { $object->Load($id); $object->SetDBField($status_field, $event->Name == 'OnMassApprove' ? 1 : 0); if ( $object->Update() ) { if ( $propagate_category_status ) { $sql = 'UPDATE ' . $object->TableName . ' SET ' . $status_field . ' = ' . $object->GetDBField($status_field) . ' WHERE TreeLeft BETWEEN ' . $object->GetDBField('TreeLeft') . ' AND ' . $object->GetDBField('TreeRight'); $this->Conn->Query($sql); } $event->status = kEvent::erSUCCESS; $email_event = $event->Name == 'OnMassApprove' ? 'CATEGORY.APPROVE' : 'CATEGORY.DENY'; $this->Application->emailUser($email_event, $object->GetDBField('CreatedById'), $object->getEmailParams()); } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } $this->clearSelectedIDs($event); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool * @access protected */ protected function checkItemStatus(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->isLoaded() ) { return true; } if ( $object->GetDBField('Status') != STATUS_ACTIVE && $object->GetDBField('Status') != 4 ) { if ( !$object->GetDBField('DirectLinkEnabled') || !$object->GetDBField('DirectLinkAuthKey') ) { return false; } return $this->Application->GetVar('authkey') == $object->GetDBField('DirectLinkAuthKey'); } return true; } /** * Set's correct sorting for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetSorting(kEvent $event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); if ( in_array('search', $types) ) { $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ // 1. no user sorting - sort by relevance $default_sortings = parent::_getDefaultSorting($event); $default_sorting = key($default_sortings['Sorting']) . ',' . current($default_sortings['Sorting']); if ( $object->isMainList() ) { $sort_by = $this->Application->GetVar('sort_by', ''); if ( !$sort_by ) { $this->Application->SetVar('sort_by', 'Relevance,desc|' . $default_sorting); } elseif ( strpos($sort_by, 'Relevance,') !== false ) { $this->Application->SetVar('sort_by', $sort_by . '|' . $default_sorting); } } else { $sorting_settings = $this->getListSetting($event, 'Sortings'); $sort_by = trim(getArrayValue($sorting_settings, 'Sort1') . ',' . getArrayValue($sorting_settings, 'Sort1_Dir'), ','); if ( !$sort_by ) { $event->setEventParam('sort_by', 'Relevance,desc|' . $default_sorting); } elseif ( strpos($sort_by, 'Relevance,') !== false ) { $event->setEventParam('sort_by', $sort_by . '|' . $default_sorting); } } $this->_removeForcedSortings($event); } parent::SetSorting($event); } /** * Removes forced sortings * * @param kEvent $event */ protected function _removeForcedSortings(kEvent $event) { $config = $event->getUnitConfig(); foreach ($config->getListSortingSpecials() as $special) { $list_sortings = $config->getListSortingsBySpecial($special); unset($list_sortings['ForcedSorting']); $config->setListSortingsBySpecial('', $list_sortings); } } /** * Default sorting in search results only comes from relevance field * * @param kEvent $event * @return Array * @access protected */ protected function _getDefaultSorting(kEvent $event) { $types = $event->getEventParam('types'); $types = $types ? explode(',', $types) : Array (); return in_array('search', $types) ? Array () : parent::_getDefaultSorting($event); } // ============= for cms page processing ======================= /** * Returns default design template * * @return string */ function _getDefaultDesign() { $default_design = trim($this->Application->ConfigValue('cms_DefaultDesign'), '/'); if (!$default_design) { // theme-based alias for default design return '#default_design#'; } if (strpos($default_design, '#') === false) { // real template, not alias, so prefix with "/" return '/' . $default_design; } // alias return $default_design; } /** * Returns default design based on given virtual template (used from kApplication::Run) * * @param string $t * @return string * @access public */ public function GetDesignTemplate($t = null) { if ( !isset($t) ) { $t = $this->Application->GetVar('t'); } $page = $this->Application->recallObject($this->Prefix . '.-virtual', null, Array ('page' => $t)); /* @var $page CategoriesItem */ if ( $page->isLoaded() ) { $real_t = $page->GetDBField('CachedTemplate'); $this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId')); if ( $page->GetDBField('FormId') ) { $this->Application->SetVar('form_id', $page->GetDBField('FormId')); } } else { $not_found = $this->Application->ConfigValue('ErrorTemplate'); $real_t = $not_found ? $not_found : 'error_notfound'; $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $theme_id = $this->Application->GetVar('m_theme'); $category_id = $themes_helper->getPageByTemplate($real_t, $theme_id); $this->Application->SetVar('m_cat_id', $category_id); header('HTTP/1.0 404 Not Found'); } // replace alias in form #alias_name# to actual template used in this theme if ( $this->Application->isAdmin ) { $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ // only, used when in "Design Mode" $this->Application->SetVar('theme.current_id', $themes_helper->getCurrentThemeId()); } $theme = $this->Application->recallObject('theme.current'); /* @var $theme kDBItem */ $template = $theme->GetField('TemplateAliases', $real_t); if ( $template ) { return $template; } return $real_t; } /** * Sets category id based on found template (used from kApplication::Run) * * @deprecated */ /*function SetCatByTemplate() { $t = $this->Application->GetVar('t'); $page = $this->Application->recallObject($this->Prefix . '.-virtual'); if ( $page->isLoaded() ) { $this->Application->SetVar('m_cat_id', $page->GetDBField('CategoryId')); } }*/ /** * Prepares template paths * * @param kEvent $event */ function _beforeItemChange($event) { $object = $event->getObject(); /* @var $object CategoriesItem */ $object->checkFilename(); $object->generateFilename(); $now = time(); if ( !$this->Application->isDebugMode() && strpos($event->Special, 'rebuild') === false ) { $object->SetDBField('Type', $object->GetOriginalField('Type')); $object->SetDBField('Protected', $object->GetOriginalField('Protected')); if ( $object->GetDBField('Protected') ) { // some fields are read-only for protected pages, when debug mode is off $object->SetDBField('AutomaticFilename', $object->GetOriginalField('AutomaticFilename')); $object->SetDBField('Filename', $object->GetOriginalField('Filename')); $object->SetDBField('Status', $object->GetOriginalField('Status')); } } $is_admin = $this->Application->isAdminUser; if ( (!$object->IsTempTable() && !$is_admin) || ($is_admin && !$object->GetDBField('CreatedById')) ) { $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } if ($object->GetChangedFields()) { $object->SetDBField('Modified_date', $now); $object->SetDBField('Modified_time', $now); } $object->setRequired('PageCacheKey', $object->GetDBField('OverridePageCacheKey')); $object->SetDBField('Template', $this->_stripTemplateExtension( $object->GetDBField('Template') )); if ($object->GetDBField('Type') == PAGE_TYPE_TEMPLATE) { if (!$this->_templateFound($object->GetDBField('Template'), $object->GetDBField('ThemeId'))) { $object->SetError('Template', 'template_file_missing', 'la_error_TemplateFileMissing'); } } $this->_saveTitleField($object, 'Title'); $this->_saveTitleField($object, 'MenuTitle'); $root_category = $this->Application->getBaseCategory(); if ( file_exists(FULL_PATH . '/themes') && ($object->GetDBField('ParentId') == $root_category) && ($object->GetDBField('Template') == CATEGORY_TEMPLATE_INHERIT) ) { // there are themes + creating top level category $object->SetError('Template', 'no_inherit'); } if ( !$this->Application->isAdminUser && $object->isVirtualField('cust_RssSource') ) { // only administrator can set/change "cust_RssSource" field if ($object->GetDBField('cust_RssSource') != $object->GetOriginalField('cust_RssSource')) { $object->SetError('cust_RssSource', 'not_allowed', 'la_error_OperationNotAllowed'); } } if ( !$object->GetDBField('DirectLinkAuthKey') ) { $key_parts = Array ( $object->GetID(), $object->GetDBField('ParentId'), $object->GetField('Name'), 'b38' ); $object->SetDBField('DirectLinkAuthKey', substr( md5( implode(':', $key_parts) ), 0, 20 )); } } /** * Sets page name to requested field in case when: * 1. page was auto created (through theme file rebuild) * 2. requested field is empty * * @param kDBItem $object * @param string $field * @author Alex */ function _saveTitleField(&$object, $field) { $value = $object->GetField($field, 'no_default'); // current value of target field $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $src_field = $ml_formatter->LangFieldName('Name'); $dst_field = $ml_formatter->LangFieldName($field); $dst_field_not_changed = $object->GetOriginalField($dst_field) == $value; if ($value == '' || preg_match('/^_Auto: (.*)/', $value) || (($object->GetOriginalField($src_field) == $value) && $dst_field_not_changed)) { // target field is empty OR target field value starts with "_Auto: " OR (source field value // before change was equals to current target field value AND target field value wasn't changed) $object->SetField($dst_field, $object->GetField($src_field)); } } /** * Don't allow to delete system pages, when not in debug mode * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemDelete(kEvent $event) { parent::OnBeforeItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField('Protected') && !$this->Application->isDebugMode(false) ) { $event->status = kEvent::erFAIL; } } /** * Creates category based on given TPL file * * @param CategoriesItem $object * @param string $template * @param int $theme_id * @param int $system_mode * @param array $template_info * @return bool */ function _prepareAutoPage(&$object, $template, $theme_id = null, $system_mode = SMS_MODE_AUTO, $template_info = Array ()) { $template = $this->_stripTemplateExtension($template); if ($system_mode == SMS_MODE_AUTO) { $page_type = $this->_templateFound($template, $theme_id) ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL; } else { $page_type = $system_mode == SMS_MODE_FORCE ? PAGE_TYPE_TEMPLATE : PAGE_TYPE_VIRTUAL; } if (($page_type == PAGE_TYPE_TEMPLATE) && ($template_info === false)) { // do not auto-create system pages, when browsing through site return false; } if (!isset($theme_id)) { $theme_id = $this->_getCurrentThemeId(); } $root_category = $this->Application->getBaseCategory(); $page_category = $this->Application->GetVar('m_cat_id'); if (!$page_category) { $page_category = $root_category; $this->Application->SetVar('m_cat_id', $page_category); } if (($page_type == PAGE_TYPE_VIRTUAL) && (strpos($template, '/') !== false)) { // virtual page, but have "/" in template path -> create it's path $category_path = explode('/', $template); $template = array_pop($category_path); $page_category = $this->_getParentCategoryFromPath($category_path, $root_category, $theme_id); } $page_name = ($page_type == PAGE_TYPE_TEMPLATE) ? '_Auto: ' . $template : $template; $page_description = ''; if ($page_type == PAGE_TYPE_TEMPLATE) { $design_template = strtolower($template); // leading "/" not added ! if ($template_info) { if (array_key_exists('name', $template_info) && $template_info['name']) { $page_name = $template_info['name']; } if (array_key_exists('desc', $template_info) && $template_info['desc']) { $page_description = $template_info['desc']; } if (array_key_exists('section', $template_info) && $template_info['section']) { // this will override any global "m_cat_id" $page_category = $this->_getParentCategoryFromPath(explode('||', $template_info['section']), $root_category, $theme_id); } } } else { $design_template = $this->_getDefaultDesign(); // leading "/" added ! } $object->Clear(); $object->SetDBField('ParentId', $page_category); $object->SetDBField('Type', $page_type); $object->SetDBField('Protected', 1); // $page_type == PAGE_TYPE_TEMPLATE $object->SetDBField('IsMenu', 0); $object->SetDBField('ThemeId', $theme_id); // put all templates to then end of list (in their category) $min_priority = $this->_getNextPriority($page_category, $object->TableName); $object->SetDBField('Priority', $min_priority); $object->SetDBField('Template', $design_template); $object->SetDBField('CachedTemplate', $design_template); $primary_language = $this->Application->GetDefaultLanguageId(); $current_language = $this->Application->GetVar('m_lang'); $object->SetDBField('l' . $primary_language . '_Name', $page_name); $object->SetDBField('l' . $current_language . '_Name', $page_name); $object->SetDBField('l' . $primary_language . '_Description', $page_description); $object->SetDBField('l' . $current_language . '_Description', $page_description); return $object->Create(); } function _getParentCategoryFromPath($category_path, $base_category, $theme_id = null) { static $category_ids = Array (); if (!$category_path) { return $base_category; } if (array_key_exists(implode('||', $category_path), $category_ids)) { return $category_ids[ implode('||', $category_path) ]; } $backup_category_id = $this->Application->GetVar('m_cat_id'); $object = $this->Application->recallObject($this->Prefix . '.rebuild-path', null, Array ('skip_autoload' => true)); /* @var $object CategoriesItem */ $parent_id = $base_category; $filenames_helper = $this->Application->recallObject('FilenamesHelper'); /* @var $filenames_helper kFilenamesHelper */ $safe_category_path = array_map(Array (&$filenames_helper, 'replaceSequences'), $category_path); foreach ($category_path as $category_order => $category_name) { $this->Application->SetVar('m_cat_id', $parent_id); // get virtual category first, when possible $sql = 'SELECT ' . $object->IDField . ' FROM ' . $object->TableName . ' WHERE ( Filename = ' . $this->Conn->qstr($safe_category_path[$category_order]) . ' OR Filename = ' . $this->Conn->qstr( $filenames_helper->replaceSequences('_Auto: ' . $category_name) ) . ' ) AND (ParentId = ' . $parent_id . ') AND (ThemeId = 0 OR ThemeId = ' . $theme_id . ') ORDER BY ThemeId ASC'; $parent_id = $this->Conn->GetOne($sql); if ($parent_id === false) { // page not found $template = implode('/', array_slice($safe_category_path, 0, $category_order + 1)); // don't process system templates in sub-categories $system = $this->_templateFound($template, $theme_id) && (strpos($template, '/') === false); if (!$this->_prepareAutoPage($object, $category_name, $theme_id, $system ? SMS_MODE_FORCE : false)) { // page was not created break; } $parent_id = $object->GetID(); } } $this->Application->SetVar('m_cat_id', $backup_category_id); $category_ids[ implode('||', $category_path) ] = $parent_id; return $parent_id; } /** * Returns theme name by it's id. Used in structure page creation. * * @param int $theme_id * @return string */ function _getThemeName($theme_id) { static $themes = null; if (!isset($themes)) { $theme_config = $this->Application->getUnitConfig('theme'); $id_field = $theme_config->getIDField(); $table_name = $theme_config->getTableName(); $sql = 'SELECT Name, ' . $id_field . ' FROM ' . $table_name . ' WHERE Enabled = 1'; $themes = $this->Conn->GetCol($sql, $id_field); } return array_key_exists($theme_id, $themes) ? $themes[$theme_id] : false; } /** * Resets SMS-menu cache * * @param kEvent $event */ function OnResetCMSMenuCache($event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } $this->_resetMenuCache(); $event->SetRedirectParam('action_completed', 1); } /** * Performs reset of category-related caches (menu, structure dropdown, template mapping) * * @return void * @access protected */ protected function _resetMenuCache() { // reset cms menu cache (all variables are automatically rebuild, when missing) if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->rebuildCache('master:cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime); $this->Application->rebuildCache('master:StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime); $this->Application->rebuildCache('master:template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime); } else { $this->Application->rebuildDBCache('cms_menu', kCache::REBUILD_LATER, CacheSettings::$cmsMenuRebuildTime); $this->Application->rebuildDBCache('StructureTree', kCache::REBUILD_LATER, CacheSettings::$structureTreeRebuildTime); $this->Application->rebuildDBCache('template_mapping', kCache::REBUILD_LATER, CacheSettings::$templateMappingRebuildTime); } } /** * Updates structure config * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); if (defined('IS_INSTALL') && IS_INSTALL) { // skip any processing, because Categories table doesn't exists until install is finished $this->addViewPermissionJoin($event); return ; } $site_config_helper = $this->Application->recallObject('SiteConfigHelper'); /* @var $site_config_helper SiteConfigHelper */ $settings = $site_config_helper->getSettings(); $root_category = $this->Application->getBaseCategory(); $config = $event->getUnitConfig(); // set root category $config->addSectionAdjustments(Array ( 'in-portal:browse' => Array ( 'url' => Array ('m_cat_id' => $root_category), 'late_load' => Array ('m_cat_id' => $root_category), 'onclick' => 'checkCatalog(' . $root_category . ')', ), 'in-portal:browse_site' => Array ( 'url' => Array ('editing_mode' => $settings['default_editing_mode']), ) )); // prepare structure dropdown $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $fields = $config->getFields(); $fields['ParentId']['default'] = (int)$this->Application->GetVar('m_cat_id'); $fields['ParentId']['options'] = $category_helper->getStructureTreeAsOptions(); // limit design list by theme $theme_id = $this->_getCurrentThemeId(); $design_sql = $fields['Template']['options_sql']; $design_sql = str_replace('(tf.FilePath = "/designs")', '(' . implode(' OR ', $this->getDesignFolders()) . ')' . ' AND (t.ThemeId = ' . $theme_id . ')', $design_sql); $fields['Template']['options_sql'] = $design_sql; // adds "Inherit From Parent" option to "Template" field $fields['Template']['options'] = Array (CATEGORY_TEMPLATE_INHERIT => $this->Application->Phrase('la_opt_InheritFromParent')); $config->setFields($fields); if ($this->Application->isAdmin) { // don't sort by Front-End sorting fields $config_mapping = $config->getConfigMapping(); $remove_keys = Array ('DefaultSorting1Field', 'DefaultSorting2Field', 'DefaultSorting1Dir', 'DefaultSorting2Dir'); foreach ($remove_keys as $remove_key) { unset($config_mapping[$remove_key]); } $config->setConfigMapping($config_mapping); } else { // sort by parent path on Front-End only $config->setListSortingsBySpecial('', Array ( 'ForcedSorting' => Array ('CurrentSort' => 'asc'), )); } $this->addViewPermissionJoin($event); // add grids for advanced view (with primary category column) foreach (Array ('Default', 'Radio') as $process_grid) { $grid_data = $config->getGridByName($process_grid); $grid_data['Fields']['CachedNavbar'] = Array ('title' => 'la_col_Path', 'data_block' => 'grid_parent_category_td', 'filter_block' => 'grid_like_filter'); $config->addGrids($grid_data, $process_grid . 'ShowAll'); } } /** * Adds permission table table JOIN clause only, when advanced catalog view permissions enabled. * * @param kEvent $event Event. * * @return self * @access protected */ protected function addViewPermissionJoin(kEvent $event) { if ( $this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) { $join_clause = 'LEFT JOIN ' . TABLE_PREFIX . 'CategoryPermissionsCache perm ON perm.CategoryId = %1$s.CategoryId'; } else { $join_clause = ''; } $config = $event->getUnitConfig(); foreach ( $config->getListSQLSpecials() as $special ) { $list_sql = str_replace('{PERM_JOIN}', $join_clause, $config->getListSQLsBySpecial($special)); $config->setListSQLsBySpecial($special, $list_sql); } return $this; } /** * Returns folders, that can contain design templates * * @return array * @access protected */ protected function getDesignFolders() { $ret = Array ('tf.FilePath = "/designs"', 'tf.FilePath = "/platform/designs"'); foreach ($this->Application->ModuleInfo as $module_info) { $ret[] = 'tf.FilePath = "/' . $module_info['TemplatePath'] . 'designs"'; } return array_unique($ret); } /** * Removes this item and it's children (recursive) from structure dropdown * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); if ( !$this->Application->isAdmin ) { // calculate priorities dropdown only for admin return; } $object = $event->getObject(); /* @var $object kDBItem */ // remove this category & it's children from dropdown $sql = 'SELECT ' . $object->IDField . ' FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE ParentPath LIKE "' . $object->GetDBField('ParentPath') . '%"'; $remove_categories = $this->Conn->GetCol($sql); $options = $object->GetFieldOption('ParentId', 'options'); foreach ($remove_categories as $remove_category) { unset($options[$remove_category]); } $object->SetFieldOption('ParentId', 'options', $options); } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $object = $event->getObject(); /* @var $object CategoriesItem */ // need to update path after category is created, so category is included in that path $fields_hash = $object->buildParentBasedFields(); $this->Conn->doUpdate($fields_hash, $object->TableName, $object->IDField . ' = ' . $object->GetID()); $object->SetDBFieldsFromHash($fields_hash); } /** * Enter description here... * * @param kEvent $event */ function OnAfterRebuildThemes($event) { $sql = 'SELECT t.ThemeId, CONCAT( tf.FilePath, \'/\', tf.FileName ) AS Path, tf.FileMetaInfo FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId WHERE t.Enabled = 1 AND tf.FileType = 1 AND ( SELECT COUNT(CategoryId) FROM ' . TABLE_PREFIX . 'Categories c WHERE CONCAT(\'/\', c.Template, \'.tpl\') = CONCAT( tf.FilePath, \'/\', tf.FileName ) AND (c.ThemeId = t.ThemeId) ) = 0 '; $files = $this->Conn->Query($sql, 'Path'); if ( !$files ) { // all possible pages are already created return; } kUtil::setResourceLimit(); $dummy = $this->Application->recallObject($event->Prefix . '.rebuild', NULL, Array ('skip_autoload' => true)); /* @var $dummy CategoriesItem */ $error_count = 0; foreach ($files as $a_file => $file_info) { $status = $this->_prepareAutoPage($dummy, $a_file, $file_info['ThemeId'], SMS_MODE_FORCE, unserialize($file_info['FileMetaInfo'])); // create system page if ( !$status ) { $error_count++; } } if ( $this->Application->ConfigValue('CategoryPermissionRebuildMode') == CategoryPermissionRebuild::SILENT ) { $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); } $this->_resetMenuCache(); if ( $error_count ) { // allow user to review error after structure page creation $event->MasterEvent->redirect = false; } } /** * Processes OnMassMoveUp, OnMassMoveDown events * * @param kEvent $event */ function OnChangePriority($event) { $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); $event->CallSubEvent('priority:' . $event->Name); $this->Application->StoreVar('RefreshStructureTree', 1); $this->_resetMenuCache(); } /** * Completely recalculates priorities in current category * * @param kEvent $event */ function OnRecalculatePriorities($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $this->Application->SetVar('priority_prefix', $event->getPrefixSpecial()); $event->CallSubEvent('priority:' . $event->Name); $this->_resetMenuCache(); } /** * Update Preview Block for FCKEditor * * @param kEvent $event */ function OnUpdatePreviewBlock($event) { $event->status = kEvent::erSTOP; - $string = htmlspecialchars_decode($this->Application->GetVar('preview_content')); + $string = $this->Application->unescapeRequestVariable($this->Application->GetVar('preview_content')); $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $string = $category_helper->replacePageIds($string); $this->Application->StoreVar('_editor_preview_content_', $string); } /** * Makes simple search for categories * based on keywords string * * @param kEvent $event */ function OnSimpleSearch($event) { $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('HTTPQuery'); /* @var $query_object kHTTPQuery */ $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; if ( !isset($query_object->Get['keywords']) && !isset($query_object->Post['keywords']) && $this->Conn->Query($sql) ) { // used when navigating by pages or changing sorting in search results return; } if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); $this->Application->SetVar('keywords_too_short', 1); return; // if no or too short keyword entered, doing nothing } $this->Application->StoreVar('keywords', $keywords); $this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search $keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_')); $event->setPseudoClass('_List'); $object = $event->getObject(); /* @var $object kDBList */ $config = $event->getUnitConfig(); $this->Application->SetVar($event->getPrefixSpecial().'_Page', 1); $lang = $this->Application->GetVar('m_lang'); $items_table = $config->getTableName(); $module_name = 'In-Portal'; $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('confs')->getTableName() . ' WHERE ModuleName = ' . $this->Conn->qstr($module_name) . ' AND SimpleSearch = 1'; $search_config = $this->Conn->Query($sql, 'FieldName'); $field_list = array_keys($search_config); $join_clauses = Array(); // field processing $weight_sum = 0; $alias_counter = 0; $custom_fields = $config->getCustomFields(); if ($custom_fields) { $custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; } // what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table)) $search_config_map = Array(); foreach ($field_list as $key => $field) { $local_table = TABLE_PREFIX.$search_config[$field]['TableName']; $weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause // processing multilingual fields if ( !$search_config[$field]['CustomFieldId'] && $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage' ) { $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $field_list[$key] = 'l'.$lang.'_'.$field; if (!isset($search_config[$field]['ForeignField'])) { $field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary']; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } } // processing fields from other tables $foreign_field = $search_config[$field]['ForeignField']; if ( $foreign_field ) { $exploded = explode(':', $foreign_field, 2); if ($exploded[0] == 'CALC') { // ignoring having type clauses in simple search unset($field_list[$key]); continue; } else { $multi_lingual = false; if ($exploded[0] == 'MULTI') { $multi_lingual = true; $foreign_field = $exploded[1]; } $exploded = explode('.', $foreign_field); // format: table.field_name $foreign_table = TABLE_PREFIX.$exploded[0]; $alias_counter++; $alias = 't'.$alias_counter; if ($multi_lingual) { $field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1]; $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; $search_config_map[ $field_list[$key] ] = $field; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } else { $field_list[$key] = $alias.'.'.$exploded[1]; $search_config_map[ $field_list[$key] ] = $field; } $join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']); $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); $join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.' ON '.$join_clause; } } else { // processing fields from local table if ($search_config[$field]['CustomFieldId']) { $local_table = 'custom_data'; // search by custom field value on current language $custom_field_id = array_search($field_list[$key], $custom_fields); $field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id; // search by custom field value on primary language $field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id; $search_config_map[ $field_list[$key.'_primary'] ] = $field; } $field_list[$key] = $local_table.'.'.$field_list[$key]; $search_config_map[ $field_list[$key] ] = $field; } } // keyword string processing $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $where_clause = Array (); foreach ($field_list as $field) { if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) { // local real field $filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false); if ($filter_data) { $where_clause[] = $filter_data['value']; } } elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) { $custom_field_name = 'cust_' . $search_config_map[$field]; $filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false); if ($filter_data) { $where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']); } } else { $where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field)); } } $where_clause = '((' . implode(') OR (', $where_clause) . '))'; // 2 braces for next clauses, see below! $where_clause = $where_clause . ' AND (' . $items_table . '.Status = ' . STATUS_ACTIVE . ')'; if ($event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild') { $sub_search_ids = $event->MasterEvent->getEventParam('ResultIds'); if ( $sub_search_ids !== false ) { if ( $sub_search_ids ) { $where_clause .= 'AND (' . $items_table . '.ResourceId IN (' . implode(',', $sub_search_ids) . '))'; } else { $where_clause .= 'AND FALSE'; } } } // exclude template based sections from search results (ie. registration) if ( $this->Application->ConfigValue('ExcludeTemplateSectionsFromSearch') ) { $where_clause .= ' AND ' . $items_table . '.ThemeId = 0'; } // making relevance clause $positive_words = $search_helper->getPositiveKeywords($keywords); $this->Application->StoreVar('highlight_keywords', serialize($positive_words)); $revelance_parts = Array(); reset($search_config); foreach ($positive_words as $keyword_index => $positive_word) { $positive_word = $search_helper->transformWildcards($positive_word); $positive_words[$keyword_index] = $this->Conn->escape($positive_word); } foreach ($field_list as $field) { if (!array_key_exists($field, $search_config_map)) { $map_key = $search_config_map[$items_table . '.' . $field]; } else { $map_key = $search_config_map[$field]; } $config_elem = $search_config[ $map_key ]; $weight = $config_elem['Priority']; // search by whole words only ([[:<:]] - word boundary) /*$revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.implode(' ', $positive_words).')[[:>:]]", '.$weight.', 0)'; foreach ($positive_words as $keyword) { $revelance_parts[] = 'IF('.$field.' REGEXP "[[:<:]]('.$keyword.')[[:>:]]", '.$weight.', 0)'; }*/ // search by partial word matches too $revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; foreach ($positive_words as $keyword) { $revelance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)'; } } $revelance_parts = array_unique($revelance_parts); $conf_postfix = $config->getSearchConfigPostfix(); $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100; $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100; $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100; $relevance_clause = '('.implode(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords; if ($rel_pop && $object->isField('Hits')) { $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; } if ($rel_rating && $object->isField('CachedRating')) { $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; } // building final search query if (!$this->Application->GetVar('do_not_drop_search_table')) { $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event $this->Application->SetVar('do_not_drop_search_table', true); } $search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"'); if ($search_table_exists) { $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) '; } else { $select_intro = 'CREATE TABLE '.$search_table.' AS '; } $edpick_clause = $config->getFieldByName('EditorsPick') ? $items_table.'.EditorsPick' : '0'; $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance, '.$items_table.'.'.$config->getIDField().' AS ItemId, '.$items_table.'.ResourceId, '.$config->getItemType().' AS ItemType, '.$edpick_clause.' AS EdPick FROM '.$object->TableName.' '.implode(' ', $join_clauses).' WHERE '.$where_clause.' GROUP BY '.$items_table.'.'.$config->getIDField().' ORDER BY Relevance DESC'; $this->Conn->Query($sql); if ( !$search_table_exists ) { $sql = 'ALTER TABLE ' . $search_table . ' ADD INDEX (ResourceId), ADD INDEX (Relevance)'; $this->Conn->Query($sql); } } /** * Enter description here... * * @param kEvent $event */ function OnSubSearch($event) { // keep search results from other items after doing a sub-search on current item type $this->Application->SetVar('do_not_drop_search_table', true); $ids = Array (); $search_table = TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search'; $sql = 'SHOW TABLES LIKE "' . $search_table . '"'; if ( $this->Conn->Query($sql) ) { $item_type = $event->getUnitConfig()->getItemType(); // 1. get ids to be used as search bounds $sql = 'SELECT DISTINCT ResourceId FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $ids = $this->Conn->GetCol($sql); // 2. delete previously found ids $sql = 'DELETE FROM ' . $search_table . ' WHERE ItemType = ' . $item_type; $this->Conn->Query($sql); } $event->setEventParam('ResultIds', $ids); $event->CallSubEvent('OnSimpleSearch'); } /** * Make record to search log * * @param string $keywords * @param int $search_type 0 - simple search, 1 - advanced search */ function saveToSearchLog($keywords, $search_type = 0) { // don't save keywords for each module separately, just one time // static variable can't help here, because each module uses it's own class instance ! if (!$this->Application->GetVar('search_logged')) { $sql = 'UPDATE '.TABLE_PREFIX.'SearchLogs SET Indices = Indices + 1 WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search $this->Conn->Query($sql); if ($this->Conn->getAffectedRows() == 0) { $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLogs'); } $this->Application->SetVar('search_logged', 1); } } /** * Load item if id is available * * @param kEvent $event * @return void * @access protected */ protected function LoadItem(kEvent $event) { if ( !$this->_isVirtual($event) ) { parent::LoadItem($event); return; } $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, null, true) ) { $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $object->setID($id); } } /** * Returns constrain for priority calculations * * @param kEvent $event * @return void * @see PriorityEventHandler * @access protected */ protected function OnGetConstrainInfo(kEvent $event) { $constrain = ''; // for OnSave $event_name = $event->getEventParam('original_event'); $actual_event_name = $event->getEventParam('actual_event'); if ( $actual_event_name == 'OnSavePriorityChanges' || $event_name == 'OnAfterItemLoad' || $event_name == 'OnAfterItemDelete' ) { $object = $event->getObject(); /* @var $object kDBItem */ $constrain = 'ParentId = ' . $object->GetDBField('ParentId'); } elseif ( $actual_event_name == 'OnPreparePriorities' ) { $constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); } elseif ( $event_name == 'OnSave' ) { $constrain = ''; } else { $constrain = 'ParentId = ' . $this->Application->GetVar('m_cat_id'); } $event->setEventParam('constrain_info', Array ($constrain, '')); } /** * Parses category part of url, build main part of url * * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE. * @param string $prefix Prefix, that listener uses for system integration * @param Array $params Params, that are used for url building or created during url parsing. * @param Array $url_parts Url parts to parse (only for parsing). * @param bool $keep_events Keep event names in resulting url (only for building). * @return bool|string|Array Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener. */ public function CategoryRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false) { if ($rewrite_mode == REWRITE_MODE_BUILD) { return $this->_buildMainUrl($prefix, $params, $keep_events); } if ( $this->_parseFriendlyUrl($url_parts, $params) ) { // friendly urls work like exact match only! return false; } $this->_parseCategory($url_parts, $params); return true; } /** * Build main part of every url * * @param string $prefix_special * @param Array $params * @param bool $keep_events * @return string */ protected function _buildMainUrl($prefix_special, &$params, $keep_events) { $ret = ''; list ($prefix) = explode('.', $prefix_special); $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); if ($processed_params === false) { return ''; } // add language if ($processed_params['m_lang'] && ($processed_params['m_lang'] != $rewrite_processor->primaryLanguageId)) { $language_name = $this->Application->getCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]'); if ($language_name === false) { $sql = 'SELECT PackName FROM ' . TABLE_PREFIX . 'Languages WHERE LanguageId = ' . $processed_params['m_lang']; $language_name = $this->Conn->GetOne($sql); $this->Application->setCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]', $language_name); } $ret .= $language_name . '/'; } // add theme if ($processed_params['m_theme'] && ($processed_params['m_theme'] != $rewrite_processor->primaryThemeId)) { $theme_name = $this->Application->getCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]'); if ($theme_name === false) { $sql = 'SELECT Name FROM ' . TABLE_PREFIX . 'Themes WHERE ThemeId = ' . $processed_params['m_theme']; $theme_name = $this->Conn->GetOne($sql); $this->Application->setCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]', $theme_name); } $ret .= $theme_name . '/'; } // inject custom url parts made by other rewrite listeners just after language/theme url parts if ($params['inject_parts']) { $ret .= implode('/', $params['inject_parts']) . '/'; } // add category if ($processed_params['m_cat_id'] > 0 && $params['pass_category']) { $category_filename = $this->Application->getCategoryCache($processed_params['m_cat_id'], 'filenames'); preg_match('/^Content\/(.*)/i', $category_filename, $regs); if ($regs) { $template = array_key_exists('t', $params) ? $params['t'] : false; if (strtolower($regs[1]) == strtolower($template)) { // we could have category path like "Content/<template_path>" in this case remove template $params['pass_template'] = false; } $ret .= $regs[1] . '/'; } $params['category_processed'] = true; } // reset category page $force_page_adding = false; if (array_key_exists('reset', $params) && $params['reset']) { unset($params['reset']); if ($processed_params['m_cat_id']) { $processed_params['m_cat_page'] = 1; $force_page_adding = true; } } if ((array_key_exists('category_processed', $params) && $params['category_processed'] && ($processed_params['m_cat_page'] > 1)) || $force_page_adding) { // category name was added before AND category page number found $ret = rtrim($ret, '/') . '_' . $processed_params['m_cat_page'] . '/'; } $template = array_key_exists('t', $params) ? $params['t'] : false; $category_template = ($processed_params['m_cat_id'] > 0) && $params['pass_category'] ? $this->Application->getCategoryCache($processed_params['m_cat_id'], 'category_designs') : ''; if ((strtolower($template) == '__default__') && ($processed_params['m_cat_id'] == 0)) { // for "Home" category set template to index when not set $template = 'index'; } // remove template from url if it is category index cached template if ( ($template == $category_template) || (mb_strtolower($template) == '__default__') ) { // given template is also default template for this category OR '__default__' given $params['pass_template'] = false; } // remove template from url if it is site homepage on primary language & theme if ( ($template == 'index') && $processed_params['m_lang'] == $rewrite_processor->primaryLanguageId && $processed_params['m_theme'] == $rewrite_processor->primaryThemeId ) { // given template is site homepage on primary language & theme $params['pass_template'] = false; } if ($template && $params['pass_template']) { $ret .= $template . '/'; } return mb_strtolower( rtrim($ret, '/') ); } /** * Checks if whole url_parts matches a whole In-CMS page * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parseFriendlyUrl($url_parts, &$vars) { if (!$url_parts) { return false; } $sql = 'SELECT CategoryId, NamedParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE FriendlyURL = ' . $this->Conn->qstr(implode('/', $url_parts)); $friendly = $this->Conn->GetRow($sql); $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ if ($friendly) { $vars['m_cat_id'] = $friendly['CategoryId']; $vars['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']); while ($url_parts) { $rewrite_processor->partParsed( array_shift($url_parts) ); } return true; } return false; } /** * Extracts category part from url * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parseCategory($url_parts, &$vars) { if (!$url_parts) { return false; } $res = false; $url_part = array_shift($url_parts); $category_id = 0; $last_category_info = false; $category_path = $url_part == 'content' ? '' : 'content'; $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ do { $category_path = trim($category_path . '/' . $url_part, '/'); // bb_<topic_id> -> forums/bb_2 if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $rets) ) { $category_path = $rets[1]; $vars['m_cat_page'] = $rets[2]; } $sql = 'SELECT CategoryId, SymLinkCategoryId, NamedParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)'; $category_info = $this->Conn->GetRow($sql); if ($category_info !== false) { $last_category_info = $category_info; $rewrite_processor->partParsed($url_part); $url_part = array_shift($url_parts); $res = true; } } while ($category_info !== false && $url_part); if ($last_category_info) { // this category is symlink to other category, so use it's url instead // (used in case if url prior to symlink adding was indexed by spider or was bookmarked) if ($last_category_info['SymLinkCategoryId']) { $sql = 'SELECT CategoryId, NamedParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE (CategoryId = ' . $last_category_info['SymLinkCategoryId'] . ')'; $category_info = $this->Conn->GetRow($sql); if ($category_info) { // web symlinked category was found use it // TODO: maybe 302 redirect should be made to symlinked category url (all other url parts should stay) $last_category_info = $category_info; } } // 1. Set virtual page as template, this will be replaced to physical template later in kApplication::Run. // 2. Don't set CachedTemplate field as template here, because we will loose original page associated with it's cms blocks! $vars['t'] = mb_strtolower( preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath'])); $vars['m_cat_id'] = $last_category_info['CategoryId']; $vars['is_virtual'] = true; // for template from POST, strange code there! } /*else { $vars['m_cat_id'] = 0; }*/ return $res; } /** * Set's new unique resource id to user * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemValidate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $resource_id = $object->GetDBField('ResourceId'); if ( !$resource_id ) { $object->SetDBField('ResourceId', $this->Application->NextResourceId()); } } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { parent::OnBeforeClone($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('ResourceId', 0); // this will reset it } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/translator/translator_event_handler.php =================================================================== --- branches/5.3.x/core/units/translator/translator_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/translator/translator_event_handler.php (revision 16111) @@ -1,182 +1,182 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class TranslatorEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnChangeLanguage' => Array('subitem' => 'add|edit'), 'OnSaveAndClose' => Array('subitem' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Check permission of item, that being translated * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { list($prefix, ) = $this->getPrefixAndField($event); $top_prefix = $this->Application->GetTopmostPrefix($prefix, true); $event->setEventParam('top_prefix', $top_prefix); return parent::CheckPermission($event); } /** * Returns prefix and field being translated * * @param kEvent $event */ function getPrefixAndField($event) { $field = $this->Application->GetVar($event->getPrefixSpecial(true).'_field'); if (strpos($field,':') !== false) { list($prefix, $field) = explode(':', $field); } else { $prefix = $this->Application->GetVar($event->getPrefixSpecial(true).'_prefix'); } return Array($prefix, $field); } /** * Loads record to be translated * * @param kEvent $event * @return void * @access protected */ protected function OnLoad($event) { list($obj_prefix, $field) = $this->getPrefixAndField($event); $object = $this->Application->recallObject($obj_prefix); /* @var $object kDBItem */ $translator = $event->getObject(); /* @var $translator kDBItem */ $def_lang = $this->Application->GetDefaultLanguageId(); $current_lang = $translator->GetDBField('Language'); if (!$current_lang) $current_lang = $this->Application->RecallVar('trans_lang'); if (!$current_lang) $current_lang = $this->Application->GetVar('m_lang'); /*if ($current_lang == $def_lang) { $current_lang = $def_lang + 1; }*/ $this->Application->StoreVar('trans_lang', $current_lang); //remember translation language for user friendlyness $translator->SetID(1); $translator->SetDBField('Original', $object->GetDBField('l'.$this->Application->GetVar('m_lang').'_'.$field)); $translator->SetDBField('Language', $current_lang); $translator->SetDBField('SwitchLanguage', $current_lang); $translator->SetDBField('Translation', $object->GetDBField('l'.$current_lang.'_'.$field)); $cur_lang = $this->Application->recallObject('lang.current'); /* @var $cur_lang LanguagesItem */ $cur_lang->Load($current_lang); $translator->SetDBField('Charset', CHARSET); $event->redirect = false; } /** * Saves changes into temporary table and closes editing window * * @param kEvent $event * @return void * @access protected */ protected function OnSaveAndClose($event) { $event->CallSubEvent('OnPreSave'); $event->SetRedirectParam('opener', 'u'); } /** * Saves edited item into temp table * If there is no id, new item is created in temp table * * @param kEvent $event * @return void * @access protected */ protected function OnPreSave(kEvent $event) { $translator = $event->getObject(); /* @var $translator kDBItem */ $field_values = $this->getSubmittedFields($event); - $translator->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $translator->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); list($obj_prefix, $field) = $this->getPrefixAndField($event); $object = $this->Application->recallObject($obj_prefix); /* @var $object kDBItem */ $lang = $translator->GetDBField('Language'); $object->SetFieldOptions('l' . $lang . '_' . $field, Array ()); $object->SetDBField('l' . $lang . '_' . $field, $translator->GetDBField('Translation')); $this->RemoveRequiredFields($object); $object->Update(); } /** * Changes current language in translation popup * * @param kEvent $event * @return void * @access protected */ protected function OnChangeLanguage($event) { $event->CallSubEvent('OnPreSave'); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('Language', $object->GetDBField('SwitchLanguage')); $event->CallSubEvent('OnLoad'); $event->redirect = false; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/users/users_tag_processor.php =================================================================== --- branches/5.3.x/core/units/users/users_tag_processor.php (revision 16110) +++ branches/5.3.x/core/units/users/users_tag_processor.php (revision 16111) @@ -1,377 +1,377 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UsersTagProcessor extends kDBTagProcessor { function LogoutLink($params) { $pass = Array('pass' => 'all,m,u', 'u_event' => 'OnLogout', 'm_cat_id' => 0); $logout_template = $this->SelectParam($params, 'template,t'); return $this->Application->HREF($logout_template, '', $pass); } function RegistrationEnabled($params) { return $this->Application->ConfigValue('User_Allow_New') != 2; } function SuggestRegister($params) { return !$this->Application->LoggedIn() && !$this->Application->ConfigValue('Comm_RequireLoginBeforeCheckout') && $this->RegistrationEnabled($params); } function ConfirmPasswordLink($params) { $user = $this->Application->recallObject($this->Prefix . '.email-to'); /* @var $user UsersItem */ $code = $this->getCachedCode(); $user->SetDBField('PwResetConfirm', $code); $user->SetDBField('PwRequestTime_date', time()); $user->SetDBField('PwRequestTime_time', time()); if ( $user->GetChangedFields() ) { // tag is called 2 times within USER.PWDC email event, so don't update user record twice $user->Update(); } $params['user_key'] = $code; if ( !$this->SelectParam($params, 'template,t') ) { $params['template'] = $this->Application->GetVar('reset_confirm_template'); } return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Generates & caches code for password confirmation link * * @return string */ function getCachedCode() { static $code = null; if ( !isset($code) ) { $code = md5(kUtil::generateId()); } return $code; } function TestCodeIsValid($params) { $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $code_type = isset($params['code_type']) ? $params['code_type'] : 'forgot_password'; $expiration_timeout = isset($params['expiration_timeout']) ? $params['expiration_timeout'] : null; $user_id = $user_helper->validateUserCode($this->Application->GetVar('user_key'), $code_type, $expiration_timeout); if ( !is_numeric($user_id) ) { // used for error reporting only -> rewrite code + theme (by Alex) $object = $this->getObject( Array('skip_autoload' => true) ); // TODO: change theme too /* @var $object UsersItem */ $object->SetError('PwResetConfirm', $user_id, $this->_getUserCodeErrorMsg($user_id, $code_type, $params)); return false; } return true; } /** * Tries to restore user email * * @param Array $params * @return bool * @access protected */ protected function RestoreEmail($params) { $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $hash = $this->Application->GetVar('hash'); $error_code = $user_helper->restoreEmail($hash); if ( $error_code ) { // used for error reporting only -> rewrite code + theme (by Alex) $object = $this->getObject(Array ('skip_autoload' => true)); // TODO: change theme too /* @var $object UsersItem */ $object->SetError('PwResetConfirm', 'restore', $params[$error_code]); return false; } return true; } /** * Returns error message set by given code type * * @param string $error_code * @param string $code_type * @param Array $params * @return string */ function _getUserCodeErrorMsg($error_code, $code_type, $params) { $error_messages = Array ( 'forgot_password' => Array ( 'code_is_not_valid' => 'lu_code_is_not_valid', 'code_expired' => 'lu_code_expired', ), 'activation' => Array ( 'code_is_not_valid' => 'lu_error_ActivationCodeNotValid', 'code_expired' => 'lu_error_ActivationCodeExpired', ), 'verify_email' => Array ( 'code_is_not_valid' => 'lu_error_VerificationCodeNotValid', 'code_expired' => 'lu_error_VerificationCodeExpired', ), ); if ($code_type == 'custom') { // custom error messages are given directly in tag $error_messages[$code_type] = Array ( 'code_is_not_valid' => $params['error_invalid'], 'code_expired' => $params['error_expired'], ); } return $error_messages[$code_type][$error_code]; } /** * Returns site administrator email * * @param Array $params * @return string */ function SiteAdminEmail($params) { return $this->Application->ConfigValue('DefaultEmailSender'); } /** * Returns login name of user * * @param Array $params * @return string * @access protected */ protected function LoginName($params) { $object = $this->getObject($params); /* @var $object UsersItem */ return $object->GetID() != USER_ROOT ? $object->GetDBField('Username') : 'root'; } function CookieUsername($params) { $items_info = $this->Application->GetVar( $this->getPrefixSpecial(true) ); if ( $items_info !== false ) { return $items_info[USER_GUEST][ $params['field'] ]; } $username = $this->Application->GetVar('save_username'); // from cookie if ($username == 'super-root') { $username = 'root'; } return $username === false ? '' : $username; } /** * Checks if user have one of required permissions * * @param Array $params * @return bool */ function HasPermission($params) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->TagPermissionCheck($params); } /** * Returns link to user public profile * * @param Array $params * @return string */ function ProfileLink($params) { $object = $this->getObject($params); $params['user_id'] = $object->GetID(); return $this->Application->ProcessParsedTag('m', 'Link', $params); } function ImageSrc($params) { list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial()); return $tag_processed ? $ret : false; } function LoggedIn($params) { static $loggedin_status = Array (); $object = $this->getObject($params); /* @var $object kDBList */ if (!isset($loggedin_status[$this->Special])) { $user_ids = $object->GetCol($object->IDField); $sql = 'SELECT LastAccessed, '.$object->IDField.' FROM '.TABLE_PREFIX.'UserSessions WHERE (PortalUserId IN ('.implode(',', $user_ids).'))'; $loggedin_status[$this->Special] = $this->Conn->GetCol($sql, $object->IDField); } return isset($loggedin_status[$this->Special][$object->GetID()]); } /** * Prints user activation link * * @param Array $params * @return string */ function ActivationLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $code = $this->getCachedCode(); $object->SetDBField('PwResetConfirm', $code); $object->SetDBField('PwRequestTime_date', time()); $object->SetDBField('PwRequestTime_time', time()); $object->Update(); $params['user_key'] = $code; return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Returns link to revert e-mail change in user record * * @param Array $params * @return string * @access protected */ protected function UndoEmailChangeLink($params) { $params['hash'] = $this->Application->Parser->GetParam('hash'); if ( !$this->SelectParam($params, 'template,t') ) { $params['template'] = $this->Application->GetVar('undo_email_template'); } return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Activates user using given code * * @param Array $params * @return string * @access protected */ protected function ActivateUser($params) { $this->_updateAndLogin(Array ('Status' => STATUS_ACTIVE, 'EmailVerified' => 1)); return ''; } /** * Marks user e-mail as verified using given code * * @param Array $params * @return string * @access protected */ protected function MarkUserEmailAsVerified($params) { $this->_updateAndLogin(Array ('EmailVerified' => 1)); return ''; } /** * Activates user using given code * * @param Array $fields_hash * @return void * @access protected */ protected function _updateAndLogin($fields_hash) { $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $user = $this->Application->recallObject($this->Prefix . '.activate', null, Array ('skip_autoload' => true)); /* @var $user UsersItem */ $user->Load(trim($this->Application->GetVar('user_key')), 'PwResetConfirm'); if ( !$user->isLoaded() ) { return ; } - $user->SetFieldsFromHash($fields_hash); + $user->SetDBFieldsFromHash($fields_hash); $user->SetDBField('PwResetConfirm', ''); $user->SetDBField('PwRequestTime_date', NULL); $user->SetDBField('PwRequestTime_time', NULL); $user->Update(); $login_user =& $user_helper->getUserObject(); $login_user->Load( $user->GetID() ); if ( ($login_user->GetDBField('Status') == STATUS_ACTIVE) && $user_helper->checkLoginPermission() ) { $user_helper->loginUserById( $login_user->GetID() ); } } /** * Returns user selector title * * @param Array $params * @return string * @access protected */ protected function UserSelectorTitle($params) { $object = $this->getObject($params); /* @var $object kDBItem */ return $object->GetDBField('Email') ? $object->GetDBField('Email') : $object->GetDBField('Username'); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/users/users_event_handler.php =================================================================== --- branches/5.3.x/core/units/users/users_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/users/users_event_handler.php (revision 16111) @@ -1,1971 +1,1958 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UsersEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( // admin 'OnSetPersistantVariable' => Array('self' => 'view'), // because setting to logged in user only 'OnUpdatePassword' => Array('self' => true), 'OnSaveSelected' => Array ('self' => 'view'), 'OnGeneratePassword' => Array ('self' => 'view'), // front 'OnRefreshForm' => Array('self' => true), 'OnForgotPassword' => Array('self' => true), 'OnSubscribeQuery' => Array('self' => true), 'OnSubscribeUser' => Array('self' => true), 'OnRecommend' => Array('self' => true), 'OnItemBuild' => Array('self' => true), 'OnMassResetSettings' => Array('self' => 'edit'), 'OnMassCloneUsers' => Array('self' => 'add'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** - * Returns fields, that are not allowed to be changed from request - * - * @param Array $hash - * @return Array - * @access protected - */ - protected function getRequestProtectedFields($hash) - { - $fields = parent::getRequestProtectedFields($hash); - - $fields = array_merge($fields, Array ('PrevEmails', 'ResourceId', 'IPAddress', 'IsBanned', 'PwResetConfirm', 'PwRequestTime', 'OldStyleLogin')); - - if ( !$this->Application->isAdmin ) { - $fields = array_merge($fields, Array ('UserType', 'Status', 'EmailVerified', 'IsBanned')); - } - - return $fields; - } - - /** * Builds item (loads if needed) * * Pattern: Prototype Manager * * @param kEvent $event * @access protected */ protected function OnItemBuild(kEvent $event) { parent::OnItemBuild($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $event->Special == 'forgot' || $object->getFormName() == 'registration' ) { $this->_makePasswordRequired($event); } } /** * Shows only admins when required * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ if ( $event->Special == 'regular' ) { $object->addFilter('primary_filter', '%1$s.UserType = ' . UserType::USER); } if ( $event->Special == 'admins' ) { $object->addFilter('primary_filter', '%1$s.UserType = ' . UserType::ADMIN); } if ( !$this->Application->isAdminUser ) { $object->addFilter('status_filter', '%1$s.Status = ' . STATUS_ACTIVE); } if ( $event->Special == 'online' ) { $object->addFilter('online_users_filter', 's.PortalUserId IS NOT NULL'); } if ( $event->Special == 'group' ) { $group_id = $this->Application->GetVar('g_id'); if ( $group_id !== false ) { // show only users, that user doesn't belong to current group $sql = 'SELECT PortalUserId FROM ' . $this->Application->GetTempName(TABLE_PREFIX . 'UserGroupRelations', 'prefix:g') . ' WHERE GroupId = ' . (int)$group_id; $user_ids = $this->Conn->GetCol($sql); if ( $user_ids ) { $object->addFilter('already_member_filter', '%1$s.PortalUserId NOT IN (' . implode(',', $user_ids) . ')'); } } } } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( $event->Name == 'OnLogin' || $event->Name == 'OnLoginAjax' || $event->Name == 'OnLogout' ) { // permission is checked in OnLogin event directly return true; } if ( $event->Name == 'OnResetRootPassword' ) { return defined('DBG_RESET_ROOT') && DBG_RESET_ROOT; } if ( $event->Name == 'OnLoginAs' ) { $admin_session = $this->Application->recallObject('Session.admin'); /* @var $admin_session Session */ return $admin_session->LoggedIn(); } if ( !$this->Application->isAdminUser ) { $user_id = $this->Application->RecallVar('user_id'); $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( ($event->Name == 'OnCreate' || $event->Name == 'OnRegisterAjax') && $user_id == USER_GUEST ) { // "Guest" can create new users return true; } if ( $event->Name == 'OnUpdate' && $user_id > 0 ) { $user_dummy = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $user_dummy UsersItem */ foreach ($items_info as $id => $field_values) { if ( $id != $user_id ) { // registered users can update their record only return false; } $user_dummy->Load($id); $status_field = $event->getUnitConfig()->getStatusField(true); if ( $user_dummy->GetDBField($status_field) != STATUS_ACTIVE ) { // not active user is not allowed to update his record (he could not activate himself manually) return false; } if ( isset($field_values[$status_field]) && $user_dummy->GetDBField($status_field) != $field_values[$status_field] ) { // user can't change status by himself return false; } } return true; } if ( $event->Name == 'OnResetLostPassword' && $event->Special == 'forgot' && $user_id == USER_GUEST ) { // non-logged in users can reset their password, when reset code is valid return is_numeric($this->getPassedID($event)); } if ( $event->Name == 'OnUpdate' && $user_id <= 0 ) { // guests are not allowed to update their record, because they don't have it :) return false; } } return parent::CheckPermission($event); } /** * Handles session expiration (redirects to valid template) * * @param kEvent $event */ function OnSessionExpire($event) { $this->Application->resetCounters('UserSessions'); // place 2 of 2 (also in kHTTPQuery::getRedirectParams) $admin_url_params = Array ( 'm_cat_id' => 0, // category means nothing on admin login screen 'm_wid' => '', // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets) 'pass' => 'm', // don't pass any other (except "m") prefixes to admin session expiration template 'expired' => 1, // expiration mark to show special error on login screen 'no_pass_through' => 1, // this way kApplication::HREF won't add them again ); if ($this->Application->isAdmin) { $this->Application->Redirect('index', $admin_url_params, '', 'index.php'); } if ($this->Application->GetVar('admin') == 1) { // Front-End showed in admin's right frame $session_admin = $this->Application->recallObject('Session.admin'); /* @var $session_admin Session */ if (!$session_admin->LoggedIn()) { // front-end session created from admin session & both expired $this->Application->DeleteVar('admin'); $this->Application->Redirect('index', $admin_url_params, '', 'admin/index.php'); } } // Front-End session expiration $get = $this->Application->HttpQuery->getRedirectParams(); $t = $this->Application->GetVar('t'); $get['js_redirect'] = $this->Application->ConfigValue('UseJSRedirect'); $this->Application->Redirect($t ? $t : 'index', $get); } /** * [SCHEDULED TASK] Deletes expired sessions * * @param kEvent $event */ function OnDeleteExpiredSessions($event) { if (defined('IS_INSTALL') && IS_INSTALL) { return ; } $this->Application->Session->DeleteExpired(); } /** * Checks user data and logs it in if allowed * * @param kEvent $event * @return void * @access protected */ protected function OnLogin($event) { $object = $event->getObject( Array ('form_name' => 'login') ); /* @var $object kDBItem */ - $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($this->getSubmittedFields($event)); $username = $object->GetDBField('UserLogin'); $password = $object->GetDBField('UserPassword'); $remember_login = $object->GetDBField('UserRememberLogin') == 1; /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user_helper->event =& $event; $result = $user_helper->loginUser($username, $password, false, $remember_login); if ($result != LoginResult::OK) { $event->status = kEvent::erFAIL; $object->SetError('UserLogin', $result == LoginResult::NO_PERMISSION ? 'no_permission' : 'invalid_password'); } if ( is_object($event->MasterEvent) && ($event->MasterEvent->Name == 'OnLoginAjax') ) { // used to insert just logged-in user e-mail on "One Step Checkout" form in "Modern Store" theme $user =& $user_helper->getUserObject(); $event->SetRedirectParam('user_email', $user->GetDBField('Email')); } } /** * Performs user login from ajax request * * @param kEvent $event * @return void * @access protected */ protected function OnLoginAjax($event) { $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); /* @var $ajax_form_helper AjaxFormHelper */ - $ajax_form_helper->transitEvent($event, 'OnLogin'); //, Array ('do_refresh' => 1)); + $ajax_form_helper->transitEvent($event, 'OnLogin'); } /** * [HOOK] Auto-Logins Front-End user when "Remember Login" cookie is found * * @param kEvent $event */ function OnAutoLoginUser($event) { $remember_login_cookie = $this->Application->GetVar('remember_login'); if (!$remember_login_cookie || $this->Application->isAdmin || $this->Application->LoggedIn()) { return ; } /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user_helper->loginUser('', '', false, false, $remember_login_cookie); } /** * Called when user logs in using old in-portal * * @param kEvent $event */ function OnInpLogin($event) { $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ $sync_manager->performAction('LoginUser', $event->getEventParam('user'), $event->getEventParam('pass') ); if ($event->redirect && is_string($event->redirect)) { // some real template specified instead of true $this->Application->Redirect($event->redirect, $event->getRedirectParams()); } } /** * Called when user logs in using old in-portal * * @param kEvent $event */ function OnInpLogout($event) { $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ $sync_manager->performAction('LogoutUser'); } /** * Performs user logout * * @param kEvent $event * @return void * @access protected */ protected function OnLogout($event) { /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user_helper->event =& $event; $user_helper->logoutUser(); } /** * Redirects user after successful registration to confirmation template (on Front only) * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $this->afterItemChanged($event); $this->assignToPrimaryGroup($event); } /** * Performs user registration * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { if ( $this->Application->isAdmin ) { parent::OnCreate($event); return ; } $object = $event->getObject( Array('form_name' => 'registration') ); /* @var $object UsersItem */ $field_values = $this->getSubmittedFields($event); $user_email = getArrayValue($field_values, 'Email'); $subscriber_id = $user_email ? $this->getSubscriberByEmail($user_email) : false; if ( $subscriber_id ) { // update existing subscriber $object->Load($subscriber_id); $object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup')); $this->Application->SetVar($event->getPrefixSpecial(true), Array ($object->GetID() => $field_values)); } - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $status = $object->isLoaded() ? $object->Update() : $object->Create(); if ( !$status ) { $event->status = kEvent::erFAIL; $event->redirect = false; $object->setID( (int)$object->GetID() ); } $this->setNextTemplate($event, true); if ( ($event->status == kEvent::erSUCCESS) && $event->redirect ) { $this->assignToPrimaryGroup($event); $object->sendEmails(); $this->autoLoginUser($event); } } /** * Processes user registration from ajax request * * @param kEvent $event * @return void * @access protected */ protected function OnRegisterAjax(kEvent $event) { $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); /* @var $ajax_form_helper AjaxFormHelper */ $ajax_form_helper->transitEvent($event, 'OnCreate', Array ('do_refresh' => 1)); } /** * Returns subscribed user ID by given e-mail address * * @param string $email * @return int|bool * @access protected */ protected function getSubscriberByEmail($email) { $verify_user = $this->Application->recallObject('u.verify', null, Array ('skip_autoload' => true)); /* @var $verify_user UsersItem */ $verify_user->Load($email, 'Email'); return $verify_user->isLoaded() && $verify_user->isSubscriberOnly() ? $verify_user->GetID() : false; } /** * Login user if possible, if not then redirect to corresponding template * * @param kEvent $event */ function autoLoginUser($event) { $object = $event->getObject(); /* @var $object UsersItem */ if ( $object->GetDBField('Status') == STATUS_ACTIVE ) { /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user =& $user_helper->getUserObject(); $user->Load($object->GetID()); if ( $user_helper->checkLoginPermission() ) { $user_helper->loginUserById( $user->GetID() ); } } } /** * Set's new unique resource id to user * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->beforeItemChanged($event); $cs_helper = $this->Application->recallObject('CountryStatesHelper'); /* @var $cs_helper kCountryStatesHelper */ $object = $event->getObject(); /* @var $object UsersItem */ if ( !$object->isSubscriberOnly() ) { // don't check state-to-country relations for subscribers $cs_helper->CheckStateField($event, 'State', 'Country'); } if ( $object->getFormName() != 'login' ) { $this->_makePasswordRequired($event); } $cs_helper->PopulateStates($event, 'State', 'Country'); $this->setUserGroup($object); /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); if ( !$user_helper->checkBanRules($object) ) { $object->SetError('Username', 'banned'); } $object->SetDBField('IPAddress', $this->Application->getClientIp()); if ( !$this->Application->isAdmin ) { $object->SetDBField('FrontLanguage', $this->Application->GetVar('m_lang')); } } /** * Sets primary group of the user * * @param kDBItem $object */ protected function setUserGroup(&$object) { if ($object->Special == 'subscriber') { $object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_SubscriberGroup')); return ; } // set primary group to user if ( !$this->Application->isAdminUser ) { $group_id = $object->GetDBField('PrimaryGroupId'); if ($group_id) { // check, that group is allowed for Front-End $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroups WHERE GroupId = ' . (int)$group_id . ' AND FrontRegistration = 1'; $group_id = $this->Conn->GetOne($sql); } if (!$group_id) { // when group not selected OR not allowed -> use default group $object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup')); } } } /** * Assigns a user to it's primary group * * @param kEvent $event */ protected function assignToPrimaryGroup($event) { $object = $event->getObject(); /* @var $object kDBItem */ $primary_group_id = $object->GetDBField('PrimaryGroupId'); if ($primary_group_id) { $ug_table = TABLE_PREFIX . 'UserGroupRelations'; if ( $object->IsTempTable() ) { $ug_table = $this->Application->GetTempName($ug_table, 'prefix:' . $event->Prefix); } $sql = 'SELECT COUNT(*) FROM ' . $ug_table . ' WHERE PortalUserId = ' . $object->GetID() . ' AND GroupId = ' . $primary_group_id; if ( $this->Conn->GetOne($sql) ) { return; } $fields_hash = Array ( 'PortalUserId' => $object->GetID(), 'GroupId' => $primary_group_id, ); if ( $object->IsTempTable() ) { $new_id = (int)$this->Conn->GetOne('SELECT MIN(Id) FROM ' . $ug_table .' WHERE Id < 0' ); $fields_hash['Id'] = $new_id - 1; } $this->Conn->doInsert($fields_hash, $ug_table); } } /** * Set's new unique resource id to user * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemValidate(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $resource_id = $object->GetDBField('ResourceId'); if ( !$resource_id ) { $object->SetDBField('ResourceId', $this->Application->NextResourceId()); } } /** * Enter description here... * * @param kEvent $event */ function OnRecommend($event) { $object = $event->getObject( Array ('form_name' => 'recommend') ); /* @var $object kDBItem */ - $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($this->getSubmittedFields($event)); if ( !$object->ValidateField('RecommendEmail') ) { $event->status = kEvent::erFAIL; return ; } $send_params = Array ( 'to_email' => $object->GetDBField('RecommendEmail'), 'to_name' => $object->GetDBField('RecommendEmail'), ); $user_id = $this->Application->RecallVar('user_id'); $email_sent = $this->Application->emailUser('USER.SUGGEST', $user_id, $object->getEmailParams($send_params)); $this->Application->emailAdmin('USER.SUGGEST', null, $object->getEmailParams()); if ( $email_sent ) { $event->SetRedirectParam('pass', 'all'); $event->redirect = $this->Application->GetVar('template_success'); } else { $event->status = kEvent::erFAIL; $object->SetError('RecommendEmail', 'send_error'); } } /** * Saves address changes and mades no redirect * * @param kEvent $event */ function OnUpdateAddress($event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { list ($id, $field_values) = each($items_info); if ( $id > 0 ) { $object->Load($id); } - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); $object->setID($id); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); + $object->Validate(); } $cs_helper = $this->Application->recallObject('CountryStatesHelper'); /* @var $cs_helper kCountryStatesHelper */ $cs_helper->PopulateStates($event, 'State', 'Country'); $event->redirect = false; } /** * Validate subscriber's email & store it to session -> redirect to confirmation template * * @param kEvent $event */ function OnSubscribeQuery($event) { $object = $event->getObject( Array ('form_name' => 'subscription') ); /* @var $object UsersItem */ - $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($this->getSubmittedFields($event)); if ( !$object->ValidateField('SubscriberEmail') ) { $event->status = kEvent::erFAIL; return ; } $user_email = $object->GetDBField('SubscriberEmail'); $object->Load($user_email, 'Email'); $event->SetRedirectParam('subscriber_email', $user_email); if ( $object->isLoaded() && $object->isSubscribed() ) { $event->redirect = $this->Application->GetVar('unsubscribe_template'); } else { $event->redirect = $this->Application->GetVar('subscribe_template'); } $event->SetRedirectParam('pass', 'm'); } /** * Subscribe/Unsubscribe user based on email stored in previous step * * @param kEvent $event */ function OnSubscribeUser($event) { $object = $event->getObject( Array ('form_name' => 'subscription') ); /* @var $object UsersItem */ $user_email = $this->Application->GetVar('subscriber_email'); $object->SetDBField('SubscriberEmail', $user_email); if ( !$object->ValidateField('SubscriberEmail') ) { $event->status = kEvent::erFAIL; return ; } $username_required = $object->isRequired('Username'); $this->RemoveRequiredFields($object); $object->Load($user_email, 'Email'); if ( $object->isLoaded() ) { if ( $object->isSubscribed() ) { if ( $event->getEventParam('no_unsubscribe') ) { // for customization code from FormsEventHandler return ; } if ( $object->isSubscriberOnly() ) { $temp_handler = $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($event->Prefix, '', Array($object->GetID())); } else { $this->RemoveSubscriberGroup( $object->GetID() ); } $event->redirect = $this->Application->GetVar('unsubscribe_ok_template'); } else { $this->AddSubscriberGroup($object); $event->redirect = $this->Application->GetVar('subscribe_ok_template'); } } else { $object->generatePassword(); $object->SetDBField('Email', $user_email); if ( $username_required ) { $object->SetDBField('Username', str_replace('@', '_at_', $user_email)); } $object->SetDBField('Status', STATUS_ACTIVE); // make user subscriber Active by default if ( $object->Create() ) { $this->AddSubscriberGroup($object); $event->redirect = $this->Application->GetVar('subscribe_ok_template'); } } } /** * Adding user to subscribers group * * @param UsersItem $object */ function AddSubscriberGroup(&$object) { if ( !$object->isSubscriberOnly() ) { $fields_hash = Array ( 'PortalUserId' => $object->GetID(), 'GroupId' => $this->Application->ConfigValue('User_SubscriberGroup'), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserGroupRelations'); } $send_params = $object->getEmailParams(); $this->Application->emailUser('USER.SUBSCRIBE', $object->GetID(), $send_params); $this->Application->emailAdmin('USER.SUBSCRIBE', null, $send_params); } /** * Removing user from subscribers group * * @param int $user_id */ function RemoveSubscriberGroup($user_id) { $group_id = $this->Application->ConfigValue('User_SubscriberGroup'); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserGroupRelations WHERE PortalUserId = ' . $user_id . ' AND GroupId = ' . $group_id; $this->Conn->Query($sql); $send_params = Array ( 'PrefixSpecial' => 'u', 'item_id' => $user_id ); $this->Application->emailUser('USER.UNSUBSCRIBE', $user_id, $send_params); $this->Application->emailAdmin('USER.UNSUBSCRIBE', null, $send_params); } /** * Validates forgot password form and sends password reset confirmation e-mail * * @param kEvent $event * @return void */ function OnForgotPassword($event) { $object = $event->getObject( Array ('form_name' => 'forgot_password') ); /* @var $object kDBItem */ - $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($this->getSubmittedFields($event)); $user = $this->Application->recallObject('u.tmp', null, Array ('skip_autoload' => true)); /* @var $user UsersItem */ $found = $allow_reset = false; $email_or_username = $object->GetDBField('ForgotLogin'); $is_email = strpos($email_or_username, '@') !== false; if ( strlen($email_or_username) ) { $user->Load($email_or_username, $is_email ? 'Email' : 'Username'); } if ( $user->isLoaded() ) { $min_pwd_reset_delay = $this->Application->ConfigValue('Users_AllowReset'); $found = ($user->GetDBField('Status') == STATUS_ACTIVE) && strlen($user->GetDBField('Password')); if ( !$user->GetDBField('PwResetConfirm') ) { // no reset made -> allow $allow_reset = true; } else { // reset made -> wait N minutes, then allow $allow_reset = TIMENOW > $user->GetDBField('PwRequestTime') + $min_pwd_reset_delay; } } if ($found && $allow_reset) { $this->Application->emailUser('USER.PSWDC', $user->GetID(), $user->getEmailParams()); $event->redirect = $this->Application->GetVar('template_success'); return ; } if ( strlen($email_or_username) ) { $object->SetError('ForgotLogin', $found ? 'reset_denied' : ($is_email ? 'unknown_email' : 'unknown_username')); - } + } if ( !$object->ValidateField('ForgotLogin') ) { $event->status = kEvent::erFAIL; } } /** * Updates kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { parent::OnUpdate($event); if ( !$this->Application->isAdmin ) { $this->setNextTemplate($event); } } /** * Checks state against country * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->beforeItemChanged($event); $cs_helper = $this->Application->recallObject('CountryStatesHelper'); /* @var $cs_helper kCountryStatesHelper */ $cs_helper->CheckStateField($event, 'State', 'Country'); $cs_helper->PopulateStates($event, 'State', 'Country'); $object = $event->getObject(); /* @var $object kDBItem */ if ( $event->Special == 'forgot' ) { $object->SetDBField('PwResetConfirm', ''); $object->SetDBField('PwRequestTime_date', NULL); $object->SetDBField('PwRequestTime_time', NULL); } $changed_fields = array_keys($object->GetChangedFields()); if ( $changed_fields && !in_array('Modified', $changed_fields) ) { $object->SetDBField('Modified_date', time()); $object->SetDBField('Modified_time', time()); } if ( !$this->Application->isAdmin && in_array('Email', $changed_fields) && ($event->Special != 'email-restore') ) { $object->SetDBField('EmailVerified', 0); } } /** * Occurs before item is changed * * @param kEvent $event */ function beforeItemChanged($event) { $object = $event->getObject(); /* @var $object UsersItem */ if ( !$this->Application->isAdmin && $object->getFormName() == 'registration' ) { // sets new user's status based on config options $status_map = Array (1 => STATUS_ACTIVE, 2 => STATUS_DISABLED, 3 => STATUS_PENDING, 4 => STATUS_PENDING); $object->SetDBField('Status', $status_map[ $this->Application->ConfigValue('User_Allow_New') ]); if ( $this->Application->ConfigValue('User_Password_Auto') ) { $object->generatePassword( rand(5, 8) ); } if ( $this->Application->ConfigValue('RegistrationCaptcha') ) { $captcha_helper = $this->Application->recallObject('CaptchaHelper'); /* @var $captcha_helper kCaptchaHelper */ $captcha_helper->validateCode($event, false); } if ( $event->Name == 'OnBeforeItemUpdate' ) { // when a subscriber-only users performs normal registration, then assign him to Member group $this->setUserGroup($object); } } } /** * Sets redirect template based on user status & user request contents * * @param kEvent $event * @param bool $for_registration */ function setNextTemplate($event, $for_registration = false) { $event->SetRedirectParam('opener', 's'); $object = $event->getObject(); /* @var $object UsersItem */ $next_template = false; if ( $object->GetDBField('Status') == STATUS_ACTIVE && $this->Application->GetVar('next_template') ) { $next_template = $this->Application->GetVar('next_template'); } elseif ( $for_registration ) { switch ( $this->Application->ConfigValue('User_Allow_New') ) { case 1: // Immediate $next_template = $this->Application->GetVar('registration_confirm_template'); break; case 3: // Upon Approval case 4: // Email Activation $next_template = $this->Application->GetVar('registration_confirm_pending_template'); break; } } if ($next_template) { $event->redirect = $next_template; } } /** * Delete users from groups if their membership is expired * * @param kEvent $event */ function OnCheckExpiredMembership($event) { // send pre-expiration reminders: begin $pre_expiration = time() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24; $sql = 'SELECT PortalUserId, GroupId FROM '.TABLE_PREFIX.'UserGroupRelations WHERE (MembershipExpires IS NOT NULL) AND (ExpirationReminderSent = 0) AND (MembershipExpires < '.$pre_expiration.')'; $skip_clause = $event->getEventParam('skip_clause'); if ($skip_clause) { $sql .= ' AND !('.implode(') AND !(', $skip_clause).')'; } $records = $this->Conn->Query($sql); if ($records) { $conditions = Array(); $send_params = Array ('PrefixSpecial' => 'u'); foreach ($records as $record) { $send_params['item_id'] = $record['PortalUserId']; $this->Application->emailUser('USER.MEMBERSHIP.EXPIRATION.NOTICE', $record['PortalUserId'], $send_params); $this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE', null, $send_params); $conditions[] = '(PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'].')'; } $sql = 'UPDATE '.TABLE_PREFIX.'UserGroupRelations SET ExpirationReminderSent = 1 WHERE '.implode(' OR ', $conditions); $this->Conn->Query($sql); } // send pre-expiration reminders: end // remove users from groups with expired membership: begin $sql = 'SELECT PortalUserId FROM '.TABLE_PREFIX.'UserGroupRelations WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.time().')'; $user_ids = $this->Conn->GetCol($sql); if ($user_ids) { $send_params = Array ('PrefixSpecial' => 'u'); foreach ($user_ids as $id) { $send_params['item_id'] = $id; $this->Application->emailUser('USER.MEMBERSHIP.EXPIRED', $id, $send_params); $this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRED', null, $send_params); } } $sql = 'DELETE FROM '.TABLE_PREFIX.'UserGroupRelations WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.time().')'; $this->Conn->Query($sql); // remove users from groups with expired membership: end } /** * Used to keep user registration form data, while showing affiliate registration form fields * * @param kEvent $event * @return void * @access protected */ protected function OnRefreshForm($event) { $event->redirect = false; $item_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); list($id, $field_values) = each($item_info); $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ - $object->setID($id); $object->IgnoreValidation = true; - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + + $object->setID($id); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); } /** * Sets persistant variable * * @param kEvent $event */ function OnSetPersistantVariable($event) { $field = $this->Application->GetVar('field'); $value = $this->Application->GetVar('value'); $this->Application->StorePersistentVar($field, $value); $force_tab = $this->Application->GetVar('SetTab'); if ($force_tab) { $this->Application->StoreVar('force_tab', $force_tab); } } /** * Return user from order by special .ord * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { switch ($event->Special) { case 'ord': $order = $this->Application->recallObject('ord'); /* @var $order OrdersItem */ return $order->GetDBField('PortalUserId'); break; case 'profile': $id = $this->Application->GetVar('user_id'); if ( !$id ) { // if none user_id given use current user id $id = $this->Application->RecallVar('user_id'); } return $id; break; case 'forgot': /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $id = $user_helper->validateUserCode($this->Application->GetVar('user_key'), 'forgot_password'); if ( is_numeric($id) ) { return $id; } break; } if ( preg_match('/^(login|register|recommend|subscribe|forgot)/', $event->Special) ) { // this way we can have 2+ objects stating with same special, e.g. "u.login-sidebox" and "u.login-main" return USER_GUEST; } + elseif ( preg_match('/^(update|delete)/', $event->Special) ) { + // This way we can have 2+ objects stating with same special, e.g. "u.update-sidebox" and "u.update-profile". + return $this->Application->RecallVar('user_id'); + } return parent::getPassedID($event); } /** * Allows to change root password * * @param kEvent $event * @return void * @access protected */ protected function OnUpdatePassword($event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { return; } list ($id, $field_values) = each($items_info); $user_id = $this->Application->RecallVar('user_id'); if ( $id == $user_id && ($user_id > 0 || $user_id == USER_ROOT) ) { $user_dummy = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $user_dummy kDBItem */ $user_dummy->Load($id); $status_field = $event->getUnitConfig()->getStatusField(true); if ( $user_dummy->GetDBField($status_field) != STATUS_ACTIVE ) { // not active user is not allowed to update his record (he could not activate himself manually) return ; } } if ( $user_id == USER_ROOT ) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object UsersItem */ // this is internal hack to allow root/root passwords for dev if ( $this->Application->isDebugMode() && $field_values['RootPassword'] == 'root' ) { $object->SetFieldOption('RootPassword', 'min_length', 4); } $this->RemoveRequiredFields($object); $object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass')); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->setID(-1); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); if ( $object->Validate() ) { // validation on, password match too $fields_hash = Array ('VariableValue' => $object->GetDBField('RootPassword')); $conf_table = $this->Application->getUnitConfig('conf')->getTableName(); $this->Conn->doUpdate($fields_hash, $conf_table, 'VariableName = "RootPass"'); $event->SetRedirectParam('opener', 'u'); } else { $event->status = kEvent::erFAIL; $event->redirect = false; return ; } } else { - $object =& $event->getObject(); - /* @var $object kDBItem */ + /** @var kDBItem $object */ + $object = $event->getObject(); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); if ( !$object->Update() ) { $event->status = kEvent::erFAIL; $event->redirect = false; } } $event->SetRedirectParam('opener', 'u'); } /** * Resets grid settings, remembered in each user record * * @param kEvent $event * @return void * @access protected */ protected function OnMassResetSettings($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $ids = $this->StoreSelectedIDs($event); $default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId'); if ( in_array($default_user_id, $ids) ) { array_splice($ids, array_search($default_user_id, $ids), 1); } if ( $ids ) { $q = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId IN (' . join(',', $ids) . ') AND (VariableName LIKE "%_columns_%" OR VariableName LIKE "%_filter%" OR VariableName LIKE "%_PerPage%")'; $this->Conn->Query($q); } $this->clearSelectedIDs($event); } /** * Checks, that currently loaded item is allowed for viewing (non permission-based) * * @param kEvent $event * @return bool * @access protected */ protected function checkItemStatus(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( !$object->isLoaded() ) { return true; } $virtual_users = Array (USER_ROOT, USER_GUEST); return ($object->GetDBField('Status') == STATUS_ACTIVE) || in_array($object->GetID(), $virtual_users); } /** * Sends approved/declined email event on user status change * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { parent::OnAfterItemUpdate($event); $this->afterItemChanged($event); $object = $event->getObject(); /* @var $object UsersItem */ if ( !$this->Application->isAdmin && ($event->Special != 'email-restore') ) { $this->sendEmailChangeEvent($event); } if ( !$this->Application->isAdmin || $object->IsTempTable() ) { return; } $this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status')); } /** * Occurs, after item is changed * * @param kEvent $event */ protected function afterItemChanged($event) { $this->saveUserImages($event); $object = $event->getObject(); /* @var $object UsersItem */ if ( $object->GetDBField('EmailPassword') && $object->GetDBField('Password_plain') ) { $email_passwords = $this->Application->RecallVar('email_passwords'); $email_passwords = $email_passwords ? unserialize($email_passwords) : Array (); $email_passwords[ $object->GetID() ] = $object->GetDBField('Password_plain'); $this->Application->StoreVar('email_passwords', serialize($email_passwords)); } // update user subscription status (via my profile or new user registration) if ( !$this->Application->isAdmin && !$object->isSubscriberOnly() ) { if ( $object->GetDBField('SubscribeToMailing') && !$object->isSubscribed() ) { $this->AddSubscriberGroup($object); } elseif ( !$object->GetDBField('SubscribeToMailing') && $object->isSubscribed() ) { $this->RemoveSubscriberGroup( $object->GetID() ); } } } /** * Stores user's original Status before overwriting with data from temp table * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { parent::OnBeforeDeleteFromLive($event); $user_id = $event->getEventParam('id'); $user_status = $this->Application->GetVar('user_status', Array ()); if ( $user_id > 0 ) { $user_status[$user_id] = $this->getUserStatus($user_id); $this->Application->SetVar('user_status', $user_status); } } /** * Sends approved/declined email event on user status change (in temp tables during editing) * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCopyToLive(kEvent $event) { parent::OnAfterCopyToLive($event); $temp_id = $event->getEventParam('temp_id'); $email_passwords = $this->Application->RecallVar('email_passwords'); if ( $email_passwords ) { $email_passwords = unserialize($email_passwords); if ( isset($email_passwords[$temp_id]) ) { $object = $event->getObject(); /* @var $object kDBItem */ $object->SwitchToLive(); $object->Load( $event->getEventParam('id') ); $object->SetField('Password', $email_passwords[$temp_id]); $object->SetField('VerifyPassword', $email_passwords[$temp_id]); $this->Application->emailUser($temp_id > 0 ? 'USER.NEW.PASSWORD': 'USER.ADD.BYADMIN', $object->GetID(), $object->getEmailParams()); unset($email_passwords[$temp_id]); $this->Application->StoreVar('email_passwords', serialize($email_passwords)); } } if ( $temp_id > 0 ) { // only send status change e-mail on user update $new_status = $this->getUserStatus($temp_id); $user_status = $this->Application->GetVar('user_status'); $this->sendStatusChangeEvent($temp_id, $user_status[$temp_id], $new_status); } } /** * Returns user status (active, pending, disabled) based on ID and temp mode setting * * @param int $user_id * @return int */ function getUserStatus($user_id) { $config = $this->getUnitConfig(); $sql = 'SELECT Status FROM '. $config->getTableName() .' WHERE '. $config->getIDField() .' = '.$user_id; return $this->Conn->GetOne($sql); } /** * Sends approved/declined email event on user status change * * @param int $user_id * @param int $prev_status * @param int $new_status */ function sendStatusChangeEvent($user_id, $prev_status, $new_status) { $status_events = Array ( STATUS_ACTIVE => 'USER.APPROVE', STATUS_DISABLED => 'USER.DENY', ); $email_event = isset($status_events[$new_status]) ? $status_events[$new_status] : false; if (($prev_status != $new_status) && $email_event) { $send_params = Array ( 'PrefixSpecial' => 'u', 'item_id' => $user_id, ); $this->Application->emailUser($email_event, $user_id, $send_params); $this->Application->emailAdmin($email_event, null, $send_params); } // deletes sessions from users, that are no longer active if (($prev_status != $new_status) && ($new_status != STATUS_ACTIVE)) { $sql = 'SELECT SessionKey FROM ' . TABLE_PREFIX . 'UserSessions WHERE PortalUserId = ' . $user_id; $session_ids = $this->Conn->GetCol($sql); $this->Application->Session->DeleteSessions($session_ids); } } /** * Sends restore/validation email event on user email change * * @param kEvent $event * @return void * @access protected */ protected function sendEmailChangeEvent(kEvent $event) { $object = $event->getObject(); /* @var $object UsersItem */ $new_email = $object->GetDBField('Email'); $prev_email = $object->GetOriginalField('Email'); if ( !$new_email || ($prev_email == $new_email) ) { return; } $prev_emails = $object->GetDBField('PrevEmails'); $prev_emails = $prev_emails ? unserialize($prev_emails) : Array (); $fields_hash = Array ( 'PrevEmails' => serialize($prev_emails), 'EmailVerified' => 0, ); $user_id = $object->GetID(); if ( $prev_email ) { $hash = md5(TIMENOW + $user_id); $prev_emails[$hash] = $prev_email; $fields_hash['PrevEmails'] = serialize($prev_emails); $send_params = Array ( 'hash' => $hash, 'to_email' => $prev_email, 'to_name' => trim($object->GetDBField('FirstName') . ' ' . $object->GetDBField('LastName')), ); $this->Application->emailUser('USER.EMAIL.CHANGE.UNDO', null, $object->getEmailParams($send_params)); } if ( $new_email ) { $this->Application->emailUser('USER.EMAIL.CHANGE.VERIFY', $user_id, $object->getEmailParams()); } // direct DB update, since USER.EMAIL.CHANGE.VERIFY puts verification code in user record, that we don't want to loose $this->Conn->doUpdate($fields_hash, $object->TableName, 'PortalUserId = ' . $user_id); } /** * OnAfterConfigRead for users * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); $config = $event->getUnitConfig(); $default_form = $config->getFormByName('default'); $form_fields =& $default_form['Fields']; // 1. arrange user registration countries $site_helper = $this->Application->recallObject('SiteHelper'); /* @var $site_helper SiteHelper */ $first_country = $site_helper->getDefaultCountry('', false); if ($first_country === false) { $first_country = $this->Application->ConfigValue('User_Default_Registration_Country'); } if ($first_country) { // update user country dropdown sql $form_fields['Country']['options_sql'] = preg_replace('/ORDER BY (.*)/', 'ORDER BY IF (CountryStateId = '.$first_country.', 1, 0) DESC, \\1', $form_fields['Country']['options_sql']); } // 2. set default user registration group $form_fields['PrimaryGroupId']['default'] = $this->Application->ConfigValue('User_NewGroup'); // 3. allow avatar upload on Front-End $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->createItemFiles($event->Prefix, true); // create image fields if ($this->Application->isAdminUser) { // 4. when in administrative console, then create all users with Active status $form_fields['Status']['default'] = STATUS_ACTIVE; // 5. remove groups tab on editing forms when AdvancedUserManagement config variable not set if (!$this->Application->ConfigValue('AdvancedUserManagement')) { $edit_tab_presets = $config->getEditTabPresets(); foreach ($edit_tab_presets as $preset_name => $preset_tabs) { if (array_key_exists('groups', $preset_tabs)) { unset($edit_tab_presets[$preset_name]['groups']); if (count($edit_tab_presets[$preset_name]) == 1) { // only 1 tab left -> remove it too $edit_tab_presets[$preset_name] = Array (); } } } $config->setEditTabPresets($edit_tab_presets); } } if ( $this->Application->ConfigValue('RegistrationUsernameRequired') ) { // Username becomes required only, when it's used in registration process $max_username = $this->Application->ConfigValue('MaxUserName'); $form_fields['Username']['required'] = 1; $form_fields['Username']['min_len'] = $this->Application->ConfigValue('Min_UserName'); $form_fields['Username']['max_len'] = $max_username ? $max_username : 255; } $config->addForms($default_form, 'default'); } /** * OnMassCloneUsers * * @param kEvent $event */ function OnMassCloneUsers($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $temp_handler = $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $ids = $this->StoreSelectedIDs($event); $temp_handler->CloneItems($event->Prefix, '', $ids); $this->clearSelectedIDs($event); } /** * When cloning users, reset password (set random) * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { parent::OnBeforeClone($event); $object = $event->getObject(); /* @var $object UsersItem */ $object->generatePassword(); $object->SetDBField('ResourceId', 0); // this will reset it // change email because it should be unique $object->NameCopy(Array (), $object->GetID(), 'Email', 'copy%1$s.%2$s'); } /** * Saves selected ids to session * * @param kEvent $event */ function OnSaveSelected($event) { $this->StoreSelectedIDs($event); // remove current ID, otherwise group selector will use it in filters $this->Application->DeleteVar($event->getPrefixSpecial(true) . '_id'); } /** * Sets primary group of selected users * * @param kEvent $event */ function OnProcessSelected($event) { $event->SetRedirectParam('opener', 'u'); $user_ids = $this->getSelectedIDs($event, true); $this->clearSelectedIDs($event); $dst_field = $this->Application->RecallVar('dst_field'); if ($dst_field != 'PrimaryGroupId') { return ; } $group_ids = array_keys($this->Application->GetVar('g')); $primary_group_id = $group_ids ? array_shift($group_ids) : false; if (!$user_ids || !$primary_group_id) { return ; } $table_name = $this->Application->getUnitConfig('ug')->getTableName(); // 1. mark group as primary $sql = 'UPDATE ' . TABLE_PREFIX . 'Users SET PrimaryGroupId = ' . $primary_group_id . ' WHERE PortalUserId IN (' . implode(',', $user_ids) . ')'; $this->Conn->Query($sql); $sql = 'SELECT PortalUserId FROM ' . $table_name . ' WHERE (GroupId = ' . $primary_group_id . ') AND (PortalUserId IN (' . implode(',', $user_ids) . '))'; $existing_members = $this->Conn->GetCol($sql); // 2. add new members to a group $new_members = array_diff($user_ids, $existing_members); foreach ($new_members as $user_id) { $fields_hash = Array ( 'GroupId' => $primary_group_id, 'PortalUserId' => $user_id, ); $this->Conn->doInsert($fields_hash, $table_name); } } /** * Loads user images * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); // linking existing images for item with virtual fields $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $object = $event->getObject(); /* @var $object UsersItem */ $image_helper->LoadItemImages($object); $cs_helper = $this->Application->recallObject('CountryStatesHelper'); /* @var $cs_helper kCountryStatesHelper */ $cs_helper->PopulateStates($event, 'State', 'Country'); // get user subscription status $object->SetDBField('SubscribeToMailing', $object->isSubscribed() ? 1 : 0); if ( !$this->Application->isAdmin ) { $object->SetFieldOption('FrontLanguage', 'options', $this->getEnabledLanguages()); } } /** * Returns list of enabled languages with their names * * @return Array * @access protected */ protected function getEnabledLanguages() { $cache_key = 'user_languages[%LangSerial%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $languages = $this->Application->recallObject('lang.enabled', 'lang_List'); /* @var $languages kDBList */ $ret = Array (); foreach ($languages as $language_info) { $ret[$languages->GetID()] = $language_info['LocalName']; } $this->Application->setCache($cache_key, $ret); } return $ret; } /** * Save user images * * @param kEvent $event */ function saveUserImages($event) { if (!$this->Application->isAdmin) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $object = $event->getObject(); /* @var $object kDBItem */ // process image upload in virtual fields $image_helper->SaveItemImages($object); } } /** * Makes password required for new users * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); if ( $event->status != kEvent::erSUCCESS ) { return; } $object = $event->getObject(); /* @var $object kDBItem */ $user_type = $this->Application->GetVar('user_type'); if ( $user_type ) { $object->SetDBField('UserType', $user_type); if ( $user_type == UserType::ADMIN ) { $object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_AdminGroup')); } } if ( $this->Application->ConfigValue('User_Password_Auto') ) { $object->SetDBField('EmailPassword', 1); } $this->_makePasswordRequired($event); } /** * Makes password required for new users * * @param kEvent $event */ function _makePasswordRequired($event) { $object = $event->getObject(); /* @var $object kDBItem */ $required_fields = Array ('Password', 'Password_plain', 'VerifyPassword', 'VerifyPassword_plain'); $object->setRequired($required_fields); } /** * Load item if id is available * * @param kEvent $event * @return void * @access protected */ protected function LoadItem(kEvent $event) { $id = $this->getPassedID($event); if ( $id < 0 ) { // when root, guest and so on $object = $event->getObject(); /* @var $object kDBItem */ $object->Clear($id); return; } parent::LoadItem($event); } /** * Occurs just after login (for hooking) * * @param kEvent $event */ function OnAfterLogin($event) { if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) { $event->MasterEvent->SetRedirectParam('login', 1); } } /** * Occurs just before logout (for hooking) * * @param kEvent $event */ function OnBeforeLogout($event) { if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) { $event->MasterEvent->SetRedirectParam('logout', 1); } } /** * Generates password * * @param kEvent $event */ function OnGeneratePassword($event) { $event->status = kEvent::erSTOP; if ( $this->Application->isAdminUser ) { echo kUtil::generatePassword(); } } /** * Changes user's password and logges him in * * @param kEvent $event */ function OnResetLostPassword($event) { $object = $event->getObject(); /* @var $object kDBItem */ $event->CallSubEvent('OnUpdate'); if ( $event->status == kEvent::erSUCCESS ) { /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user =& $user_helper->getUserObject(); $user->Load( $object->GetID() ); if ( $user_helper->checkLoginPermission() ) { $user_helper->loginUserById( $user->GetID() ); } } } /** * Generates new Root password and email it * * @param kEvent $event * @return void * @access protected */ protected function OnResetRootPassword($event) { $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ $new_root_password = kUtil::generatePassword(); $this->Application->SetConfigValue('RootPass', $password_formatter->hashPassword($new_root_password)); $this->Application->emailAdmin('ROOT.RESET.PASSWORD', null, Array ('password' => $new_root_password)); $event->SetRedirectParam('reset', 1); $event->SetRedirectParam('pass', 'm'); } /** * Perform login of user, selected in Admin Console, on Front-End in a separate window * * @param kEvent $event * @return void * @access protected */ protected function OnLoginAs(kEvent $event) { /* @var $user_helper UserHelper */ $user_helper = $this->Application->recallObject('UserHelper'); $user =& $user_helper->getUserObject(); $user->Load( $this->Application->GetVar('user_id') ); if ( !$user->isLoaded() ) { return ; } if ( $user_helper->checkLoginPermission() ) { $user_helper->loginUserById( $user->GetID() ); } } } Index: branches/5.3.x/core/units/users/users_item.php =================================================================== --- branches/5.3.x/core/units/users/users_item.php (revision 16110) +++ branches/5.3.x/core/units/users/users_item.php (revision 16111) @@ -1,200 +1,220 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UsersItem extends kDBItem { /** * Returns IDs of groups to which user belongs and membership is not expired * * @param bool $force_reload * @return Array * @access public */ public function getMembershipGroups($force_reload = false) { $user_groups = $this->Application->RecallVar('UserGroups'); if ( $user_groups === false || $force_reload ) { // primary group goes first $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroupRelations WHERE (PortalUserId = ' . $this->GetID() . ') AND ( (MembershipExpires IS NULL) OR (MembershipExpires >= ' . time() . ') ) ORDER BY IF(GroupId = ' . $this->GetDBField('PrimaryGroupId') . ', 1, 0) DESC'; $groups = $this->Conn->GetCol($sql); $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $user_groups = Array (); $ip_restrictions = $user_helper->getGroupsWithIPRestrictions(); foreach ($groups as $group_id) { if ( !isset($ip_restrictions[$group_id]) || kUtil::ipMatch($ip_restrictions[$group_id], "\n") ) { $user_groups[] = $group_id; } } return $user_groups; } return explode(',', $user_groups); } function sendEmails() { $send_params = $this->getEmailParams(); switch ( $this->GetDBField('Status') ) { case STATUS_ACTIVE: $event_name = $this->Application->ConfigValue('User_Password_Auto') ? 'USER.VALIDATE' : 'USER.ADD'; $this->Application->emailUser($event_name, $this->GetID(), $send_params); $this->Application->emailAdmin($event_name, null, $send_params); break; case STATUS_PENDING: $this->Application->emailUser('USER.ADD.PENDING', $this->GetID(), $send_params); $this->Application->emailAdmin('USER.ADD.PENDING', null, $send_params); break; } } /** * Checks that user is subscriber only * * @return bool */ public function isSubscriberOnly() { return $this->GetDBField('PrimaryGroupId') == $this->Application->ConfigValue('User_SubscriberGroup'); } /** * Checks that user is subscribed * * @return bool */ public function isSubscribed() { $group_id = $this->Application->ConfigValue('User_SubscriberGroup'); $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroupRelations WHERE (PortalUserId = ' . $this->GetID() . ') AND (GroupId = ' . $group_id . ')'; return $this->Conn->GetOne($sql); } /** * 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) { $ret = parent::Create($force_id, $system_create); if ( $ret ) { // find out how to synchronize user only when it's copied to live table $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ $sync_manager->performAction('createUser', $this->FieldValues); } return $ret; } /** * Updates previously loaded record with current item' values * * @access public * @param int $id Primary Key Id to update * @param Array $update_fields * @param bool $system_update * @return bool * @access public */ public function Update($id = null, $update_fields = null, $system_update = false) { $ret = parent::Update($id, $update_fields, $system_update); if ( $ret ) { // find out how to synchronize user only when it's copied to live table $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ $sync_manager->performAction('updateUser', $this->FieldValues); } return $ret; } /** * Deletes the record from database * * @param int $id * @return bool * @access public */ public function Delete($id = null) { $ret = parent::Delete($id); if ( $ret ) { $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ $sync_manager->performAction('deleteUser', $this->FieldValues); } return $ret; } function setName($full_name) { $full_name = explode(' ', $full_name); if (count($full_name) > 2) { $last_name = array_pop($full_name); $first_name = implode(' ', $full_name); } else { $last_name = $full_name[1]; $first_name = $full_name[0]; } $this->SetDBField('FirstName', $first_name); $this->SetDBField('LastName', $last_name); } /** * Generates new password for given user * * @param int $length * @return string * @access public */ public function generatePassword($length = 10) { $password = kUtil::generatePassword($length); $this->SetField('Password', $password); $this->SetField('VerifyPassword', $password); return $password; } - } \ No newline at end of file + + /** + * Returns fields, that are not allowed to be changed from request. + * + * @param array $fields_hash Fields hash. + * + * @return array + */ + protected function getRequestProtectedFields(array $fields_hash) + { + $fields = parent::getRequestProtectedFields($fields_hash); + + $fields = array_merge($fields, Array ('PrevEmails', 'ResourceId', 'IPAddress', 'IsBanned', 'PwResetConfirm', 'PwRequestTime', 'OldStyleLogin')); + + if ( !$this->Application->isAdmin ) { + $fields = array_merge($fields, Array ('UserType', 'Status', 'EmailVerified', 'IsBanned')); + } + + return $fields; + } + } Index: branches/5.3.x/core/units/helpers/curl_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/curl_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/curl_helper.php (revision 16111) @@ -1,528 +1,569 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kCurlHelper extends kHelper { const REQUEST_METHOD_GET = 1; const REQUEST_METHOD_POST = 2; /** * ID of database record of currently active curl request * * @var int * @access protected */ protected $logId = 0; /** * Connection to host * * @var resource * @access protected */ protected $connectionID = NULL; /** * Response waiting timeout in seconds * * @var int * @access public */ public $timeout = 90; /** * Follow to url, if redirect received instead of document (only works when open_basedir and safe mode is off) * * @var bool * @access public */ public $followLocation = false; /** * Last response received by Curl * * @var string * @access public */ public $lastResponse = ''; /** * Last error code * * @var int * @access public */ public $lastErrorCode = 0; /** * Last error message * * @var string * @access public */ public $lastErrorMsg = ''; /** * Most recent HTTP response code received * * @var int * @access public */ public $lastHTTPCode = 0; /** * Count of intermediate redirects performed to get actual content * * @var int * @access protected */ protected $lastRedirectCount = 0; /** * Default request method * * @var int * @access protected */ protected $requestMethod = self::REQUEST_METHOD_GET; /** * Data to be sent using curl * * @var string * @access protected */ protected $requestData = ''; /** * Request headers (associative array) * * @var Array * @access protected */ protected $requestHeaders = Array (); /** * Response headers * * @var Array * @access protected */ protected $responseHeaders = Array (); /** * CURL options * * @var Array * @access protected */ protected $options = Array (); /** * Indicates debug mode status * * @var bool * @access public */ public $debugMode = false; /** * Creates an instance of kCurlHelper class */ public function __construct() { parent::__construct(); $this->debugMode = kUtil::constOn('DBG_CURL'); } /** * Reset connection settings (not results) after connection was closed * * @access protected */ protected function _resetSettings() { $this->timeout = 90; $this->followLocation = false; $this->requestMethod = self::REQUEST_METHOD_GET; $this->requestData = ''; $this->requestHeaders = Array (); $this->options = Array (); } /** + * Resets information in last* properties. + * + * @return void + */ + protected function resetLastInfo() + { + $this->lastErrorCode = 0; + $this->lastErrorMsg = ''; + $this->lastHTTPCode = 0; + $this->lastRedirectCount = 0; + } + + /** * Sets CURL options (adds to options set before) * * @param Array $options_hash * @access public */ public function setOptions($options_hash) { $this->options = kUtil::array_merge_recursive($this->options, $options_hash); } /** * Combines user-defined and default options before setting them to CURL * * @access protected */ protected function prepareOptions() { $default_options = Array ( // customizable options CURLOPT_TIMEOUT => $this->timeout, // hardcoded options CURLOPT_RETURNTRANSFER => 1, CURLOPT_REFERER => PROTOCOL.SERVER_NAME, + CURLOPT_MAXREDIRS => 5, // don't verify SSL certificates CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => Array ('Expect:'), ); if ( isset($_SERVER['HTTP_USER_AGENT']) ) { $default_options[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT']; } if ($this->requestHeaders) { $default_options[CURLOPT_HTTPHEADER] = $this->prepareHeaders(); } // if we have post data, then POST else use GET method instead if ($this->requestMethod == self::REQUEST_METHOD_POST) { $default_options[CURLOPT_POST] = 1; $default_options[CURLOPT_POSTFIELDS] = $this->requestData; } // $default_options[CURLOPT_HEADERFUNCTION] = Array(&$this, 'ParseHeader'); $user_options = $this->options; // backup options, that user set directly $this->setOptions($default_options); $this->setOptions($user_options); $this->applyOptions(); } /** * Sets prepared options to CURL * * @access protected */ protected function applyOptions() { foreach ($this->options as $option_name => $option_value) { curl_setopt($this->connectionID, $option_name, $option_value); } } /** * Parses headers from CURL request * * @param resource $ch * @param string $header * @return int * @access protected */ protected function ParseHeader(&$ch, $header) { $this->responseHeaders[] = $header; return strlen($header); } /** * Sets request data for next query * * @param mixed $data Array or string */ public function SetRequestData($data) { if ( is_array($data) ) { $data = http_build_query($data); } $this->requestData = $data; } /** * Sets request data for next query and switches request method to POST * * @param mixed $data Array or string * @access public */ public function SetPostData($data) { $this->requestMethod = self::REQUEST_METHOD_POST; $this->SetRequestData($data); } /** * Sets request method to be used in next request * * @param int $request_method + * + * @throws InvalidArgumentException When invalid request method given. */ public function SetRequestMethod($request_method) { - if ($request_method != self::REQUEST_METHOD_GET || $request_method != self::REQUEST_METHOD_POST) { - throw new Exception('Method "' . __METHOD__ . '": Invalid $request_method parameter value'); + if ($request_method != self::REQUEST_METHOD_GET && $request_method != self::REQUEST_METHOD_POST) { + throw new InvalidArgumentException('Method "' . __METHOD__ . '": Invalid $request_method parameter value'); } $this->requestMethod = $request_method; } /** * Sets headers to be sent along with next query * * @param Array $headers * @access public */ public function SetHeaders($headers) { $this->requestHeaders = array_merge($this->requestHeaders, $headers); } /** * Returns compiled header to be used by curl * * @return Array * @access protected */ protected function prepareHeaders() { $ret = Array (); foreach ($this->requestHeaders as $header_name => $header_value) { $ret[] = is_numeric($header_name) ? $header_value : $header_name . ': ' . $header_value; } return $ret; } /** * Performs CURL request and returns it's result * * @param string $url * @param bool $close_connection * @param bool $log_status * @param string $log_message * @return string * @access public */ public function Send($url, $close_connection = true, $log_status = NULL, $log_message = '') { if ( isset($log_status) ) { // override debug mode setting $this->debugMode = $log_status; } $request_url = $url; if ( $this->requestMethod == self::REQUEST_METHOD_GET && $this->requestData ) { $request_url .= (strpos($request_url, '?') !== false ? '&' : '?') . $this->requestData; } $this->connectionID = curl_init($request_url); if ( $this->debugMode ) { // collect page data $page_data = Array (); if ( $_GET ) { $page_data[] = '_GET:' . "\n" . print_r($_GET, true); } if ( $_POST ) { $page_data[] = '_POST:' . "\n" . print_r($_POST, true); } if ( $_COOKIE ) { $page_data[] = '_COOKIE:' . "\n" . print_r($_COOKIE, true); } // create log record $fields_hash = Array ( 'Message' => $log_message, 'PageUrl' => $_SERVER['REQUEST_URI'], 'RequestUrl' => $url, 'PortalUserId' => $this->Application->RecallVar('user_id'), 'SessionKey' => $this->Application->GetSID(), 'IsAdmin' => $this->Application->isAdminUser ? 1 : 0, 'PageData' => implode("\n", $page_data), 'RequestData' => $this->requestData, 'RequestDate' => time(), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CurlLog'); $this->logId = $this->Conn->getInsertID(); } $this->responseHeaders = Array (); $this->prepareOptions(); $this->lastResponse = $this->_sendRequest(); $this->Finalize($close_connection); return $this->lastResponse; } /** * Reads data from remote url * * @return string * @access protected */ protected function _sendRequest() { + $this->resetLastInfo(); curl_setopt($this->connectionID, CURLOPT_RETURNTRANSFER, true); if ( $this->followLocation ) { if ( $this->followLocationLimited() ) { return $this->_followLocationManually(); } else { // no restrictions - let curl do automatic redirects curl_setopt($this->connectionID, CURLOPT_FOLLOWLOCATION, true); } } return curl_exec($this->connectionID); } /** * Fixes curl inability to automatically follow location when safe_mode/open_basedir restriction in effect * * @return string * @access protected */ protected function _followLocationManually() { curl_setopt($this->connectionID, CURLOPT_HEADER, true); $data = curl_exec($this->connectionID); $http_code = $this->getInfo(CURLINFO_HTTP_CODE); if ( $http_code == 301 || $http_code == 302 ) { // safe more or open_basedir restriction - do redirects manually list ($header) = explode("\r\n\r\n", $data, 2); preg_match('/(Location:|URI:)(.*?)\n/', $header, $regs); $url = trim(array_pop($regs)); $url_parsed = parse_url($url); + if ( $this->lastRedirectCount == $this->options[CURLOPT_MAXREDIRS] ) { + return $this->setError(CURLE_TOO_MANY_REDIRECTS, 'Maximum (' . $this->options[CURLOPT_MAXREDIRS] . ') redirects followed'); + } + if ( isset($url_parsed) ) { curl_setopt($this->connectionID, CURLOPT_URL, $url); $this->lastRedirectCount++; return $this->_followLocationManually(); } } list(, $body) = explode("\r\n\r\n", $data, 2); return $body; } /** + * Sets error manually. + * + * @param integer $code Code. + * @param string $message Message. + * + * @return boolean + */ + protected function setError($code, $message) + { + $this->lastErrorCode = $code; + $this->lastErrorMsg = $message; + + return false; + } + + /** * Returns various info about request made * * @param int $info_type * @return mixed * * @see http://www.php.net/manual/ru/function.curl-getinfo.php * @access public */ public function getInfo($info_type) { if ( $info_type == CURLINFO_REDIRECT_COUNT && $this->followLocationLimited() ) { return $this->lastRedirectCount; } return curl_getinfo($this->connectionID, $info_type); } /** * Detects, that follow location can't be done automatically by curl due safe_mode/open_basedir restrictions * * @return bool * @access protected */ protected function followLocationLimited() { return (defined('SAFE_MODE') && SAFE_MODE) || ini_get('open_basedir'); } /** * Finalizes curl request and saves some data from curl before closing connection * * @param bool $close_connection * @return void * @access public */ public function Finalize($close_connection = true) { - $this->lastErrorCode = curl_errno($this->connectionID); - $this->lastErrorMsg = curl_error($this->connectionID); + if ( $this->lastErrorCode == 0 ) { + // error not set manually -> get it from curl + $this->lastErrorCode = curl_errno($this->connectionID); + $this->lastErrorMsg = curl_error($this->connectionID); + } + $this->lastHTTPCode = $this->getInfo(CURLINFO_HTTP_CODE); if ( $close_connection ) { $this->CloseConnection(); } $this->_resetSettings(); } /** * Closes connection to server * * @access public */ public function CloseConnection() { curl_close($this->connectionID); if ( $this->debugMode ) { $fields_hash = Array ( 'ResponseData' => $this->lastResponse, 'ResponseDate' => time(), 'ResponseHttpCode' => $this->lastHTTPCode, 'CurlError' => $this->lastErrorCode != 0 ? '#' . $this->lastErrorCode . ' (' . $this->lastErrorMsg . ')' : '', ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'CurlLog', 'LogId = ' . $this->logId); } // restore debug mode setting $this->debugMode = kUtil::constOn('DBG_CURL'); } /** * Checks, that last curl request was successful * * @return bool * @access public */ public function isGoodResponseCode() { if ( $this->lastErrorCode != 0 ) { return false; } return ($this->lastHTTPCode == 200) || ($this->lastHTTPCode >= 300 && $this->lastHTTPCode < 310); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/ajax_form_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/ajax_form_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/ajax_form_helper.php (revision 16111) @@ -1,147 +1,151 @@ <?php class AjaxFormHelper extends kHelper { /** * Sets error info as parameters in response object * Expects that event status is not erSUCCESS * * @param kEvent $event * @param Array $response */ public function prepareJSONErrors($event, &$response) { $object = $event->getObject(); /* @var $object kDBItem */ $response['status'] = 'FAILED'; $response['field_errors'] = $this->getErrorMessages($object); } /** * Returns object errors * * @param kDBItem $object * @return Array */ public function getErrorMessages(&$object) { $error_msgs = Array (); $field_errors = array_keys( $object->GetFieldErrors() ); foreach ($field_errors as $field) { if ( !$object->GetErrorPseudo($field) ) { continue; } if ( $object->GetFieldOption($field, 'formatter') == 'kMultiLanguage') { $field = 'l' . $this->Application->GetVar('m_lang') . '_' . $field; } $error_field = $object->GetFieldOption($field, 'error_field', false, $field); $error_msgs[$field] = $object->GetErrorMsg($error_field, false); } return $error_msgs; } /** * Returns information about all uploader fields. * * @param kDBItem $object Object to process * @param array|null $fields Fields filter. * * @return array * @access public */ public function getUploaderInfo(kDBItem $object, $fields = null) { $ret = array(); if ( !isset($fields) ) { $fields = array_keys($object->getFields()); } foreach ($fields as $field) { $formatter = $object->GetFieldOption($field, 'formatter'); if ( !$formatter ) { continue; } if ( $formatter == 'kUploadFormatter' || in_array('kUploadFormatter', class_parents($formatter)) ) { $ret[$field] = array( 'urls' => $object->GetField($field, 'file_urls'), 'names' => $object->GetField($field, 'file_names'), 'sizes' => $object->GetField($field, 'file_sizes'), ); } } return $ret; } /** * Sends JSON-encoded response as event result to the browser * * @param kEvent $event * @param Array $response */ public function sendResponse($event, $response = Array ()) { if ( !isset($response['status']) ) { $response['status'] = 'OK'; } $json_helper = $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ echo $json_helper->encode($response); $event->status = kEvent::erSTOP; } /** * Calls given event and returns nice JSON output * * @param kEvent $event * @param string $call_event * @param Array $params * @param Closure $callback * @return bool * @access public */ public function transitEvent($event, $call_event, $params = Array (), $callback = null) { $params['status'] = 'OK'; $event->CallSubEvent($call_event); $form_data = $event->getEventParam('form_data'); if ( $form_data !== false ) { $object = $event->getObject(); /* @var $object kDBItem */ $params['uploader_info'] = $this->getUploaderInfo($object, array_keys($form_data)); } if ( is_callable($callback) ) { call_user_func($callback, $event); } if ( $event->status != kEvent::erSUCCESS ) { $this->prepareJSONErrors($event, $params); } else { if ( !isset($params['redirect_to']) && ($event->redirect === true || strlen($event->redirect) > 0) ) { + if ( $event->redirect === true ) { + $event->redirect = ''; + } + $params['redirect_to'] = $this->Application->HREF($event->redirect, '', $event->getRedirectParams(), $event->redirectScript); } $params = array_merge($params, $event->getRedirectParams()); } $this->sendResponse($event, $params); return $params['status'] == 'OK'; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/user_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/user_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/user_helper.php (revision 16111) @@ -1,786 +1,795 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UserHelper extends kHelper { /** * Event to be used during login processings * * @var kEvent */ var $event = null; /** * Performs user login and returns the result * * @param string $username * @param string $password * @param bool $dry_run * @param bool $remember_login * @param string $remember_login_cookie * @return int */ function loginUser($username, $password, $dry_run = false, $remember_login = false, $remember_login_cookie = '') { if ( !isset($this->event) ) { $this->event = new kEvent('u:OnLogin'); } if ( !$password && !$remember_login_cookie ) { return LoginResult::INVALID_PASSWORD; } $object =& $this->getUserObject(); // process "Save Username" checkbox if ( $this->Application->isAdmin ) { $save_username = $this->Application->GetVar('cb_save_username') ? $username : ''; $this->Application->Session->SetCookie('save_username', $save_username, strtotime('+1 year')); // cookie will be set on next refresh, but refresh won't occur if // login error present, so duplicate cookie in kHTTPQuery $this->Application->SetVar('save_username', $save_username); } // logging in "root" (admin only) $super_admin = ($username == 'super-root') && $this->verifySuperAdmin(); if ( $this->Application->isAdmin && ($username == 'root') || ($super_admin && $username == 'super-root') ) { $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ if ( !$password_formatter->checkPasswordFromSetting('RootPass', $password) ) { return LoginResult::INVALID_PASSWORD; } $user_id = USER_ROOT; $object->Clear($user_id); $object->SetDBField('Username', 'root'); if ( !$dry_run ) { $this->loginUserById($user_id, $remember_login_cookie); if ( $super_admin ) { $this->Application->StoreVar('super_admin', 1); } // reset counters $this->Application->resetCounters('UserSessions'); $this->_processLoginRedirect('root', $password); $this->_processInterfaceLanguage(); $this->_fixNextTemplate(); } return LoginResult::OK; } $user_id = $this->getUserId($username, $password, $remember_login_cookie); if ( $user_id ) { $object->Load($user_id); if ( !$this->checkBanRules($object) ) { return LoginResult::BANNED; } if ( $object->GetDBField('Status') == STATUS_ACTIVE ) { if ( !$this->checkLoginPermission() ) { return LoginResult::NO_PERMISSION; } if ( !$dry_run ) { $this->loginUserById($user_id, $remember_login_cookie); if ( $remember_login ) { // remember username & password when "Remember Login" checkbox us checked (when user is using login form on Front-End) $sql = 'SELECT MD5(Password) FROM ' . TABLE_PREFIX . 'Users WHERE PortalUserId = ' . $user_id; $remember_login_hash = $this->Conn->GetOne($sql); $this->Application->Session->SetCookie('remember_login', $username . '|' . $remember_login_hash, strtotime('+1 month')); } if ( !$remember_login_cookie ) { // reset counters $this->Application->resetCounters('UserSessions'); $this->_processLoginRedirect($username, $password); $this->_processInterfaceLanguage(); $this->_fixNextTemplate(); } } return LoginResult::OK; } else { $pending_template = $this->Application->GetVar('pending_disabled_template'); if ( $pending_template !== false && !$dry_run ) { // when user found, but it's not yet approved redirect hit to notification template $this->event->redirect = $pending_template; return LoginResult::OK; } else { // when no notification template given return an error return LoginResult::INVALID_PASSWORD; } } } if ( !$dry_run ) { $this->event->SetRedirectParam('pass', 'all'); // $this->event->SetRedirectParam('pass_category', 1); // to test } return LoginResult::INVALID_PASSWORD; } /** * Login user by it's id * * @param int $user_id * @param bool $remember_login_cookie */ function loginUserById($user_id, $remember_login_cookie = false) { $object =& $this->getUserObject(); - $this->Application->removeObject($object->getPrefixSpecial()); - $this->Application->StoreVar('user_id', $user_id); $this->Application->SetVar('u.current_id', $user_id); + + if ( !$this->Application->isAdmin ) { + // needed for "profile edit", "registration" forms ON FRONT ONLY + $this->Application->SetVar('u_id', $user_id); + } + + $this->Application->StoreVar('user_id', $user_id); $this->Application->Session->SetField('PortalUserId', $user_id); if ($user_id != USER_ROOT) { $groups = $this->Application->RecallVar('UserGroups'); list ($first_group, ) = explode(',', $groups); $this->Application->Session->SetField('GroupId', $first_group); $this->Application->Session->SetField('GroupList', $groups); $this->Application->Session->SetField('TimeZone', $object->GetDBField('TimeZone')); } $this->Application->LoadPersistentVars(); if (!$remember_login_cookie) { // don't change last login time when auto-login is used $this_login = (int)$this->Application->RecallPersistentVar('ThisLogin'); $this->Application->StorePersistentVar('LastLogin', $this_login); $this->Application->StorePersistentVar('ThisLogin', time()); } $hook_event = new kEvent('u:OnAfterLogin'); $hook_event->MasterEvent = $this->event; $this->Application->HandleEvent($hook_event); } /** * Checks login permission * * @return bool */ function checkLoginPermission() { $object =& $this->getUserObject(); $ip_restrictions = $object->GetDBField('IPRestrictions'); if ( $ip_restrictions && !$this->Application->isDebugMode() && !kUtil::ipMatch($ip_restrictions, "\n") ) { return false; } $groups = $object->getMembershipGroups(true); if ( !$groups ) { $groups = Array (); } $default_group = $this->getUserTypeGroup(); if ( $default_group !== false ) { array_push($groups, $default_group); } // store groups, because kApplication::CheckPermission will use them! array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup')); $groups = array_unique($groups); $this->Application->StoreVar('UserGroups', implode(',', $groups), true); // true for optional return $this->Application->CheckPermission($this->Application->isAdmin ? 'ADMIN' : 'LOGIN', 1); } /** * Returns default user group for it's type * * @return bool|string * @access protected */ protected function getUserTypeGroup() { $group_id = false; $object =& $this->getUserObject(); if ( $object->GetDBField('UserType') == UserType::USER ) { $group_id = $this->Application->ConfigValue('User_NewGroup'); } elseif ( $object->GetDBField('UserType') == UserType::ADMIN ) { $group_id = $this->Application->ConfigValue('User_AdminGroup'); } $ip_restrictions = $this->getGroupsWithIPRestrictions(); if ( !isset($ip_restrictions[$group_id]) || kUtil::ipMatch($ip_restrictions[$group_id], "\n") ) { return $group_id; } return false; } /** * Returns groups with IP restrictions * * @return Array * @access public */ public function getGroupsWithIPRestrictions() { static $cache = null; if ( $this->Application->isDebugMode() ) { return Array (); } if ( !isset($cache) ) { $sql = 'SELECT IPRestrictions, GroupId FROM ' . TABLE_PREFIX . 'UserGroups WHERE IPRestrictions IS NOT NULL'; $cache = $this->Conn->GetCol($sql, 'GroupId'); } return $cache; } /** * Performs user logout * */ function logoutUser() { if (!isset($this->event)) { $this->event = new kEvent('u:OnLogout'); } $hook_event = new kEvent('u:OnBeforeLogout'); $hook_event->MasterEvent = $this->event; $this->Application->HandleEvent($hook_event); $this->_processLoginRedirect(); $user_id = USER_GUEST; $this->Application->SetVar('u.current_id', $user_id); $object = $this->Application->recallObject('u.current', null, Array('skip_autoload' => true)); /* @var $object UsersItem */ $object->Load($user_id); $this->Application->DestroySession(); $this->Application->StoreVar('user_id', $user_id, true); $this->Application->Session->SetField('PortalUserId', $user_id); $group_list = $this->Application->ConfigValue('User_GuestGroup') . ',' . $this->Application->ConfigValue('User_LoggedInGroup'); $this->Application->StoreVar('UserGroups', $group_list, true); $this->Application->Session->SetField('GroupList', $group_list); if ($this->Application->ConfigValue('UseJSRedirect')) { $this->event->SetRedirectParam('js_redirect', 1); } $this->Application->resetCounters('UserSessions'); $this->Application->Session->SetCookie('remember_login', '', strtotime('-1 hour')); // don't pass user prefix on logout, since resulting url will have broken "env" $this->event->SetRedirectParam('pass', MOD_REWRITE ? 'm' : 'all'); $this->_fixNextTemplate(); } /** * Returns user id based on given criteria * * @param string $username * @param string $password * @param string $remember_login_cookie * @return int */ function getUserId($username, $password, $remember_login_cookie) { if ( $remember_login_cookie ) { list ($username, $password) = explode('|', $remember_login_cookie); // 0 - username, 1 - md5(password_hash) } $sql = 'SELECT PortalUserId, Password, PasswordHashingMethod FROM ' . TABLE_PREFIX . 'Users WHERE Email = %1$s OR Username = %1$s'; $user_info = $this->Conn->GetRow(sprintf($sql, $this->Conn->qstr($username))); if ( $user_info ) { if ( $remember_login_cookie ) { return md5($user_info['Password']) == $password; } else { $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ $hashing_method = $user_info['PasswordHashingMethod']; if ( $password_formatter->checkPassword($password, $user_info['Password'], $hashing_method) ) { if ( $hashing_method != PasswordHashingMethod::PHPPASS ) { $this->_fixUserPassword($user_info['PortalUserId'], $password); } return $user_info['PortalUserId']; } } } return false; } /** * Apply new password hashing to given user's password * * @param int $user_id * @param string $password * @return void * @access protected */ protected function _fixUserPassword($user_id, $password) { $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ $fields_hash = Array ( 'Password' => $password_formatter->hashPassword($password), 'PasswordHashingMethod' => PasswordHashingMethod::PHPPASS, ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Users', 'PortalUserId = ' . $user_id); } /** * Process all required data and redirect logged-in user * * @param string $username * @param string $password * @return void */ protected function _processLoginRedirect($username = null, $password = null) { // set next template $next_template = $this->Application->GetVar('next_template'); if ( $next_template ) { $this->event->redirect = $next_template; } // process IIS redirect if ( $this->Application->ConfigValue('UseJSRedirect') ) { $this->event->SetRedirectParam('js_redirect', 1); } // synchronize login $sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array (), Array ('InPortalSyncronize')); /* @var $sync_manager UsersSyncronizeManager */ if ( isset($username) && isset($password) ) { $sync_manager->performAction('LoginUser', $username, $password); } else { $sync_manager->performAction('LogoutUser'); } } /** * Sets correct interface language after successful login, based on user settings * * @return void * @access protected */ protected function _processInterfaceLanguage() { if ( defined('IS_INSTALL') && IS_INSTALL ) { $this->event->SetRedirectParam('m_lang', 1); // data $this->Application->Session->SetField('Language', 1); // interface return; } $language_field = $this->Application->isAdmin ? 'AdminLanguage' : 'FrontLanguage'; $primary_language_field = $this->Application->isAdmin ? 'AdminInterfaceLang' : 'PrimaryLang'; $is_root = $this->Application->RecallVar('user_id') == USER_ROOT; $object =& $this->getUserObject(); $user_language_id = $is_root ? $this->Application->RecallPersistentVar($language_field) : $object->GetDBField($language_field); $sql = 'SELECT LanguageId, IF(LanguageId = ' . (int)$user_language_id . ', 2, ' . $primary_language_field . ') AS SortKey FROM ' . TABLE_PREFIX . 'Languages WHERE Enabled = 1 HAVING SortKey <> 0 ORDER BY SortKey DESC'; $language_info = $this->Conn->GetRow($sql); $language_id = $language_info && $language_info['LanguageId'] ? $language_info['LanguageId'] : $user_language_id; if ( $user_language_id != $language_id ) { // first login OR language was deleted or disabled if ( $is_root ) { $this->Application->StorePersistentVar($language_field, $language_id); } else { $object->SetDBField($language_field, $language_id); $object->Update(); } } // set language for Admin Console & Front-End with disabled Mod-Rewrite $this->event->SetRedirectParam('m_lang', $language_id); // data $this->Application->Session->SetField('Language', $language_id); // interface } /** * Injects redirect params into next template, which doesn't happen if next template starts with "external:" * * @return void * @access protected */ protected function _fixNextTemplate() { if ( !MOD_REWRITE || !is_object($this->event) ) { return; } // solve problem, when template is "true" instead of actual template name $template = is_string($this->event->redirect) ? $this->event->redirect : ''; $url = $this->Application->HREF($template, '', $this->event->getRedirectParams(), $this->event->redirectScript); $vars = $this->Application->parseRewriteUrl($url, 'pass'); unset($vars['login'], $vars['logout']); // merge back url params, because they were ignored if this was "external:" url $vars = array_merge($vars, $this->getRedirectParams($vars['pass'], 'pass')); - $template = $vars['t']; + if ( $template != 'index' ) { + // The 'index.html' becomes '', which in turn leads to current page instead of 'index.html'. + $template = $vars['t']; + } + unset($vars['is_virtual'], $vars['t']); $this->event->redirect = $template; $this->event->setRedirectParams($vars, false); } /** * Returns current event redirect params with given $prefixes injected into 'pass'. * * @param array $prefixes List of prefixes to inject. * @param string $pass_name Name of array key in redirect params, containing comma-separated prefixes list. * * @return string * @access protected */ protected function getRedirectParams($prefixes, $pass_name = 'passed') { $redirect_params = $this->event->getRedirectParams(); if ( isset($redirect_params[$pass_name]) ) { $redirect_prefixes = explode(',', $redirect_params[$pass_name]); $prefixes = array_unique(array_merge($prefixes, $redirect_prefixes)); } $redirect_params[$pass_name] = implode(',', $prefixes); return $redirect_params; } /** * Checks that user is allowed to use super admin mode * * @return bool */ function verifySuperAdmin() { $sa_mode = kUtil::ipMatch(defined('SA_IP') ? SA_IP : ''); return $sa_mode || $this->Application->isDebugMode(); } /** * Returns user object, used during login processing * * @return UsersItem * @access public */ public function &getUserObject() { $prefix_special = $this->Application->isAdmin ? 'u.current' : 'u'; // "u" used on front not to change theme $object = $this->Application->recallObject($prefix_special, null, Array('skip_autoload' => true)); /* @var $object UsersItem */ return $object; } /** * Checks, if given user fields matches at least one of defined ban rules * * @param kDBItem $object * @return bool */ function checkBanRules(&$object) { $table = $this->Application->getUnitConfig('ban-rule')->getTableName(); if (!$this->Conn->TableFound($table)) { // when ban table not found -> assume user is ok by default return true; } $sql = 'SELECT * FROM ' . $table . ' WHERE ItemType = 6 AND Status = ' . STATUS_ACTIVE . ' ORDER BY Priority DESC'; $rules = $this->Conn->Query($sql); $found = false; foreach ($rules as $rule) { $field = $rule['ItemField']; $this_value = mb_strtolower( $object->GetDBField($field) ); $test_value = mb_strtolower( $rule['ItemValue'] ); switch ( $rule['ItemVerb'] ) { case 1: // is if ($this_value == $test_value) { $found = true; } break; case 2: // is not if ($this_value != $test_value) { $found = true; } break; case 3: // contains if ( strstr($this_value, $test_value) ) { $found = true; } break; case 4: // not contains if ( !strstr($this_value, $test_value) ) { $found = true; } break; case 7: // exists if ( strlen($this_value) > 0 ) { $found = true; } break; case 8: // unique if ( $this->_checkValueExist($field, $this_value) ) { $found = true; } break; } if ( $found ) { // check ban rules, until one of them matches if ( $rule['RuleType'] ) { // invert rule type $found = false; } break; } } return !$found; } /** * Checks if value is unique in Users table against the specified field * * @param string $field * @param string $value * @return string */ function _checkValueExist($field, $value) { $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('u')->getTableName() . ' WHERE '. $field .' = ' . $this->Conn->qstr($value); return $this->Conn->GetOne($sql); } public function validateUserCode($user_code, $code_type, $expiration_timeout = null) { $expiration_timeouts = Array ( 'forgot_password' => 'config:Users_AllowReset', 'activation' => 'config:UserEmailActivationTimeout', 'verify_email' => 'config:Users_AllowReset', 'custom' => '', ); if ( !$user_code ) { return 'code_is_not_valid'; } $sql = 'SELECT PwRequestTime, PortalUserId FROM ' . TABLE_PREFIX . 'Users WHERE PwResetConfirm = ' . $this->Conn->qstr( trim($user_code) ); $user_info = $this->Conn->GetRow($sql); if ( $user_info === false ) { return 'code_is_not_valid'; } $expiration_timeout = isset($expiration_timeout) ? $expiration_timeout : $expiration_timeouts[$code_type]; if ( preg_match('/^config:(.*)$/', $expiration_timeout, $regs) ) { $expiration_timeout = $this->Application->ConfigValue( $regs[1] ); } if ( $expiration_timeout && $user_info['PwRequestTime'] < strtotime('-' . $expiration_timeout . ' minutes') ) { return 'code_expired'; } return $user_info['PortalUserId']; } /** * Restores user's email, returns error label, if error occurred * * @param string $hash * @return string * @access public */ public function restoreEmail($hash) { if ( !preg_match('/^[a-f0-9]{32}$/', $hash) ) { return 'invalid_hash'; } $sql = 'SELECT PortalUserId, PrevEmails FROM ' . TABLE_PREFIX . 'Users WHERE PrevEmails LIKE ' . $this->Conn->qstr('%' . $hash . '%'); $user_info = $this->Conn->GetRow($sql); if ( $user_info === false ) { return 'invalid_hash'; } $prev_emails = $user_info['PrevEmails']; $prev_emails = $prev_emails ? unserialize($prev_emails) : Array (); if ( !isset($prev_emails[$hash]) ) { return 'invalid_hash'; } $email_to_restore = $prev_emails[$hash]; unset($prev_emails[$hash]); $object = $this->Application->recallObject('u.email-restore', null, Array ('skip_autoload' => true)); /* @var $object UsersItem */ $object->Load($user_info['PortalUserId']); $object->SetDBField('PrevEmails', serialize($prev_emails)); $object->SetDBField('Email', $email_to_restore); $object->SetDBField('EmailVerified', 1); return $object->Update() ? '' : 'restore_impossible'; } /** * Generates random string * * @param int $length * @param bool $special_chars * @param bool $extra_special_chars * @return string * @access public */ public function generateRandomString($length = 12, $special_chars = true, $extra_special_chars = false) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ( $special_chars ) { $chars .= '!@#$%^&*()'; } if ( $extra_special_chars ) { $chars .= '-_ []{}<>~`+=,.;:/?|'; } $password = ''; for ($i = 0; $i < $length; $i++) { $password .= substr($chars, $this->_generateRandomNumber(0, strlen($chars) - 1), 1); } return $password; } /** * Generates a random number * * @param int $min Lower limit for the generated number (optional, default is 0) * @param int $max Upper limit for the generated number (optional, default is 4294967295) * @return int A random number between min and max * @access protected */ protected function _generateRandomNumber($min = 0, $max = 0) { static $rnd_value = ''; // Reset $rnd_value after 14 uses // 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value if ( strlen($rnd_value) < 8 ) { $random_seed = $this->Application->getDBCache('random_seed'); $rnd_value = md5(uniqid(microtime() . mt_rand(), true) . $random_seed); $rnd_value .= sha1($rnd_value); $rnd_value .= sha1($rnd_value . $random_seed); $random_seed = md5($random_seed . $rnd_value); $this->Application->setDBCache('random_seed', $random_seed); } // Take the first 8 digits for our value $value = substr($rnd_value, 0, 8); // Strip the first eight, leaving the remainder for the next call to wp_rand(). $rnd_value = substr($rnd_value, 8); $value = abs(hexdec($value)); // Reduce the value to be within the min - max range // 4294967295 = 0xffffffff = max random number if ( $max != 0 ) { $value = $min + (($max - $min + 1) * ($value / (4294967295 + 1))); } return abs(intval($value)); } } \ No newline at end of file Index: branches/5.3.x/core/units/helpers/file_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/file_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/file_helper.php (revision 16111) @@ -1,460 +1,460 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FileHelper extends kHelper { /** * Puts existing item images (from sub-item) to virtual fields (in main item) * * @param kCatDBItem $object * @return void * @access public */ public function LoadItemFiles(&$object) { $max_file_count = $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); // file count equals to image count (temporary measure) $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogFiles WHERE ResourceId = '.$object->GetDBField('ResourceId').' ORDER BY FileId ASC LIMIT 0, '.(int)$max_file_count; $item_files = $this->Conn->Query($sql); $file_counter = 1; foreach ($item_files as $item_file) { $file_path = $item_file['FilePath']; $object->SetDBField('File'.$file_counter, $file_path); $object->SetOriginalField('File'.$file_counter, $file_path); $object->SetFieldOption('File'.$file_counter, 'original_field', $item_file['FileName']); $file_counter++; } } /** * Saves newly uploaded images to external image table * * @param kCatDBItem $object * @return void * @access public */ public function SaveItemFiles(&$object) { $table_name = $this->Application->getUnitConfig('#file')->getTableName(); $max_file_count = $object->getUnitConfig()->getFileCount(); // $max_file_count = $this->Application->ConfigValue($object->Prefix . '_MaxImageCount'); $this->CheckFolder(FULL_PATH . ITEM_FILES_PATH); $i = 0; while ($i < $max_file_count) { $field = 'File'.($i + 1); $field_options = $object->GetFieldOptions($field); $file_path = $object->GetDBField($field); if ($file_path) { if (isset($field_options['original_field'])) { $key_clause = 'FileName = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId'); if ($object->GetDBField('Delete'.$field)) { // if item was cloned, then new filename is in db (not in $image_src) $sql = 'SELECT FilePath FROM '.$table_name.' WHERE '.$key_clause; $file_path = $this->Conn->GetOne($sql); if (@unlink(FULL_PATH.ITEM_FILES_PATH.$file_path)) { $sql = 'DELETE FROM '.$table_name.' WHERE '.$key_clause; $this->Conn->Query($sql); } } else { // image record found -> update $fields_hash = Array ( 'FilePath' => $file_path, ); $this->Conn->doUpdate($fields_hash, $table_name, $key_clause); } } else { // record not found -> create $fields_hash = Array ( 'ResourceId' => $object->GetDBField('ResourceId'), 'FileName' => $field, 'Status' => STATUS_ACTIVE, 'FilePath' => $file_path, ); $this->Conn->doInsert($fields_hash, $table_name); $field_options['original_field'] = $field; $object->SetFieldOptions($field, $field_options); } } $i++; } } /** * Preserves cloned item images/files to be rewritten with original item images/files * * @param Array $field_values * @return void * @access public */ public function PreserveItemFiles(&$field_values) { foreach ($field_values as $field_name => $field_value) { if ( !is_array($field_value) ) { continue; } if ( isset($field_value['upload']) && ($field_value['error'] == UPLOAD_ERR_NO_FILE) ) { // this is upload field, but nothing was uploaded this time unset($field_values[$field_name]); } } } /** * Determines what image/file fields should be created (from post or just dummy fields for 1st upload) * * @param string $prefix * @param bool $is_image * @return void * @access public */ public function createItemFiles($prefix, $is_image = false) { $items_info = $this->Application->GetVar($prefix); if ($items_info) { list (, $fields_values) = each($items_info); $this->createUploadFields($prefix, $fields_values, $is_image); } else { $this->createUploadFields($prefix, Array(), $is_image); } } /** * Dynamically creates virtual fields for item for each image/file field in submit * * @param string $prefix * @param Array $fields_values * @param bool $is_image * @return void * @access public */ public function createUploadFields($prefix, $fields_values, $is_image = false) { $field_options = Array ('type' => 'string', 'max_len' => 240, 'default' => '',); if ( $is_image ) { $field_options['formatter'] = 'kPictureFormatter'; $field_options['include_path'] = 1; $field_options['allowed_types'] = Array ('image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif', 'image/bmp'); $field_prefix = 'Image'; } else { $field_options['formatter'] = 'kUploadFormatter'; $field_options['upload_dir'] = ITEM_FILES_PATH; $field_options['allowed_types'] = Array ('application/pdf', 'application/msexcel', 'application/msword', 'application/mspowerpoint'); $field_prefix = 'File'; } $image_count = 0; $config = $this->Application->getUnitConfig($prefix); foreach ($fields_values as $field_name => $field_value) { if ( preg_match('/^(' . $field_prefix . '[\d]+|Primary' . $field_prefix . ')$/', $field_name) ) { $config->addFields($field_options, $field_name); $config->addVirtualFields($field_options, $field_name); $this->_createCustomFields($prefix, $field_name, $config, $is_image); $image_count++; } } if ( !$image_count ) { // no images found in POST -> create default image fields $image_count = $this->Application->ConfigValue($prefix . '_MaxImageCount'); if ( $is_image ) { $created_count = 1; $image_names = Array ('Primary' . $field_prefix => ''); while ( $created_count < $image_count ) { $image_names[$field_prefix . $created_count] = ''; $created_count++; } } else { $created_count = 0; $image_names = Array (); while ( $created_count < $image_count ) { $image_names[$field_prefix . ($created_count + 1)] = ''; $created_count++; } } if ( $created_count ) { $this->createUploadFields($prefix, $image_names, $is_image); } return; } $config->setSetting($field_prefix . 'Count', $image_count); } /** * Adds ability to create more virtual fields associated with main image/file * * @param string $prefix * @param string $field_name * @param kUnitConfig $config * @param bool $is_image * @return void * @access protected */ protected function _createCustomFields($prefix, $field_name, kUnitConfig $config, $is_image = false) { $config->addVirtualFields(Array ('type' => 'int', 'default' => 0), 'Delete' . $field_name); if ( $is_image ) { $config->addVirtualFields(Array ('type' => 'string', 'default' => ''), $field_name . 'Alt'); } } /** * Downloads file to user * * @param string $filename * @return void * @access public */ public function DownloadFile($filename) { $this->Application->setContentType(kUtil::mimeContentType($filename), false); header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); header('Content-Length: ' . filesize($filename)); readfile($filename); flush(); } /** * Creates folder with given $path * * @param string $path * @return bool * @access public */ public function CheckFolder($path) { $result = true; if (!file_exists($path) || !is_dir($path)) { $parent_path = preg_replace('#(/|\\\)[^/\\\]+(/|\\\)?$#', '', rtrim($path , '/\\')); $result = $this->CheckFolder($parent_path); if ($result) { $result = mkdir($path); if ($result) { chmod($path, 0777); // don't commit any files from created folder if (file_exists(FULL_PATH . '/CVS')) { $cvsignore = fopen($path . '/.cvsignore', 'w'); fwrite($cvsignore, '*.*'); fclose($cvsignore); chmod($path . '/.cvsignore', 0777); } } else { trigger_error('Cannot create directory "<strong>' . $path . '</strong>"', E_USER_WARNING); return false; } } } return $result; } /** * Copies all files and directories from $source to $destination directory. Create destination directory, when missing. * * @param string $source * @param string $destination * @return bool * @access public */ public function copyFolderRecursive($source, $destination) { if ( substr($source, -1) == DIRECTORY_SEPARATOR ) { $source = substr($source, 0, -1); $destination .= DIRECTORY_SEPARATOR . basename($source); } $iterator = new DirectoryIterator($source); /* @var $file_info DirectoryIterator */ $result = $this->CheckFolder($destination); foreach ($iterator as $file_info) { if ( $file_info->isDot() ) { continue; } $file = $file_info->getFilename(); if ( $file_info->isDir() ) { $result = $this->copyFolderRecursive($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); } else { $result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); } if (!$result) { trigger_error('Cannot create file/directory "<strong>' . $destination . DIRECTORY_SEPARATOR . $file . '</strong>"', E_USER_WARNING); break; } } return $result; } /** * Copies all files from $source to $destination directory. Create destination directory, when missing. * * @param string $source * @param string $destination * @return bool * @access public */ public function copyFolder($source, $destination) { if ( substr($source, -1) == DIRECTORY_SEPARATOR ) { $source = substr($source, 0, -1); $destination .= DIRECTORY_SEPARATOR . basename($source); } $iterator = new DirectoryIterator($source); /* @var $file_info DirectoryIterator */ $result = $this->CheckFolder($destination); foreach ($iterator as $file_info) { if ( $file_info->isDot() || !$file_info->isFile() ) { continue; } $file = $file_info->getFilename(); $result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); if ( !$result ) { trigger_error('Cannot create file "<strong>' . $destination . DIRECTORY_SEPARATOR . $file . '</strong>"', E_USER_WARNING); break; } } return $result; } /** * Transforms given path to file into it's url, where each each component is encoded (excluding domain and protocol) * * @param string $path Path to file. * @param string|null $domain Alternative domain to use in url. * * @return string * @access public */ public function pathToUrl($path, $domain = null) { $path = str_replace(DIRECTORY_SEPARATOR, '/', preg_replace('/^' . preg_quote(FULL_PATH, '/') . '(.*)/', '\\1', $path, 1)); // TODO: why? $path = implode('/', array_map('rawurlencode', explode('/', $path))); return rtrim($this->Application->BaseURL($domain), '/') . $path; } /** * Transforms given url to path to it * * @param string $url Url. * @param string|null $domain Alternative domain to use in url. * * @return string */ public function urlToPath($url, $domain = null) { $base_url = rtrim($this->Application->BaseURL($domain), '/'); // escape replacement patterns, like "\<number>" $full_path = preg_replace('/(\\\[\d]+)/', '\\\\\1', FULL_PATH); $path = preg_replace('/^' . preg_quote($base_url, '/') . '(.*)/', $full_path . '\\1', $url, 1); - return str_replace('/', DIRECTORY_SEPARATOR, rawurldecode($path)); + return str_replace('/', DIRECTORY_SEPARATOR, kUtil::unescape($path, kUtil::ESCAPE_URL)); } /** * Ensures, that new file will not overwrite any of previously created files with same name * * @param string $path * @param string $name * @param Array $forbidden_names * @return string */ public function ensureUniqueFilename($path, $name, $forbidden_names = Array ()) { $parts = pathinfo($name); $ext = '.' . $parts['extension']; $filename = $parts['filename']; $path = rtrim($path, '/'); $original_checked = false; $new_name = $filename . $ext; if ( $parts['dirname'] != '.' ) { $path .= '/' . ltrim($parts['dirname'], '/'); } // make sure target folder always exists, especially for cases, // when storage engine folder is supplied as a part of $name $this->CheckFolder($path); while (file_exists($path . '/' . $new_name) || in_array($path . '/' . $new_name, $forbidden_names)) { if ( preg_match('/(.*)_([0-9]*)(' . preg_quote($ext, '/') . ')/', $new_name, $regs) ) { $new_name = $regs[1] . '_' . ((int)$regs[2] + 1) . $regs[3]; } elseif ( $original_checked ) { $new_name = $filename . '_1' . $ext; } $original_checked = true; } if ( $parts['dirname'] != '.' ) { $new_name = $parts['dirname'] . '/' . $new_name; } return $new_name; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/mod_rewrite_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/mod_rewrite_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/mod_rewrite_helper.php (revision 16111) @@ -1,351 +1,356 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class CategoryItemRewrite extends kHelper { /** * Builds/parses category item part of url * * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE. * @param string $prefix Prefix, that listener uses for system integration * @param Array $params Params, that are used for url building or created during url parsing. * @param Array $url_parts Url parts to parse (only for parsing). * @param bool $keep_events Keep event names in resulting url (only for building). * @return bool Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener. * @access public */ public function RewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false) { if ($rewrite_mode == REWRITE_MODE_BUILD) { return $this->_buildCategoryItemUrl($prefix, $params, $keep_events); } $module_prefix = $this->_parseCategoryItemUrl($url_parts, $params, $prefix); if ($module_prefix !== false) { $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $params['pass'][] = $module_prefix; $rewrite_processor->setModulePrefix($module_prefix); } return true; } /** * Build category item part of url * * @param string $prefix_special * @param Array $params * @param bool $keep_events * @return string * @access protected */ protected function _buildCategoryItemUrl($prefix_special, &$params, $keep_events) { static $default_per_page = Array (); $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $ret = ''; list ($prefix) = explode('.', $prefix_special); $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); if ($processed_params === false) { return ''; } + if ( isset($params[$prefix_special . '_event']) && $params[$prefix_special . '_event'] ) { + $params['events[' . $prefix_special . ']'] = $params[$prefix_special . '_event']; + unset($params[$prefix_special . '_event']); + } + if (!array_key_exists($prefix, $default_per_page)) { $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); } if ($processed_params[$prefix_special . '_id']) { $category_id = array_key_exists('m_cat_id', $params) ? $params['m_cat_id'] : $this->Application->GetVar('m_cat_id'); // if template is also item template of category, then remove template $template = array_key_exists('t', $params) ? $params['t'] : false; $item_template = $rewrite_processor->GetItemTemplate($category_id, $prefix); if ($template == $item_template || strtolower($template) == '__default__') { // given template is also default template for this category item or '__default__' given $params['pass_template'] = false; } // get item's filename if ($prefix == 'bb') { $ret .= 'bb_' . $processed_params[$prefix_special . '_id'] . '/'; } else { $filename = $this->getFilename($prefix, $processed_params[$prefix_special . '_id'], $category_id); if ($filename !== false) { $ret .= $filename . '/'; } } } else { if ($processed_params[$prefix_special . '_Page'] == 1) { // when printing category items and we are on the 1st page -> there is no information about // category item prefix and $params['pass_category'] will not be added automatically $params['pass_category'] = true; } elseif ($processed_params[$prefix_special . '_Page'] > 1) { // $ret .= $processed_params[$prefix_special . '_Page'] . '/'; $params['page'] = $processed_params[$prefix_special . '_Page']; } $per_page = $processed_params[$prefix_special . '_PerPage']; if ($per_page && ($per_page != $default_per_page[$prefix])) { $params['per_page'] = $processed_params[$prefix_special . '_PerPage']; } } return mb_strtolower( rtrim($ret, '/') ); } /** * Builds/parses review part of url * * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE. * @param string $prefix_special Prefix, that listener uses for system integration * @param Array $params Params, that are used for url building or created during url parsing. * @param Array $url_parts Url parts to parse (only for parsing). * @param bool $keep_events Keep event names in resulting url (only for building). * @return bool Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener. * @access public */ public function ReviewRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix_special, &$params, &$url_parts, $keep_events = false) { static $default_per_page = Array (); if ( $rewrite_mode != REWRITE_MODE_BUILD ) { // don't parse anything return true; } $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $ret = ''; list ($prefix) = explode('.', $prefix_special); $processed_params = $rewrite_processor->getProcessedParams($prefix_special, $params, $keep_events); if ($processed_params === false) { return ''; } if (!array_key_exists($prefix, $default_per_page)) { $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix); } if ($processed_params[$prefix_special . '_id']) { return false; } else { if ($processed_params[$prefix_special . '_Page'] == 1) { // when printing category items and we are on the 1st page -> there is no information about // category item prefix and $params['pass_category'] will not be added automatically $params['pass_category'] = true; } elseif ($processed_params[$prefix_special . '_Page'] > 1) { // $ret .= $processed_params[$prefix_special . '_Page'] . '/'; $params['page'] = $processed_params[$prefix_special . '_Page']; } $per_page = $processed_params[$prefix_special . '_PerPage']; if ($per_page && ($per_page != $default_per_page[$prefix])) { $params['per_page'] = $processed_params[$prefix_special . '_PerPage']; } } return mb_strtolower( rtrim($ret, '/') ); } /** * Returns item's filename that corresponds id passed. If possible, then get it from cache * * @param string $prefix * @param int $id * @param int $category_id * @return string * @access protected */ protected function getFilename($prefix, $id, $category_id = null) { if ($prefix == 'c') { throw new Exception('Method "<strong>' . __FUNCTION__ . '</strong>" no longer work with "<strong>c</strong>" prefix. Please use "<strong>getCategoryCache</strong>" method instead'); } $category_id = isset($category_id) ? $category_id : $this->Application->GetVar('m_cat_id'); $cache_key = 'filenames[%' . $this->Application->incrementCacheSerial($prefix, $id, false) . '%]:' . (int)$category_id; $filename = $this->Application->getCache($cache_key); if ($filename === false) { $this->Conn->nextQueryCachable = true; $config = $this->Application->getUnitConfig($prefix); $sql = 'SELECT ResourceId FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' = ' . $this->Conn->qstr($id); $resource_id = $this->Conn->GetOne($sql); $this->Conn->nextQueryCachable = true; $sql = 'SELECT Filename FROM ' . TABLE_PREFIX . 'CategoryItems WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')'; $filename = $this->Conn->GetOne($sql); if ($filename !== false) { $this->Application->setCache($cache_key, $filename); } } return $filename; } /** * Sets template and id, corresponding to category item given in url * * @param Array $url_parts * @param Array $vars * @param string $prefix Prefix, that listener uses for system integration * * @return boolean|string * @access protected */ protected function _parseCategoryItemUrl(&$url_parts, &$vars, $prefix) { if ( !$url_parts ) { return false; } $item_filename = end($url_parts); if ( is_numeric($item_filename) ) { // this page, don't process here return false; } $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ if ( $prefix == 'bb' && preg_match('/^bb_([\d]+)/', $item_filename, $regs) ) { // process topics separately, because they don't use item filenames array_pop($url_parts); $rewrite_processor->partParsed($item_filename, 'rtl'); return $this->_parseTopicUrl($regs[1], $vars); } $cat_item = $this->findCategoryItem((int)$vars['m_cat_id'], $item_filename, $prefix); if ( $cat_item !== false ) { // item found $module_prefix = $cat_item['ItemPrefix']; $item_template = $rewrite_processor->GetItemTemplate($cat_item, $module_prefix, $vars['m_theme']); // converting ResourceId to corresponding Item id $module_config = $this->Application->getUnitConfig($module_prefix); $sql = 'SELECT ' . $module_config->getIDField() . ' FROM ' . $module_config->getTableName() . ' WHERE ResourceId = ' . $cat_item['ItemResourceId']; $item_id = $this->Conn->GetOne($sql); if ( $item_id ) { array_pop($url_parts); $rewrite_processor->partParsed($item_filename, 'rtl'); if ( $item_template ) { // when template is found in category -> set it $vars['t'] = $item_template; } // we have category item id $vars[$module_prefix . '_id'] = $item_id; return $module_prefix; } } return false; } /** * Locating the item in CategoryItems by filename to detect its ItemPrefix and its category ParentPath. * * @param integer $category_id Category. * @param string $filename Filename. * @param string $prefix Prefix, that listener uses for system integration * * @return string */ protected function findCategoryItem($category_id, $filename, $prefix) { static $cache = array(); $cache_key = $category_id . ':' . $filename; if ( !isset($cache[$cache_key]) ) { $sql = 'SELECT ci.ItemResourceId, ci.ItemPrefix, c.ParentPath, ci.CategoryId FROM ' . TABLE_PREFIX . 'CategoryItems AS ci LEFT JOIN ' . TABLE_PREFIX . 'Categories AS c ON c.CategoryId = ci.CategoryId WHERE (ci.CategoryId = ' . $category_id . ') AND (ci.Filename = ' . $this->Conn->qstr($filename) . ')'; $cache[$cache_key] = $this->Conn->GetRow($sql); } $category_item = $cache[$cache_key]; return $category_item && $category_item['ItemPrefix'] == $prefix ? $category_item : false; } /** * Set's template and topic id corresponding to topic given in url * * @param int $topic_id * @param Array $vars * @return string * @access protected */ protected function _parseTopicUrl($topic_id, &$vars) { $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ $sql = 'SELECT c.ParentPath, c.CategoryId FROM ' . TABLE_PREFIX . 'Categories AS c WHERE c.CategoryId = ' . (int)$vars['m_cat_id']; $cat_item = $this->Conn->GetRow($sql); $item_template = $rewrite_processor->GetItemTemplate($cat_item, 'bb', $vars['m_theme']); if ($item_template) { $vars['t'] = $item_template; } $vars['bb_id'] = $topic_id; return 'bb'; } } Index: branches/5.3.x/core/units/helpers/deployment_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/deployment_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/deployment_helper.php (revision 16111) @@ -1,753 +1,761 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class DeploymentHelper extends kHelper { /** * How many symbols from sql should be shown. */ const SQL_TRIM_LENGTH = 120; const STAGE_DB_MIGRATE = 'db-migrate'; const STAGE_CACHE_RESET = 'cache-reset'; /** * Name of module, that is processed right now. * * @var string */ private $moduleName = ''; /** * List of sqls, associated with each revision (from project_upgrades.sql file). * * @var array */ private $revisionSqls = array(); /** * List of revision titles as user typed them (from project_upgrades.sql file). * * @var array */ private $revisionTitles = array(); /** * Revision dependencies. * * @var array */ private $revisionDependencies = array(); /** * Numbers of revisions, that were already applied. * * @var array */ private $appliedRevisions = array(); /** * Don't change database, but only check syntax of project_upgrades.sql file and mark all revisions discovered as applied. * * @var boolean */ private $dryRun = false; /** * Remembers script invocation method. * * @var boolean */ public $isCommandLine = false; /** * IP Address of script invoker. * * @var string */ public $ip = ''; /** * Deployment stages to run. * * @var array */ public $stages = array( self::STAGE_DB_MIGRATE, self::STAGE_CACHE_RESET, ); /** * Event, that triggered deployment. * * @var kEvent */ private $_event; /** * Field values for log record. * * @var string */ private $logData = array(); /** * Creates class instance. */ public function __construct() { parent::__construct(); kUtil::setResourceLimit(); $this->_event = new kEvent('adm:OnDummy'); $this->isCommandLine = isset($GLOBALS['argv']) && count($GLOBALS['argv']); if ( !$this->isCommandLine ) { $this->ip = $this->Application->getClientIp(); } else { if ( isset($GLOBALS['argv'][3]) ) { $this->ip = $GLOBALS['argv'][3]; } if ( isset($GLOBALS['argv'][4]) ) { $new_stages = explode(',', $GLOBALS['argv'][4]); $unknown_stages = array_diff($new_stages, $this->stages); if ( $unknown_stages ) { throw new InvalidArgumentException('Unknown deployment stages: ' . implode(', ', $unknown_stages)); } $this->stages = $new_stages; } } } /** * Sets event, associated with deployment. * * @param kEvent $event Event. * * @return void */ public function setEvent(kEvent $event) { $this->_event = $event; } /** * Adds message to script execution log. * * @param string $message Message. * @param boolean $new_line Jump to next line. * * @return string */ private function toLog($message, $new_line = true) { if ( $new_line ) { $message .= PHP_EOL; } $this->logData['Output'] .= $message; return $message; } /** * Loads already applied revisions list of current module. * * @return self */ private function loadAppliedRevisions() { $sql = 'SELECT RevisionNumber FROM ' . TABLE_PREFIX . 'ModuleDeploymentLog WHERE Module = ' . $this->Conn->qstr($this->moduleName); $this->appliedRevisions = array_flip($this->Conn->GetCol($sql)); return $this; } /** * Deploys changes from all installed modules. * * @param boolean $dry_run Use dry run mode? * * @return boolean */ public function deployAll($dry_run = false) { if ( !$this->isCommandLine ) { echo '<pre style="font-size: 10pt; color: #BBB; background-color: black; border: 2px solid darkgreen; padding: 8px;">' . PHP_EOL; } $ret = true; $this->dryRun = $dry_run; if ( in_array(self::STAGE_DB_MIGRATE, $this->stages) ) { foreach ( $this->Application->ModuleInfo as $module_name => $module_info ) { $this->moduleName = $module_name; if ( !file_exists($this->getModuleFile('project_upgrades.sql')) ) { continue; } $ret = $ret && $this->deploy($module_name); } } if ( in_array(self::STAGE_CACHE_RESET, $this->stages) ) { if ( $ret && !$this->dryRun ) { $this->resetCaches(); $this->refreshThemes(); } } if ( !$this->isCommandLine ) { echo kUtil::escape($this->_runShellScript()); echo '</pre>' . PHP_EOL; } return $ret; } /** * Runs user-specific shell script when deployment happens from Web. * * @return string */ protected function _runShellScript() { if ( !$this->Application->isDebugMode(false) ) { return ''; } $wrapper_script = '/usr/local/bin/guest2host_server.sh'; $script_name = FULL_PATH . '/tools/' . ($this->dryRun ? 'synchronize.sh' : 'deploy.sh'); if ( file_exists($wrapper_script) && file_exists($script_name) ) { $script_name = preg_replace('/^.*\/web/', constant('DBG_LOCAL_BASE_PATH'), $script_name); return shell_exec($wrapper_script . ' ' . $script_name . ' 2>&1'); } return ''; } /** * Deploys pending changes to a site. * * @param string $module_name Module name. * * @return boolean */ private function deploy($module_name) { echo $this->colorText('Deploying Module "' . $module_name . '":', 'cyan', true) . PHP_EOL; if ( !$this->upgradeDatabase() ) { return false; } - if ( $this->dryRun ) { - $this->exportLanguagePack(); + try { + if ( $this->dryRun ) { + $this->exportLanguagePack(); + } + else { + $this->importLanguagePack(); + } } - else { - $this->importLanguagePack(); + catch ( Exception $e ) { + echo $this->colorText('Failed with Module "' . $module_name . '".', 'red', true) . PHP_EOL . PHP_EOL; + + return false; } echo $this->colorText('Done with Module "' . $module_name . '".', 'green', true) . PHP_EOL . PHP_EOL; return true; } /** * Import latest language pack (without overwrite). * * @return self */ private function importLanguagePack() { $language_import_helper = $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ $this->out('Importing LanguagePack ... '); $filename = $this->getModuleFile('english.lang'); $language_import_helper->performImport($filename, '|0|1|2|', $this->moduleName); $this->displayStatus('OK'); return $this; } /** * Exports latest language pack. * * @return self */ private function exportLanguagePack() { static $languages = null; if ( !isset($languages) ) { $sql = 'SELECT LanguageId FROM ' . $this->Application->getUnitConfig('lang')->getTableName() . ' WHERE Enabled = 1'; $languages = $this->Conn->GetCol($sql); } $language_import_helper = $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ $language_import_helper->performExport(EXPORT_PATH . '/' . $this->moduleName . '.lang', '|0|1|2|', $languages, '|' . $this->moduleName . '|'); return $this; } /** * Resets unit and section cache. * * @return self */ private function resetCaches() { // 2. reset unit config cache (so new classes get auto-registered) $this->out('Resetting Configs Files Cache and Parsed System Data ... '); $this->_event->CallSubEvent('OnResetConfigsCache'); $this->displayStatus('OK'); // 3. reset sections cache $this->out('Resetting Admin Console Sections ... '); $this->_event->CallSubEvent('OnResetSections'); $this->displayStatus('OK'); // 4. reset mod-rewrite cache $this->out('Resetting ModRewrite Cache ... '); $this->_event->CallSubEvent('OnResetModRwCache'); $this->displayStatus('OK'); return $this; } /** * Rebuild theme files. * * @return self */ private function refreshThemes() { $this->out('Refreshing Theme Files ... '); $this->_event->CallSubEvent('OnRebuildThemes'); $this->displayStatus('OK'); return $this; } /** * Runs database upgrade script. * * @return boolean */ private function upgradeDatabase() { $this->loadAppliedRevisions(); $this->Conn->setErrorHandler(array(&$this, 'handleSqlError')); $this->out('Verifying Database Revisions ... '); if ( !$this->collectDatabaseRevisions() || !$this->checkRevisionDependencies() ) { return false; } $this->displayStatus('OK'); return $this->applyRevisions(); } /** * Collects database revisions from "project_upgrades.sql" file. * * @return boolean */ private function collectDatabaseRevisions() { $filename = $this->getModuleFile('project_upgrades.sql'); if ( !file_exists($filename) ) { return true; } $sqls = file_get_contents($filename); preg_match_all("/# r([\d]+)([^\:]*):(.*?)(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); if ( !$matches ) { $this->displayStatus('FAILED' . PHP_EOL . 'No Database Revisions Found'); return false; } foreach ( $matches as $index => $match ) { $revision = $match[1][0]; if ( $this->revisionApplied($revision) ) { // skip applied revisions continue; } if ( isset($this->revisionSqls[$revision]) ) { // duplicate revision among non-applied ones $this->displayStatus('FAILED' . PHP_EOL . 'Duplicate revision #' . $revision . ' found'); return false; } // get revision sqls $start_pos = $match[0][1] + strlen($match[0][0]); $end_pos = isset($matches[$index + 1]) ? $matches[$index + 1][0][1] : strlen($sqls); $revision_sqls = substr($sqls, $start_pos, $end_pos - $start_pos); if ( !$revision_sqls ) { // revision without sqls continue; } $this->revisionTitles[$revision] = trim($match[3][0]); $this->revisionSqls[$revision] = $revision_sqls; $revision_dependencies = $this->parseRevisionDependencies($match[2][0]); if ( $revision_dependencies ) { $this->revisionDependencies[$revision] = $revision_dependencies; } } ksort($this->revisionSqls); ksort($this->revisionDependencies); return true; } /** * Checks that all dependent revisions are either present now OR were applied before. * * @return boolean */ private function checkRevisionDependencies() { foreach ( $this->revisionDependencies as $revision => $revision_dependencies ) { foreach ( $revision_dependencies as $revision_dependency ) { if ( $this->revisionApplied($revision_dependency) ) { // revision dependent upon already applied -> dependency fulfilled continue; } if ( $revision_dependency >= $revision ) { $this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' has incorrect dependency to revision #' . $revision_dependency . '. Only dependencies to older revisions are allowed!'); return false; } if ( !isset($this->revisionSqls[$revision_dependency]) ) { $this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' depends on missing revision #' . $revision_dependency . '!'); return false; } } } return true; } /** * Runs all pending sqls. * * @return boolean */ private function applyRevisions() { if ( !$this->revisionSqls ) { return true; } if ( $this->dryRun ) { foreach ( $this->revisionSqls as $revision => $sqls ) { $this->initLog($revision, ModuleDeploymentLog::MODE_MANUAL); echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... '; echo $this->toLog($this->colorText('SKIPPING', 'purple')); $this->saveLog(ModuleDeploymentLog::STATUS_SKIPPED); } return true; } $this->out('Upgrading Database ... ', true); foreach ( $this->revisionSqls as $revision => $sqls ) { echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... '; $sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings $no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", "# \\1;\n", $sqls); // add ";" to each comment end to ensure correct split $sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); $this->initLog($revision); - foreach ( $sqls as $sql ) { - if ( substr($sql, 0, 1) == '#' ) { - // output comment as is - echo $this->toLog($this->colorText($sql, 'purple')); - - continue; - } - elseif ( $sql ) { - echo $this->toLog($this->shortenQuery($sql), false); - $this->Conn->Query($sql); - - if ( $this->Conn->hasError() ) { - // consider revisions with errors applied - $this->saveLog(ModuleDeploymentLog::STATUS_ERROR); + try { + foreach ( $sqls as $sql ) { + if ( substr($sql, 0, 1) == '#' ) { + // output comment as is + echo $this->toLog($this->colorText($sql, 'purple')); - return false; + continue; } - else { + elseif ( $sql ) { + echo $this->toLog($this->shortenQuery($sql), false); + $this->Conn->Query($sql); + $this->displayStatus('OK (' . $this->Conn->getAffectedRows() . ')', true, true); } } } + catch ( Exception $e ) { + // consider revisions with errors applied + $this->saveLog(ModuleDeploymentLog::STATUS_ERROR); + + return false; + } $this->saveLog(ModuleDeploymentLog::STATUS_SUCCESS); } echo PHP_EOL; return true; } /** * Returns shortened version of SQL query. * * @param string $sql SQL query. * * @return string */ protected function shortenQuery($sql) { $escaped_sql = $this->isCommandLine ? $sql : kUtil::escape($sql); $single_line_sql = preg_replace('/(\n|\t| )+/is', ' ', $escaped_sql); return mb_substr(trim($single_line_sql), 0, self::SQL_TRIM_LENGTH) . ' ... '; } /** * Initializes log record for a revision. * * @param integer $revision Revision. * @param integer $mode Mode. * * @return self */ protected function initLog($revision, $mode = ModuleDeploymentLog::MODE_AUTOMATIC) { $this->logData = array( 'Module' => $this->moduleName, 'RevisionNumber' => $revision, 'RevisionTitle' => $this->revisionTitles[$revision], 'IPAddress' => $this->ip, 'Output' => '', 'Mode' => $mode, 'Status' => ModuleDeploymentLog::STATUS_SUCCESS, ); return $this; } /** * Creates log record. * * @param integer $status Status. * * @return self */ private function saveLog($status) { $this->logData['Status'] = $status; $log = $this->Application->recallObject('module-deployment-log', null, array('skip_autoload' => true)); /* @var $log kDBItem */ $log->Clear(); $log->SetFieldsFromHash($this->logData); $log->Create(); return $this; } /** * Error handler for sql errors. * * @param int $code Error code. * @param string $msg Error message. * @param string $sql SQL query, that raised an error. * - * @return boolean + * @return void + * @throws Exception When SQL error happens. */ public function handleSqlError($code, $msg, $sql) { $this->displayStatus('FAILED', true, true); $error_msg = 'SQL Error #' . $code . ': ' . $msg; $this->logData['ErrorMessage'] = $error_msg; $this->displayStatus($error_msg); $this->out('Please execute rest of SQLs in this Revision by hand and run deployment script again.', true); - return true; + throw new Exception($msg, $code); } /** * Checks if given revision was already applied. * * @param int $revision Revision. * * @return boolean */ private function revisionApplied($revision) { return isset($this->appliedRevisions[$revision]); } /** * Returns path to given file in current module install folder. * * @param string $filename Filename. * * @return string */ private function getModuleFile($filename) { $module_folder = $this->Application->findModule('Name', $this->moduleName, 'Path'); return FULL_PATH . DIRECTORY_SEPARATOR . $module_folder . 'install/' . $filename; } /** * Extracts revisions from string in format "(1,3,5464,23342,3243)". * * @param string $string Comma-separated revision list. * * @return array */ private function parseRevisionDependencies($string) { if ( !$string ) { return array(); } $string = explode(',', substr($string, 1, -1)); return array_map('trim', $string); } /** * Applies requested color and bold attributes to given text string. * * @param string $text Text. * @param string $color Color. * @param boolean $bold Bold flag. * * @return string */ private function colorText($text, $color, $bold = false) { if ( $this->isCommandLine ) { $color_map = array( 'black' => 30, // dark gray (in bold) 'blue' => 34, // light blue (in bold) 'green' => 32, // light green (in bold) 'cyan' => 36, // light cyan (in bold) 'red' => 31, // light red (in bold) 'purple' => 35, // light purple (in bold) 'brown' => 33, // yellow (in bold) 'gray' => 37, // white (in bold) ); return "\033[" . ($bold ? 1 : 0) . ";" . $color_map[$color] . "m" . $text . "\033[0m"; } $html_color_map = array( 'black' => array('normal' => '#000000', 'bold' => '#666666'), 'blue' => array('normal' => '#00009C', 'bold' => '#3C3CFF'), 'green' => array('normal' => '#009000', 'bold' => '#00FF00'), 'cyan' => array('normal' => '#009C9C', 'bold' => '#00FFFF'), 'red' => array('normal' => '#9C0000', 'bold' => '#FF0000'), 'purple' => array('normal' => '#900090', 'bold' => '#F99CF9'), 'brown' => array('normal' => '#C9C909', 'bold' => '#FFFF00'), 'gray' => array('normal' => '#909090', 'bold' => '#FFFFFF'), ); $html_color = $html_color_map[$color][$bold ? 'bold' : 'normal']; return '<span style="color: ' . $html_color . '">' . kUtil::escape($text, kUtil::ESCAPE_HTML) . '</span>'; } /** * Displays last command execution status. * * @param string $status_text Status text. * @param boolean $new_line Jump to next line. * @param boolean $to_log Also write to log. * * @return self */ private function displayStatus($status_text, $new_line = true, $to_log = false) { $color = substr($status_text, 0, 2) == 'OK' ? 'green' : 'red'; $ret = $this->colorText($status_text, $color, false); if ( $to_log ) { echo $this->toLog($ret, $new_line); } else { echo $ret . ($new_line ? PHP_EOL : ''); } return $this; } /** * Outputs a text and escapes it if necessary. * * @param string $text Text. * @param boolean $new_line Jump to next line. * * @return self */ private function out($text, $new_line = false) { if ( !$this->isCommandLine ) { $text = kUtil::escape($text); } echo $text . ($new_line ? PHP_EOL : ''); return $this; } } Index: branches/5.3.x/core/units/helpers/image_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/image_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/image_helper.php (revision 16111) @@ -1,753 +1,753 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ImageHelper extends kHelper { /** * File helper reference * * @var FileHelper */ var $fileHelper = null; public function __construct() { parent::__construct(); ini_set('gd.jpeg_ignore_warning', 1); $this->fileHelper = $this->Application->recallObject('FileHelper'); } /** * Parses format string into array * * @param string $format sample format: "resize:300x500;wm:inc/wm.png|c|-20" * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20) */ function parseFormat($format) { $res = Array (); $format_parts = explode(';', $format); foreach ($format_parts as $format_part) { if (preg_match('/^resize:(\d*)x(\d*)$/', $format_part, $regs)) { $res['max_width'] = $regs[1]; $res['max_height'] = $regs[2]; } elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; $res['h_margin'] = strtolower($regs[2]); $res['v_margin'] = strtolower($regs[3]); } elseif (preg_match('/^crop:([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['crop_x'] = strtolower($regs[1]); $res['crop_y'] = strtolower($regs[2]); } elseif ($format_part == 'img_size' || $format_part == 'img_sizes') { $res['image_size'] = true; } elseif (preg_match('/^fill:(.*)$/', $format_part, $regs)) { $res['fill'] = $regs[1]; } elseif (preg_match('/^default:(.*)$/', $format_part, $regs)) { $res['default'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; } elseif ( preg_match('/^filter:(.*)$/', $format_part, $regs) ) { $format_part_params = explode('|', $regs[1]); $res['filter_type'] = array_shift($format_part_params); $res['filter_params'] = $format_part_params; } } return $res; } /** * Resized given image to required dimensions & saves resized image to "resized" subfolder in source image folder * * @param string $src_image full path to image (on server) * @param mixed $max_width maximal allowed resized image width or false if no limit * @param mixed $max_height maximal allowed resized image height or false if no limit * * @return string direct url to resized image - * @throws \RuntimeException When image doesn't exist. + * @throws RuntimeException When image doesn't exist. */ function ResizeImage($src_image, $max_width, $max_height = false) { $image_size = false; if (is_numeric($max_width)) { $params['max_width'] = $max_width; $params['max_height'] = $max_height; } else { $params = $this->parseFormat($max_width); if (array_key_exists('image_size', $params)) { // image_size param shouldn't affect resized file name (crc part) $image_size = $params['image_size']; unset($params['image_size']); } } if ((!$src_image || !file_exists($src_image)) && array_key_exists('default', $params) && !(defined('DBG_IMAGE_RECOVERY') && DBG_IMAGE_RECOVERY)) { $src_image = $params['default']; } if ( !strlen($src_image) || !file_exists($src_image) ) { - throw new \RuntimeException(sprintf('Image "%s" doesn\'t exist', $src_image)); + throw new RuntimeException(sprintf('Image "%s" doesn\'t exist', $src_image)); } if ($params['max_width'] > 0 || $params['max_height'] > 0) { list ($params['target_width'], $params['target_height'], $needs_resize) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); if (!is_numeric($params['max_width'])) { $params['max_width'] = $params['target_width']; } if (!is_numeric($params['max_height'])) { $params['max_height'] = $params['target_height']; } $src_path = dirname($src_image); $transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename', 'filter_type'); if ($needs_resize || array_intersect(array_keys($params), $transform_keys)) { // resize required OR watermarking required -> change resulting image name ! ksort($params); $src_path_escaped = preg_replace('/(\\\[\d]+)/', '\\\\\1', $src_path); // escape replacement patterns, like "\<number>" $dst_image = preg_replace('/^'.preg_quote($src_path, '/').'(.*)\.(.*)$/', $src_path_escaped . DIRECTORY_SEPARATOR . 'resized\\1_' . crc32(serialize($params)) . '.\\2', $src_image); $this->fileHelper->CheckFolder( dirname($dst_image) ); if (!file_exists($dst_image) || filemtime($src_image) > filemtime($dst_image)) { // resized image not available OR should be recreated due source image change $params['dst_image'] = $dst_image; $image_resized = $this->ScaleImage($src_image, $params); if (!$image_resized) { // resize failed, because of server error $dst_image = $src_image; } } // resize/watermarking ok $src_image = $dst_image; } } if ($image_size) { // return only image size (resized or not) $image_info = $this->getImageInfo($src_image); return $image_info ? $image_info[3] : ''; } return $this->fileHelper->pathToUrl($src_image); } /** * Proportionally resizes given image to destination dimensions * * @param string $src_image full path to source image (already existing) * @param Array $params * @return bool */ function ScaleImage($src_image, $params) { $image_info = $this->getImageInfo($src_image); if (!$image_info) { return false; } /*list ($params['max_width'], $params['max_height'], $resized) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); if (!$resized) { // image dimensions are smaller or equals to required dimensions return false; }*/ if (!$this->Application->ConfigValue('ForceImageMagickResize') && function_exists('imagecreatefromjpeg')) { // try to resize using GD $resize_map = Array ( 'image/jpeg' => 'imagecreatefromjpeg:imagejpeg:jpg', 'image/gif' => 'imagecreatefromgif:imagegif:gif', 'image/png' => 'imagecreatefrompng:imagepng:png', 'image/bmp' => 'imagecreatefrombmp:imagejpeg:bmp', 'image/x-ms-bmp' => 'imagecreatefrombmp:imagejpeg:bmp', ); $mime_type = $image_info['mime']; if (!isset($resize_map[$mime_type])) { return false; } list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]); // when source image has large dimensions (over 1MB filesize), then 16M is not enough kUtil::setResourceLimit(); $src_image_rs = @$read_function($src_image); if ($src_image_rs) { $dst_image_rs = imagecreatetruecolor($params['target_width'], $params['target_height']); // resize target size $preserve_transparency = ($file_extension == 'gif') || ($file_extension == 'png'); if ($preserve_transparency) { // preserve transparency of PNG and GIF images $dst_image_rs = $this->_preserveTransparency($src_image_rs, $dst_image_rs, $image_info[2]); } // 1. resize imagecopyresampled($dst_image_rs, $src_image_rs, 0, 0, 0, 0, $params['target_width'], $params['target_height'], $image_info[0], $image_info[1]); $watermark_size = 'target'; if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) { // 2.1. crop image to given size $dst_image_rs =& $this->_cropImage($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false); $watermark_size = 'max'; } elseif (array_key_exists('fill', $params)) { // 2.2. fill image margins from resize with given color $dst_image_rs =& $this->_applyFill($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false); $watermark_size = 'max'; } // 3. apply watermark $dst_image_rs =& $this->_applyWatermark($dst_image_rs, $params[$watermark_size . '_width'], $params[$watermark_size . '_height'], $params); if ($write_function == 'imagegif') { return @$write_function($dst_image_rs, $params['dst_image']); } // 4. apply filter $this->applyFilter($dst_image_rs, $params); return @$write_function($dst_image_rs, $params['dst_image'], $write_function == 'imagepng' ? 0 : 100); } } else { // try to resize using ImageMagick // TODO: implement crop and watermarking using imagemagick exec('/usr/bin/convert '.$src_image.' -resize '.$params['target_width'].'x'.$params['target_height'].' '.$params['dst_image'], $shell_output, $exec_status); return $exec_status == 0; } return false; } /** * Preserve transparency for GIF and PNG images * * @param resource $src_image_rs * @param resource $dst_image_rs * @param int $image_type * @return resource */ function _preserveTransparency($src_image_rs, $dst_image_rs, $image_type) { $transparent_index = imagecolortransparent($src_image_rs); // if we have a specific transparent color if ( $transparent_index >= 0 && $transparent_index < imagecolorstotal($src_image_rs) ) { // get the original image's transparent color's RGB values $transparent_color = imagecolorsforindex($src_image_rs, $transparent_index); // allocate the same color in the new image resource $transparent_index = imagecolorallocate($dst_image_rs, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); // completely fill the background of the new image with allocated color imagefill($dst_image_rs, 0, 0, $transparent_index); // set the background color for new image to transparent imagecolortransparent($dst_image_rs, $transparent_index); return $dst_image_rs; } // always make a transparent background color for PNGs that don't have one allocated already if ( $image_type == IMAGETYPE_PNG ) { // turn off transparency blending (temporarily) imagealphablending($dst_image_rs, false); // create a new transparent color for image $transparent_color = imagecolorallocatealpha($dst_image_rs, 0, 0, 0, 127); // completely fill the background of the new image with allocated color imagefill($dst_image_rs, 0, 0, $transparent_color); // restore transparency blending imagesavealpha($dst_image_rs, true); } return $dst_image_rs; } /** * Fills margins (if any) of resized are with given color * * @param resource $src_image_rs resized image resource * @param Array $params crop parameters * @param int|bool $image_type * @return resource */ function &_applyFill(&$src_image_rs, $params, $image_type = false) { $x_position = round(($params['max_width'] - $params['target_width']) / 2); // center $y_position = round(($params['max_height'] - $params['target_height']) / 2); // center // crop resized image $fill_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']); if ($image_type !== false) { $fill_image_rs = $this->_preserveTransparency($src_image_rs, $fill_image_rs, $image_type); } $fill = $params['fill']; if (substr($fill, 0, 1) == '#') { // hexdecimal color $color = imagecolorallocate($fill_image_rs, hexdec( substr($fill, 1, 2) ), hexdec( substr($fill, 3, 2) ), hexdec( substr($fill, 5, 2) )); } else { // for now we don't support color names, but we will in future return $src_image_rs; } imagefill($fill_image_rs, 0, 0, $color); imagecopy($fill_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']); return $fill_image_rs; } /** * Crop given image resource using given params and return resulting image resource * * @param resource $src_image_rs resized image resource * @param Array $params crop parameters * @param int|bool $image_type * @return resource */ function &_cropImage(&$src_image_rs, $params, $image_type = false) { if ($params['crop_x'] == 'c') { $x_position = round(($params['max_width'] - $params['target_width']) / 2); // center } elseif ($params['crop_x'] >= 0) { $x_position = $params['crop_x']; // margin from left } else { $x_position = $params['target_width'] - ($params['max_width'] - $params['crop_x']); // margin from right } if ($params['crop_y'] == 'c') { $y_position = round(($params['max_height'] - $params['target_height']) / 2); // center } elseif ($params['crop_y'] >= 0) { $y_position = $params['crop_y']; // margin from top } else { $y_position = $params['target_height'] - ($params['max_height'] - $params['crop_y']); // margin from bottom } // crop resized image $crop_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']); if ($image_type !== false) { $crop_image_rs = $this->_preserveTransparency($src_image_rs, $crop_image_rs, $image_type); } if (array_key_exists('fill', $params)) { // fill image margins from resize with given color $crop_image_rs =& $this->_applyFill($crop_image_rs, $params, $image_type); } imagecopy($crop_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']); return $crop_image_rs; } /** * Apply watermark (transparent PNG image) to given resized image resource * * @param resource $src_image_rs * @param int $max_width * @param int $max_height * @param Array $params * @return resource */ function &_applyWatermark(&$src_image_rs, $max_width, $max_height, $params) { $watermark_file = array_key_exists('wm_filename', $params) ? $params['wm_filename'] : false; if (!$watermark_file || !file_exists($watermark_file)) { // no watermark required, or provided watermark image is missing return $src_image_rs; } $watermark_img_rs = imagecreatefrompng($watermark_file); list ($watermark_width, $watermark_height) = $this->getImageInfo($watermark_file); imagealphablending($src_image_rs, true); if ($params['h_margin'] == 'c') { $x_position = round($max_width / 2 - $watermark_width / 2); // center } elseif ($params['h_margin'] >= 0) { $x_position = $params['h_margin']; // margin from left } else { $x_position = $max_width - ($watermark_width - $params['h_margin']); // margin from right } if ($params['v_margin'] == 'c') { $y_position = round($max_height / 2 - $watermark_height / 2); // center } elseif ($params['v_margin'] >= 0) { $y_position = $params['v_margin']; // margin from top } else { $y_position = $max_height - ($watermark_height - $params['v_margin']); // margin from bottom } imagecopy($src_image_rs, $watermark_img_rs, $x_position, $y_position, 0, 0, $watermark_width, $watermark_height); return $src_image_rs; } /** * Applies filter to an image. * * @param resource $src_image_rs Source image. * @param array $params Parameters. * * @return boolean * @access protected * @throws InvalidArgumentException When unknown filter type given. * @link http://php.net/manual/en/function.imagefilter.php */ protected function applyFilter(&$src_image_rs, array $params) { if ( !array_key_exists('filter_type', $params) ) { return true; } $filter_type = strtoupper($params['filter_type']); $filter_params = (array)$params['filter_params']; if ( !defined('IMG_FILTER_' . $filter_type) ) { throw new InvalidArgumentException(sprintf('Unknown filter type "%s"', $filter_type)); } array_unshift($filter_params, constant('IMG_FILTER_' . $filter_type)); array_unshift($filter_params, $src_image_rs); return call_user_func_array('imagefilter', $filter_params); } /** * Returns destination image size without actual resizing (useful for <img .../> HTML tag) * * @param string $src_image full path to source image (already existing) * @param int $dst_width destination image width (in pixels) * @param int $dst_height destination image height (in pixels) * @param Array $params * @return Array resized image dimensions (0 - width, 1 - height) */ function GetImageDimensions($src_image, $dst_width, $dst_height, $params) { $image_info = $this->getImageInfo($src_image); if (!$image_info) { return false; } $orig_width = $image_info[0]; $orig_height = $image_info[1]; $too_large = is_numeric($dst_width) ? ($orig_width > $dst_width) : false; $too_large = $too_large || (is_numeric($dst_height) ? ($orig_height > $dst_height) : false); if ($too_large) { $width_ratio = $dst_width ? $dst_width / $orig_width : 1; $height_ratio = $dst_height ? $dst_height / $orig_height : 1; if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) { // resize by smallest inverted radio $resize_by = $this->_getCropImageMinRatio($image_info, $dst_width, $dst_height); if ($resize_by === false) { return Array ($orig_width, $orig_height, false); } $ratio = $resize_by == 'width' ? $width_ratio : $height_ratio; } else { $ratio = min($width_ratio, $height_ratio); } $width = ceil($orig_width * $ratio); $height = ceil($orig_height * $ratio); } else { $width = $orig_width; $height = $orig_height; } return Array ($width, $height, $too_large); } /** * Returns ratio type with smaller relation of original size to target size * * @param Array $image_info image information from "ImageHelper::getImageInfo" * @param int $dst_width destination image width (in pixels) * @param int $dst_height destination image height (in pixels) * @return Array */ function _getCropImageMinRatio($image_info, $dst_width, $dst_height) { $width_ratio = $dst_width ? $image_info[0] / $dst_width : 1; $height_ratio = $dst_height ? $image_info[1] / $dst_height : 1; $minimal_ratio = min($width_ratio, $height_ratio); if ($minimal_ratio < 1) { // ratio is less then 1, image will be enlarged -> don't allow that return false; } return $width_ratio < $height_ratio ? 'width' : 'height'; } /** * Returns image dimensions + checks if given file is existing image * * @param string $src_image full path to source image (already existing) * @return mixed */ function getImageInfo($src_image) { if (!file_exists($src_image)) { return false; } $image_info = @getimagesize($src_image); if (!$image_info) { trigger_error('Image <b>'.$src_image.'</b> <span class="debug_error">missing or invalid</span>', E_USER_WARNING); return false; } return $image_info; } /** * Returns maximal image size (width & height) among fields specified * * @param kDBItem $object * @param string $fields * @param string $format any format, that returns full url (e.g. files_resized:WxH, resize:WxH, full_url, full_urls) * @return string */ function MaxImageSize(&$object, $fields, $format = null) { static $cached_sizes = Array (); $cache_key = $object->getPrefixSpecial().'_'.$object->GetID(); if (!isset($cached_sizes[$cache_key])) { $images = Array (); $fields = explode(',', $fields); foreach ($fields as $field) { $image_data = $object->GetField($field, $format); if (!$image_data) { continue; } $images = array_merge($images, explode('|', $image_data)); } $max_width = 0; $max_height = 0; $base_url = rtrim($this->Application->BaseURL(), '/'); foreach ($images as $image_url) { $image_path = preg_replace('/^'.preg_quote($base_url, '/').'(.*)/', FULL_PATH.'\\1', $image_url); $image_info = $this->getImageInfo($image_path); $max_width = max($max_width, $image_info[0]); $max_height = max($max_height, $image_info[1]); } $cached_sizes[$cache_key] = Array ($max_width, $max_height); } return $cached_sizes[$cache_key]; } /** * Puts existing item images (from sub-item) to virtual fields (in main item) * * @param kCatDBItem|kDBItem $object */ function LoadItemImages(&$object) { if (!$this->_canUseImages($object)) { return ; } $max_image_count = $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogImages WHERE ResourceId = '.$object->GetDBField('ResourceId').' ORDER BY Priority DESC LIMIT 0, ' . (int)$max_image_count; $item_images = $this->Conn->Query($sql); $image_counter = 1; foreach ($item_images as $item_image) { $image_path = $item_image['ThumbPath']; if ($item_image['DefaultImg'] == 1 || $item_image['Name'] == 'main') { // process primary image separately if ( $object->isField('PrimaryImage') ) { $object->SetDBField('PrimaryImage', $image_path); $object->SetOriginalField('PrimaryImage', $image_path); $object->SetFieldOption('PrimaryImage', 'original_field', $item_image['Name']); $this->_loadCustomFields($object, $item_image, 0); } continue; } if (abs($item_image['Priority'])) { // use Priority as image counter, when specified $image_counter = abs($item_image['Priority']); } if ( $object->isField('Image'.$image_counter) ) { $object->SetDBField('Image'.$image_counter, $image_path); $object->SetOriginalField('Image'.$image_counter, $image_path); $object->SetFieldOption('Image'.$image_counter, 'original_field', $item_image['Name']); $this->_loadCustomFields($object, $item_image, $image_counter); } $image_counter++; } } /** * Saves newly uploaded images to external image table * * @param kCatDBItem|kDBItem $object */ function SaveItemImages(&$object) { if (!$this->_canUseImages($object)) { return ; } $table_name = $this->Application->getUnitConfig('img')->getTableName(); $max_image_count = $object->getUnitConfig()->getImageCount(); // $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); $i = 0; while ($i < $max_image_count) { $field = $i ? 'Image'.$i : 'PrimaryImage'; $field_options = $object->GetFieldOptions($field); $image_src = $object->GetDBField($field); if ($image_src) { if (isset($field_options['original_field'])) { $key_clause = 'Name = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId'); if ($object->GetDBField('Delete'.$field)) { // if item was cloned, then new filename is in db (not in $image_src) $sql = 'SELECT ThumbPath FROM '.$table_name.' WHERE '.$key_clause; $image_src = $this->Conn->GetOne($sql); if (@unlink(FULL_PATH.$image_src)) { $sql = 'DELETE FROM '.$table_name.' WHERE '.$key_clause; $this->Conn->Query($sql); } } else { // image record found -> update $fields_hash = Array ( 'ThumbPath' => $image_src, ); $this->_saveCustomFields($object, $fields_hash, $i); $this->Conn->doUpdate($fields_hash, $table_name, $key_clause); } } else { // image record not found -> create $fields_hash = Array ( 'ResourceId' => $object->GetDBField('ResourceId'), 'Name' => $field, 'AltName' => $field, 'Enabled' => STATUS_ACTIVE, 'DefaultImg' => $i ? 0 : 1, // first image is primary, others not primary 'ThumbPath' => $image_src, 'Priority' => ($i == 0)? 0 : $i * (-1), ); $this->_saveCustomFields($object, $fields_hash, $i); $this->Conn->doInsert($fields_hash, $table_name); $field_options['original_field'] = $field; $object->SetFieldOptions($field, $field_options); } } $i++; } } /** * Adds ability to load custom fields along with main image field * * @param kCatDBItem|kDBItem $object * @param Array $fields_hash * @param int $counter 0 - primary image, other number - additional image number */ function _loadCustomFields(&$object, $fields_hash, $counter) { $field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt'; $object->SetDBField($field_name, (string)$fields_hash['AltName']); } /** * Adds ability to save custom field along with main image save * * @param kCatDBItem|kDBItem $object * @param Array $fields_hash * @param int $counter 0 - primary image, other number - additional image number */ function _saveCustomFields(&$object, &$fields_hash, $counter) { $field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt'; $fields_hash['AltName'] = (string)$object->GetDBField($field_name); } /** * Checks, that item can use image upload capabilities * * @param kCatDBItem|kDBItem $object * @return bool */ function _canUseImages(&$object) { $prefix = $object->Prefix == 'p' ? 'img' : $object->Prefix . '-img'; return $this->Application->prefixRegistred($prefix); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/upload_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/upload_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/upload_helper.php (revision 16111) @@ -1,337 +1,332 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2012 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ class kUploadHelper extends kHelper { /** * Creates kUploadHelper instance. */ public function __construct() { parent::__construct(); // 5 minutes execution time @set_time_limit(5 * 60); } /** * Handles the upload. * * @param kEvent $event Event. * * @return string * @throws kUploaderException When upload could not be handled properly. */ public function handle(kEvent $event) { $this->disableBrowserCache(); // Uncomment this one to fake upload time // sleep(5); 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. throw new kUploaderException('File size exceeds allowed limit.', 413); } if ( !$this->checkPermissions($event) ) { // 403 Forbidden throw new kUploaderException('You don\'t have permissions to upload.', 403); } $value = $this->Application->GetVar('file'); if ( !$value || ($value['error'] != UPLOAD_ERR_OK) ) { // 413 Request Entity Too Large (file uploads disabled OR uploaded file was // too large for web server to accept, see "upload_max_filesize" in php.ini) throw new kUploaderException('File size exceeds allowed limit.', 413); } - if ( !$this->Application->isAdmin ) { - $value = array_map('htmlspecialchars_decode', $value); - } + $value = $this->Application->HttpQuery->unescapeRequestVariable($value); $tmp_path = WRITEABLE . '/tmp/'; $filename = $this->getUploadedFilename() . '.tmp'; $id = $this->Application->GetVar('id'); if ( $id ) { $filename = $id . '_' . $filename; } if ( !is_writable($tmp_path) ) { // 500 Internal Server Error // check both temp and live upload directory throw new kUploaderException('Write permissions not set on the server, please contact server administrator.', 500); } /** @var FileHelper $file_helper */ $file_helper = $this->Application->recallObject('FileHelper'); $filename = $file_helper->ensureUniqueFilename($tmp_path, $filename); $storage_format = $this->getStorageFormat($this->Application->GetVar('field'), $event); if ( $storage_format ) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $this->moveUploadedFile($value['tmp_name'] . '.jpg'); // add extension, so ResizeImage can work $url = $image_helper->ResizeImage($value['tmp_name'] . '.jpg', $storage_format); $tmp_name = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', '/', $url); rename($tmp_name, $tmp_path . $filename); } else { $this->moveUploadedFile($tmp_path . $filename); } $this->deleteTempFiles($tmp_path); if ( file_exists($tmp_path . 'resized/') ) { $this->deleteTempFiles($tmp_path . 'resized/'); } return preg_replace('/^' . preg_quote($id, '/') . '_/', '', $filename); } /** * Sends headers to ensure, that response is never cached. * * @return void */ protected function disableBrowserCache() { header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); } /** * Checks, that flash uploader is allowed to perform upload * * @param kEvent $event * @return bool */ protected function checkPermissions(kEvent $event) { // Flash uploader does NOT send correct cookies, so we need to make our own check $cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName'); $this->Application->HttpQuery->Cookie['cookies_on'] = 1; $this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid'); // this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used $this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid'); $admin_session = $this->Application->recallObject('Session.admin'); /* @var $admin_session Session */ if ( $admin_session->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_session->RecallVar('user_id')); $backup_user_groups = $this->Application->RecallVar('UserGroups'); $this->Application->StoreVar('UserGroups', $admin_session->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)); /** @var kEventHandler $event_handler */ $event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler'); $allowed_to_upload = $event_handler->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; } /** * Returns uploaded filename. * * @return string */ protected function getUploadedFilename() { if ( isset($_REQUEST['name']) ) { $file_name = $_REQUEST['name']; } elseif ( !empty($_FILES) ) { $file_name = $_FILES['file']['name']; } else { $file_name = uniqid('file_'); } return $file_name; } /** * Gets storage format for a given field. * * @param string $field_name * @param kEvent $event * @return bool */ protected function getStorageFormat($field_name, kEvent $event) { $config = $event->getUnitConfig(); $field_options = $config->getFieldByName($field_name); if ( !$field_options ) { $field_options = $config->getVirtualFieldByName($field_name); } return isset($field_options['storage_format']) ? $field_options['storage_format'] : false; } /** * Moves uploaded file to given location. * * @param string $file_path File path. * * @return void * @throws kUploaderException When upload could not be handled properly. */ protected function moveUploadedFile($file_path) { // Chunking might be enabled $chunk = (int)$this->Application->GetVar('chunk', 0); $chunks = (int)$this->Application->GetVar('chunks', 0); // Open temp file if ( !$out = @fopen("{$file_path}.part", $chunks ? 'ab' : 'wb') ) { throw new kUploaderException('Failed to open output stream.', 102); } if ( !empty($_FILES) ) { if ( $_FILES['file']['error'] || !is_uploaded_file($_FILES['file']['tmp_name']) ) { throw new kUploaderException('Failed to move uploaded file.', 103); } // Read binary input stream and append it to temp file if ( !$in = @fopen($_FILES['file']['tmp_name'], 'rb') ) { throw new kUploaderException('Failed to open input stream.', 101); } } else { if ( !$in = @fopen('php://input', 'rb') ) { throw new kUploaderException('Failed to open input stream.', 101); } } while ( $buff = fread($in, 4096) ) { fwrite($out, $buff); } @fclose($out); @fclose($in); // Check if file has been uploaded if ( !$chunks || $chunk == $chunks - 1 ) { // Strip the temp .part suffix off rename("{$file_path}.part", $file_path); } } /** * Delete temporary files, that won't be used for sure * * @param string $path * @return void */ protected function deleteTempFiles($path) { $files = glob($path . '*.*'); $max_file_date = strtotime('-1 day'); foreach ($files as $file) { if (filemtime($file) < $max_file_date) { unlink($file); } } } /** * Prepares object for operations with file on given field. * * @param kEvent $event Event. * @param string $field Field. * * @return kDBItem */ public function prepareUploadedFile(kEvent $event, $field) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $filename = $this->getSafeFilename(); if ( !$filename ) { $object->SetDBField($field, ''); return $object; } // set current uploaded file if ( $this->Application->GetVar('tmp') ) { $options = $object->GetFieldOptions($field); $options['upload_dir'] = WRITEBALE_BASE . '/tmp/'; unset($options['include_path']); $object->SetFieldOptions($field, $options); $filename = $this->Application->GetVar('id') . '_' . $filename; } $object->SetDBField($field, $filename); return $object; } /** * Returns safe version of filename specified in url * * @return bool|string * @access protected */ protected function getSafeFilename() { $filename = $this->Application->GetVar('file'); - - if ( !$this->Application->isAdmin ) { - $filename = htmlspecialchars_decode($filename); - } + $filename = $this->Application->unescapeRequestVariable($filename); if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) { // when relative paths or special chars are found template names from url, then it's hacking attempt return false; } return $filename; } } class kUploaderException extends Exception { } Index: branches/5.3.x/core/units/helpers/country_states_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/country_states_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/country_states_helper.php (revision 16111) @@ -1,225 +1,246 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kCountryStatesHelper extends kHelper { + + /** + * ID of current language. + * + * @var integer + */ + protected $currentLanguage; + + /** + * ID of primary language. + * + * @var integer + */ + protected $primaryLanguage; + + /** + * Populates language ids that are used. + */ + public function __construct() + { + parent::__construct(); + + // Don't use GetVar('m_lang') since it's always equals to default language on editing form in admin. + $this->currentLanguage = $this->Application->Phrases->LanguageId; + $this->primaryLanguage = $this->Application->GetDefaultLanguageId(); + } + /** * Returns countries, that have states * * @return Array */ function getCountriesWithStates() { static $cache = null; if ( !isset($cache) ) { $table_name = $this->Application->getUnitConfig('country-state')->getTableName(); $sql = 'SELECT DISTINCT cname.IsoCode, cid.StateCountryId FROM ' . $table_name . ' cid JOIN ' . $table_name . ' cname ON cname.CountryStateId = cid.StateCountryId WHERE cid.StateCountryId IS NOT NULL'; $cache = $this->Conn->GetCol($sql, 'StateCountryId'); } return $cache; } /** * Checks, that country with given 3symbol ISO code has states * * @param string $country_code * @return bool */ function CountryHasStates($country_code) { return $country_code ? in_array($country_code, $this->getCountriesWithStates()) : false; } /** * Prepares states dropdown based on country selected * * @param kEvent $event * @param string $state_field * @param string $country_field */ function PopulateStates($event, $state_field, $country_field) { - static $cache = Array (); - $object = $event->getObject(); /* @var $object kDBItem */ $country_iso = $object->GetDBField($country_field); - if (!$country_iso) { - return ; - } - - if (!array_key_exists($country_iso, $cache)) { - $states = $this->getStates($country_iso); - - if ( !$states ) { + if ( !$country_iso ) { return; } - $cache[$country_iso] = $states; - } - $field_options = $object->GetFieldOptions($state_field); - - $field_options['options'] = $cache[$country_iso]; + $field_options['options'] = $this->getStates($country_iso); $field_options['options'][''] = ''; - - $object->SetFieldOptions($state_field, $field_options); + $object->SetFieldOptions($state_field, $field_options, $object->isVirtualField($state_field)); } /** * Returns list of given country states * * @param string $country_iso * @return Array */ public function getStates($country_iso) { $country_id = $this->getCountryStateId($country_iso, DESTINATION_TYPE_COUNTRY); if ( !$country_id ) { return Array (); } - // don't use GetVar('m_lang') since it's always equals to default language on editing form in admin - $current_language = $this->Application->Phrases->LanguageId; - $primary_language = $this->Application->GetDefaultLanguageId(); + $cache_key = 'country_states[%CountryStateSerial%]'; + $cache_key .= ':PL=' . $this->primaryLanguage . ':CL=' . $this->currentLanguage . ':ISO=' . $country_iso; + $states = $this->Application->getCache($cache_key); - $sql = 'SELECT IF(l' . $current_language . '_Name = "", l' . $primary_language . '_Name, l' . $current_language . '_Name) AS Name, IsoCode + if ( $states === false ) { + $sql = 'SELECT IF(l' . $this->currentLanguage . '_Name = "", l' . $this->primaryLanguage . '_Name, l' . $this->currentLanguage . '_Name) AS Name, IsoCode FROM ' . $this->Application->getUnitConfig('country-state')->getTableName() . ' WHERE (Type = ' . DESTINATION_TYPE_STATE . ') AND (StateCountryId = ' . $country_id . ') ORDER BY Name ASC'; + $states = $this->Conn->GetCol($sql, 'IsoCode'); + + $this->Application->setCache($cache_key, $states); + } - return $this->Conn->GetCol($sql, 'IsoCode'); + return $states; } /** * Returns valid state ISO code for state name and country code passed * * @param string $state_name * @param string $country_iso * @return string */ function getStateIso($state_name, $country_iso) { if ( !$this->CountryHasStates($country_iso) ) { return $state_name; } $table_name = $this->Application->getUnitConfig('country-state')->getTableName(); $country_id = $this->getCountryStateId($country_iso, DESTINATION_TYPE_COUNTRY); - // don't use GetVar('m_lang') since it's always equals to default language on editing form in admin - $current_language = $this->Application->Phrases->LanguageId; - $primary_language = $this->Application->GetDefaultLanguageId(); - $sql = 'SELECT IsoCode FROM ' . $table_name . ' WHERE (Type = ' . DESTINATION_TYPE_STATE . ') AND (StateCountryId = %1$s) AND ( (IsoCode = %2$s) OR (UPPER(l%3$s_Name) = %2$s) OR (UPPER(l%4$s_Name) = %2$s) )'; $state_name = trim(mb_strtoupper($state_name)); - $sql = sprintf($sql, $country_id, $this->Conn->qstr($state_name), $current_language, $primary_language); + $sql = sprintf($sql, $country_id, $this->Conn->qstr($state_name), $this->currentLanguage, $this->primaryLanguage); return $this->Conn->GetOne($sql); } /** * Checks, that entered state matches entered country * * @param kEvent $event * @param string $state_field * @param string $country_field * @param bool $auto_required * @return void */ function CheckStateField($event, $state_field, $country_field, $auto_required = true) { $object = $event->getObject(); /* @var $object kDBItem */ $country_iso = $object->GetDBField($country_field); if ( $auto_required ) { $object->setRequired($state_field, $this->CountryHasStates($country_iso)); } $state = $object->GetDBField($state_field); if ( $country_iso && $state ) { $state_iso = $this->getStateIso($state, $country_iso); if ( $state_iso !== false ) { // replace state name with it's ISO code $object->SetDBField($state_field, $state_iso); } else { // state not found by name -> report error $object->SetError($state_field, 'invalid_state', 'la_invalid_state'); } } } /** * Returns country/state id based on given iso code and it's type * * @param string $iso_code * @param int $type * @return int */ function getCountryStateId($iso_code, $type) { $config = $this->Application->getUnitConfig('country-state'); + $cache_key = 'country_state_id[%CountryStateSerial%]:ISO=' . $iso_code . ';Type=' . $type; + $id = $this->Application->getCache($cache_key); + + if ( $id === false ) { + $sql = 'SELECT ' . $config->getIDField() . ' + FROM ' . $config->getTableName() . ' + WHERE (Type = ' . $type . ') AND (IsoCode = ' . $this->Conn->qstr($iso_code) . ')'; + $id = (int)$this->Conn->GetOne($sql); - $sql = 'SELECT ' . $config->getIDField() . ' - FROM ' . $config->getTableName() . ' - WHERE (Type = ' . $type . ') AND (IsoCode = ' . $this->Conn->qstr($iso_code) . ')'; + $this->Application->setCache($cache_key, $id); + } - return (int)$this->Conn->GetOne($sql); + return $id; } /** * Returns 3 symbols ISO code from 2 symbols ISO code or otherwise, when $from_short parameter is used * * @param string $iso_code * @param bool $from_short * @return string */ function getCountryIso($iso_code, $from_short = false) { if ($from_short) { $sql = 'SELECT IsoCode FROM ' . TABLE_PREFIX . 'CountryStates WHERE ShortIsoCode = ' . $this->Conn->qstr($iso_code) . ' AND `Type` = ' . DESTINATION_TYPE_COUNTRY; } else { $sql = 'SELECT ShortIsoCode FROM ' . TABLE_PREFIX . 'CountryStates WHERE IsoCode = ' . $this->Conn->qstr($iso_code) . ' AND `Type` = ' . DESTINATION_TYPE_COUNTRY; } return $this->Conn->GetOne($sql); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/cat_dbitem_export_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/cat_dbitem_export_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/cat_dbitem_export_helper.php (revision 16111) @@ -1,1577 +1,1581 @@ <?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('EXPORT_STEP', 100); // export by 200 items (e.g. links) define('IMPORT_STEP', 20); // export by 200 items (e.g. links) define('IMPORT_CHUNK', 10240); // 10240); //30720); //50120); // 5 KB define('IMPORT_TEMP', 1); define('IMPORT_LIVE', 2); class kCatDBItemExportHelper extends kHelper { var $false = false; var $cache = Array(); /** * Allows to find out what items are new in cache * * @var Array */ var $cacheStatus = Array(); var $cacheTable = ''; var $exportFields = Array(); /** * Export options * * @var Array */ var $exportOptions = Array(); /** * Item being currently exported * * @var kCatDBItem */ var $curItem = null; /** * Dummy category object * * @var CategoriesItem */ var $dummyCategory = null; /** * Pointer to opened file * * @var resource */ var $filePointer = null; /** * Custom fields definition of current item * * @var Array */ var $customFields = Array(); public function __construct() { parent::__construct(); $this->cacheTable = TABLE_PREFIX.'ImportCache'; } /** * Returns value from cache if found or false otherwise * * @param string $type * @param int $key * @return mixed */ function getFromCache($type, $key) { return getArrayValue($this->cache, $type, $key); } /** * Adds value to be cached * * @param string $type * @param int $key * @param mixed $value * @param bool $is_new */ function addToCache($type, $key, $value, $is_new = true) { /*if ( !isset($this->cache[$type]) ) { $this->cache[$type] = Array (); }*/ $this->cache[$type][$key] = $value; if ( $is_new ) { $this->cacheStatus[$type][$key] = true; } } function storeCache($cache_types) { $fields_hash = Array (); $cache_types = explode(',', $cache_types); foreach ($cache_types as $cache_type) { $fields_hash = Array ('CacheName' => $cache_type); $cache = getArrayValue($this->cacheStatus, $cache_type); if ( !$cache ) { $cache = Array (); } foreach ($cache as $var_name => $cache_status) { $fields_hash['VarName'] = $var_name; $fields_hash['VarValue'] = $this->cache[$cache_type][$var_name]; $this->Conn->doInsert($fields_hash, $this->cacheTable, 'INSERT', false); } } if ( isset($fields_hash['VarName']) ) { $this->Conn->doInsert($fields_hash, $this->cacheTable, 'INSERT'); } } function loadCache() { $this->cache = Array (); $sql = 'SELECT * FROM ' . $this->cacheTable; $records = $this->Conn->GetIterator($sql); foreach ($records as $record) { $this->addToCache($record['CacheName'], $record['VarName'], $record['VarValue'], false); } } /** * Fill required fields with dummy values * * @param kEvent|bool $event * @param kCatDBItem|bool $object * @param bool $set_status */ function fillRequiredFields($event, &$object, $set_status = false) { if ( $object == $this->false ) { $object = $event->getObject(); /* @var $object kCatDBItem */ } $has_empty = false; $fields = $object->getFields(); if ( $object->isField('CreatedById') ) { // CSV file was created without required CreatedById column if ( $object->isRequired('CreatedById') ) { $object->setRequired('CreatedById', false); } if ( !is_numeric( $object->GetDBField('CreatedById') ) ) { $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id')); } } foreach ($fields as $field_name => $field_options) { if ( $object->isVirtualField($field_name) || !$object->isRequired($field_name) ) { continue; } if ( $object->GetDBField($field_name) ) { continue; } $formatter_class = getArrayValue($field_options, 'formatter'); if ( $formatter_class ) { // not tested $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kFormatter */ $sample_value = $formatter->GetSample($field_name, $field_options, $object); } $has_empty = true; $object->SetField($field_name, isset($sample_value) && $sample_value ? $sample_value : 'no value'); } $object->UpdateFormattersSubFields(); if ( $set_status && $has_empty ) { $object->SetDBField('Status', 0); } } /** * Verifies that all user entered export params are correct * * @param kEvent $event * @return bool * @access protected */ protected function verifyOptions($event) { if ($this->Application->RecallVar($event->getPrefixSpecial().'_ForceNotValid')) { $this->Application->StoreVar($event->getPrefixSpecial().'_ForceNotValid', 0); return false; } $this->fillRequiredFields($event, $this->false); $object = $event->getObject(); /* @var $object kCatDBItem */ $cross_unique_fields = Array('FieldsSeparatedBy', 'FieldsEnclosedBy'); if (($object->GetDBField('CategoryFormat') == 1) || ($event->Special == 'import')) // in one field { $object->setRequired('CategorySeparator'); $cross_unique_fields[] = 'CategorySeparator'; } $ret = $object->Validate(); // check if cross unique fields has no same values foreach ($cross_unique_fields as $field_index => $field_name) { if ($object->GetErrorPseudo($field_name) == 'required') { continue; } $check_fields = $cross_unique_fields; unset($check_fields[$field_index]); foreach ($check_fields as $check_field) { if ($object->GetDBField($field_name) == $object->GetDBField($check_field)) { $object->SetError($check_field, 'unique'); } } } if ($event->Special == 'import') { $this->exportOptions = $this->loadOptions($event); $automatic_fields = ($object->GetDBField('FieldTitles') == 1); $object->setRequired('ExportColumns', !$automatic_fields); $category_prefix = '__CATEGORY__'; if ( $automatic_fields && ($this->exportOptions['SkipFirstRow']) ) { $this->openFile($event); $this->exportOptions['ExportColumns'] = $this->readRecord(); if (!$this->exportOptions['ExportColumns']) { $this->exportOptions['ExportColumns'] = Array (); } $this->closeFile(); // remove additional (non-parseble columns) foreach ($this->exportOptions['ExportColumns'] as $field_index => $field_name) { if (!$this->validateField($field_name, $object)) { unset($this->exportOptions['ExportColumns'][$field_index]); } } $category_prefix = ''; } // 1. check, that we have column definitions if (!$this->exportOptions['ExportColumns']) { $object->setError('ExportColumns', 'required'); $ret = false; } else { // 1.1. check that all required fields are present in imported file $missing_columns = Array(); $fields = $object->getFields(); foreach ($fields as $field_name => $field_options) { if ($object->skipField($field_name)) continue; if ( $object->isRequired($field_name) && !in_array($field_name, $this->exportOptions['ExportColumns']) ) { $missing_columns[] = $field_name; $object->setError('ExportColumns', 'required_fields_missing', 'la_error_RequiredColumnsMissing'); $ret = false; } } if (!$ret && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Missing required for import/export:'); $this->Application->Debugger->dumpVars($missing_columns); } } // 2. check, that we have only mixed category field or only separated category fields $category_found['mixed'] = false; $category_found['separated'] = false; foreach ($this->exportOptions['ExportColumns'] as $import_field) { if (preg_match('/^'.$category_prefix.'Category(Path|[0-9]+)/', $import_field, $rets)) { $category_found[$rets[1] == 'Path' ? 'mixed' : 'separated'] = true; } } if ($category_found['mixed'] && $category_found['separated']) { $object->SetError('ExportColumns', 'unique_category', 'la_error_unique_category_field'); $ret = false; } // 3. check, that duplicates check fields are selected & present in imported fields if ($this->exportOptions['ReplaceDuplicates']) { if ($this->exportOptions['CheckDuplicatesMethod'] == 1) { $check_fields = Array($object->IDField); } else { $check_fields = $this->exportOptions['DuplicateCheckFields'] ? explode('|', substr($this->exportOptions['DuplicateCheckFields'], 1, -1)) : Array(); $object = $event->getObject(); $fields = $object->getFields(); $language_id = $this->Application->GetDefaultLanguageId(); foreach ($check_fields as $index => $check_field) { foreach ($fields as $field_name => $field_options) { if ($field_name == 'l'.$language_id.'_'.$check_field) { $check_fields[$index] = 'l'.$language_id.'_'.$check_field; break; } } } } $this->exportOptions['DuplicateCheckFields'] = $check_fields; if (!$check_fields) { $object->setError('CheckDuplicatesMethod', 'required'); $ret = false; } else { foreach ($check_fields as $check_field) { $check_field = preg_replace('/^cust_(.*)/', 'Custom_\\1', $check_field); if (!in_array($check_field, $this->exportOptions['ExportColumns'])) { $object->setError('ExportColumns', 'required'); $ret = false; break; } } } } $this->saveOptions($event); } return $ret; } /** * Returns filename to read import data from * * @return string */ function getImportFilename() { if ($this->exportOptions['ImportSource'] == 1) { $ret = $this->exportOptions['ImportFilename']; // ['name']; commented by Kostja } else { $ret = $this->exportOptions['ImportLocalFilename']; } return EXPORT_PATH.'/'.$ret; } /** * Returns filename to write export data to * * @return string */ function getExportFilename() { $extension = $this->getFileExtension(); $filename = preg_replace('/(.*)\.' . $extension . '$/', '\1', $this->exportOptions['ExportFilename']) . '.' . $extension; return EXPORT_PATH . DIRECTORY_SEPARATOR . $filename; } /** * Opens file required for export/import operations * * @param kEvent $event */ function openFile($event) { $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->CheckFolder(EXPORT_PATH); - if ($event->Special == 'export') { - $write_mode = ($this->exportOptions['start_from'] == 0) ? 'w' : 'a'; - $this->filePointer = fopen($this->getExportFilename(), $write_mode); + if ( $event->Special == 'export' ) { + $first_step = $this->exportOptions['start_from'] == 0; + $this->filePointer = fopen($this->getExportFilename(), $first_step ? 'w' : 'r+'); + + if ( !$first_step ) { + fseek($this->filePointer, 0, SEEK_END); + } } else { $this->filePointer = fopen($this->getImportFilename(), 'r'); - } - // skip UTF-8 BOM Modifier - $first_chars = fread($this->filePointer, 3); - if (bin2hex($first_chars) != 'efbbbf') { - fseek($this->filePointer, 0); + // skip UTF-8 BOM Modifier + $first_chars = fread($this->filePointer, 3); + if ( bin2hex($first_chars) != 'efbbbf' ) { + fseek($this->filePointer, 0); + } } } /** * Closes opened file * */ function closeFile() { fclose($this->filePointer); } function getCustomSQL() { $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $custom_sql = ''; foreach ($this->customFields as $custom_id => $custom_name) { $custom_sql .= 'custom_data.' . $ml_formatter->LangFieldName('cust_' . $custom_id) . ' AS cust_' . $custom_name . ', '; } return substr($custom_sql, 0, -2); } function getPlainExportSQL($count_only = false) { if ( $count_only && isset($this->exportOptions['ForceCountSQL']) ) { $sql = $this->exportOptions['ForceCountSQL']; } elseif ( !$count_only && isset($this->exportOptions['ForceSelectSQL']) ) { $sql = $this->exportOptions['ForceSelectSQL']; } else { $items_list = $this->Application->recallObject($this->curItem->Prefix . '.export-items-list', $this->curItem->Prefix . '_List'); /* @var $items_list kDBList */ $items_list->SetPerPage(-1); if ( $this->exportOptions['export_ids'] != '' ) { $items_list->addFilter('export_ids', $items_list->TableName . '.' . $items_list->IDField . ' IN (' . implode(',', $this->exportOptions['export_ids']) . ')'); } if ( $count_only ) { $sql = $items_list->getCountSQL($items_list->GetSelectSQL(true, false)); } else { $sql = $items_list->GetSelectSQL(); } } if ( !$count_only ) { $sql .= ' LIMIT ' . $this->exportOptions['start_from'] . ',' . EXPORT_STEP; } /*else { $sql = preg_replace("/^\s*SELECT(.*?\s)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql); }*/ return $sql; } function getExportSQL($count_only = false) { if ( !$this->curItem->getUnitConfig()->getCatalogItem() ) { return $this->GetPlainExportSQL($count_only); // in case this is not a CategoryItem } if ( $this->exportOptions['export_ids'] === false ) { // get links from current category & all it's subcategories $join_clauses = Array (); $custom_sql = $this->getCustomSQL(); if ( $custom_sql ) { $custom_table = $this->Application->getUnitConfig($this->curItem->Prefix . '-cdata')->getTableName(); $join_clauses[$custom_table . ' custom_data'] = 'custom_data.ResourceId = item_table.ResourceId'; } $join_clauses[TABLE_PREFIX . 'CategoryItems ci'] = 'ci.ItemResourceId = item_table.ResourceId'; $join_clauses[TABLE_PREFIX . 'Categories c'] = 'c.CategoryId = ci.CategoryId'; $sql = 'SELECT item_table.*, ci.CategoryId' . ($custom_sql ? ', ' . $custom_sql : '') . ' FROM ' . $this->curItem->TableName . ' item_table'; foreach ($join_clauses as $table_name => $join_expression) { $sql .= ' LEFT JOIN ' . $table_name . ' ON ' . $join_expression; } $sql .= ' WHERE '; if ( $this->exportOptions['export_cats_ids'][0] == 0 ) { $sql .= '1'; } else { foreach ($this->exportOptions['export_cats_ids'] as $category_id) { $sql .= '(c.ParentPath LIKE "%|' . $category_id . '|%") OR '; } $sql = substr($sql, 0, -4); } $sql .= ' ORDER BY ci.PrimaryCat DESC'; // NEW } else { // get only selected links $sql = 'SELECT item_table.*, ' . $this->exportOptions['export_cats_ids'][0] . ' AS CategoryId FROM ' . $this->curItem->TableName . ' item_table WHERE ' . $this->curItem->IDField . ' IN (' . implode(',', $this->exportOptions['export_ids']) . ')'; } if ( !$count_only ) { $sql .= ' LIMIT ' . $this->exportOptions['start_from'] . ',' . EXPORT_STEP; } else { $sql = preg_replace("/^\s*SELECT(.*?\s)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql); } return $sql; } /** * Enter description here... * * @param kEvent $event */ function performExport($event) { $this->exportOptions = $this->loadOptions($event); $this->exportFields = $this->exportOptions['ExportColumns']; $this->curItem = $event->getObject(Array ('skip_autoload' => true)); $this->customFields = $event->getUnitConfig()->getCustomFields(); $this->openFile($event); if ( $this->exportOptions['start_from'] == 0 ) { // first export step if ( !getArrayValue($this->exportOptions, 'IsBaseCategory') ) { $this->exportOptions['IsBaseCategory'] = 0; } if ( $this->exportOptions['IsBaseCategory'] ) { $sql = 'SELECT ParentPath FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . (int)$this->Application->GetVar('m_cat_id'); $parent_path = $this->Conn->GetOne($sql); $parent_path = explode('|', substr($parent_path, 1, -1)); if ( $parent_path && $parent_path[0] == $this->Application->getBaseCategory() ) { array_shift($parent_path); } $this->exportOptions['BaseLevel'] = count($parent_path); // level to cut from other categories } // 1. export field titles if required if ( $this->exportOptions['IncludeFieldTitles'] ) { $data_array = Array (); foreach ($this->exportFields as $export_field) { $data_array = array_merge($data_array, $this->getFieldCaption($export_field)); } $this->writeRecord($data_array); } $this->exportOptions['total_records'] = $this->Conn->GetOne($this->getExportSQL(true)); } // 2. export data $records = $this->Conn->Query( $this->getExportSQL() ); $records_exported = 0; foreach ($records as $record_info) { $this->curItem->LoadFromHash($record_info); $data_array = Array(); foreach ($this->exportFields as $export_field) { $data_array = array_merge($data_array, $this->getFieldValue($export_field) ); } $this->writeRecord($data_array); $records_exported++; } $this->closeFile(); $this->exportOptions['start_from'] += $records_exported; $this->saveOptions($event); return $this->exportOptions; } function getItemFields() { // just in case dummy user selected automtic mode & moved columns too :( $src_options = $this->curItem->GetFieldOption('ExportColumns', 'options'); $dst_options = $this->curItem->GetFieldOption('AvailableColumns', 'options'); return array_merge($dst_options, $src_options); } /** * Checks if field really belongs to importable field list * * @param string $field_name * @param kCatDBItem $object * @return bool */ function validateField($field_name, &$object) { // 1. convert custom field $field_name = preg_replace('/^Custom_(.*)/', '__CUSTOM__\\1', $field_name); // 2. convert category field (mixed version & separated version) $field_name = preg_replace('/^Category(Path|[0-9]+)/', '__CATEGORY__Category\\1', $field_name); $valid_fields = $object->getPossibleExportColumns(); return isset($valid_fields[$field_name]) || isset($valid_fields['__VIRTUAL__'.$field_name]); } /** * Enter description here... * * @param kEvent $event */ function performImport($event) { if (!$this->exportOptions) { // load import options in case if not previously loaded in verification function $this->exportOptions = $this->loadOptions($event); } $backup_category_id = $this->Application->GetVar('m_cat_id'); $this->Application->SetVar('m_cat_id', (int)$this->Application->RecallVar('ImportCategory') ); $this->openFile($event); $bytes_imported = 0; if ($this->exportOptions['start_from'] == 0) // first export step { // 1st time run if ($this->exportOptions['SkipFirstRow']) { $this->readRecord(); $this->exportOptions['start_from'] = ftell($this->filePointer); $bytes_imported = ftell($this->filePointer); } $current_category_id = $this->Application->GetVar('m_cat_id'); if ($current_category_id > 0) { $sql = 'SELECT ParentPath FROM '.TABLE_PREFIX.'Categories WHERE CategoryId = '.$current_category_id; $this->exportOptions['ImportCategoryPath'] = $this->Conn->GetOne($sql); } else { $this->exportOptions['ImportCategoryPath'] = ''; } $this->exportOptions['total_records'] = filesize($this->getImportFilename()); } else { $this->loadCache(); } $this->exportFields = $this->exportOptions['ExportColumns']; $this->addToCache('category_parent_path', $this->Application->GetVar('m_cat_id'), $this->exportOptions['ImportCategoryPath']); // 2. import data $this->dummyCategory = $this->Application->recallObject('c.-tmpitem', 'c', Array('skip_autoload' => true)); fseek($this->filePointer, $this->exportOptions['start_from']); $items_processed = 0; while (($bytes_imported < IMPORT_CHUNK && $items_processed < IMPORT_STEP) && !feof($this->filePointer)) { $data = $this->readRecord(); if ($data) { if ($this->exportOptions['ReplaceDuplicates']) { // set fields used as keys for replace duplicates code $this->resetImportObject($event, IMPORT_TEMP, $data); } $this->processCurrentItem($event, $data); } $bytes_imported = ftell($this->filePointer) - $this->exportOptions['start_from']; $items_processed++; } $this->closeFile(); $this->Application->SetVar('m_cat_id', $backup_category_id); $this->exportOptions['start_from'] += $bytes_imported; $this->storeCache('new_ids'); $this->saveOptions($event); if ($this->exportOptions['start_from'] == $this->exportOptions['total_records']) { $this->Conn->Query('TRUNCATE TABLE '.$this->cacheTable); } return $this->exportOptions; } function setCurrentID() { $this->curItem->setID( $this->curItem->GetDBField($this->curItem->IDField) ); } /** * Sets value of import/export object * @param int $field_index * @param mixed $value * @return void * @access protected */ protected function setFieldValue($field_index, $value) { if ( empty($value) ) { $value = null; } $field_name = getArrayValue($this->exportFields, $field_index); if ( $field_name == 'ResourceId' ) { return ; } if ( substr($field_name, 0, 7) == 'Custom_' ) { $field_name = 'cust_' . substr($field_name, 7); $this->curItem->SetField($field_name, $value); } elseif ( $field_name == 'CategoryPath' || $field_name == '__CATEGORY__CategoryPath' ) { $this->curItem->CategoryPath = $value ? explode($this->exportOptions['CategorySeparator'], $value) : Array (); } elseif ( substr($field_name, 0, 8) == 'Category' ) { $this->curItem->CategoryPath[(int)substr($field_name, 8) - 1] = $value; } elseif ( substr($field_name, 0, 20) == '__CATEGORY__Category' ) { $this->curItem->CategoryPath[(int)substr($field_name, 20) - 1] = $value; } elseif ( substr($field_name, 0, 11) == '__VIRTUAL__' ) { $field_name = substr($field_name, 11); $this->curItem->SetField($field_name, $value); } else { $this->curItem->SetField($field_name, $value); } if ( $this->curItem->GetErrorPseudo($field_name) ) { $this->curItem->SetDBField($field_name, null); $this->curItem->RemoveError($field_name); } } /** * Resets import object * * @param kEvent $event * @param int $object_type * @param Array $record_data * @return void */ function resetImportObject($event, $object_type, $record_data = null) { switch ($object_type) { case IMPORT_TEMP: $this->curItem = $event->getObject( Array('skip_autoload' => true) ); break; case IMPORT_LIVE: $this->curItem = $this->Application->recallObject($event->Prefix.'.-tmpitem'.$event->Special, $event->Prefix, Array('skip_autoload' => true)); break; } $this->curItem->Clear(); $this->curItem->SetDBField('CategoryId', NULL); // since default value is import root category $this->customFields = $event->getUnitConfig()->getCustomFields(); if (isset($record_data)) { $this->setImportData($record_data); } } function setImportData($record_data) { foreach ($record_data as $field_index => $field_value) { $this->setFieldValue($field_index, $field_value); } $this->setCurrentID(); } function getItemCategory() { static $lang_prefix = null; $backup_category_id = $this->Application->GetVar('m_cat_id'); $category_id = $this->getFromCache('category_names', implode(':', $this->curItem->CategoryPath)); if ($category_id) { $this->Application->SetVar('m_cat_id', $category_id); return $category_id; } if (is_null($lang_prefix)) { $lang_prefix = 'l'.$this->Application->GetVar('m_lang').'_'; } foreach ($this->curItem->CategoryPath as $category_index => $category_name) { if (!$category_name) continue; $category_key = kUtil::crc32( implode(':', array_slice($this->curItem->CategoryPath, 0, $category_index + 1) ) ); $category_id = $this->getFromCache('category_names', $category_key); if ($category_id === false) { // get parent category path to search only in it $current_category_id = $this->Application->GetVar('m_cat_id'); // $parent_path = $this->getParentPath($current_category_id); // get category id from database by name $sql = 'SELECT CategoryId FROM '.TABLE_PREFIX.'Categories WHERE ('.$lang_prefix.'Name = '.$this->Conn->qstr($category_name).') AND (ParentId = '.(int)$current_category_id.')'; $category_id = $this->Conn->GetOne($sql); if ( $category_id === false ) { // category not in db -> create $category_fields = Array ( $lang_prefix.'Name' => $category_name, $lang_prefix.'Description' => $category_name, 'Status' => STATUS_ACTIVE, 'ParentId' => $current_category_id, 'AutomaticFilename' => 1 ); $this->dummyCategory->Clear(); $this->dummyCategory->SetDBFieldsFromHash($category_fields); if ( $this->dummyCategory->Create() ) { $category_id = $this->dummyCategory->GetID(); $this->addToCache('category_parent_path', $category_id, $this->dummyCategory->GetDBField('ParentPath')); $this->addToCache('category_names', $category_key, $category_id); } } else { $this->addToCache('category_names', $category_key, $category_id); } } if ($category_id) { $this->Application->SetVar('m_cat_id', $category_id); } } if (!$this->curItem->CategoryPath) { $category_id = $backup_category_id; } return $category_id; } /** * Enter description here... * * @param kEvent $event * @param Array $record_data * @return bool */ function processCurrentItem($event, $record_data) { $save_method = 'Create'; $load_keys = Array (); // create/update categories $backup_category_id = $this->Application->GetVar('m_cat_id'); // perform replace duplicates code if ( $this->exportOptions['ReplaceDuplicates'] ) { // get replace keys first, then reset current item to empty one $category_id = $this->getItemCategory(); if ( $this->exportOptions['CheckDuplicatesMethod'] == 1 ) { if ( $this->curItem->GetID() ) { $load_keys = Array ($this->curItem->IDField => $this->curItem->GetID()); } } else { $key_fields = $this->exportOptions['DuplicateCheckFields']; foreach ($key_fields as $key_field) { $load_keys[$key_field] = $this->curItem->GetDBField($key_field); } } $this->resetImportObject($event, IMPORT_LIVE); if ( count($load_keys) ) { $where_clause = ''; $language_id = (int)$this->Application->GetVar('m_lang'); if ( !$language_id ) { $language_id = 1; } foreach ($load_keys as $field_name => $field_value) { if ( preg_match('/^cust_(.*)/', $field_name, $regs) ) { $custom_id = array_search($regs[1], $this->customFields); $field_name = 'l' . $language_id . '_cust_' . $custom_id; $where_clause .= '(custom_data.`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ') AND '; } else { $where_clause .= '(item_table.`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ') AND '; } } $where_clause = substr($where_clause, 0, -5); $item_id = $this->getFromCache('new_ids', kUtil::crc32($where_clause)); if ( !$item_id ) { if ( $this->exportOptions['CheckDuplicatesMethod'] == 2 ) { // by other fields $parent_path = $this->getParentPath($category_id); $where_clause = '(c.ParentPath LIKE "' . $parent_path . '%") AND ' . $where_clause; } $cdata_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $sql = 'SELECT ' . $this->curItem->IDField . ' FROM ' . $this->curItem->TableName . ' item_table LEFT JOIN ' . $cdata_table . ' custom_data ON custom_data.ResourceId = item_table.ResourceId LEFT JOIN ' . TABLE_PREFIX . 'CategoryItems ci ON ci.ItemResourceId = item_table.ResourceId LEFT JOIN ' . TABLE_PREFIX . 'Categories c ON c.CategoryId = ci.CategoryId WHERE ' . $where_clause; $item_id = $this->Conn->GetOne($sql); } $save_method = $item_id && $this->curItem->Load($item_id) ? 'Update' : 'Create'; if ( $save_method == 'Update' ) { // replace id from csv file with found id (only when ID is found in cvs file) if ( in_array($this->curItem->IDField, $this->exportFields) ) { $record_data[array_search($this->curItem->IDField, $this->exportFields)] = $item_id; } } } $this->setImportData($record_data); } else { $this->resetImportObject($event, IMPORT_LIVE, $record_data); $category_id = $this->getItemCategory(); } // create main record if ( $save_method == 'Create' ) { $this->fillRequiredFields($this->false, $this->curItem, true); } // $sql_start = microtime(true); if ( !$this->curItem->$save_method() ) { $this->Application->SetVar('m_cat_id', $backup_category_id); return false; } // $sql_end = microtime(true); // $this->saveLog('SQL ['.$save_method.'] Time: '.($sql_end - $sql_start).'s'); if ( $load_keys && ($save_method == 'Create') && $this->exportOptions['ReplaceDuplicates'] ) { // map new id to old id $this->addToCache('new_ids', kUtil::crc32($where_clause), $this->curItem->GetID()); } // assign item to categories $this->curItem->assignToCategory($category_id, false); $this->Application->SetVar('m_cat_id', $backup_category_id); return true; } /*function saveLog($msg) { static $first_time = true; $fp = fopen((defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/sqls.log', $first_time ? 'w' : 'a'); fwrite($fp, $msg."\n"); fclose($fp); $first_time = false; }*/ /** * Returns category parent path, if possible, then from cache * * @param int $category_id * @return string */ function getParentPath($category_id) { $parent_path = $this->getFromCache('category_parent_path', $category_id); if ($parent_path === false) { $sql = 'SELECT ParentPath FROM '.TABLE_PREFIX.'Categories WHERE CategoryId = '.$category_id; $parent_path = $this->Conn->GetOne($sql); $this->addToCache('category_parent_path', $category_id, $parent_path); } return $parent_path; } function getFileExtension() { return $this->exportOptions['ExportFormat'] == 1 ? 'csv' : 'xml'; } function getLineSeparator($option = 'LineEndings') { return $this->exportOptions[$option] == 1 ? "\r\n" : "\n"; } /** * Returns field caption for any exported field * * @param string $field * @return string */ function getFieldCaption($field) { if (substr($field, 0, 10) == '__CUSTOM__') { $ret = 'Custom_'.substr($field, 10, strlen($field) ); } elseif (substr($field, 0, 12) == '__CATEGORY__') { return $this->getCategoryTitle(); } elseif (substr($field, 0, 11) == '__VIRTUAL__') { $ret = substr($field, 11); } else { $ret = $field; } return Array($ret); } /** * Returns requested field value (including custom fields and category fields) * * @param string $field * @return string */ function getFieldValue($field) { if (substr($field, 0, 10) == '__CUSTOM__') { $field = 'cust_'.substr($field, 10, strlen($field)); $ret = $this->curItem->GetField($field); } elseif (substr($field, 0, 12) == '__CATEGORY__') { return $this->getCategoryPath(); } elseif (substr($field, 0, 11) == '__VIRTUAL__') { $field = substr($field, 11); $ret = $this->curItem->GetField($field); } else { $ret = $this->curItem->GetField($field); } $ret = str_replace("\r\n", $this->getLineSeparator('LineEndingsInside'), $ret); return Array($ret); } /** * Returns category field(-s) caption based on export mode * * @return string */ function getCategoryTitle() { // category path in separated fields $category_count = $this->getMaxCategoryLevel(); if ($this->exportOptions['CategoryFormat'] == 1) { // category path in one field return $category_count ? Array('CategoryPath') : Array(); } else { $i = 0; $ret = Array(); while ($i < $category_count) { $ret[] = 'Category'.($i + 1); $i++; } return $ret; } } /** * Returns category path in required format for current link * * @return string */ function getCategoryPath() { $category_id = $this->curItem->GetDBField('CategoryId'); $category_path = $this->getFromCache('category_path', $category_id); if ( !$category_path ) { $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $sql = 'SELECT ' . $ml_formatter->LangFieldName('CachedNavbar') . ' FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . $category_id; $category_path = $this->Conn->GetOne($sql); $category_path = $category_path ? explode('&|&', $category_path) : Array (); if ( $category_path && strtolower($category_path[0]) == 'content' ) { array_shift($category_path); } if ( $this->exportOptions['IsBaseCategory'] ) { $i = $this->exportOptions['BaseLevel']; while ( $i > 0 ) { array_shift($category_path); $i--; } } $category_count = $this->getMaxCategoryLevel(); if ( $this->exportOptions['CategoryFormat'] == 1 ) { // category path in single field $category_path = $category_count ? Array (implode($this->exportOptions['CategorySeparator'], $category_path)) : Array (); } else { // category path in separated fields $levels_used = count($category_path); if ( $levels_used < $category_count ) { $i = 0; while ( $i < $category_count - $levels_used ) { $category_path[] = ''; $i++; } } } $this->addToCache('category_path', $category_id, $category_path); } return $category_path; } /** * Get maximal category deep level from links beeing exported * * @return int */ function getMaxCategoryLevel() { static $max_level = -1; if ($max_level != -1) { return $max_level; } $sql = 'SELECT IF(c.CategoryId IS NULL, 0, MAX( LENGTH(c.ParentPath) - LENGTH( REPLACE(c.ParentPath, "|", "") ) - 1 )) FROM '.$this->curItem->TableName.' item_table LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON item_table.ResourceId = ci.ItemResourceId LEFT JOIN '.TABLE_PREFIX.'Categories c ON c.CategoryId = ci.CategoryId WHERE (ci.PrimaryCat = 1) AND '; $where_clause = ''; if ($this->exportOptions['export_ids'] === false) { // get links from current category & all it's subcategories if ($this->exportOptions['export_cats_ids'][0] == 0) { $where_clause = 1; } else { foreach ($this->exportOptions['export_cats_ids'] as $category_id) { $where_clause .= '(c.ParentPath LIKE "%|'.$category_id.'|%") OR '; } $where_clause = substr($where_clause, 0, -4); } } else { // get only selected links $where_clause = $this->curItem->IDField.' IN ('.implode(',', $this->exportOptions['export_ids']).')'; } $max_level = $this->Conn->GetOne($sql.'('.$where_clause.')'); if ($this->exportOptions['IsBaseCategory'] ) { $max_level -= $this->exportOptions['BaseLevel']; } return $max_level; } /** * Saves one record to export file * * @param Array $fields_hash */ function writeRecord($fields_hash) { kUtil::fputcsv($this->filePointer, $fields_hash, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy'], $this->getLineSeparator() ); } function readRecord() { return fgetcsv($this->filePointer, 10000, $this->exportOptions['FieldsSeparatedBy'], $this->exportOptions['FieldsEnclosedBy']); } /** * Saves import/export options * * @param kEvent $event * @param Array $options * @return void */ function saveOptions($event, $options = null) { if ( !isset($options) ) { $options = $this->exportOptions; } $this->Application->StoreVar($event->getPrefixSpecial() . '_options', serialize($options)); } /** * Loads import/export options * * @param kEvent $event * @return Array */ function loadOptions($event) { return unserialize( $this->Application->RecallVar($event->getPrefixSpecial() . '_options') ); } /** * Sets correct available & export fields * * @param kEvent $event */ function prepareExportColumns($event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kCatDBItem */ if ( !$object->isField('ExportColumns') ) { // import/export prefix was used (see kDBEventHandler::prepareObject) but object don't plan to be imported/exported return; } $available_columns = Array (); if ( $event->getUnitConfig()->getCatalogItem() ) { // category field (mixed) $available_columns['__CATEGORY__CategoryPath'] = 'CategoryPath'; if ( $event->Special == 'import' ) { // category field (separated fields) $max_level = $this->Application->ConfigValue('MaxImportCategoryLevels'); $i = 0; while ( $i < $max_level ) { $available_columns['__CATEGORY__Category' . ($i + 1)] = 'Category' . ($i + 1); $i++; } } } // db fields $fields = $object->getFields(); foreach ($fields as $field_name => $field_options) { if ( !$object->skipField($field_name) ) { $available_columns[$field_name] = $field_name . ($object->isRequired($field_name) ? '*' : ''); } } $handler = $this->Application->recallObject($event->Prefix . '_EventHandler'); /* @var $handler kDBEventHandler */ $available_columns = array_merge($available_columns, $handler->getCustomExportColumns($event)); // custom fields $custom_fields = $object->getCustomFields(); foreach ($custom_fields as $custom_id => $custom_name) { $available_columns['__CUSTOM__' . $custom_name] = $custom_name; } // columns already in use $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { list($item_id, $field_values) = each($items_info); $export_keys = $field_values['ExportColumns']; $export_keys = $export_keys ? explode('|', substr($export_keys, 1, -1)) : Array (); } else { $export_keys = Array (); } $export_columns = Array (); foreach ($export_keys as $field_key) { $field_name = $this->getExportField($field_key); $export_columns[$field_key] = $field_name; unset($available_columns[$field_key]); } $options = $object->GetFieldOptions('ExportColumns'); $options['options'] = $export_columns; $object->SetFieldOptions('ExportColumns', $options); $options = $object->GetFieldOptions('AvailableColumns'); $options['options'] = $available_columns; $object->SetFieldOptions('AvailableColumns', $options); $this->updateImportFiles($event); $this->PrepareExportPresets($event); } /** * Prepares export presets * * @param kEvent $event * @return void */ function PrepareExportPresets($event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $options = $object->GetFieldOptions('ExportPresets'); $export_settings = $this->Application->RecallPersistentVar('export_settings'); if ( !$export_settings ) { return; } $export_settings = unserialize($export_settings); if ( !isset($export_settings[$event->Prefix]) ) { return; } $export_presets = array ('' => ''); foreach ($export_settings[$event->Prefix] as $key => $val) { $export_presets[implode('|', $val['ExportColumns'])] = $key; } $options['options'] = $export_presets; $object->SetFieldOptions('ExportPresets', $options); } function getExportField($field_key) { $prepends = Array('__CUSTOM__', '__CATEGORY__'); foreach ($prepends as $prepend) { if (substr($field_key, 0, strlen($prepend) ) == $prepend) { $field_key = substr($field_key, strlen($prepend), strlen($field_key) ); break; } } return $field_key; } /** * Updates uploaded files list * * @param kEvent $event * @return void * @access protected */ protected function updateImportFiles($event) { if ( $event->Special != 'import' ) { return ; } $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $import_filenames = Array (); $file_helper->CheckFolder(EXPORT_PATH); $iterator = new DirectoryIterator(EXPORT_PATH); /* @var $file_info DirectoryIterator */ foreach ($iterator as $file_info) { $file = $file_info->getFilename(); if ( $file_info->isDir() || $file == 'dummy' || $file_info->getSize() == 0 ) { continue; } $import_filenames[$file] = $file . ' (' . kUtil::formatSize( $file_info->getSize() ) . ')'; } $object = $event->getObject(); /* @var $object kDBItem */ $object->SetFieldOption('ImportLocalFilename', 'options', $import_filenames); } /** * Returns module folder * * @param kEvent $event * @return string */ function getModuleName($event) { $module_path = $event->getUnitConfig()->getModuleFolder() . '/'; $module_name = $this->Application->findModule('Path', $module_path, 'Name'); return mb_strtolower($module_name); } /** * Export form validation & processing * * @param kEvent $event */ function OnExportBegin($event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { $items_info = unserialize($this->Application->RecallVar($event->getPrefixSpecial() . '_ItemsInfo')); $this->Application->SetVar($event->getPrefixSpecial(true), $items_info); } list($item_id, $field_values) = each($items_info); $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->SetFieldsFromHash($field_values); $field_values['ImportFilename'] = $object->GetDBField('ImportFilename'); //if upload formatter has renamed the file during moving !!! $object->setID($item_id); $this->setRequiredFields($event); // save export/import options if ( $event->Special == 'export' ) { $export_ids = $this->Application->RecallVar($event->Prefix . '_export_ids'); $export_cats_ids = $this->Application->RecallVar($event->Prefix . '_export_cats_ids'); // used for multistep export $field_values['export_ids'] = $export_ids ? explode(',', $export_ids) : false; $field_values['export_cats_ids'] = $export_cats_ids ? explode(',', $export_cats_ids) : Array ($this->Application->GetVar('m_cat_id')); } $field_values['ExportColumns'] = $field_values['ExportColumns'] ? explode('|', substr($field_values['ExportColumns'], 1, -1) ) : Array(); $field_values['start_from'] = 0; $nevent = new kEvent($event->Prefix . ':OnBeforeExportBegin'); $nevent->setEventParam('options', $field_values); $this->Application->HandleEvent($nevent); $field_values = $nevent->getEventParam('options'); $this->saveOptions($event, $field_values); if ( $this->verifyOptions($event) ) { if ( $this->_getExportSavePreset($object) ) { $name = $object->GetDBField('ExportPresetName'); $export_settings = $this->Application->RecallPersistentVar('export_settings'); $export_settings = $export_settings ? unserialize($export_settings) : array (); $export_settings[$event->Prefix][$name] = $field_values; $this->Application->StorePersistentVar('export_settings', serialize($export_settings)); } $progress_t = $this->Application->RecallVar('export_progress_t'); if ( $progress_t ) { $this->Application->RemoveVar('export_progress_t'); } else { $progress_t = $this->getModuleName($event) . '/' . $event->Special . '_progress'; } $event->redirect = $progress_t; if ( $event->Special == 'import' ) { $import_category = (int)$this->Application->RecallVar('ImportCategory'); // in future could use module root category if import category will be unavailable :) $event->SetRedirectParam('m_cat_id', $import_category); // for template permission checking $this->Application->StoreVar('m_cat_id', $import_category); // for event permission checking } } else { // make uploaded file local & change source selection $filename = getArrayValue($field_values, 'ImportFilename'); if ( $filename ) { $this->updateImportFiles($event); $object->SetDBField('ImportSource', 2); $field_values['ImportSource'] = 2; $object->SetDBField('ImportLocalFilename', $filename); $field_values['ImportLocalFilename'] = $filename; $this->saveOptions($event, $field_values); } $event->status = kEvent::erFAIL; $event->redirect = false; } } /** * Returns export save preset name, when used at all * * @param kDBItem $object * @return string */ function _getExportSavePreset(&$object) { if ( !$object->isField('ExportSavePreset') ) { return ''; } return $object->GetDBField('ExportSavePreset'); } /** * set required fields based on import or export params * * @param kEvent $event */ function setRequiredFields($event) { $required_fields['common'] = Array('FieldsSeparatedBy', 'LineEndings', 'CategoryFormat'); $required_fields['export'] = Array('ExportFormat', 'ExportFilename','ExportColumns'); $object = $event->getObject(); /* @var $object kDBItem */ if ($this->_getExportSavePreset($object)) { $required_fields['export'][] = 'ExportPresetName'; } $required_fields['import'] = Array('FieldTitles', 'ImportSource', 'CheckDuplicatesMethod'); // ImportFilename, ImportLocalFilename if ($event->Special == 'import') { $import_source = Array(1 => 'ImportFilename', 2 => 'ImportLocalFilename'); $used_field = $import_source[ $object->GetDBField('ImportSource') ]; $required_fields[$event->Special][] = $used_field; $object->SetFieldOption($used_field, 'error_field', 'ImportSource'); if ($object->GetDBField('FieldTitles') == 2) $required_fields[$event->Special][] = 'ExportColumns'; // manual field titles } $required_fields = array_merge($required_fields['common'], $required_fields[$event->Special]); $object->setRequired($required_fields); } } \ No newline at end of file Index: branches/5.3.x/core/units/helpers/brackets_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/brackets_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/brackets_helper.php (revision 16111) @@ -1,476 +1,476 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kBracketsHelper extends kHelper { /** * Field name holding minimal amount * * @var string */ var $min_field = ''; /** * Field name holding maximal amount * * @var string */ var $max_field = ''; /** * Default values to be set to automtically created price brackets * * @var Array */ var $default_values = Array(); var $defaultStartValue = 1; /** * Decimal separator * * @var string */ var $_decimalSeparator = ''; /** * Thousands separator * * @var string */ var $_thousandsSeparator = ''; /** * Current language * * @var LanguagesItem */ var $_language = null; public function __construct() { parent::__construct(); $this->_language = $this->Application->recallObject('lang.current'); /* @var $lang kDBItem */ $this->_decimalSeparator = $this->_language->GetDBField('DecimalPoint'); $this->_thousandsSeparator = $this->_language->GetDBField('ThousandSep'); } function InitHelper($min_field, $max_field, $default_values, $default_start_value = null) { $this->min_field = $min_field; $this->max_field = $max_field; $this->default_values = $default_values; if (isset($default_start_value)) { $this->defaultStartValue = $default_start_value; } } /** * Converts number to operatable form * * @param string $value * @return float */ function _parseNumber($value) { $value = str_replace($this->_thousandsSeparator, '', $value); $value = str_replace($this->_decimalSeparator, '.', $value); return $value; } /** * Returns brackets from form with all numbers parsed * * @param kEvent $event * @return Array */ function getBrackets($event) { $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); return $this->parseBrackets($items_info); } function parseBrackets($brackets) { if (!$brackets) { return $brackets; } foreach ($brackets as $id => $field_values) { if (strlen($brackets[$id][$this->min_field])) { $brackets[$id][$this->min_field] = (float)$this->_parseNumber($brackets[$id][$this->min_field]); } if (strlen($brackets[$id][$this->max_field])) { $brackets[$id][$this->max_field] = (float)$this->_parseNumber($brackets[$id][$this->max_field]); } } return $brackets; } /** * Formats given brackets and sets them back to request * * @param kEvent $event * @param Array $brackets */ function setBrackets($event, $brackets) { $brackets = $this->formatBrackets($brackets); $this->Application->SetVar($event->getPrefixSpecial(true), $brackets); } function formatBrackets($brackets) { if (!$brackets) { return $brackets; } foreach ($brackets as $id => $field_values) { if (strlen($brackets[$id][$this->min_field])) { $brackets[$id][$this->min_field] = $this->_language->formatNumber($brackets[$id][$this->min_field]); } if (strlen($brackets[$id][$this->max_field])) { $brackets[$id][$this->max_field] = $this->_language->formatNumber($brackets[$id][$this->max_field]); } } return $brackets; } /** * Adds 5 more empty brackets to brackets * * @param kEvent $event */ function OnMoreBrackets($event) { $field_values = $this->getBrackets($event); $object = $event->getObject(); foreach($field_values as $id => $record) { if($record[$this->max_field] == '∞') $field_values[$id][$this->max_field] = -1; } $new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName); if($new_id > 0) $new_id = 0; do { $new_id--; }while( $this->arraySearch($field_values, $object->IDField, $new_id) ); $last_max_qty = $this->Conn->GetOne('SELECT MAX('.$this->max_field.') FROM '.$object->TableName); $min_qty = $this->Conn->GetOne('SELECT MIN('.$this->max_field.') FROM '.$object->TableName); if($min_qty == -1) $last_max_qty = -1; if(!$last_max_qty) $last_max_qty = $this->defaultStartValue; for($i = $new_id; $i > $new_id - 5; $i--) { $field_values[$i][$object->IDField] = $i; $field_values[$i][$this->min_field] = ($i == $new_id-4 && $last_max_qty != -1) ? $last_max_qty : ''; $field_values[$i][$this->max_field] = ($i == $new_id-4 && $last_max_qty != -1) ? -1 : ''; $field_values[$i] = array_merge($field_values[$i], $this->default_values); } $event->CallSubEvent('OnPreSaveBrackets'); $this->setBrackets($event, $field_values); } /** * Adds infinity bracket * * @param kEvent $event */ function OnInfinity($event) { $object = $event->getObject(); $infinite_exists = $this->Conn->GetOne('SELECT COUNT(*) FROM '.$object->TableName.' WHERE '.$this->max_field.' = -1'); $field_values = $this->getBrackets($event); /*if(is_array($field_values)) { foreach($field_values as $values) { $infinite_exists = $infinite_exists || ($values[$this->max_field] == -1); } }*/ if ($infinite_exists == 0) { reset($field_values); $last_bracket = end($field_values); $new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName); $brackets_exist = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM '.$object->TableName); if($new_id > 0) $new_id = 0; do { $new_id--; }while( $this->arraySearch($field_values, $object->IDField, $new_id) ); $infinite_bracket[$object->IDField] = $new_id; $infinite_bracket[$this->min_field] = ($brackets_exist > 0) ? $last_bracket[$this->max_field] : $this->defaultStartValue; $infinite_bracket[$this->max_field] = '-1'; $infinite_bracket = array_merge($infinite_bracket, $this->default_values); $field_values[$new_id] = $infinite_bracket; reset($field_values); $this->setBrackets($event, $field_values); } } /** * Saves brackets to database * * @param kEvent $event */ function OnPreSaveBrackets($event) { $items_info = $this->getBrackets($event); if ($items_info) { $object = $event->getObject(); /* @var $object kDBItem */ $linked_info = $object->getLinkedInfo(); $stored_ids = $this->Conn->GetCol('SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE '.$linked_info['ForeignKey'].' = '.$linked_info['ParentId']); uasort($items_info, Array(&$this, 'compareBrackets') ); foreach ($items_info as $item_id => $values) { if (in_array($item_id, $stored_ids)) { //if it's already exist $object->Load($item_id); - $object->SetFieldsFromHash($values/*, $this->getRequestProtectedFields($values)*/); + $object->SetFieldsFromHash($values); if (!$object->Validate()) { unset($stored_ids[array_search($item_id, $stored_ids)]); $event->redirect = false; continue; } if( $object->Update($item_id) ) { $event->status = kEvent::erSUCCESS; } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } unset( $stored_ids[ array_search($item_id, $stored_ids) ] ); } else { $object->Clear(); - $object->SetFieldsFromHash($values/*, $this->getRequestProtectedFields($values)*/); + $object->SetFieldsFromHash($values); $object->SetDBField($linked_info['ForeignKey'], $linked_info['ParentId']); if ($object->Create()) { $event->status = kEvent::erSUCCESS; } } } // delete foreach ($stored_ids as $stored_id) { $this->Conn->Query('DELETE FROM '.$object->TableName.' WHERE '.$object->IDField.' = '.$stored_id); } } } /** * Sorts brackets and returns sorted array * * @param kEvent $event * @return Array */ function arrangeBrackets($event) { $object = $event->getObject(); /* @var $object kDBItem */ $temp = $this->getBrackets($event); foreach ($temp as $id => $record) { if ( $record[$this->max_field] == '∞' ) { $temp[$id][$this->max_field] = -1; } } $temp_orig = $temp; reset($temp); if( is_array($temp) ) { // array to store max values (2nd column) $end_values = Array(); // get minimal value of Min $first_elem = current($temp); $start = $first_elem[$this->min_field]; if (!strlen($start)) { $start = $this->defaultStartValue; } foreach($temp as $id => $record) { if( // MAX is less than start ($record[$this->max_field] <= $start && $record[$this->max_field] != -1) || // Max is empty !strlen($record[$this->max_field]) || // Max already defined in $end_values (array_search($record[$this->max_field], $end_values) !== false) ) { // then delete from brackets list unset($temp[$id]); } else { // this is when ok - add to end_values list $end_values[] = $record[$this->max_field]; } } // sort brackets by 2nd column (Max values) uasort($temp, Array (&$this, 'compareBrackets')); reset($temp); $first_item = each($temp); $first_item_key = $first_item['key']; $linked_info = $object->getLinkedInfo(); $sql = 'SELECT %s FROM %s WHERE %s = %s'; $ids = $this->Conn->GetCol( sprintf($sql, $object->IDField, $object->TableName, $linked_info['ForeignKey'], $linked_info['ParentId']) ); if ( is_array($ids) ) { usort($ids, Array (&$this, 'sortBracketIDs')); } // $min_id = min( min($ids) - 1, -1 ); foreach ($temp as $key => $record) { $temp[$key][$this->min_field] = $start; $start = $temp[$key][$this->max_field]; } } $this->setBrackets($event, $temp); return $temp; } function compareBrackets($bracket1, $bracket2) // ap_bracket_comp { $bracket1_min = $bracket1[$this->min_field]; $bracket1_max = $bracket1[$this->max_field]; $bracket2_min = $bracket2[$this->min_field]; $bracket2_max = $bracket2[$this->max_field]; // limits if( ($bracket1_min != '') && ($bracket1_max == '') && ($bracket2_min != '') && ($bracket2_max != '') ) return 1; if( ($bracket1_min != '') && ($bracket1_max == '') && ($bracket2_min == '') && ($bracket2_max == '') ) return -1; if( ($bracket1_max == '') && ($bracket2_max != '') ) return 1; if( ($bracket1_max != '') && ($bracket2_max == '') ) return -1; if( ( ($bracket1_max > $bracket2_max) && ($bracket2_max != -1) ) || ( ($bracket1_max == -1) && ($bracket2_max != -1) ) ) { return 1; } elseif( ($bracket1_max < $bracket2_max) || ( ($bracket2_max == -1) && ($bracket1_max != -1) ) ) { return -1; } else { return 0; } } function sortBracketIDs($first_id, $second_id) // pr_bracket_id_sort { $first_abs = abs($first_id); $second_abs = abs($second_id); $first_sign = ($first_id == 0) ? 0 : $first_id / $first_abs; $second_sign = ($second_id == 0) ? 0 : $second_id / $second_abs; if($first_sign != $second_sign) { if($first_id > $second_id) { $bigger =& $first_abs; $smaller =& $second_abs; } else { $bigger =& $second_abs; $smaller =& $first_abs; } $smaller = $bigger + $smaller; } return ($first_abs > $second_abs) ? 1 : ($first_abs < $second_abs ? -1 : 0); } /** * Searches through submitted grid data to find record with specific value in specific field * * @param Array $records // grid data from REQUEST * @param string $field * @param string $value * @return bool */ function arraySearch($records, $field, $value) // check_array { foreach ($records as $record) { if ($record[$field] == $value) { return true; } } return false; } /** * Replace infinity mark with -1 before saving to db * * @param kEvent $event */ function replaceInfinity($event) { $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField($this->max_field) == '∞' ) { $object->SetDBField($this->max_field, -1); } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/form_submission_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/form_submission_helper.php (revision 16110) +++ branches/5.3.x/core/units/helpers/form_submission_helper.php (revision 16111) @@ -1,161 +1,160 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FormSubmissionHelper extends kHelper { /** * Role names for easy usage via FormField tag * * @var Array */ var $roleNames = Array ( 'name' => SubmissionFormField::COMMUNICATION_ROLE_NAME, 'email' => SubmissionFormField::COMMUNICATION_ROLE_EMAIL, 'subject' => SubmissionFormField::COMMUNICATION_ROLE_SUBJECT, 'body' => SubmissionFormField::COMMUNICATION_ROLE_BODY, ); /** * Returns submission field based on given role * * @param kDBItem $form_submission * @param string $role * @param bool $formatted * @param string $format * @return string * @access public */ public function getFieldByRole(&$form_submission, $role, $formatted = false, $format = null) { $form_id = $form_submission->GetDBField('FormId'); $field_name = $this->getFieldNameByRole($form_id, $role); if ( $field_name ) { return $formatted ? $form_submission->GetField($field_name, $format) : $form_submission->GetDBField($field_name); } return false; } /** * Returns submission field name based on given role * * @param int $form_id * @param string $role * @return string * @access public */ public function getFieldNameByRole($form_id, $role) { static $cache = Array (); if ( !array_key_exists($form_id, $cache) ) { $config = $this->Application->getUnitConfig('formflds'); $sql = 'SELECT ' . $config->getIDField() . ', EmailCommunicationRole FROM ' . $config->getTableName() . ' WHERE FormId = ' . $form_id . ' AND EmailCommunicationRole <> 0'; $cache[$form_id] = $this->Conn->GetCol($sql, 'EmailCommunicationRole'); } // convert string representation of role to numeric if ( !is_numeric($role) ) { $role = strtolower($role); $role = array_key_exists($role, $this->roleNames) ? $this->roleNames[$role] : false; } // get field name by role return array_key_exists($role, $cache[$form_id]) ? 'fld_' . $cache[$form_id][$role] : false; } /** * Returns submission field based on given name * * @param kDBItem $form_submission * @param string $name * @param bool $formatted * @param string $format * @return string - * @todo Might not work correctly! */ function getFieldByName(&$form_submission, $name, $formatted = false, $format = null) { static $cache = Array (); $form_id = $form_submission->GetDBField('FormId'); if ( !array_key_exists($form_id, $cache) ) { $config = $this->Application->getUnitConfig('formflds'); $sql = 'SELECT ' . $config->getIDField() . ', FieldName FROM ' . $config->getTableName() . ' WHERE FormId = ' . $form_id; $cache[$form_id] = $this->Conn->GetCol($sql, 'FieldName'); } // get field by name $field_id = array_key_exists($name, $cache[$form_id]) ? $cache[$form_id][$name] : false; if ( $field_id ) { return $formatted ? $form_submission->GetField('fld_' . $field_id, $format) : $form_submission->GetDBField('fld_' . $field_id); } return false; } /** * Returns form object field based on form submission * * @param $form_submission kDBItem * @return kDBItem */ function &getForm(&$form_submission) { $form_id = $form_submission->GetDBField('FormId'); $form = $this->Application->recallObject('form', null, Array ('skip_autoload' => true)); /* @var $form kDBItem */ if ( !$form->isLoaded() || ($form->GetID() != $form_id) ) { $form->Load($form_id); } return $form; } /** * Returns form submission based on given submission log * * @param kDBItem $submission_log * @return kDBItem * @access public */ public function getSubmissionFromLog($submission_log) { $submission_id = $submission_log->GetDBField('FormSubmissionId'); $form_submission = $this->Application->recallObject('formsubs.-item', null, Array ('skip_autoload' => true)); /* @var $form_submission kDBItem */ if ( $form_submission->isLoaded() && ($form_submission->GetID() == $submission_id) ) { return $form_submission; } $form_submission->Load($submission_id); return $form_submission; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/navigation_bar.php =================================================================== --- branches/5.3.x/core/units/helpers/navigation_bar.php (revision 16110) +++ branches/5.3.x/core/units/helpers/navigation_bar.php (revision 16111) @@ -1,369 +1,381 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2012 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kNavigationBar extends kBase { /** * Parameters to indicate how exactly navigation bar should look like * * @var Array * @access protected */ protected $_params = Array (); /** * Prints category path using given blocks. Also supports used defined path elements at the end. * * @param Array $params * @return string */ public function build($params) { // elements: // - current_render_as - currently selected element (automatic detection) // - render_as - link to a regular template // - category_render_as - link to category path element // - custom_render_as - link to custom element (that have "__" in front of them) // - root_cat_render_as - link to Home page $this->_params = $params; $this->_params['is_first'] = 1; $home_element = $this->_getHomeElement(); if ( !getArrayValue($this->_params, 'titles') && !getArrayValue($this->_params, 'templates') ) { // no static templates given, show only category path return $home_element . $this->getCategoryPath(); } $ret = ''; $block_params = $this->_getBaseParams(); $current_template = $this->_getCurrentTemplate(); $navigation_parts = $this->getNavigationParts(); foreach ($navigation_parts as $template => $title) { $block_params['template'] = $template; if ( $title == '__categorypath__' ) { $ret .= $this->getCategoryPath(); } else { $is_current = $template == $current_template; $block_params['current'] = $is_current; if ( substr($title, 0, 2) == '__' ) { $block_params['title'] = $title; $block_params['name'] = $this->SelectParam($this->_params, 'custom_render_as,render_as'); } else { $block_params['title'] = $this->Application->Phrase($title); $block_params['name'] = $this->_params[$is_current ? 'current_render_as' : 'render_as']; } $ret .= $this->Application->ParseBlock($block_params); } } return $home_element . $ret; } /** * Returns base params for rendering each navigation bar element * * @return Array * @access protected */ protected function _getBaseParams() { $block_params = Array ( 'no_editing' => 1, 'category' => 0, 'separator' => $this->_params['separator'], 'current' => 0, ); return $block_params; } /** * Returns the name of current physical template * * @return string * @access protected */ protected function _getCurrentTemplate() { return $this->Application->getPhysicalTemplate($this->Application->GetVar('t')); } /** * Returns element for "Home" category * * @return string * @access protected */ protected function _getHomeElement() { if ( isset($this->_params['shift']) && $this->_params['shift'] ) { $home_element = ''; $this->_params['shift']--; } else { $home_element = $this->_getHomeCategoryPath(); unset($this->_params['is_first']); } return $home_element; } /** * Renders path to top catalog category * * @return string * @access protected */ protected function _getHomeCategoryPath() { $block_params = $this->_getBaseParams(); $block_params['cat_id'] = $this->Application->getBaseCategory(); $block_params['current'] = $this->_getCurrentCategoryId() == $block_params['cat_id'] ? 1 : 0; $block_params['is_first'] = $this->_params['is_first']; $block_params['template'] = ''; // to prevent warning when category element is rendered using general "render_as" block $category_name = $this->Application->Phrase(($this->Application->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name'); $block_params['cat_name'] = $block_params['title'] = $category_name; $block_params['name'] = $this->SelectParam($this->_params, 'root_cat_render_as,category_render_as,render_as'); if ( $block_params['current'] ) { $block_params['name'] = $this->SelectParam($this->_params, 'current_render_as,render_as'); } return $this->Application->ParseBlock($block_params); } /** * Returns currently selected category * * @return mixed */ protected function _getCurrentCategoryId() { return isset($this->_params['cat_id']) ? $this->_params['cat_id'] : $this->Application->GetVar('m_cat_id'); } /** * Get navigation parts * * @return Array * @access protected */ protected function getNavigationParts() { $titles = explode(',', $this->_params['titles']); $templates = explode(',', $this->_params['templates']); if ( getArrayValue($this->_params, 'show_category') && !in_array('__categorypath__', $titles) ) { // insert before __item__ or first element, when __item__ isn't specified $item_index = (int)array_search('__item__', $titles); array_splice($titles, $item_index, 0, '__categorypath__'); array_splice($templates, $item_index, 0, '__categorypath__'); } return array_combine($templates, $titles); } /** * Renders path to given category using given blocks. * * @return string * @access protected */ protected function getCategoryPath() { $category_path = $this->getCategoryParentPath(); if ( !$category_path ) { // in "Home" category return ''; } $main_category_id = $this->_getCurrentCategoryId(); - if ( isset($this->_params['shift']) && $this->_params['shift'] ) { - array_splice($category_path, 0, $this->_params['shift']); - } - $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $module_info = $category_helper->getCategoryModule($this->_params, array_keys($category_path)); $module_item_id = $this->Application->GetVar($module_info['Var'] . '_id'); $ret = ''; $block_params = $this->_getBaseParams(); $block_params['category'] = 1; $block_params['template'] = ''; // to prevent warning when category element is rendered using general "render_as" block if ( isset($this->_params['is_first']) ) { $block_params['is_first'] = $this->_params['is_first']; } $block_params['separator'] = $this->_params['separator']; $no_current = isset($this->_params['no_current']) && $this->_params['no_current']; $backup_category_id = $this->Application->GetVar('c_id'); - foreach ($category_path as $category_id => $category_name) { + foreach ($this->shiftCategoryPath($category_path) as $category_id => $category_name) { $block_params['cat_id'] = $category_id; $block_params['cat_name'] = $block_params['title'] = $category_name; if ( $no_current ) { $block_params['current'] = 0; } else { $block_params['current'] = ($main_category_id == $category_id) && !$module_item_id ? 1 : 0; } $block_params['name'] = $this->SelectParam($this->_params, 'category_render_as,render_as'); if ( $block_params['current'] ) { $block_params['name'] = $this->SelectParam($this->_params, 'current_render_as,render_as'); } $this->Application->SetVar('c_id', $category_id); $ret .= $this->Application->ParseBlock($block_params); if ( array_key_exists('is_first', $block_params) ) { unset($block_params['is_first']); } } $this->Application->SetVar('c_id', $backup_category_id); return $ret; } /** + * Shift category path. + * + * @param array $category_path Category path. + * + * @return array + */ + protected function shiftCategoryPath(array $category_path) + { + if ( isset($this->_params['shift']) && $this->_params['shift'] ) { + return array_slice($category_path, $this->_params['shift'], null, true); + } + + return $category_path; + } + + /** * Returns given category's parent path as array of id=>name elements * * @return Array * @access protected */ protected function getCategoryParentPath() { $main_category_id = $this->_getCurrentCategoryId(); if ( $main_category_id == 0 ) { // don't query path for "Home" category return Array (); } $category_title = isset($this->_params['category_title']) ? $this->_params['category_title'] : 'Name'; $cache_key = 'parent_paths_named[%CIDSerial:' . $main_category_id . '%]:' . $category_title; $cached_path = $this->Application->getCache($cache_key); if ( $cached_path === false ) { $parent_path = explode('|', substr($this->getParentPath($main_category_id), 1, -1)); $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $navbar_field = $ml_formatter->LangFieldName($category_title); $config = $this->Application->getUnitConfig('c'); $id_field = $config->getIDField(); $this->Conn->nextQueryCachable = true; $sql = 'SELECT ' . $navbar_field . ', ' . $id_field . ' FROM ' . $config->getTableName() . ' WHERE ' . $id_field . ' IN (' . implode(',', $parent_path) . ')'; $category_names = $this->Conn->GetCol($sql, $id_field); $cached_path = Array (); $skip_category = $this->Application->getBaseCategory(); if ( $category_names ) { foreach ($parent_path as $category_id) { if ( $category_id == $skip_category ) { continue; } $cached_path[$category_id] = $category_names[$category_id]; } } $this->Application->setCache($cache_key, $cached_path); } return $cached_path; } /** * Returns parent path from a given category * * @param int $category_id * @return string * @access public */ public function getParentPath($category_id) { $cache_key = 'parent_paths[%CIDSerial:' . $category_id . '%]'; $parent_path = $this->Application->getCache($cache_key); if ( $parent_path !== false ) { return $parent_path; } $config = $this->Application->getUnitConfig('c'); $this->Conn->nextQueryCachable = true; $sql = 'SELECT ParentPath FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' = ' . $category_id; $parent_path = $this->Conn->GetOne($sql); $this->Application->setCache($cache_key, $parent_path); return $parent_path; } /** * Not tag. Method for parameter selection from list in this TagProcessor * * @param Array $params * @param Array $possible_names * * @return string * @access protected */ protected function SelectParam($params, $possible_names) { if ( !is_array($params) ) { return ''; } if ( !is_array($possible_names) ) { $possible_names = explode(',', $possible_names); } foreach ($possible_names as $name) { if ( isset($params[$name]) ) { return $params[$name]; } } return ''; } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/selectors/selectors_event_handler.php =================================================================== --- branches/5.3.x/core/units/selectors/selectors_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/selectors/selectors_event_handler.php (revision 16111) @@ -1,457 +1,462 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class SelectorsEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnResetToBase' => Array('subitem' => 'add|edit'), 'OnMassResetToBase' => Array('subitem' => 'add|edit'), 'OnOpenStyleEditor' => Array('subitem' => 'add|edit'), 'OnSaveStyle' => Array('subitem' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Occurs before an item has been cloned * Id of newly created item is passed as event' 'id' param * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeClone(kEvent $event) { parent::OnBeforeClone($event); $event->Init($event->Prefix, '-item'); $object = $event->getObject(); /* @var $object kDBItem */ $title_field = 'SelectorName'; $new_name = $object->GetDBField($title_field); $original_checked = false; $foreign_key = $event->getEventParam('foreign_key'); // in case if whole stylesheet is cloned if ( $foreign_key === false ) { $foreign_key = $object->GetDBField('StylesheetId'); } // in case if selector is copied ifself do { if ( preg_match('/(.*)-([\d]+)/', $new_name, $regs) ) { $new_name = $regs[1] . '-' . ($regs[2] + 1); } elseif ( $original_checked ) { $new_name = $new_name . '-1'; } // 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 ' . $object->TableName . ' WHERE ' . $title_field . ' = ' . $this->Conn->qstr($new_name) . ' AND StylesheetId = ' . $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 ); $object->SetDBField($title_field, $new_name); } /** * Show base styles or block styles * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ switch ($event->Special) { case 'base': $object->addFilter('type_filter', '%1$s.Type = 1'); break; case 'block': $object->addFilter('type_filter', '%1$s.Type = 2'); break; } } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->SerializeSelectorData($event); } /** * Occurs before creating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->SerializeSelectorData($event); } /** * Occurs after updating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { parent::OnAfterItemUpdate($event); $this->UnserializeSelectorData($event); } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $this->UnserializeSelectorData($event); } /** * Returns special of main item for linking with sub-item * * @param kEvent $event * @return string * @access protected */ protected function getMainSpecial(kEvent $event) { return ''; } /** * Save css-style name & description before opening css editor * * @param kEvent $event */ function OnOpenStyleEditor($event) { $this->SaveChanges($event); $event->redirect = false; } /** * Saves Changes to Item * * @param kEvent $event */ function SaveChanges($event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( $items_info ) { list($id, $field_values) = each($items_info); if ( $id == 0 ) { $parent_id = getArrayValue($field_values, 'ParentId'); if ( $parent_id ) { $object->Load($parent_id); } - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->setID(0); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $object->Create(); $this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID()); } else { $object->Load($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $object->Update(); } } } /** * Save style changes from style editor * * @param kEvent $event */ function OnSaveStyle($event) { $this->SaveChanges($event); $object = $event->getObject(); $this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetId() ); $this->finalizePopup($event); } /** * Extract styles * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( $selector_data ) { $selector_data = unserialize($selector_data); $object->SetDBField('SelectorData', $selector_data); } else { $selector_data = Array (); } $this->AddParentProperties($event, $selector_data); } /** * Serialize item before saving to db * * @param kEvent $event */ function SerializeSelectorData($event) { $object = $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( !$selector_data ) { $selector_data = Array (); } $selector_data = $this->RemoveParentProperties($event, $selector_data); if ( !kUtil::IsSerialized($selector_data) ) { $selector_data = serialize($selector_data); } $object->SetDBField('SelectorData', $selector_data); } /** * Unserialize data back when update was made * * @param kEvent $event */ function UnserializeSelectorData($event) { $object = $event->getObject(); /* @var $object kDBItem */ $selector_data = $object->GetDBField('SelectorData'); if ( !$selector_data ) { $selector_data = Array (); } if ( kUtil::IsSerialized($selector_data) ) { $selector_data = unserialize($selector_data); } $selector_data = $this->AddParentProperties($event, $selector_data); $object->SetDBField('SelectorData', $selector_data); } /** * Populate options based on temporary table :) * * @param kEvent $event */ function OnPrepareBaseStyles($event) { $object = $event->getObject(); $parent_info = $object->getLinkedInfo(); $title_field = $event->getUnitConfig()->getTitleField(); $sql = 'SELECT '.$title_field.', '.$object->IDField.' FROM '.$object->TableName.' WHERE Type = 1 AND StylesheetId = '.$parent_info['ParentId'].' ORDER BY '.$title_field; $options = $this->Conn->GetCol($sql,$object->IDField); $object->SetFieldOption('ParentId', 'options', $options); } /** * Remove properties of parent style that match by value from style * * @param kEvent $event * @param Array $selector_data * @return Array */ function RemoveParentProperties($event, $selector_data) { $object = $event->getObject(); /* @var $object kDBItem */ $parent_id = $object->GetDBField('ParentId'); if ( $parent_id ) { $sql = 'SELECT SelectorData FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $parent_id; $base_selector_data = $this->Conn->GetOne($sql); if ( kUtil::IsSerialized($base_selector_data) ) { $base_selector_data = unserialize($base_selector_data); } foreach ($selector_data as $prop_name => $prop_value) { if ( !$prop_value || getArrayValue($base_selector_data, $prop_name) == $prop_value ) { unset($selector_data[$prop_name]); } } } else { foreach ($selector_data as $prop_name => $prop_value) { if ( !$prop_value ) { unset($selector_data[$prop_name]); } } } $object->SetDBField('SelectorData', $selector_data); return $selector_data; } /** * Add back properties from parent style, that match this style property values * * @param kEvent $event * @param Array $selector_data * @return Array */ function AddParentProperties($event, $selector_data) { $object = $event->getObject(); /* @var $object kDBItem */ $parent_id = $object->GetDBField('ParentId'); if ( $parent_id ) { $sql = 'SELECT SelectorData FROM ' . $object->TableName . ' WHERE ' . $object->IDField . ' = ' . $parent_id; $base_selector_data = $this->Conn->GetOne($sql); if ( kUtil::IsSerialized($base_selector_data) ) { $base_selector_data = unserialize($base_selector_data); } $selector_data = kUtil::array_merge_recursive($base_selector_data, $selector_data); $object->SetDBField('SelectorData', $selector_data); return $selector_data; } return Array (); } /** * Reset Style definition to base style -> no customizations * * @param kEvent $event */ function OnResetToBase($event) { $object = $event->getObject(); /* @var $object SelectorsItem */ $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); + $object->ResetStyle(); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); } /** * Resets selected styles properties to values of their base classes * * @param kEvent $event */ function OnMassResetToBase($event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object SelectorsItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ( $items_info ) { foreach ($items_info as $id => $field_values) { $object->Load($id); $object->ResetStyle(); } } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/images/image_tag_processor.php =================================================================== --- branches/5.3.x/core/units/images/image_tag_processor.php (revision 16110) +++ branches/5.3.x/core/units/images/image_tag_processor.php (revision 16111) @@ -1,504 +1,504 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ImageTagProcessor extends kDBTagProcessor { /** * Prepares all image parameters as list block parameters (for easy usage) * * @param kDBList $object * @param Array $block_params * @return void * @access protected * @author Alex */ protected function PrepareListElementParams(&$object, &$block_params) { $image_url = $this->ImageSrc($block_params); if ( !$image_url ) { return ; } $parent_prefix = $object->getUnitConfig()->getParentPrefix(); $parent_item = $this->Application->recallObject($parent_prefix); /* @var $parent_item kDBItem */ $block_params['img_path'] = $image_url; $image_dimensions = $this->ImageSize($block_params); $block_params['img_size'] = $image_dimensions ? $image_dimensions : ' width="' . $block_params['DefaultWidth'] . '"'; $block_params['alt'] = $object->GetField('AltName') ? $object->GetField('AltName') : $this->getItemTitle($parent_item); $block_params['align'] = array_key_exists('align', $block_params) ? $block_params['align'] : 'left'; // TODO: consider escaping in template instead $block_params['alt'] = kUtil::escape($block_params['alt']); } /** * Returns value of object's title field * * @param kDBItem $object * @return string * @access protected */ protected function getItemTitle(&$object) { $title_field = $object->getUnitConfig()->getTitleField(); return $object->GetField($title_field); } /** * [AGGREGATED TAGS] works as <inp2:CatalogItemPrefix_Image, ImageSize, ImageSrc ..../> * * @param Array $params * @return string */ function ItemImageTag($params) { $this->LoadItemImage($params); return $this->$params['original_tag']($params); } function LargeImageExists($params) { $object = $this->getObject($params); if ($object->GetDBField('SameImages') == null || $object->GetDBField('SameImages') == 1) { return false; } else { return true; } } function LoadItemImage($params) { $parent_item = $this->Application->recallObject($params['PrefixSpecial']); /* @var $parent_item kCatDBItem */ $object = $this->Application->recallObject($this->getPrefixSpecial(), null, Array('skip_autoload' => true)); /* @var $object kDBItem */ $object->Clear(); // if we need primary thumbnail which is preloaded with category item's list $is_primary = $this->SelectParam($params, 'primary,Primary'); $image_name = $this->SelectParam($params, 'name,Name'); $image_field = $this->SelectParam($params, 'field,Field'); // ie. virtual names PrimaryImage, Image1, Image2 $image_id = $this->Application->GetVar($this->Prefix.'_id'); if ( // is primary, when primary mark set OR name & field not given ($is_primary || !($image_name || $image_field)) && // primary image is preloaded AND direct id not given $parent_item->isField('ThumbPath') && !$image_id ) { if (is_null($parent_item->GetDBField('SameImages'))) { // JOIN definetly failed, because it's not-null column $object->setLoaded(false); } else { $object->SetDBField('Url', $parent_item->GetDBField('FullUrl')); - $object->SetDBFieldsFromHash($parent_item->GetFieldValues(), null, Array('AltName', 'SameImages', 'LocalThumb', 'ThumbPath', 'ThumbUrl', 'LocalImage', 'LocalPath')); + $object->SetDBFieldsFromHash($parent_item->GetFieldValues(), Array('AltName', 'SameImages', 'LocalThumb', 'ThumbPath', 'ThumbUrl', 'LocalImage', 'LocalPath')); if (!$object->GetDBField('AltName')) { $object->SetDBField('AltName', $this->getItemTitle($parent_item)); } $object->setLoaded(); } } else { // if requested image is not primary thumbnail - load it directly $config = $this->getUnitConfig(); $id_field = $config->getForeignKey(); $parent_table_key = $config->getParentTableKey(); $keys[$id_field] = $parent_item->GetDBField($parent_table_key); // which image to load? if ($is_primary) { // by PrimaryImage mark $keys['DefaultImg'] = 1; } elseif ($image_name) { // by ImageName $keys['Name'] = $image_name; } elseif ($image_field) { // by virtual field name in main object $field_options = $parent_item->GetFieldOptions( $image_field ); $keys['Name'] = isset($field_options['original_field']) ? $field_options['original_field'] : $image_field; } elseif ($image_id) { // by ID $keys['ImageId'] = $image_id; } else { // by PrimaryImage if no other criteria given $keys['DefaultImg'] = 1; } $object->Load($keys); if ( $image_field ) { $image_src = $parent_item->GetDBField( $image_field ); // when image is uploaded to virtual field in main item, but not saved to db $object->SetDBField('ThumbPath', $image_src); if (!$object->isLoaded() && $image_src) { // set fields for displaying new image during main item suggestion with errors $fields_hash = Array ( 'Url' => '', 'ThumbUrl' => '', 'LocalPath' => '', 'SameImages' => 1, 'LocalThumb' => 1, 'LocalImage' => 1, ); $object->SetDBFieldsFromHash($fields_hash); $object->setLoaded(); } } } } function getImageDimension($type, $params) { $ret = isset($params['Max'.$type]) ? $params['Max'.$type] : false; if (!$ret) { return $ret; } $parent_prefix = $this->getUnitConfig()->getParentPrefix(); if ($ret == 'thumbnail') { $ret = $this->Application->ConfigValue($parent_prefix.'_ThumbnailImage'.$type); } if ($ret == 'fullsize') { $ret = $this->Application->ConfigValue($parent_prefix.'_FullImage'.$type); } return $ret; } /** * Appends "/" to beginning of image path (in case when missing) * * @param kDBItem $object * @todo old in-portal doesn't append first slash, but we do => append first slash for him :) */ function makeRelativePaths(&$object) { $thumb_path = $object->GetDBField('ThumbPath'); if ($thumb_path && substr($thumb_path, 0, 1) != DIRECTORY_SEPARATOR) { $object->SetDBField('ThumbPath', DIRECTORY_SEPARATOR . $thumb_path); } $local_path = $object->GetDBField('LocalPath'); if ($local_path && substr($local_path, 0, 1) != DIRECTORY_SEPARATOR) { $object->SetDBField('LocalPath', DIRECTORY_SEPARATOR . $local_path); } } function ImageSrc($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $this->makeRelativePaths($object); // show "noimage.gif" when requested image is missing OR was not uploaded $use_default_image = !(defined('DBG_IMAGE_RECOVERY') && DBG_IMAGE_RECOVERY); $src_image_url = $this->_getImageUrl($params); $src_image = $this->_getImagePath($src_image_url); if (!$object->isLoaded() || ($src_image_url && $src_image)) { // we can auto-resize image, when it is stored locally $max_width = $this->getImageDimension('Width', $params); $max_height = $this->getImageDimension('Height', $params); $format = array_key_exists('format', $params) ? $params['format'] : false; if (!$max_width && $format) { // user watermarks from format param $max_width = $format; } if ($max_width > 0 || $max_height > 0 || $format) { list ($max_width, $max_height) = $this->_transformParams($params, $max_width, $max_height); if ($object->isLoaded() && file_exists($src_image)) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ return $image_helper->ResizeImage($src_image, $max_width, $max_height); } elseif ($use_default_image) { return $this->_getDefaultImage($params, $max_width, $max_height); } return $src_image_url; } } if ($src_image_url) { // convert full url to full path! $dst_image = $this->_getImagePath($src_image_url); $image_found = $dst_image ? file_exists($dst_image) : true; if ($image_found) { // image isn't deleted OR is stored on remote location return $src_image_url; } } // return Default Image or false if NOT specified (only for case, when SameImages = 0) return $use_default_image ? $this->_getDefaultImage($params) : $src_image_url; } /** * Get location on disk for images, stored locally and false for remote images * * @param string $src_image * @return string */ function _getImagePath($src_image) { if (!$src_image) { return false; } $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $dst_image = $file_helper->urlToPath($src_image); return $dst_image != $src_image ? $dst_image : false; } function _getImageUrl($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $base_url = rtrim($this->Application->BaseURL(), '/'); // if we need thumbnail, or full image is same as thumbnail $show_thumbnail = $this->SelectParam($params, 'thumbnail,Thumbnail') || // old style (isset($params['MaxWidth']) && $params['MaxWidth'] == 'thumbnail') || // new style (isset($params['MaxHeight']) && $params['MaxHeight'] == 'thumbnail'); if ($show_thumbnail || $object->GetDBField('SameImages')) { // return local image or url $ret = $object->GetDBField('LocalThumb') ? $base_url . $object->GetDBField('ThumbPath') : $object->GetDBField('ThumbUrl'); } else { // if we need full which is not the same as thumb $ret = $object->GetDBField('LocalImage') ? $base_url . $object->GetDBField('LocalPath') : $object->GetDBField('Url'); } return $ret == $base_url ? '' : $ret; } /** * Transforms Image/ImageSrc aggregated tag parameters into ones, that ResizeImage method understands * * @param Array $params * @param int|bool $max_width * @param int|bool $max_height * @return Array */ function _transformParams($params, $max_width = false, $max_height = false) { $resize_format = 'resize:' . $max_width . 'x' . $max_height; $crop = $this->SelectParam($params, 'Crop,crop'); if ($crop) { - if (strpos($crop, ';') === false) { + if (strpos($crop, '|') === false) { $crop = 'c|c'; } $max_width = (is_null($max_height) ? $max_width : $resize_format) . ';crop:' . $crop; $max_height = null; } $fill = $this->SelectParam($params, 'Fill,fill'); if ($fill) { $max_width = (is_null($max_height) ? $max_width : $resize_format) . ';fill:' . $fill; $max_height = null; } $watermark = $this->SelectParam($params, 'Watermark,watermark'); if ($watermark) { $max_width = (is_null($max_height) ? $max_width : $resize_format) . ';wm:' . $watermark; $max_height = null; } return Array ($max_width, $max_height); } /** * Returns default full url to default images * * @param Array $params * @param int|bool $max_width * @param int|bool $max_height * @return string */ function _getDefaultImage($params, $max_width = false, $max_height = false) { $default_image = $this->SelectParam($params, 'default_image,DefaultImage'); if (!$default_image) { return ''; } // show default image, use different base urls for admin and front-end $base_url = rtrim($this->Application->BaseURL(), '/'); $sub_folder = $this->Application->isAdmin ? rtrim(IMAGES_PATH, '/') : THEMES_PATH; if (($max_width !== false) || ($max_height !== false)) { $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $src_image = FULL_PATH . $sub_folder . '/' . $default_image; return $image_helper->ResizeImage($src_image, $max_width, $max_height); } return $base_url . $sub_folder . '/' . $default_image; } function getFullPath($path) { if (!$path) { return $path; } // absolute url if (preg_match('/^(.*):\/\/(.*)$/U', $path)) { $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ return $file_helper->urlToPath($path); } // TODO: change to urlToPath usage later // relative url (we add sort of <inp2:m_TemplatesBase/> does - return FULL_PATH . '/' . mb_substr(THEMES_PATH, 1) . '/' . rawurldecode($path); + return FULL_PATH . '/' . mb_substr(THEMES_PATH, 1) . '/' . kUtil::unescape($path, kUtil::ESCAPE_URL); } /** * Makes size clause for img tag, such as * ' width="80" height="100"' according to max_width * and max_heght limits. * * @param array $params * @return string */ function ImageSize($params) { $img_path = $this->getFullPath($params['img_path']); $image_helper = $this->Application->recallObject('ImageHelper'); /* @var $image_helper ImageHelper */ $max_width = $this->getImageDimension('Width', $params); $max_height = $this->getImageDimension('Height', $params); $image_dimensions = $image_helper->GetImageDimensions($img_path, $max_width, $max_height, $params); if (!$image_dimensions) { return false; } return ' width="'.$image_dimensions[0].'" height="'.$image_dimensions[1].'"'; } /** * Prepares image parameters & parses block with them (for admin) * * @param Array $params * @return string * @access protected */ protected function Image($params) { $image_url = $this->ImageSrc($params); if ( !$image_url ) { return ''; } $object = $this->getObject($params); /* @var $object kDBItem */ $params['img_path'] = $image_url; $image_dimensions = $this->ImageSize($params); $params['img_size'] = $image_dimensions ? $image_dimensions : ' width="' . $params['DefaultWidth'] . '"'; $params['alt'] = $object->GetField('AltName'); // really used ? $params['name'] = $this->SelectParam($params, 'block,render_as'); $params['align'] = array_key_exists('align', $params) ? $params['align'] : 'left'; $params['no_editing'] = 1; if ( !$object->isLoaded() && !$this->SelectParam($params, 'default_image,DefaultImage') ) { return ''; } // TODO: consider escaping in template instead $params['alt'] = kUtil::escape($params['alt']); return $this->Application->ParseBlock($params); } /** * Returns url for image in case when image source is url (for admin) * * @param Array $params * @return string */ function ImageUrl($params) { $object = $this->getObject($params); if ($object->GetDBField('SameImages') ? $object->GetDBField('LocalThumb') : $object->GetDBField('LocalImage') ) { $ret = $this->Application->Phrase(getArrayValue($params,'local_phrase')); } else { $ret = $object->GetDBField('SameImages') ? $object->GetDBField('ThumbUrl') : $object->GetDBField('Url'); } return $ret; } /** * If data was modfied & is in TempTables mode, then parse block with name passed; * remove modification mark if not in TempTables mode * * @param Array $params * @return string * @access public * @author Alexey */ function SaveWarning($params) { if ($this->Prefix == 'c-img') { return $this->Application->ProcessParsedTag('c', 'SaveWarning', $params); } return parent::SaveWarning($params); } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/configuration/configuration_event_handler.php =================================================================== --- branches/5.3.x/core/units/configuration/configuration_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/configuration/configuration_event_handler.php (revision 16111) @@ -1,563 +1,563 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ConfigurationEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnGenerateMaintenancePage' => Array ('self' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Changes permission section to one from REQUEST, not from config * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $event->setEventParam('PermSection', $this->Application->GetVar('section')); return parent::CheckPermission($event); } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ $module = $this->Application->GetVar('module'); $section = $this->Application->GetVar('section'); $object->addFilter('module_filter', '%1$s.ModuleOwner = ' . $this->Conn->qstr($module)); $object->addFilter('section_filter', '%1$s.Section = ' . $this->Conn->qstr($section)); $can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange'); if ( !$can_change && !$this->Application->isDebugMode() ) { $object->addFilter('interface_change_filter', '%1$s.VariableName NOT IN ("AdminConsoleInterface", "AllowAdminConsoleInterfaceChange")'); } if ( defined('IS_INSTALL') && IS_INSTALL ) { $object->addFilter('install_filter', '%1$s.Install = 1'); } $object->addFilter('visible_filter', '%1$s.Heading <> ""'); } /** * Presets new system setting fields * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('Section', $this->Application->GetVar('section')); $object->SetDBField('ModuleOwner', $this->Application->GetVar('module')); } /** * Sets custom validation * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { static $default_field_options = null; parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ // ability to validate each configuration variable separately if ( !isset($default_field_options) ) { $default_field_options = $object->GetFieldOptions('VariableValue'); } $new_field_options = $default_field_options; $validation = $object->GetDBField('Validation'); if ( $validation ) { $new_field_options = array_merge($new_field_options, unserialize($validation)); } $object->SetFieldOptions('VariableValue', $new_field_options); } /** * Performs custom validation * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemValidate(kEvent $event) { parent::OnBeforeItemValidate($event); $object = $event->getObject(); /* @var $object kDBItem */ // if password field is empty, then don't update if ( $object->GetDBField('ElementType') == 'password' ) { if ( trim($object->GetDBField('VariableValue')) != '' ) { $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ $object->SetDBField('VariableValue', $password_formatter->hashPassword($object->GetDBField('VariableValue'))); } } $this->_processCountryState($event); $variable_name = $object->GetDBField('VariableName'); $new_value = $object->GetDBField('VariableValue'); if ( $variable_name == 'AdminConsoleInterface' ) { $can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange'); if ( ($new_value != $object->GetOriginalField('VariableValue')) && !$can_change ) { $object->SetError('VariableValue', 'not_allowed', 'la_error_OperationNotAllowed'); } } elseif ( $variable_name == 'HardMaintenanceTemplate' ) { $compile = $event->MasterEvent->getEventParam('compile_maintenance_template'); $compile = $compile || $new_value != $object->GetOriginalField('VariableValue'); if ( $compile && !$this->_generateMaintenancePage($new_value) ) { $object->SetError('VariableValue', 'template_file_missing', 'la_error_TemplateFileMissing'); } } elseif ( $variable_name == 'DefaultEmailRecipients' ) { $email_event_data = $this->Application->GetVar('email-template_' . $event->Prefix); $object->SetDBField('VariableValue', $email_event_data[0]['Recipients']); } $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section = $object->GetDBField('Section'); if ( $section && !$sections_helper->getSectionData($section) ) { $object->SetError('Section', 'unknown_section'); } } /** * Checks, that state belongs to selected country * * @param kEvent $event * @access protected */ protected function _processCountryState(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $country_iso = $this->_getCountryByState($event); $state_name = $object->GetDBField('VariableValue'); if ( !$country_iso || !$state_name ) { return; } $cs_helper = $this->Application->recallObject('CountryStatesHelper'); /* @var $cs_helper kCountryStatesHelper */ $state_iso = $cs_helper->getStateIso($state_name, $country_iso); if ( $state_iso !== false ) { $object->SetDBField('VariableValue', $state_iso); } else { // selected state doesn't belong to selected country $object->SetError('VariableValue', 'invalid_state', 'la_InvalidState'); } } /** * Returns country iso code, that matches current state variable name * * @param kEvent $event * @return bool * @access protected */ protected function _getCountryByState(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $variable_name = $object->GetDBField('VariableName'); $state_country_hash = Array ( 'Comm_State' => 'Comm_Country', 'Comm_Shipping_State' => 'Comm_Shipping_Country' ); if ( !array_key_exists($variable_name, $state_country_hash) ) { return false; } $field_values = $this->Application->GetVar($event->getPrefixSpecial(true)); $sql = 'SELECT VariableId FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE VariableName = ' . $this->Conn->qstr($state_country_hash[$variable_name]); $country_variable_id = $this->Conn->GetOne($sql); return $field_values[$country_variable_id]['VariableValue']; } /** * Does custom password setting processing * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ // if password field is empty, then don't update if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) { $object->SetFieldOption('VariableValue', 'skip_empty', 1); } } /** * Occurs after updating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { static $skin_deleted = false; parent::OnAfterItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) { $object->SetFieldOption('VariableValue', 'skip_empty', 0); } // allows to check if variable's value was changed now $variable_name = $object->GetDBField('VariableName'); $changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ()); if ( $object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue') ) { $changed[] = $variable_name; $this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed); // update value in cache, so other code (during this script run) would use new value $this->Application->SetConfigValue($variable_name, $object->GetDBField('VariableValue'), true); } if ( $variable_name == 'Require_AdminSSL' || $variable_name == 'AdminSSLDomain' ) { // when administrative console is moved to SSL mode, then delete skin if ( in_array($variable_name, $changed) && !$skin_deleted ) { $skin_helper = $this->Application->recallObject('SkinHelper'); /* @var $skin_helper SkinHelper */ $skin_file = $skin_helper->getSkinPath(); if ( file_exists($skin_file) ) { unlink($skin_file); } $skin_deleted = true; } } } /** * Updates kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); // 1. save user selected module root category $new_category_id = getArrayValue($items_info, 'ModuleRootCategory', 'VariableValue'); if ( $new_category_id !== false ) { unset($items_info['ModuleRootCategory']); } $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ if ( $items_info ) { $has_error = false; foreach ($items_info as $id => $field_values) { $object->Clear(); // clear validation errors from previous variable $object->Load($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); if ( !$object->Update($id) ) { // don't stop when error found ! $has_error = true; } } $event->status = $has_error ? kEvent::erFAIL : kEvent::erSUCCESS; } if ( $event->status == kEvent::erSUCCESS ) { $event->SetRedirectParam('action_completed', 1); if ( $new_category_id !== false ) { // root category was submitted $module = $this->Application->GetVar('module'); $root_category_id = $this->Application->findModule('Name', $module, 'RootCat'); if ( $root_category_id != $new_category_id ) { // root category differs from one in db $fields_hash = Array ('RootCat' => $new_category_id); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', 'Name = ' . $this->Conn->qstr($module)); } } // reset cache $changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ()); $require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups'); $refresh_sections = array_intersect($require_refresh, $changed); $require_full_refresh = Array ('Site_Name', 'AdminConsoleInterface'); if ( array_intersect($require_full_refresh, $changed) ) { $event->SetRedirectParam('refresh_all', 1); } elseif ( $refresh_sections ) { $event->SetRedirectParam('refresh_tree', 1); } if ( $refresh_sections ) { // reset sections too, because of AdvancedUserManagement $this->Application->DeleteSectionCache(); } $this->Application->DeleteUnitCache($changed); } else{ $errors = $this->Application->GetVar('errors_' . $event->getPrefixSpecial()); if ( $errors ) { // because we have list out there, and this is item $this->Application->SetVar('first_error', key($errors)); $this->Application->removeObject($event->getPrefixSpecial()); } } // keeps module and section in REQUEST to ensure, that last admin template will work $event->SetRedirectParam('module', $this->Application->GetVar('module')); $event->SetRedirectParam('section', $this->Application->GetVar('section')); } /** * Process items from selector (selected_ids var, key - prefix, value - comma separated ids) * * @param kEvent $event */ function OnProcessSelected($event) { $selected_ids = $this->Application->GetVar('selected_ids'); $this->Application->StoreVar('ModuleRootCategory', $selected_ids['c']); $event->SetRedirectParam('opener', 'u'); } /** * Generates maintenance page * * @param kEvent $event * @return void * @access protected */ protected function OnGenerateMaintenancePage(kEvent $event) { $event->setEventParam('compile_maintenance_template', 1); $event->CallSubEvent('OnUpdate'); } /** * Generates HTML version of hard maintenance template * * @param string $template * @return bool * @access protected */ protected function _generateMaintenancePage($template = null) { if ( !isset($template) ) { $template = $this->Application->ConfigValue('HardMaintenanceTemplate'); } $curl_helper = $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $html = $curl_helper->Send($this->Application->BaseURL() . '?t=' . $template); if ( $curl_helper->isGoodResponseCode() ) { file_put_contents(WRITEABLE . DIRECTORY_SEPARATOR . 'maintenance.html', $html); return true; } return false; } /** * Returns auto-complete values for ajax-dropdown * * @param kEvent $event Event. * @param string $term Term. * * @return Array * @access protected */ protected function getAutoCompleteSuggestions(kEvent $event, $term) { $object = $event->getObject(); /* @var $object kDBItem */ $field = $this->Application->GetVar('field'); if ( !$field || !$term || !$object->isField($field) ) { return array(); } $limit = $this->Application->GetVar('limit'); if ( !$limit ) { $limit = 20; } $sql = 'SELECT DISTINCT ' . $field . ', ModuleOwner FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE ' . $field . ' LIKE ' . $this->Conn->qstr('%' . $term . '%') . ' ORDER BY ' . $field . ' ASC'; $data = $this->Conn->Query($sql); $suggestions = array(); foreach ($data as $raw_suggestion) { $suggestion = $raw_suggestion[$field]; if ( !isset($suggestions[$suggestion]) ) { $suggestions[$suggestion] = array(); } $suggestions[$suggestion][] = $raw_suggestion['ModuleOwner']; } array_splice($suggestions, $limit); $ret = array(); $of_label = $this->Application->Phrase('la_From', false); foreach ($suggestions as $suggestion_value => $suggestion_modules) { $suggestion_module = in_array('In-Portal', $suggestion_modules) ? 'In-Portal' : implode(', ', $suggestion_modules); $suggestion_title = $suggestion_value . ' <em style="color: grey;">' . $of_label . ' ' . $suggestion_module . '</em>'; $ret[$suggestion_value] = $suggestion_title; } return $ret; } /** * Prefills module dropdown * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); $options = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ( $module_name == 'Core' ) { continue; } $options[$module_name] = $module_name; if ( $module_name == 'In-Portal' ) { $options['In-Portal:Users'] = 'In-Portal:Users'; } } $config = $event->getUnitConfig(); $fields = $config->getFields(); $fields['ModuleOwner']['options'] = $options; $config->setFields($fields); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/content/content_eh.php =================================================================== --- branches/5.3.x/core/units/content/content_eh.php (revision 16110) +++ branches/5.3.x/core/units/content/content_eh.php (revision 16111) @@ -1,273 +1,274 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ContentEventHandler extends kDBEventHandler { /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $user_id = $this->Application->RecallVar('user_id'); // user can change top category $top_category = $this->Application->getBaseCategory(); $perm_status = $perm_helper->CheckUserPermission($user_id, 'CATEGORY.MODIFY', 0, $top_category); return $perm_helper->finalizePermissionCheck($event, $perm_status); } /** * Saves changes to a content block (+ creates draft if missing) * * @param kEvent $event */ function OnSaveContentBlock($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $updated = $this->saveContentBlock($event, false); if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; echo ($updated === false) ? 'FAILED' : 'OK'; } elseif ( !$updated ) { $event->status = kEvent::erFAIL; } $event->SetRedirectParam('opener', 'u'); } /** * Prepare temp tables and populate it * with items selected in the grid * * @param kEvent $event * @return void * @access protected */ protected function OnEdit(kEvent $event) { parent::OnEdit($event); $fck_helper = $this->Application->recallObject('FCKHelper'); /* @var $fck_helper fckFCKHelper */ $transit_params = $fck_helper->getTransitParams(); foreach ($transit_params as $param_name => $param_value) { $event->SetRedirectParam($param_name, $param_value); } } /** * Saves changes & changes language * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndChangeLanguage(kEvent $event) { if ( $this->UseTempTables($event) ) { parent::OnPreSaveAndChangeLanguage($event); return; } // CUSTOM: begin $event->CallSubEvent('OnSaveContentBlock'); $event->SetRedirectParam('opener', 's'); // CUSTOM: end 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); } } } /** * Performs auto-save of current content block (will create draft too) * * @param kEvent $event */ function OnAutoSave($event) { $event->status = kEvent::erSTOP; if ( $this->Application->GetVar('ajax') != 'yes' ) { return ; } echo $this->saveContentBlock($event, true); } /** * Saves content block * * @param kEvent $event * @param bool $is_draft * @return string */ function saveContentBlock($event, $is_draft) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { return ''; } list ($object, $revision) = $this->getContentBlockAndRevision($event); /* @var $revision kDBItem */ /* @var $object kDBItem */ list (, $field_values) = each($items_info); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $updated = $object->Update(); if ( $updated ) { $revision->SetDBField('AutoSavedOn_date', time()); $revision->SetDBField('AutoSavedOn_time', time()); $revision->Update(); } if ( $is_draft ) { if ( $updated ) { $page_helper = $this->Application->recallObject('PageHelper'); /* @var $page_helper PageHelper */ return $revision->GetField('AutoSavedOn') . ' (' . $page_helper->getAgoTime($revision->GetDBField('AutoSavedOn')) . ')'; } } else { return $updated; } return ''; } /** * Returns last autosave time * * @param kEvent $event */ function OnGetAutoSaveTime($event) { $event->status = kEvent::erSTOP; if ( $this->Application->GetVar('ajax') != 'yes' ) { return; } list (, $revision) = $this->getContentBlockAndRevision($event); /* @var $revision kDBItem */ $page_helper = $this->Application->recallObject('PageHelper'); /* @var $page_helper PageHelper */ $time = $revision->GetField('AutoSavedOn'); if ( $time ) { echo $time . ' (' . $page_helper->getAgoTime($revision->GetDBField('AutoSavedOn')) . ')'; } } /** * Loads content block from given revision * * @param kDBItem $object * @param kDBItem $revision */ function loadFromRevision(&$object, &$revision) { $load_keys = Array( 'PageId' => $object->GetDBField('PageId'), 'ContentNum' => $object->GetDBField('ContentNum'), 'RevisionId' => $revision->GetID(), ); $object->Load($load_keys); } /** * Returns content block. * * @param kEvent $event * * @return kDBItem[] */ function getContentBlockAndRevision(kEvent $event) { $object = $event->getObject(Array('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$items_info ) { return array(); } list ($id,) = each($items_info); $object->Load($id); $revision = $this->Application->recallObject('page-revision', null, Array('skip_autoload' => true)); /* @var $revision kDBItem */ $revision->Load($object->GetDBField('RevisionId')); if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') && !$revision->GetDBField('IsDraft') ) { // editing live revision of a page's content block -> get draft for current user and page $load_keys = Array( 'PageId' => $revision->GetDBField('PageId'), 'IsDraft' => 1, 'CreatedById' => $this->Application->RecallVar('user_id'), ); $revision->Load($load_keys); if ( $revision->isLoaded() ) { // draft found -> use draft's content block version $this->loadFromRevision($object, $revision); } else { // draft not found -> create new $revision->SetDBFieldsFromHash($load_keys); $revision->SetDBField('FromRevisionId', $object->GetDBField('RevisionId')); if ( $revision->Create() ) { $this->loadFromRevision($object, $revision); } } } return Array(&$object, &$revision); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/forms/form_submissions/form_submissions_eh.php =================================================================== --- branches/5.3.x/core/units/forms/form_submissions/form_submissions_eh.php (revision 16110) +++ branches/5.3.x/core/units/forms/form_submissions/form_submissions_eh.php (revision 16111) @@ -1,544 +1,544 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FormSubmissionsEventHandler extends kDBEventHandler { /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( !$this->Application->isAdmin ) { if ( $event->Name == 'OnCreate' ) { // anybody can submit forms on front return true; } } $section = $event->getSection(); $form_id = $this->Application->GetVar('form_id'); $event->setEventParam('PermSection', $section . ':' . $form_id); return parent::CheckPermission($event); } /** * Always allow to view feedback form * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnItemBuild' => Array ('self' => true), 'OnEdit' => Array ('self' => 'view', 'subitem' => 'view'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Returns filter block based on field element type * * @param string $element_type * @return string */ function _getFilterBlock($element_type) { $mapping = Array ( 'text' => 'grid_like_filter', 'select' => 'grid_options_filter', 'radio' => 'grid_options_filter', 'checkbox' => 'grid_options_filter', 'password' => 'grid_like_filter', 'textarea' => 'grid_like_filter', 'label' => 'grid_like_filter', 'upload' => 'grid_empty_filter', ); return $mapping[$element_type]; } protected function OnBuildFormFields(kEvent $event) { $form_id = $this->Application->GetVar('form_id'); if ( !$form_id ) { return; } $config = $event->getUnitConfig(); $helper = $this->Application->recallObject('InpCustomFieldsHelper'); /* @var $helper InpCustomFieldsHelper */ $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'FormFields WHERE FormId = ' . (int)$form_id . ' ORDER BY Priority DESC'; $fields = $this->Conn->Query($sql, 'FormFieldId'); $use_options = Array ('radio', 'select', 'checkbox'); $check_visibility = $this->Application->LoggedIn() && !$this->Application->isAdminUser; foreach ($fields as $field_id => $options) { $field_visible = $check_visibility ? $options['Visibility'] == SubmissionFormField::VISIBILITY_EVERYONE : true; $field_options = Array ('type' => 'string', 'default' => $options['DefaultValue']); if ( $options['Required'] && $field_visible ) { $field_options['required'] = 1; } if ( $options['Validation'] == 1 ) { $field_options['formatter'] = 'kFormatter'; $field_options['regexp'] = '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i'; } if ( $options['DisplayInGrid'] ) { $title = $options['Prompt']; if ( substr($title, 0, 1) == '+' ) { $this->Application->Phrases->AddCachedPhrase('form_col_title' . $field_id, substr($title, 1)); $title = 'form_col_title' . $field_id; } $grid_field_options = Array ( 'title' => $title, 'no_special' => 1, 'nl2br' => 1, 'first_chars' => 200, 'filter_block' => $this->_getFilterBlock($options['ElementType']) ); if ( $options['ElementType'] == 'upload' ) { $grid_field_options['data_block'] = 'grid_upload_td'; } if ( $options['Validation'] == 1 ) { $grid_field_options['data_block'] = 'grid_email_td'; } $config->addGridFields('Default', $grid_field_options, 'fld_' . $field_id); } if ( $options['ElementType'] == 'checkbox' && !$options['ValueList'] ) { // fix case, when user haven't defined any options for checkbox $options['ValueList'] = '1=la_Yes||0=la_No'; } if ( in_array($options['ElementType'], $use_options) && $options['ValueList'] ) { // field type can have options and user have defined them too $field_options['options'] = $helper->GetValuesHash($options['ValueList']); $field_options['formatter'] = 'kOptionsFormatter'; } if ( $options['ElementType'] == 'password' ) { $field_options['formatter'] = 'kPasswordFormatter'; $field_options['hashing_method'] = PasswordHashingMethod::NONE; $field_options['verify_field'] = 'fld_' . $field_id . '_verify'; } if ( $options['ElementType'] == 'upload' ) { $field_options['formatter'] = 'kUploadFormatter'; $field_options['upload_dir'] = WRITEBALE_BASE . DIRECTORY_SEPARATOR . 'user_files' . DIRECTORY_SEPARATOR . 'form_submissions'; if ( $options['UploadMaxSize'] ) { $field_options['max_size'] = $options['UploadMaxSize'] * 1024; // convert Kbytes to bytes } if ( $options['UploadExtensions'] ) { $field_options['file_types'] = '*.' . implode(';*.', explode(',', $options['UploadExtensions'])); } } $config->addFields($field_options, 'fld_' . $field_id); } } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ $object->addFilter('form_filter', '%1$s.FormId = ' . (int)$this->Application->GetVar('form_id')); } /** * Allows user to see it's last feedback form data * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { if ( $event->Special == 'last' ) { // allow user to see his last submitted form return $this->Application->RecallVar('last_submission_id'); } if ( $this->Application->isAdminUser ) { // don't check ids in admin return parent::getPassedID($event); } // no way to see other user's form submission by giving it's ID directly in url return 0; } /** * Creates new form submission from Front-End * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { parent::OnCreate($event); if ( $event->status != kEvent::erSUCCESS ) { return; } $object = $event->getObject(); /* @var $object kDBItem */ // allows user to view only it's last submission $this->Application->StoreVar('last_submission_id', $object->GetID()); $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($object); $notify_email = $form->GetDBField('SubmitNotifyEmail'); if ( $notify_email ) { $send_params = $object->getEmailParams(Array ( 'to_name' => $notify_email, 'to_email' => $notify_email, )); $this->Application->emailAdmin('FORM.SUBMITTED', null, $send_params); } else { $this->Application->emailAdmin('FORM.SUBMITTED', null, $object->getEmailParams()); } // $this->Application->emailUser('FORM.SUBMITTED', null, Array ('to_email' => '')); $event->SetRedirectParam('opener', 's'); $event->SetRedirectParam('m_cat_id', 0); $theme = $this->Application->recallObject('theme.current'); /* @var $theme kDBItem */ - $template = htmlspecialchars_decode($this->Application->GetVar('success_template')); // kHTTPQuery do kUtil::escape() on everything on Front-End + $template = $this->Application->unescapeRequestVariable($this->Application->GetVar('success_template')); $alias_template = $theme->GetField('TemplateAliases', $template); $event->redirect = $alias_template ? $alias_template : $template; } /** * Processes Captcha code * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('IPAddress', $this->Application->getClientIp()); if ( !$object->GetDBField('ReferrerURL') ) { $referrer = $this->Application->GetVar('original_referrer'); if ( !$referrer ) { $base_url = preg_quote($this->Application->BaseURL(), '/'); $referrer = preg_replace('/^' . $base_url . '/', '/', $_SERVER['HTTP_REFERER'], 1); } $object->SetDBField('ReferrerURL', $referrer); } $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($object); // validate captcha code if ( $form->GetDBField('UseSecurityImage') && !$this->Application->LoggedIn() ) { $captcha_helper = $this->Application->recallObject('CaptchaHelper'); /* @var $captcha_helper kCaptchaHelper */ $captcha_helper->validateCode($event, false); } } /** * Checks, that target submission was selected for merging * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->setRequired('MergeToSubmission', $object->GetDBField('IsMergeToSubmission')); } /** * Passes form_id, when using "Prev"/"Next" toolbar buttons * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndGo(kEvent $event) { parent::OnPreSaveAndGo($event); if ( $event->status == kEvent::erSUCCESS ) { $event->SetRedirectParam('pass', 'm,form,formsubs'); } } /** * Saves edited item in temp table and goes * to passed tabs, by redirecting to it with OnPreSave event * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndGoToTab(kEvent $event) { parent::OnPreSaveAndGoToTab($event); if ( $event->status == kEvent::erSUCCESS ) { $event->SetRedirectParam('pass', 'm,form,formsubs'); } } /** * Set's new per-page for grid * * @param kEvent $event * @return void * @access protected */ protected function OnSetPerPage(kEvent $event) { parent::OnSetPerPage($event); $event->SetRedirectParam('pass', 'm,form,' . $event->getPrefixSpecial()); } /** * Occurs when page is changed (only for hooking) * * @param kEvent $event * @return void * @access protected */ protected function OnSetPage(kEvent $event) { parent::OnSetPage($event); $event->SetRedirectParam('pass', 'm,form,' . $event->getPrefixSpecial()); } /** * Fills merge-to dropdown * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); if ($event->Special == 'merge-to') { return ; } $object = $event->getObject(); /* @var $object kDBItem */ $form_id = $object->GetDBField('FormId'); $email_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL); if (!$email_field) { return ; } $merge_to = $this->Application->recallObject($event->Prefix . '.merge-to', null, Array ('skip_autoload' => true)); /* @var $merge_to kDBItem */ $sql = $merge_to->GetSelectSQL() . ' WHERE (FormId = ' . $form_id . ') AND (' . $email_field . ' = ' . $this->Conn->qstr( $object->GetDBField($email_field) ) . ')'; $submissions = $this->Conn->Query($sql, $object->IDField); // remove this submission unset($submissions[ $object->GetID() ]); if (!$submissions) { return ; } $options = Array (); $name_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_NAME); $subject_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT); $language = $this->Application->recallObject('lang.current'); /* @var $language kDBItem */ $date_format = $language->GetDBField('DateFormat'); foreach ($submissions as $submission_id => $submission_data) { $option_title = ''; // SenderName (email@address.com) - Subject (06/29/2010) $merge_to->LoadFromHash($submission_data); if ($name_field) { $option_title = $merge_to->GetDBField($name_field) . ' (' . $merge_to->GetDBField($email_field) . ') - '; } else { $option_title = $merge_to->GetDBField($email_field) . ' - '; } if ($subject_field) { $option_title .= $merge_to->GetField($subject_field) . ' (' . $merge_to->GetField('SubmissionTime', $date_format) . ')'; } else { $option_title .= $merge_to->GetField('SubmissionTime', $date_format); } $options[$submission_id] = $option_title; } $object->SetFieldOption('MergeToSubmission', 'options', $options); } /** * Returns submission field name based on given role * * @param int $form_id * @param string $role * @return string * @see FormSubmissionHelper::getFieldByRole() */ function getFieldNameByRole($form_id, $role) { $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ return $form_submission_helper->getFieldNameByRole($form_id, $role); } /** * Performs submission merge * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { parent::OnUpdate($event); if ($event->status == kEvent::erSUCCESS) { $object = $event->getObject(); /* @var $object kDBItem */ $merge_to = $object->GetDBField('MergeToSubmission'); if (!$merge_to) { return ; } $form_id = $object->GetDBField('FormId'); $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'Forms WHERE FormId = ' . $form_id; $form_info = $this->Conn->GetRow($sql); $reply = $this->Application->recallObject('submission-log.merge', null, Array ('skip_autoload' => true)); /* @var $reply kDBItem */ $email_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL); $subject_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT); $body_field = $this->getFieldNameByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_BODY); $reply->SetDBField('FormSubmissionId', $merge_to); if ($email_field) { $reply->SetDBField('FromEmail', $object->GetDBField($email_field)); } $reply->SetDBField('ToEmail', $form_info['ReplyFromEmail']); if ($subject_field) { $reply->SetDBField('Subject', $object->GetDBField($subject_field)); } if ($body_field) { $reply->SetDBField('Message', $object->GetDBField($body_field)); } $reply->SetDBField('SentOn_date', $object->GetDBField('SubmissionTime')); $reply->SetDBField('SentOn_time', $object->GetDBField('SubmissionTime')); $reply->SetDBField('MessageId', $object->GetDBField('MessageId')); $reply->SetDBField('SentStatus', SUBMISSION_LOG_SENT); // as if emails was really received via mailbox $this->Application->SetVar('client_mode', 1); if ($reply->Create()) { // delete submission, since it was merged $object->Delete(); } } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/forms/forms/forms_eh.php =================================================================== --- branches/5.3.x/core/units/forms/forms/forms_eh.php (revision 16110) +++ branches/5.3.x/core/units/forms/forms/forms_eh.php (revision 16111) @@ -1,631 +1,631 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class FormsEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( // user can view any form on front-end 'OnItemBuild' => Array ('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } protected function OnCreateSubmissionNodes(kEvent $event) { if ( defined('IS_INSTALL') && IS_INSTALL ) { // skip any processing, because Forms table doesn't exists until install is finished return; } $forms = $this->getForms(); if ( !$forms ) { return; } $form_subsection = Array ( 'parent' => 'in-portal:forms', 'icon' => 'form_submission', 'label' => '', 'url' => Array ('t' => 'submissions/submissions_list', 'pass' => 'm,form'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'priority' => 1, 'type' => stTREE, ); $priority = 1; $config = $event->getUnitConfig(); foreach ($forms as $form_id => $form_name) { $this->Application->Phrases->AddCachedPhrase('form_sub_label_'.$form_id, $form_name); $this->Application->Phrases->AddCachedPhrase('la_description_in-portal:submissions:'.$form_id, $form_name.' Submissions'); $form_subsection['label'] = 'form_sub_label_'.$form_id; $form_subsection['url']['form_id'] = $form_id; $form_subsection['priority'] = $priority++; $config->addSections($form_subsection, 'in-portal:submissions:' . $form_id); } } protected function getForms() { $cache_key = 'forms[%FormSerial%]'; $forms = $this->Application->getCache($cache_key); if ( $forms === false ) { $this->Conn->nextQueryCachable = true; $sql = 'SELECT Title, FormId FROM ' . TABLE_PREFIX . 'Forms ORDER BY Title ASC'; $forms = $this->Conn->GetCol($sql, 'FormId'); $this->Application->setCache($cache_key, $forms); } return $forms; } /** * Saves content of temp table into live and * redirects to event' default redirect (normally grid template) * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { parent::OnSave($event); if ( $event->status == kEvent::erSUCCESS ) { $this->OnCreateFormFields($event); $this->_deleteSectionCache(); } } /** * Deletes all selected items. * Automatically recurse into sub-items using temp handler, and deletes sub-items * by calling its Delete method if sub-item has AutoDelete set to true in its config file * * @param kEvent $event * @return void * @access protected */ protected function OnMassDelete(kEvent $event) { parent::OnMassDelete($event); if ( $event->status == kEvent::erSUCCESS ) { $this->_deleteSectionCache(); } } function _deleteSectionCache() { $this->Application->HandleEvent(new kEvent('adm:OnResetSections')); $this->Application->StoreVar('RefreshStructureTree', 1); } /** * Dynamically fills custom data config * * @param kEvent $event */ function OnCreateFormFields($event) { $cur_fields = $this->Conn->Query('DESCRIBE '.TABLE_PREFIX.'FormSubmissions', 'Field'); $cur_fields = array_keys($cur_fields); // keep all fields, that are not created on the fly (includes ones, that are added during customizations) foreach ($cur_fields as $field_index => $field_name) { if (!preg_match('/^fld_[\d]+/', $field_name)) { unset($cur_fields[$field_index]); } } $desired_fields = $this->Conn->GetCol('SELECT CONCAT(\'fld_\', FormFieldId) FROM '.TABLE_PREFIX.'FormFields ORDER BY FormFieldId'); $sql = array(); $fields_to_add = array_diff($desired_fields, $cur_fields); foreach ($fields_to_add as $field) { $field_expression = $field.' Text NULL'; $sql[] = 'ADD COLUMN '.$field_expression; } $fields_to_drop = array_diff($cur_fields, $desired_fields); foreach ($fields_to_drop as $field) { $sql[] = 'DROP COLUMN '.$field; } if ($sql) { $query = 'ALTER TABLE '.TABLE_PREFIX.'FormSubmissions '.implode(', ', $sql); $this->Conn->Query($query); } } /** * Enter description here... * * @param kEvent $event * @return void * @access protected */ protected function OnFormSubmit($event) { $object = $event->getObject(); /* @var $object kDBItem */ $fields = explode(',', $this->Application->GetVar('fields')); $required_fields = explode(',', $this->Application->GetVar('required_fields')); $fields_params = $this->Application->GetVar('fields_params'); $virtual_fields = $event->getUnitConfig()->getVirtualFields(); foreach ($fields as $field) { $virtual_fields[$field] = Array (); if ( in_array($field, $required_fields) ) { $virtual_fields[$field]['required'] = 1; } $params = getArrayValue($fields_params, $field); if ( $params !== false ) { if ( getArrayValue($params, 'Type') == 'email' ) { $virtual_fields[$field]['formatter'] = 'kFormatter'; $virtual_fields[$field]['regexp'] = '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i'; $virtual_fields[$field]['error_msgs'] = Array ('invalid_format' => '!la_invalid_email!'); } if ( getArrayValue($params, 'Type') == 'file' ) { $virtual_fields[$field]['formatter'] = 'kUploadFormatter'; $virtual_fields[$field]['upload_dir'] = '/uploads/sketches/'; } } } $object->SetVirtualFields($virtual_fields); $field_values = $this->getSubmittedFields($event); $checkboxes = explode(',', $this->Application->GetVar('checkbox_fields')); // MailingList,In-Link,In-Newz,In-Bulletin foreach ($checkboxes as $checkbox) { if ( isset($field_values[$checkbox]) ) { $field_values[$checkbox] = 1; } else { $field_values[$checkbox] = '0'; } } - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); if ( $object->Validate() ) { $event->redirect = $this->Application->GetVar('success_template'); $this->Application->emailAdmin($this->Application->GetVar('email_event'), null, $object->getEmailParams()); $send_params = $object->getEmailParams(Array ( 'to_email' => $field_values[$this->Application->GetVar('email_field')], 'to_name' => $field_values[$this->Application->GetVar('name_field')] )); $this->Application->emailUser($this->Application->GetVar('email_event'), null, $send_params); if ( $field_values['MailingList'] ) { $this->Application->StoreVar('SubscriberEmail', $field_values['Email']); $this->Application->HandleEvent(new kEvent('u:OnSubscribeUser', Array ('no_unsubscribe' => 1))); } } else { $event->status = kEvent::erFAIL; } } /** * Don't use security image, when form requires login * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->itemChanged($event); } /** * Don't use security image, when form requires login * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->itemChanged($event); } /** * Occurs before item is changed * * @param kEvent $event */ function itemChanged($event) { $this->_validatePopSettings($event); $this->_disableSecurityImage($event); $this->_setRequired($event); } /** * Validates POP3 settings (performs test connect) * * @param kEvent $event */ function _validatePopSettings($event) { $object = $event->getObject(); /* @var $object kDBItem */ $modes = Array ('Reply', 'Bounce'); $fields = Array ('Server', 'Port', 'Username', 'Password'); $changed_fields = array_keys( $object->GetChangedFields() ); foreach ($modes as $mode) { $set = true; $changed = false; foreach ($fields as $field) { $value = $object->GetDBField($mode . $field); if (strlen( trim($value) ) == 0) { $set = false; break; } if (!$changed && in_array($mode . $field, $changed_fields)) { $changed = true; } } if ($set && $changed) { // fields are set and at least on of them is changed $connection_info = Array (); foreach ($fields as $field) { $connection_info[ strtolower($field) ] = $object->GetDBField($mode . $field); } $pop3_helper = $this->Application->makeClass('POP3Helper', Array ($connection_info, 10)); /* @var $pop3_helper POP3Helper */ switch ( $pop3_helper->initMailbox(true) ) { case 'socket': $object->SetError($mode . 'Server', 'connection_failed'); break; case 'login': $object->SetError($mode . 'Username', 'login_failed'); break; case 'list': $object->SetError($mode . 'Server', 'message_listing_failed'); break; } } } } /** * Makes email communication fields required, when form uses email communication * * @param kEvent $event */ function _setRequired($event) { $object = $event->getObject(); /* @var $object kDBItem */ $required = $object->GetDBField('EnableEmailCommunication'); $fields = Array ( 'ReplyFromName', 'ReplyFromEmail', 'ReplyServer', 'ReplyPort', 'ReplyUsername', 'ReplyPassword', ); if ($required && $object->GetDBField('BounceEmail')) { $bounce_fields = Array ('BounceEmail', 'BounceServer', 'BouncePort', 'BounceUsername', 'BouncePassword'); $fields = array_merge($fields, $bounce_fields); } $object->setRequired($fields, $required); } /** * Don't use security image, when form requires login * * @param kEvent $event */ function _disableSecurityImage($event) { $object = $event->getObject(); /* @var $object kDBItem */ if ($object->GetDBField('RequireLogin')) { $object->SetDBField('UseSecurityImage', 0); } } /** * Queries pop3 server about new incoming mail * * @param kEvent $event */ function OnProcessReplies($event) { $this->_processMailbox($event, false); } /** * Queries pop3 server about new incoming mail * * @param kEvent $event */ function OnProcessBouncedReplies($event) { $this->_processMailbox($event, true); } /** * Queries pop3 server about new incoming mail * * @param kEvent $event * @param bool $bounce_mode */ function _processMailbox($event, $bounce_mode = false) { $config = $event->getUnitConfig(); $this->Application->SetVar('client_mode', 1); $sql = 'SELECT * FROM ' . $config->getTableName() . ' WHERE EnableEmailCommunication = 1'; $forms = $this->Conn->Query($sql, $config->getIDField()); $mailbox_helper = $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ $field_prefix = $bounce_mode ? 'Bounce' : 'Reply'; foreach ($forms as $form_id => $form_info) { $recipient_email = $bounce_mode ? $form_info['BounceEmail'] : $form_info['ReplyFromEmail']; if ( !$recipient_email ) { continue; } $mailbox_helper->process( Array ( 'server' => $form_info[$field_prefix . 'Server'], 'port' => $form_info[$field_prefix . 'Port'], 'username' => $form_info[$field_prefix . 'Username'], 'password' => $form_info[$field_prefix . 'Password'] ), Array (&$this, 'isValidRecipient'), Array (&$this, 'processEmail'), Array ( 'recipient_email' => $recipient_email, 'bounce_mode' => $bounce_mode, 'form_info' => $form_info, ) ); } } function isValidRecipient($params) { $mailbox_helper = $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ $recipients = $mailbox_helper->getRecipients(); $recipient_email = $params['recipient_email']; $emails_found = preg_match_all('/((' . REGEX_EMAIL_USER . ')(@' . REGEX_EMAIL_DOMAIN . '))/i', $recipients, $all_emails); if (is_array($all_emails)) { for ($i = 0; $i < $emails_found; $i++) { if ($all_emails[1][$i] == $recipient_email) { // only read messages, that are addresses to submission reply email return true; } } } // If this is a forwarded message - we drop all the other aliases and deliver only to the x-forward to address; if (preg_match('/((' . REGEX_EMAIL_USER . ')(@' . REGEX_EMAIL_DOMAIN . '))/i', $mailbox_helper->headers['x-forward-to'], $get_to_email)) { if ($get_to_email[1] == $recipient_email) { // only read messages, that are addresses to submission reply email return true; } } return false; } function processEmail($params, &$fields_hash) { if ( $params['bounce_mode'] ) { // mark original message as bounced $mailbox_helper = $this->Application->recallObject('MailboxHelper'); /* @var $mailbox_helper MailboxHelper */ if ( !array_key_exists('attachments', $mailbox_helper->parsedMessage) ) { // for now only parse bounces based on attachments, skip other bounce types return false; } for ($i = 0; $i < count($mailbox_helper->parsedMessage['attachments']); $i++) { $attachment =& $mailbox_helper->parsedMessage['attachments'][$i]; switch ( $attachment['headers']['content-type'] ) { case 'message/delivery-status': // save as BounceInfo $mime_decode_helper = $this->Application->recallObject('MimeDecodeHelper'); /* @var $mime_decode_helper MimeDecodeHelper */ $charset = $mailbox_helper->parsedMessage[$fields_hash['MessageType']][0]['charset']; $fields_hash['Message'] = $mime_decode_helper->convertEncoding($charset, $attachment['data']); break; case 'message/rfc822': // undelivered message $fields_hash['Subject'] = $attachment['filename2'] ? $attachment['filename2'] : $attachment['filename']; break; } } } if ( !preg_match('/^(.*) #verify(.*)$/', $fields_hash['Subject'], $regs) ) { // incorrect subject, no verification code $form_info = $params['form_info']; if ( $form_info['ProcessUnmatchedEmails'] && ($fields_hash['FromEmail'] != $params['recipient_email']) ) { // it's requested to convert unmatched emails to new submissions $form_id = $form_info['FormId']; $this->Application->SetVar('form_id', $form_id); $form_submission_config = $this->Application->getUnitConfig('formsubs'); $sql = 'SELECT ' . $form_submission_config->getIDField() . ' FROM ' . $form_submission_config->getTableName() . ' WHERE MessageId = ' . $this->Conn->qstr($fields_hash['MessageId']); $found = $this->Conn->GetOne($sql); if ( $found ) { // don't process same message twice return false; } $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'FormFields WHERE (FormId = ' . $form_info['FormId'] . ') AND (EmailCommunicationRole > 0)'; $form_fields = $this->Conn->Query($sql, 'EmailCommunicationRole'); // what roles are filled from what fields $role_mapping = Array ( SubmissionFormField::COMMUNICATION_ROLE_EMAIL => 'FromEmail', SubmissionFormField::COMMUNICATION_ROLE_NAME => 'FromName', SubmissionFormField::COMMUNICATION_ROLE_SUBJECT => 'Subject', SubmissionFormField::COMMUNICATION_ROLE_BODY => 'Message', ); $submission_fields = Array (); foreach ($role_mapping as $role => $email_field) { if ( array_key_exists($role, $form_fields) ) { $submission_fields['fld_' . $form_fields[$role]['FormFieldId']] = $fields_hash[$email_field]; } } if ( $submission_fields ) { // remove object, because it's linked to single form upon creation forever $this->Application->removeObject('formsubs.-item'); $form_submission = $this->Application->recallObject('formsubs.-item', null, Array ('skip_autoload' => true)); /* @var $form_submission kDBItem */ // in case that other non-role mapped fields are required $form_submission->IgnoreValidation = true; $form_submission->SetDBFieldsFromHash($submission_fields); $form_submission->SetDBField('FormId', $form_id); $form_submission->SetDBField('MessageId', $fields_hash['MessageId']); $form_submission->SetDBField('SubmissionTime_date', time()); $form_submission->SetDBField('SubmissionTime_time', time()); $form_submission->SetDBField('ReferrerURL', $this->Application->Phrase('la_Text_Email')); return $form_submission->Create(); } } return false; } $submission_log_config = $this->Application->getUnitConfig('submission-log'); $sql = 'SELECT ' . $submission_log_config->getIDField() . ' FROM ' . $submission_log_config->getTableName() . ' WHERE MessageId = ' . $this->Conn->qstr($fields_hash['MessageId']); $found = $this->Conn->GetOne($sql); if ( $found ) { // don't process same message twice return false; } $reply_to = $this->Application->recallObject('submission-log.-reply-to', null, Array ('skip_autoload' => true)); /* @var $reply_to kDBItem */ $reply_to->Load($regs[2], 'VerifyCode'); if ( !$reply_to->isLoaded() ) { // fake verification code OR feedback, containing submission log was deleted return false; } if ( $params['bounce_mode'] ) { // mark original message as bounced $reply_to->SetDBField('BounceInfo', $fields_hash['Message']); $reply_to->SetDBField('BounceDate_date', TIMENOW); $reply_to->SetDBField('BounceDate_time', TIMENOW); $reply_to->SetDBField('SentStatus', SUBMISSION_LOG_BOUNCE); $reply_to->Update(); return true; } $reply = $this->Application->recallObject('submission-log.-reply', null, Array ('skip_autoload' => true)); /* @var $reply kDBItem */ $reply->SetDBFieldsFromHash($fields_hash); $reply->SetDBField('ReplyTo', $reply_to->GetID()); $reply->SetDBField('FormSubmissionId', $reply_to->GetDBField('FormSubmissionId')); $reply->SetDBField('ToEmail', $params['recipient_email']); $reply->SetDBField('Subject', $regs[1]); // save subject without verification code $reply->SetDBField('SentStatus', SUBMISSION_LOG_SENT); return $reply->Create(); } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/forms/form_fields/form_fields_config.php =================================================================== --- branches/5.3.x/core/units/forms/form_fields/form_fields_config.php (revision 16110) +++ branches/5.3.x/core/units/forms/form_fields/form_fields_config.php (revision 16111) @@ -1,116 +1,117 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); $config = Array ( 'Prefix' => 'formflds', 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'FormFieldEventHandler', 'file' => 'form_field_eh.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'FormFieldsTagProcessor', 'file' => 'form_fields_tp.php'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', ), 'IDField' => 'FormFieldId', 'TitleField' => 'FieldName', 'TableName' => TABLE_PREFIX.'FormFields', 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s', ), 'ForeignKey' => 'FormId', 'ParentTableKey' => 'FormId', 'ParentPrefix' => 'form', 'AutoDelete' => true, 'AutoClone' => true, 'ListSortings' => Array ( '' => Array ( 'Sorting' => Array ('Name' => 'asc'), ) ), 'ListSortings' => Array ( '' => Array ( 'ForcedSorting' => Array ('Priority' => 'desc'), 'Sorting' => Array ('FieldName' => 'asc'), ) ), 'Fields' => Array ( 'FormFieldId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'FormId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Type' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'FieldName' => Array ('type' => 'string', 'not_null' => 1, 'required' => 1, 'default' => ''), 'FieldLabel' => Array ('type' => 'string', 'required' => 1, 'default' => null), 'Heading' => Array ('type' => 'string', 'default' => null), 'Prompt' => Array ('type' => 'string', 'default' => null, 'required' => 1), 'ElementType' => Array ( 'type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ('text' => 'la_type_text', 'select' => 'la_type_select', 'radio' => 'la_type_radio', 'checkbox' => 'la_type_SingleCheckbox', 'password' => 'la_type_password', 'textarea' => 'la_type_textarea', 'upload' => 'la_type_upload', 'label' => 'la_type_label'), 'use_phrases' => 1, 'required' => 1, 'not_null' => 1, 'default' => '', ), 'ValueList' => Array ('type' => 'string', 'default' => null), 'Priority' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'IsSystem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'Required' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'DisplayInGrid' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1), 'DefaultValue' => Array ('type' => 'string', 'default' => NULL), 'Validation' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_None', 1 => 'la_ValidationEmail'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'UploadExtensions' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'UploadMaxSize' => Array ('type' => 'int', 'default' => NULL), 'Visibility' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Everyone', 2 => 'la_opt_GuestsOnly'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1 ), 'EmailCommunicationRole' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_RecipientName', 2 => 'la_opt_RecipientEmail', 3 => 'la_opt_EmailSubject', 4 => 'la_opt_EmailBody'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), ), 'VirtualFields' => Array ( + 'Value' => Array ('type' => 'string', 'default' => ''), 'DirectOptions' => Array ('type' => 'string', 'default' => ''), ), 'CalculatedFields' => Array ( ), 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array ('default' => 'icon16_item.png'), 'Fields' => Array ( 'FormFieldId' => Array ( 'title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60 ), 'FieldName' => Array ( 'title' => 'la_prompt_FieldName', 'filter_block' => 'grid_like_filter', 'width' => 100 ), 'FieldLabel' => Array ( 'title' => 'la_prompt_FieldLabel', 'data_block' => 'label_grid_data_td', 'filter_block' => 'grid_like_filter', 'width' => 150 ), 'Priority' => Array ('title' => 'la_prompt_Priority', 'filter_block' => 'grid_range_filter', 'width' => 80 ), 'ElementType' => Array ('title' => 'la_prompt_ElementType', 'filter_block' => 'grid_options_filter'), 'Required' => Array ('title' => 'la_prompt_Required', 'filter_block' => 'grid_options_filter'), 'DisplayInGrid' => Array ('title' => 'la_prompt_DisplayInGrid', 'filter_block' => 'grid_options_filter', 'width' => 150 ), 'Visibility' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'EmailCommunicationRole' => Array ('filter_block' => 'grid_options_filter', 'width' => 150), ), ), ), ); \ No newline at end of file Index: branches/5.3.x/core/units/forms/submission_log/submission_log_eh.php =================================================================== --- branches/5.3.x/core/units/forms/submission_log/submission_log_eh.php (revision 16110) +++ branches/5.3.x/core/units/forms/submission_log/submission_log_eh.php (revision 16111) @@ -1,686 +1,690 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class SubmissionLogEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnResendReply' => Array ('subitem' => 'add|edit'), 'OnSaveDraft' => Array ('subitem' => 'add|edit'), 'OnUseDraft' => Array ('subitem' => 'add|edit'), 'OnDeleteDraft' => Array ('subitem' => 'add|edit'), 'OnProcessBounceMail' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $section = $event->getSection(); $form_id = $this->Application->GetVar('form_id'); if ( $form_id ) { // copy form_id to env to be passed info upload links $this->Application->SetVar($event->getPrefixSpecial() . '_form_id', $form_id); } else { $form_id = $this->Application->GetVar($event->getPrefixSpecial() . '_form_id'); } $event->setEventParam('PermSection', $section . ':' . $form_id); return parent::CheckPermission($event); } /** * Prepares new kDBItem object * * @param kEvent $event * @return void * @access protected */ protected function OnNew(kEvent $event) { parent::OnNew($event); $object = $event->getObject(); /* @var $object kDBItem */ $form_submission = $this->Application->recallObject('formsubs'); /* @var $form_submission kDBItem */ $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($form_submission); /* @var $form kDBItem */ $from_email = $form->GetDBField('ReplyFromEmail'); $to_email = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_EMAIL); if ( $this->Application->GetVar('client_mode') ) { // debug code for sending email from client $object->SetDBField('FromEmail', $to_email); $object->SetDBField('ToEmail', $from_email); } else { $object->SetDBField('FromEmail', $from_email); $object->SetDBField('ToEmail', $to_email); } $object->SetDBField('Cc', $form->GetDBField('ReplyCc')); $object->SetDBField('Bcc', $form->GetDBField('ReplyBcc')); $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $org_message = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $org_message kDBItem */ $org_message->Load(array_shift($ids)); // client could reply from different email, so compare to admin email! if ( $org_message->GetDBField('ToEmail') == $from_email ) { // can reply only to client email, not own :) // transform subject $message_subject = $org_message->GetDBField('Subject'); if ( $message_subject ) { $object->SetDBField('Subject', $this->_transformSubject($message_subject, 'Re')); } // add signature $message_body = $form->GetDBField('ReplyMessageSignature'); if ( $org_message->GetDBField('Message') ) { // add replied marks $message_body .= '> ' . preg_replace('/([\r]*\n)/', '\\1> ', $org_message->GetDBField('Message')); } $object->SetDBField('ToEmail', $org_message->GetDBField('FromEmail')); // user client's email from reply $object->SetDBField('Message', $message_body); $object->SetDBField('ReplyTo', $org_message->GetID()); } } else { $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' WHERE FormSubmissionId = ' . $form_submission->GetID(); $replies_found = $this->Conn->GetOne($sql); if ( !$replies_found ) { // 1st message from admin -> quote subject & text from feedback $message_subject = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT, true); if ( $message_subject ) { $object->SetDBField('Subject', $this->_transformSubject($message_subject, 'Re')); } // add signature $message_body = $form->GetDBField('ReplyMessageSignature'); // add replied marks $original_message_body = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_BODY); if ( $original_message_body ) { $message_body .= '> ' . preg_replace('/([\r]*\n)/', '\\1> ', $original_message_body); } $object->SetDBField('Message', $message_body); } } $this->clearSelectedIDs($event); } /** * Parses $search string in subject and reformats it * Used for replying and forwarding * * @param string $subject * @param string $search * @return string */ function _transformSubject($subject, $search = 'Re') { $regex = '/'.$search.'(\[([\d]+)\]){0,1}:/i'; preg_match_all($regex, $subject, $regs); if ($regs[2]) { $reply_count = 0; // reply count without numbers (equals to "re[1]") $max_reply_number = 0; // maximal reply number sort($regs[2], SORT_NUMERIC); // sort ascending (non-numeric replies first) foreach ($regs[2] as $match) { if (!$match) { // found "re:" $reply_count++; } elseif ($match > $max_reply) { // found "re:[number]" $max_reply_number = $match; } } return $search.'['.($reply_count + $max_reply_number + 1).']: '.trim(preg_replace($regex, '', $subject)); } return $search.': '.$subject; } /** * Resends reply, that was not sent last time * * @param kEvent $event */ function OnResendReply($event) { $ids = $this->StoreSelectedIDs($event); if (!$ids) { return ; } $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $sql = 'SELECT f.ReplyFromEmail, sl.' . $object->IDField . ' FROM ' . $object->TableName . ' sl JOIN ' . $this->Application->getUnitConfig('formsubs')->getTableName() . ' fs ON fs.FormSubmissionId = sl.FormSubmissionId JOIN ' . $this->Application->getUnitConfig('form')->getTableName() . ' f ON f.FormId = fs.FormId WHERE sl.' . $object->IDField . ' IN (' . implode(',', $ids) . ')'; $reply_emails = $this->Conn->GetCol($sql, $object->IDField); foreach ($ids as $id) { $object->Load($id); // allow to send messages, that were successfully sended before :( if (($object->GetDBField('ToEmail') != $reply_emails[$id]) && ($object->GetDBField('SentStatus') != SUBMISSION_LOG_SENT)) { $object->SetOriginalField('SentStatus', 0); // reset sent status to update sent date automatically $this->_sendEmail($object); // resend email here } } $this->clearSelectedIDs($event); if (!$this->Application->GetVar('from_list')) { $event->SetRedirectParam('opener', 'u'); } } /** * Updates last operation dates for log record * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->_validateRecipients($event); $this->_updateStatusDates($event); } /** * Updates last operation dates for log record * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_validateRecipients($event); $this->_updateStatusDates($event); } /** * Validates email recipients * * @param kEvent $event */ function _validateRecipients($event) { $object = $event->getObject(); /* @var $object kDBItem */ $esender = $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $cc = $object->GetDBField('Cc'); if ($cc && ($esender->GetRecipients($cc) === false)) { $object->SetError('Cc', 'invalid_format'); } $bcc = $object->GetDBField('Bcc'); if ($bcc && ($esender->GetRecipients($bcc) === false)) { $object->SetError('Bcc', 'invalid_format'); } } /** * Generates verification code and sets it inside sent message * * @param kDBItem $object * @return string */ function _generateVerificationCode(&$object) { $code = Array ( $object->GetDBField('FromEmail'), $object->GetDBField('ToEmail'), $object->GetID(), microtime(true) ); $object->SetDBField('VerifyCode', md5( implode('-', $code) )); } /** * Sends email based on fields from given submission-log record * * @param kDBItem $object */ function _sendEmail(&$object) { if ($this->Application->GetVar('client_mode')) { return ; } if (!$object->GetDBField('VerifyCode')) { $this->_generateVerificationCode($object); } $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form_submission = $form_submission_helper->getSubmissionFromLog($object); $form =& $form_submission_helper->getForm($form_submission); $send_params = Array ( 'from_name' => $form->GetDBField('ReplyFromName'), 'from_email' => $object->GetDBField('FromEmail'), 'to_email' => $object->GetDBField('ToEmail'), 'subject' => $object->GetDBField('Subject'), 'message' => $object->GetDBField('Message'), ); $to_name = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_NAME); if ($to_name) { $send_params['to_name'] = $to_name; } $esender = $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $esender->SetReturnPath( $form->GetDBField('BounceEmail') ); if ($object->GetDBField('Cc')) { $recipients = $esender->GetRecipients( $object->GetDBField('Cc') ); foreach ($recipients as $recipient_info) { $esender->AddCc($recipient_info['Email'], $recipient_info['Name']); } } if ($object->GetDBField('Bcc')) { $recipients = $esender->GetRecipients( $object->GetDBField('Bcc') ); foreach ($recipients as $recipient_info) { $esender->AddBcc($recipient_info['Email'], $recipient_info['Name']); } } if ($object->GetDBField('Attachment')) { $attachments = explode('|', $object->GetField('Attachment', 'file_paths')); foreach ($attachments as $attachment) { $esender->AddAttachment($attachment); } } $this->Application->emailAdmin('FORM.SUBMISSION.REPLY.TO.USER', null, $object->getEmailParams($send_params)); // mark as sent after sending is finished $object->SetDBField('SentStatus', SUBMISSION_LOG_SENT); // reset bounce status before (re-)sending $object->SetDBField('BounceInfo', NULL); $object->SetDBField('BounceDate_date', NULL); $object->SetDBField('BounceDate_time', NULL); if ($object->GetDBField('DraftId')) { $temp_handler = $this->Application->recallObject('draft_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('draft', '', Array ($object->GetDBField('DraftId'))); $object->SetDBField('DraftId', 0); } $object->Update(); } /** * Sends new email after log record was created * Updates last update time for submission * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $this->_sendEmail($object); // send email $this->_updateSubmission($event); $reply_to = $object->GetDBField('ReplyTo'); if ( !$reply_to ) { $reply_to = $this->_getLastMessageId($event, !$this->Application->GetVar('client_mode')); } if ( $reply_to ) { // this is reply to other message -> mark it as replied $org_message = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $org_message kDBItem */ $org_message->Load($reply_to); $org_message->SetDBField('ReplyStatus', SUBMISSION_LOG_REPLIED); $org_message->Update(); } if ( $this->Application->GetVar('client_mode') ) { // new reply from client received -> send notification about it $this->Application->emailAdmin('FORM.SUBMISSION.REPLY.FROM.USER', null, $object->getEmailParams()); } } /** * Returns last message id (client OR admin) * * @param kEvent $event * @param bool $from_client * @return int */ function _getLastMessageId($event, $from_client = false) { $object = $event->getObject(); /* @var $object kDBItem */ $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form_submission = $form_submission_helper->getSubmissionFromLog($object); $form =& $form_submission_helper->getForm($form_submission); $reply_email = $form->GetDBField('ReplyFromEmail'); $sql = 'SELECT MAX(' . $object->IDField . ') FROM ' . $object->TableName . ' WHERE (FormSubmissionId = ' . $form_submission->GetID() . ') AND (ToEmail' . ($from_client ? ' = ' : ' <> ') . $this->Conn->qstr($reply_email) . ')'; return $this->Conn->GetOne($sql); } /** * Updates last update time for submission * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent $event) { parent::OnAfterItemUpdate($event); $this->_updateSubmission($event); $object = $event->getObject(); /* @var $object kDBItem */ // send out email event to admin for bouncing $sent_status = $object->GetDBField('SentStatus'); if ( $object->GetOriginalField('SentStatus') != $sent_status && $sent_status == SUBMISSION_LOG_BOUNCE ) { $this->Application->emailAdmin('FORM.SUBMISSION.REPLY.FROM.USER.BOUNCED', null, $object->getEmailParams()); } } /** * Sets last sent/reply dates based on field changes in log record * * @param kEvent $event */ function _updateStatusDates($event) { $object = $event->getObject(); /* @var $object kDBItem */ $now = time(); $sent_status = $object->GetDBField('SentStatus'); if (($event->Special != 'merge') && ($sent_status == SUBMISSION_LOG_SENT) && ($sent_status != $object->GetOriginalField('SentStatus'))) { // sent status was set $object->SetDBField('SentOn_date', $now); $object->SetDBField('SentOn_time', $now); } $reply_status = $object->GetDBField('ReplyStatus'); if (($reply_status == SUBMISSION_LOG_REPLIED) && ($reply_status != $object->GetOriginalField('ReplyStatus'))) { // sent status was set $object->SetDBField('RepliedOn_date', $now); $object->SetDBField('RepliedOn_time', $now); } } /** * Sets last updated field for form submission * * @param kEvent $event */ function _updateSubmission($event) { $object = $event->getObject(); /* @var $object kDBItem */ $form_submission_helper = $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form_submission = $form_submission_helper->getSubmissionFromLog($object); // 1. set last updated $last_updated = max ($object->GetDBField('SentOn'), $object->GetDBField('RepliedOn')); if ($form_submission->GetDBField('LastUpdatedOn') < $last_updated) { // don't set smaller last update, that currenly set $form_submission->SetDBField('LastUpdatedOn_date', $last_updated); $form_submission->SetDBField('LastUpdatedOn_time', $last_updated); } // 2. update submission status $form =& $form_submission_helper->getForm($form_submission); $client_responce = $form->GetDBField('ReplyFromEmail') == $object->GetDBField('ToEmail'); $replied = $object->GetDBField('ReplyStatus') == SUBMISSION_LOG_REPLIED; if (!$client_responce && !$replied) { // admin sends new email to client $form_submission->SetDBField('LogStatus', SUBMISSION_REPLIED); } elseif ($client_responce) { // client email becomes replied OR receiving new unreplied email from client $form_submission->SetDBField('LogStatus', $replied ? SUBMISSION_REPLIED : SUBMISSION_NEW_EMAIL); } if ($object->GetDBField('SentStatus') == SUBMISSION_LOG_BOUNCE) { // propagate bounce status from reply $form_submission->SetDBField('LogStatus', SUBMISSION_BOUNCE); } $form_submission->Update(); } /** * Saves current unsent message as draft * * @param kEvent $event */ function OnSaveDraft($event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft = $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); $draft->SetDBField('Message', $object->GetDBField('Message')); if ($draft->isLoaded()) { $draft->Update(); } else { $draft->SetDBFieldsFromHash($load_keys); $draft->Create(); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->SetRedirectParam('opener', 'u'); } /** * Uses found draft instead of submission reply body * * @param kEvent $event */ function OnUseDraft($event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft = $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); if ($draft->isLoaded()) { $object->SetDBField('Message', $draft->GetDBField('Message')); $object->SetDBField('DraftId', $draft->GetID()); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->redirect = false; } /** * Deletes draft, that matches given user and form submission * * @param kEvent $event */ function OnDeleteDraft($event) { $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft = $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); + $object->SetDBField('DraftId', 0); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); if ($draft->isLoaded()) { $temp_handler = $this->Application->recallObject('draft_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('draft', '', Array ($draft->GetID())); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->redirect = false; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/admin/admin_events_handler.php =================================================================== --- branches/5.3.x/core/units/admin/admin_events_handler.php (revision 16110) +++ branches/5.3.x/core/units/admin/admin_events_handler.php (revision 16111) @@ -1,1407 +1,1420 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class AdminEventsHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnSaveColumns' => Array ('self' => true), 'OnGetPopupSize' => Array ('self' => true), 'OnClosePopup' => Array ('self' => true), 'OnSaveSetting' => Array ('self' => true), 'OnDropTempTablesByWID' => Array ('self' => true), 'OnProcessSelected' => Array ('self' => true), // allow CSV import file upload ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $perm_value = null; $system_events = Array ( 'OnResetModRwCache', 'OnResetSections', 'OnResetConfigsCache', 'OnResetParsedData', 'OnResetMemcache', 'OnDeleteCompiledTemplates', 'OnCompileTemplates', 'OnGenerateTableStructure', 'OnSynchronizeDBRevisions', 'OnDeploy', 'OnRebuildThemes', 'OnCheckPrefixConfig', 'OnMemoryCacheGet', 'OnMemoryCacheSet' ); if ( in_array($event->Name, $system_events) ) { // events from "Tools -> System Tools" section are controlled via that section "edit" permission $perm_value = /*$this->Application->isDebugMode() ||*/ $this->Application->CheckPermission($event->getSection() . '.edit'); } $tools_events = Array ( 'OnBackup' => 'in-portal:backup.view', 'OnBackupProgress' => 'in-portal:backup.view', 'OnDeleteBackup' => 'in-portal:backup.view', 'OnBackupCancel' => 'in-portal:backup.view', 'OnRestore' => 'in-portal:restore.view', 'OnRestoreProgress' => 'in-portal:restore.view', 'OnRestoreCancel' => 'in-portal:backup.view', 'OnSqlQuery' => 'in-portal:sql_query.view', ); if ( array_key_exists($event->Name, $tools_events) ) { $perm_value = $this->Application->CheckPermission($tools_events[$event->Name]); } if ( $event->Name == 'OnSaveMenuFrameWidth' ) { $perm_value = $this->Application->isAdminUser; } $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $csv_events = Array ('OnCSVImportBegin', 'OnCSVImportStep', 'OnExportCSV', 'OnGetCSV'); if ( in_array($event->Name, $csv_events) ) { $csv_helper = $this->Application->recallObject('CSVHelper'); /* @var $csv_helper kCSVHelper */ $prefix = $csv_helper->getPrefix(stripos($event->Name, 'import') !== false); $perm_mapping = Array ( 'OnCSVImportBegin' => 'OnProcessSelected', 'OnCSVImportStep' => 'OnProcessSelected', 'OnExportCSV' => 'OnLoad', 'OnGetCSV' => 'OnLoad', ); $tmp_event = new kEvent($prefix . ':' . $perm_mapping[$event->Name] ); $perm_value = $perm_helper->CheckEventPermission($tmp_event, $this->permMapping); } if ( isset($perm_value) ) { return $perm_helper->finalizePermissionCheck($event, $perm_value); } return parent::CheckPermission($event); } /** * Reset mod-rewrite url cache * * @param kEvent $event * @return void * @access protected */ protected function OnResetModRwCache(kEvent $event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } $this->Conn->Query('DELETE FROM ' . TABLE_PREFIX . 'CachedUrls'); $event->SetRedirectParam('action_completed', 1); } /** * Resets tree section cache and refreshes admin section tree * * @param kEvent $event * @return void * @access protected */ protected function OnResetSections(kEvent $event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } else { $this->Application->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } $event->SetRedirectParam('refresh_tree', 1); $event->SetRedirectParam('action_completed', 1); } /** * Resets unit config cache * * @param kEvent $event * @return void * @access protected */ protected function OnResetConfigsCache(kEvent $event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->rebuildCache('master:config_files', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } else { $this->Application->rebuildDBCache('config_files', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } $this->OnResetParsedData($event); $skin_helper = $this->Application->recallObject('SkinHelper'); /* @var $skin_helper SkinHelper */ $skin_helper->deleteCompiled(); } /** * Resets parsed data from unit configs * * @param kEvent $event * @return void * @access protected */ protected function OnResetParsedData(kEvent $event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } $this->Application->DeleteUnitCache(); if ( $this->Application->GetVar('validate_configs') ) { $event->SetRedirectParam('validate_configs', 1); } $event->SetRedirectParam('action_completed', 1); } /** * Resets memory cache * * @param kEvent $event * @return void * @access protected */ protected function OnResetMemcache(kEvent $event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } $this->Application->resetCache(); $event->SetRedirectParam('action_completed', 1); } /** * Compiles all templates (with a progress bar) * * @param kEvent $event * @return void * @access protected */ protected function OnCompileTemplates(kEvent $event) { $compiler = $this->Application->recallObject('NParserCompiler'); /* @var $compiler NParserCompiler */ $compiler->CompileTemplatesStep(); $event->status = kEvent::erSTOP; } /** * Deletes all compiled templates * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteCompiledTemplates(kEvent $event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } $base_path = WRITEABLE . DIRECTORY_SEPARATOR . 'cache'; // delete debugger reports $debugger_reports = glob(RESTRICTED . '/debug_@*@.txt'); if ( $debugger_reports ) { foreach ($debugger_reports as $debugger_report) { unlink($debugger_report); } } $this->_deleteCompiledTemplates($base_path); $event->SetRedirectParam('action_completed', 1); } /** * Deletes compiled templates in a given folder * * @param string $folder * @param bool $unlink_folder * @return void * @access protected */ protected function _deleteCompiledTemplates($folder, $unlink_folder = false) { $sub_folders = glob($folder . '/*', GLOB_ONLYDIR); if ( is_array($sub_folders) ) { foreach ($sub_folders as $sub_folder) { $this->_deleteCompiledTemplates($sub_folder, true); } } $files = glob($folder . '/*.php'); if ( is_array($files) ) { foreach ($files as $file) { unlink($file); } } if ( $unlink_folder ) { rmdir($folder); } } /** * Generates structure for specified table * * @param kEvent $event * @return void * @access protected */ protected function OnGenerateTableStructure(kEvent $event) { $types_hash = Array ( 'string' => 'varchar|text|mediumtext|longtext|date|datetime|time|timestamp|char|year|enum|set', 'int' => 'smallint|mediumint|int|bigint|tinyint', 'float' => 'float|double|decimal', ); $table_name = $this->Application->GetVar('table_name'); if ( !$table_name ) { echo 'error: no table name specified'; return; } if ( TABLE_PREFIX && !preg_match('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', $table_name) && (strtolower($table_name) != $table_name) ) { // table name without prefix, then add it (don't affect K3 tables named in lowercase) $table_name = TABLE_PREFIX . $table_name; } if ( !$this->Conn->TableFound($table_name) ) { // table with prefix doesn't exist, assume that just config prefix passed -> resolve table name from it $prefix = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '/', '', $table_name); if ( $this->Application->prefixRegistred($prefix) ) { // when prefix is found -> use it's table (don't affect K3 tables named in lowecase) $table_name = $this->Application->getUnitConfig($prefix)->getTableName(); } } $table_info = $this->Conn->Query('DESCRIBE '.$table_name); // 1. prepare config keys $grids = Array ( 'Default' => Array ( 'Icons' => Array ('default' => 'icon16_item.png'), 'Fields' => Array (), ) ); - $grid_fields = Array(); + $grids_fields = Array(); $id_field = ''; $fields = Array (); $float_types = Array ('float', 'double', 'numeric'); foreach ($table_info as $field_info) { if ( preg_match('/l[\d]+_.*/', $field_info['Field']) ) { // don't put multilingual fields in config continue; } $field_options = Array (); if ( $field_info['Key'] == 'PRI' ) { - $grid_col_options = Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 80); + if ( $field_info['Field'] == 'Id' ) { + $grid_col_options = Array ('filter_block' => 'grid_range_filter', 'width' => 80); + } + else { + $grid_col_options = Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 80); + } } else { $grid_col_options = Array ('filter_block' => 'grid_like_filter'); } // 1. get php field type by mysql field type foreach ($types_hash as $php_type => $db_types) { if ( preg_match('/' . $db_types . '/', $field_info['Type']) ) { $field_options['type'] = $php_type; break; } } // 2. get field default value $default_value = $field_info['Default']; $not_null = $field_info['Null'] != 'YES'; if ( is_numeric($default_value) ) { $default_value = preg_match('/[\.,]/', $default_value) ? (float)$default_value : (int)$default_value; } if ( is_null($default_value) && $not_null ) { $default_value = $field_options['type'] == 'string' ? '' : 0; } if ( in_array($php_type, $float_types) ) { // this is float number if ( preg_match('/' . $db_types . '\([\d]+,([\d]+)\)/i', $field_info['Type'], $regs) ) { // size is described in structure -> add formatter $field_options['formatter'] = 'kFormatter'; $field_options['format'] = '%01.' . $regs[1] . 'f'; if ( $not_null ) { // null fields, will most likely have NULL as default value $default_value = 0; } } elseif ( $not_null ) { // no size information, just convert to float // null fields, will most likely have NULL as default value $default_value = (float)$default_value; } } if ( preg_match('/varchar\(([\d]+)\)/i', $field_info['Type'], $regs) ) { $field_options['max_len'] = (int)$regs[1]; } if ( preg_match('/tinyint\([\d]+\)/i', $field_info['Type']) ) { $field_options['formatter'] = 'kOptionsFormatter'; $field_options['options'] = Array (1 => 'la_Yes', 0 => 'la_No'); $field_options['use_phrases'] = 1; $grid_col_options['filter_block'] = 'grid_options_filter'; } if ( $not_null ) { $field_options['not_null'] = 1; } if ( $field_info['Key'] == 'PRI' ) { $default_value = 0; $id_field = $field_info['Field']; } if ( $php_type == 'int' && !$not_null ) { // numeric null field if ( preg_match('/(On|Date)$/', $field_info['Field']) || $field_info['Field'] == 'Modified' ) { $field_options['formatter'] = 'kDateFormatter'; $grid_col_options['filter_block'] = 'grid_date_range_filter'; $grid_col_options['width'] = 120; } else { $grid_col_options['filter_block'] = 'grid_range_filter'; $grid_col_options['width'] = 80; } } if ( $php_type == 'int' && ($not_null || is_numeric($default_value)) ) { // is integer field AND not null $field_options['default'] = (int)$default_value; } else { $field_options['default'] = $default_value; } $fields[$field_info['Field']] = $field_options; $grids_fields[$field_info['Field']] = $grid_col_options; } $grids['Default']['Fields'] = $grids_fields; $ret = Array ( 'IDField' => $id_field, 'Fields' => $fields, 'Grids' => $grids, ); $decorator = new UnitConfigDecorator(); $ret = $decorator->decorate($ret); $this->Application->InitParser(); ob_start(); echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"')); ?> <script type="text/javascript"> set_window_title('Table "<?php echo $table_name; ?>" Structure'); </script> <a href="javascript:window_close();">Close Window</a><br /><br /> <?php echo $GLOBALS['debugger']->highlightString($ret); ?> <br /><br /><a href="javascript:window_close();">Close Window</a><br /> <?php echo $this->Application->ParseBlock(Array('name' => 'incs/footer')); echo ob_get_clean(); $event->status = kEvent::erSTOP; } /** * Refreshes ThemeFiles & Themes tables by actual content on HDD * * @param kEvent $event * @return void * @access protected */ protected function OnRebuildThemes(kEvent $event) { if ( $this->Application->GetVar('ajax') == 'yes' ) { $event->status = kEvent::erSTOP; } $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $themes_helper->refreshThemes(); $event->SetRedirectParam('action_completed', 1); } /** * Saves grid column widths after their resize by user * * @param kEvent $event * @return void * @access protected */ protected function OnSaveColumns(kEvent $event) { $picker_helper = new kColumnPickerHelper( $this->Application->GetVar('main_prefix'), $this->Application->GetLinkedVar('grid_name') ); $picked = trim($this->Application->GetVar('picked_str'), '|'); $hidden = trim($this->Application->GetVar('hidden_str'), '|'); $picker_helper->saveColumns($picked, $hidden); $this->finalizePopup($event); } /** * Saves various admin settings via ajax * * @param kEvent $event * @return void * @access protected */ protected function OnSaveSetting(kEvent $event) { if ( $this->Application->GetVar('ajax') != 'yes' ) { return; } $var_name = $this->Application->GetVar('var_name'); $var_value = $this->Application->GetVar('var_value'); $this->Application->StorePersistentVar($var_name, $var_value); $event->status = kEvent::erSTOP; } /** * Just closes popup & deletes last_template & opener_stack if popup, that is closing * * @param kEvent $event * @return void * @access protected */ protected function OnClosePopup(kEvent $event) { $event->SetRedirectParam('opener', 'u'); } /** * Occurs right after initialization of the kernel, used mainly as hook-to event * * @param kEvent $event * @return void * @access protected */ protected function OnStartup(kEvent $event) { if ( $this->Application->isAdmin ) { return; } $base_url = preg_quote($this->Application->BaseURL(), '/'); $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; if ( $referrer && !preg_match('/^' . $base_url . '/', $referrer) ) { $this->Application->Session->SetCookie('original_referrer', $referrer); $this->Application->SetVar('original_referrer', $referrer); } } /** * Occurs right before echoing the output, in Done method of application, used mainly as hook-to event * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeShutdown(kEvent $event) { } /** * Is called after tree was build (when not from cache) * * @param kEvent $event * @return void * @access protected */ protected function OnAfterBuildTree(kEvent $event) { } /** * Called by AJAX to perform CSV export * * @param kEvent $event * @return void * @access protected */ protected function OnExportCSV(kEvent $event) { $csv_helper = $this->Application->recallObject('CSVHelper'); /* @var $csv_helper kCSVHelper */ $csv_helper->PrefixSpecial = $csv_helper->getPrefix(false); $csv_helper->grid = $this->Application->GetVar('grid'); $csv_helper->ExportStep(); $event->status = kEvent::erSTOP; } /** * Returning created by AJAX CSV file * * @param kEvent $event * @return void * @access protected */ protected function OnGetCSV(kEvent $event) { $csv_helper = $this->Application->recallObject('CSVHelper'); /* @var $csv_helper kCSVHelper */ $csv_helper->GetCSV(); } /** * Start CSV import * * @param kEvent $event * @return void * @access protected */ protected function OnCSVImportBegin(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ + $object->setID(0); $field_values = $this->getSubmittedFields($event); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); $event->redirect = false; $result = 'required'; if ( $object->GetDBField('ImportFile') ) { $csv_helper = $this->Application->recallObject('CSVHelper'); /* @var $csv_helper kCSVHelper */ $csv_helper->PrefixSpecial = $csv_helper->getPrefix(true); $csv_helper->grid = $this->Application->GetVar('grid'); $result = $csv_helper->ImportStart($object->GetField('ImportFile', 'file_paths')); if ( $result === true ) { $event->redirect = $this->Application->GetVar('next_template'); $event->SetRedirectParam('PrefixSpecial', $this->Application->GetVar('PrefixSpecial')); $event->SetRedirectParam('grid', $this->Application->GetVar('grid')); } } if ( $event->redirect === false ) { $object->SetError('ImportFile', $result); $event->status = kEvent::erFAIL; } } /** * Performs one CSV import step * * @param kEvent $event * @return void * @access protected */ protected function OnCSVImportStep(kEvent $event) { $import_helper = $this->Application->recallObject('CSVHelper'); /* @var $import_helper kCSVHelper */ $import_helper->ImportStep(); $event->status = kEvent::erSTOP; } /** * Shows unit config filename, where requested prefix is defined * * @param kEvent $event * @return void * @access protected */ protected function OnCheckPrefixConfig(kEvent $event) { $prefix = $this->Application->GetVar('config_prefix'); $config_file = $this->Application->UnitConfigReader->getPrefixFile($prefix); $this->Application->InitParser(); ob_start(); echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"')); ?> <script type="text/javascript"> set_window_title('Unit Config of "<?php echo $prefix; ?>" prefix'); </script> <a href="javascript:window_close();">Close Window</a><br /><br /> <strong>Prefix:</strong> <?php echo $prefix; ?><br /> <strong>Unit Config:</strong> <?php echo $GLOBALS['debugger']->highlightString($config_file); ?><br /> <br /><a href="javascript:window_close();">Close Window</a><br /> <?php echo $this->Application->ParseBlock(Array ('name' => 'incs/footer')); echo ob_get_clean(); $event->status = kEvent::erSTOP; } /** * Deletes temp tables, when user closes window using "x" button in top right corner * * @param kEvent $event * @return void * @access protected */ protected function OnDropTempTablesByWID(kEvent $event) { $sid = $this->Application->GetSID(); $wid = $this->Application->GetVar('m_wid'); $tables = $this->Conn->GetCol('SHOW TABLES'); $mask_edit_table = '/' . TABLE_PREFIX . 'ses_' . $sid . '_' . $wid . '_edit_(.*)$/'; foreach ($tables as $table) { if ( preg_match($mask_edit_table, $table, $rets) ) { $this->Conn->Query('DROP TABLE IF EXISTS ' . $table); } } echo 'OK'; $event->status = kEvent::erSTOP; } /** * Backup all data * * @param kEvent $event * @return void * @access protected */ protected function OnBackup(kEvent $event) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ if ( !$backup_helper->initBackup() ) { $event->status = kEvent::erFAIL; } $event->redirect = 'tools/backup2'; } /** * Perform next backup step * * @param kEvent $event * @return void * @access protected */ protected function OnBackupProgress(kEvent $event) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ $done_percent = $backup_helper->performBackup(); if ( $done_percent == 100 ) { $event->redirect = 'tools/backup3'; return; } $event->status = kEvent::erSTOP; echo $done_percent; } /** * Stops Backup & redirect to Backup template * * @param kEvent $event * @return void * @access protected */ protected function OnBackupCancel(kEvent $event) { $event->redirect = 'tools/backup1'; } /** * Starts restore process * * @param kEvent $event * @return void * @access protected */ protected function OnRestore(kEvent $event) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ $backup_helper->initRestore(); $event->redirect = 'tools/restore3'; } /** * Performs next restore step * * @param kEvent $event * @return void * @access protected */ protected function OnRestoreProgress(kEvent $event) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ $done_percent = $backup_helper->performRestore(); if ( $done_percent == BackupHelper::SQL_ERROR_DURING_RESTORE ) { $event->redirect = 'tools/restore4'; } elseif ( $done_percent == BackupHelper::FAILED_READING_BACKUP_FILE ) { $this->Application->StoreVar('adm.restore_error', 'File read error'); $event->redirect = 'tools/restore4'; } elseif ( $done_percent == 100 ) { $backup_helper->replaceRestoredFiles(); $this->Application->StoreVar('adm.restore_success', 1); $event->redirect = 'tools/restore4'; } else { $event->status = kEvent::erSTOP; echo $done_percent; } } /** * Stops Restore & redirect to Restore template * * @param kEvent $event * @return void * @access protected */ protected function OnRestoreCancel(kEvent $event) { $event->redirect = 'tools/restore1'; } /** * Deletes one backup file * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteBackup(kEvent $event) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ $backup_helper->delete(); } /** * Starts restore process * * @param kEvent $event * @return void * @access protected */ protected function OnSqlQuery(kEvent $event) { $sql = $this->Application->GetVar('sql'); if ( $sql ) { $start = microtime(true); $result = $this->Conn->Query($sql); $this->Application->SetVar('sql_time', round(microtime(true) - $start, 7)); if ( $result && is_array($result) ) { $this->Application->SetVar('sql_has_rows', 1); $this->Application->SetVar('sql_rows', serialize($result)); } $check_sql = trim(strtolower($sql)); if ( preg_match('/^(insert|update|replace|delete)/', $check_sql) ) { $this->Application->SetVar('sql_has_affected', 1); $this->Application->SetVar('sql_affected', $this->Conn->getAffectedRows()); } } $this->Application->SetVar('query_status', 1); $event->status = kEvent::erFAIL; } /** * Occurs after unit config cache was successfully rebuilt * * @param kEvent $event * @return void * @access protected */ protected function OnAfterCacheRebuild(kEvent $event) { } /** * Removes "Community -> Groups" section when it is not allowed * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); $config = $event->getUnitConfig(); if ( !$this->Application->ConfigValue('AdvancedUserManagement') ) { $config->addSectionAdjustments('remove', 'in-portal:user_groups'); } $config->addSectionAdjustments(Array ( 'label' => $this->Application->ConfigValue('Site_Name') ), 'in-portal:root'); } /** * Saves menu (tree) frame width * * @param kEvent $event * @return void * @access protected */ protected function OnSaveMenuFrameWidth(kEvent $event) { $event->status = kEvent::erSTOP; if ( !$this->Application->ConfigValue('ResizableFrames') ) { return; } $this->Application->SetConfigValue('MenuFrameWidth', (int)$this->Application->GetVar('width')); } /** * Retrieves data from memory cache * * @param kEvent $event * @return void * @access protected */ protected function OnMemoryCacheGet(kEvent $event) { $event->status = kEvent::erSTOP; $ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error $key = $this->Application->GetVar('key'); if ( !$key ) { $ret['code'] = 1; $ret['message'] = 'Key name missing'; } else { $value = $this->Application->getCache($key); $ret['value'] =& $value; $ret['size'] = is_string($value) ? kUtil::formatSize(strlen($value)) : '?'; $ret['type'] = gettype($value); if ( kUtil::IsSerialized($value) ) { $value = unserialize($value); } if ( is_array($value) ) { $ret['value'] = print_r($value, true); } if ( $ret['value'] === false ) { $ret['code'] = 2; $ret['message'] = 'Key "' . $key . '" doesn\'t exist'; } } $json_helper = $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ echo $json_helper->encode($ret); } /** * Retrieves data from memory cache * * @param kEvent $event * @return void * @access protected */ protected function OnMemoryCacheSet(kEvent $event) { $event->status = kEvent::erSTOP; $ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error $key = $this->Application->GetVar('key'); if ( !$key ) { $ret['code'] = 1; $ret['message'] = 'Key name missing'; } else { $value = $this->Application->GetVar('value'); $res = $this->Application->setCache($key, $value); $ret['result'] = $res ? 'OK' : 'FAILED'; } $json_helper = $this->Application->recallObject('JSONHelper'); /* @var $json_helper JSONHelper */ echo $json_helper->encode($ret); } /** * Deploy changes * * Usage: "php tools/run_event.php adm:OnDeploy b674006f3edb1d9cd4d838c150b0567d" * * @param kEvent $event * @return void * @access protected */ protected function OnDeploy(kEvent $event) { $this->_deploymentAction($event); } /** * Synchronizes database revisions from "project_upgrades.sql" file * * @param kEvent $event * @return void * @access protected */ protected function OnSynchronizeDBRevisions(kEvent $event) { $this->_deploymentAction($event, true); } /** * Common code to invoke deployment helper * * @param kEvent $event * @param bool $dry_run * @return void * @access protected */ protected function _deploymentAction(kEvent $event, $dry_run = false) { $deployment_helper = $this->Application->recallObject('DeploymentHelper'); /* @var $deployment_helper DeploymentHelper */ $deployment_helper->setEvent($event); if ( $deployment_helper->deployAll($dry_run) ) { $event->SetRedirectParam('action_completed', 1); if ( !$deployment_helper->isCommandLine ) { // browser invocation -> don't perform redirect $event->redirect = false; // no redirect, but deployment succeeded - set redirect params directly foreach ($event->getRedirectParams() as $param_name => $param_value) { $this->Application->SetVar($param_name, $param_value); } } } else { $event->status = kEvent::erFAIL; } } /** * [SCHEDULED TASK] * 1. Delete all Debug files from system/.restricted folder (format debug_@977827436@.txt) * 2. Run MySQL OPTIMIZE SQL one by one on all In-Portal tables (found by prefix). * * @param kEvent $event * @return void * @access protected */ protected function OnOptimizePerformance(kEvent $event) { $start_time = time(); $sql = 'SELECT SessionKey FROM ' . TABLE_PREFIX . 'UserSessions WHERE LastAccessed > ' . $start_time; $active_sessions = array_flip($this->Conn->GetCol($sql)); $files = scandir(RESTRICTED); $file_path = RESTRICTED . '/'; foreach ($files AS $file_name) { if ( !preg_match('#^debug_@([0-9]{9})@.txt$#', $file_name, $matches) ) { // not debug file continue; } $sid = $matches[1]; if ( isset($active_sessions[$sid]) || (filemtime($file_path . $file_name) > $start_time) ) { // debug file belongs to an active session // debug file is recently created (after sessions snapshot) continue; } unlink($file_path . $file_name); } $system_tables = $this->Conn->GetCol('SHOW TABLES LIKE "' . TABLE_PREFIX . '%"'); foreach ($system_tables AS $table_name) { $this->Conn->Query('OPTIMIZE TABLE ' . $table_name); } } /** * [SCHEDULED TASK] Pre-resizes images, used in templates * * @param kEvent $event * @return void * @access protected */ protected function OnPreResizeImages(kEvent $event) { $scheduled_task = $event->MasterEvent->getObject(); /* @var $scheduled_task kDBItem */ $mass_resizer = new MassImageResizer(); // rules from scheduled task itself $mass_resizer->addRules($scheduled_task->GetDBField('Settings')); // rules from all enabled themes $sql = 'SELECT ImageResizeRules FROM ' . $this->Application->getUnitConfig('theme')->getTableName() . ' WHERE Enabled = 1'; $mass_resizer->addRules($this->Conn->GetCol($sql)); $mass_resizer->run(); } /** * Returns popup size (by template), if not cached, then parse template to get value * * @param kEvent $event * @return void * @access protected */ protected function OnGetPopupSize(kEvent $event) { $event->status = kEvent::erSTOP; if ( $this->Application->GetVar('ajax') != 'yes' ) { return; } $t = $this->Application->GetVar('template_name'); $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'PopupSizes WHERE TemplateName = ' . $this->Conn->qstr($t); $popup_info = $this->Conn->GetRow($sql); $this->Application->setContentType('text/plain'); if ( !$popup_info ) { // dies when SetPopupSize tag found & in ajax request $this->Application->InitParser(); $this->Application->ParseBlock(Array ('name' => $t)); // tag SetPopupSize not found in template -> use default size echo '750x400'; } else { echo $popup_info['PopupWidth'] . 'x' . $popup_info['PopupHeight']; } } /** * Writes HTTP request to System Log * * @param kEvent $event * @return void * @access public */ public function OnLogHttpRequest(kEvent $event) { if ( defined('DBG_REQUEST_LOG') && DBG_REQUEST_LOG && $this->Application->LoggedIn() ) { $log = $this->Application->log('HTTP_REQUEST')->addRequestData(); if ( !$log->write() ) { trigger_error('Unable to log Http Request due disabled "System Log"', E_USER_WARNING); } } } } /** * Resizes multiple images according to given rules */ class MassImageResizer extends kBase { /** * Rules, that tell how images must be resized * * @var Array * @access private */ private $_rules = Array (); /** * Remembers, which fields of which unit are used * * @var Array * @access private */ private $_unitFields = Array (); /** * Remembers which fields of which units require which format * * @var Array * @access private */ private $_unitFieldFormats = Array (); /** * Adds more resize rules * * @param string|Array $rules * @return void * @access public */ public function addRules($rules) { $rules = (array)$rules; foreach ($rules as $rule) { $rule = $this->_cleanup($rule); if ( $rule ) { $this->_rules = array_merge($this->_rules, $rule); } } } /** * Normalizes given set of rules * * @param string $rules * @return Array * @access private */ private function _cleanup($rules) { $ret = explode("\n", str_replace(Array ("\r\n", "\r"), "\n", $rules)); return array_filter(array_map('trim', $ret)); } /** * Transforms rules given in raw format into 3D array of easily manageable rules * * @return void * @access private */ private function _preProcessRules() { foreach ($this->_rules as $raw_rule) { list ($prefix, $field, $format) = explode(':', $raw_rule, 3); if ( !isset($this->_unitFields[$prefix]) ) { $this->_unitFields[$prefix] = Array (); $this->_unitFieldFormats[$prefix] = Array (); } $this->_unitFields[$prefix][] = $field; if ( !isset($this->_unitFieldFormats[$prefix][$field]) ) { $this->_unitFieldFormats[$prefix][$field] = Array (); } $this->_unitFieldFormats[$prefix][$field][] = $format; } } /** * Performs resize operation * * @return void * @access public */ public function run() { $this->_preProcessRules(); foreach ($this->_unitFields as $prefix => $fields) { $sql = 'SELECT ' . implode(',', array_unique($fields)) . ' FROM ' . $this->Application->getUnitConfig($prefix)->getTableName(); $unit_data = $this->Conn->GetIterator($sql); if ( !count($unit_data) ) { continue; } $object = $this->Application->recallObject($prefix . '.resize', null, Array ('skip_autoload' => true)); /* @var $object kDBItem */ foreach ($unit_data as $field_values) { foreach ($field_values as $field => $value) { if ( !$value ) { continue; } $object->SetDBField($field, $value); foreach ($this->_unitFieldFormats[$prefix][$field] as $format) { // will trigger image resize if needed $object->GetField($field, $format); } } } } } } class UnitConfigDecorator { var $parentPath = Array (); /** * Decorates given array * * @param Array $var * @param int $level * @return string */ public function decorate($var, $level = 0) { $ret = ''; $deep_level = count($this->parentPath); if ( $deep_level && ($this->parentPath[0] == 'Fields') ) { $expand = $level < 2; } elseif ( $deep_level && ($this->parentPath[0] == 'Grids') ) { if ( $deep_level == 3 && $this->parentPath[2] == 'Icons' ) { $expand = false; } else { $expand = $level < 4; } } else { $expand = $level == 0; } if ( is_array($var) ) { - $ret .= 'Array ('; + $ret .= 'array('; $prepend = $expand ? "\n" . str_repeat("\t", $level + 1) : ''; foreach ($var as $key => $value) { array_push($this->parentPath, $key); - $ret .= $prepend . (is_string($key) ? "'" . $key . "'" : $key) . ' => ' . $this->decorate($value, $level + 1) . ', '; + $ret .= $prepend . (is_string($key) ? "'" . $key . "'" : $key) . ' => ' . $this->decorate($value, $level + 1); + $ret .= ',' . ($expand ? '' : ' '); array_pop($this->parentPath); } $prepend = $expand ? "\n" . str_repeat("\t", $level) : ''; - $ret = rtrim($ret, ', ') . $prepend . ')'; + + if ( !$expand ) { + $ret = rtrim($ret, ', '); + } + + $ret .= $prepend . ')'; } else { if ( is_null($var) ) { - $ret = 'NULL'; + $ret = 'null'; } elseif ( is_string($var) ) { $ret = "'" . $var . "'"; } else { $ret = $var; } } return $ret; } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/config_search/config_search_event_handler.php =================================================================== --- branches/5.3.x/core/units/config_search/config_search_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/config_search/config_search_event_handler.php (revision 16111) @@ -1,155 +1,157 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ConfigSearchEventHandler extends kDBEventHandler { /** * Changes permission section to one from REQUEST, not from config * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $module = $this->Application->GetVar('module'); $main_prefix = $this->Application->findModule('Name', $module, 'Var'); $section = $this->Application->getUnitConfig($main_prefix)->getPermSectionByName('search'); $event->setEventParam('PermSection', $section); return parent::CheckPermission($event); } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ // show only items that belong to selected module $module = $this->Application->GetVar('module'); $object->addFilter('module_filter', '%1$s.ModuleName = ' . $this->Conn->qstr($module)); // don't show disabled search items $object->addFilter('active_filter', '%1$s.SimpleSearch <> -1'); } /** * Updates kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return ; } parent::OnUpdate($event); $conf_update = new kEvent('conf:OnUpdate'); $conf_update->redirect = false; $this->Application->HandleEvent($conf_update); $event->SetRedirectParam('opener', 's'); // keeps module and section in REQUEST to ensure, that last admin template will work $event->SetRedirectParam('module', $this->Application->GetVar('module')); $event->SetRedirectParam('module_key', $this->Application->GetVar('module_key')); $event->SetRedirectParam('section', $this->Application->GetVar('section')); } /** * Cancels kDBItem Editing/Creation * * @param kEvent $event * @return void * @access protected */ protected function OnCancel(kEvent $event) { parent::OnCancel($event); $event->SetRedirectParam('opener', 's'); } /** * [HOOK] Creates search config record corresponding to custom field, that was just created * * @param kEvent $event * @return void * @access protected */ protected function OnCreateCustomField($event) { $custom_field = $event->MasterEvent->getObject(); /* @var $custom_field kDBItem */ if ( $custom_field->GetDBField('Type') == 6 || $custom_field->GetDBField('IsSystem') == 1 ) { // user & system custom fields are not searchable return ; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $custom_id = $custom_field->GetID(); if ( !$object->isLoaded() || ($object->GetDBField('CustomFieldId') != $custom_id) ) { $object->Load($custom_id, 'CustomFieldId'); } $cf_search = Array (); $element_type = $custom_field->GetDBField('ElementType'); $cf_search['DisplayOrder'] = $custom_field->GetDBField('DisplayOrder'); $cf_search['FieldType'] = $element_type; $cf_search['DisplayName'] = $custom_field->GetDBField('FieldLabel'); $cf_search['FieldName'] = $custom_field->GetDBField('FieldName'); $cf_search['Description'] = $custom_field->GetDBField('Prompt'); $cf_search['ConfigHeader'] = $custom_field->GetDBField('Heading'); // 'la_Text_CustomFields'; $cf_search['SimpleSearch'] = in_array($element_type, Array ('text', 'range', 'select', 'multiselect')) ? 1 : 0; $cf_search['TableName'] = 'CustomFields'; $sql = 'SELECT Module FROM ' . TABLE_PREFIX . 'ItemTypes WHERE ItemType = ' . $custom_field->GetDBField('Type'); $cf_search['ModuleName'] = $this->Conn->GetOne($sql); + // TODO: maybe this should be SetDBFieldsFromHash instead, because all data comes from inside. $object->SetFieldsFromHash($cf_search); + $event->setEventParam('form_data', $cf_search); $object->SetDBField('CustomFieldId', $custom_id); if ( $object->isLoaded() ) { $object->Update(); } else { $object->Create(); } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/user_profile/user_profile_eh.php =================================================================== --- branches/5.3.x/core/units/user_profile/user_profile_eh.php (revision 16110) +++ branches/5.3.x/core/units/user_profile/user_profile_eh.php (revision 16111) @@ -1,93 +1,93 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class UserProfileEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnItemBuild' => Array ('subitem' => true), 'OnUpdate' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Saves user profile to database * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); list ($user_id, $field_values) = each($items_info); if ($user_id != $this->Application->RecallVar('user_id')) { // we are not updating own profile return ; } $public_profile_add = Array (); $public_profile_remove = Array (); $profile_mapping = $this->Application->getUnitConfig('u')->getUserProfileMapping(); foreach ($field_values as $variable_name => $variable_value) { if (array_key_exists($variable_name, $profile_mapping)) { // old style variable for displaying fields in public profile (named "pp_*") if ($variable_value) { $public_profile_add[] = $profile_mapping[$variable_name]; } else { $public_profile_remove[] = $profile_mapping[$variable_name]; } } else { - $this->Application->StorePersistentVar($variable_name, htmlspecialchars_decode($variable_value)); + $this->Application->StorePersistentVar($variable_name, $this->Application->unescapeRequestVariable($variable_value)); } } if ($public_profile_add || $public_profile_remove) { $user = $this->Application->recallObject('u.current'); /* @var $user kDBItem */ // get current value $display_to_public_old = $user->GetDBField('DisplayToPublic'); $display_to_public_new = $display_to_public_old ? explode('|', substr($display_to_public_old, 1, -1)) : Array (); // update value $display_to_public_new = array_diff(array_merge($display_to_public_new, $public_profile_add), $public_profile_remove); $display_to_public_new = array_unique($display_to_public_new); $display_to_public_new = $display_to_public_new ? '|' . implode('|', $display_to_public_new) . '|' : ''; if ($display_to_public_new != $display_to_public_old) { $user->SetDBField('DisplayToPublic', $display_to_public_new); $user->Update(); } } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/phrases/phrases_event_handler.php =================================================================== --- branches/5.3.x/core/units/phrases/phrases_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/phrases/phrases_event_handler.php (revision 16111) @@ -1,563 +1,593 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class PhrasesEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnItemBuild' => Array ('self' => true, 'subitem' => true), 'OnPreparePhrase' => Array ('self' => true, 'subitem' => true), 'OnExportPhrases' => Array ('self' => 'view'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Hides phrases from disabled modules * * @param kEvent $event * @return void * @access protected */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ $object->addFilter('module_filter', '%1$s.Module IN (SELECT Name FROM ' . TABLE_PREFIX . 'Modules WHERE Loaded = 1)'); } /** * Apply some special processing to object being * recalled before using it in other events that * call prepareObject * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { // don't call parent if ( $event->Special == 'import' || $event->Special == 'export' ) { $this->RemoveRequiredFields($object); $object->setRequired(Array ('ExportDataTypes', 'LangFile', 'PhraseType', 'Module')); // allow multiple phrase types to be selected during import/export $object->SetFieldOption('PhraseType', 'type', 'string'); } } /** * Allow to create phrases from front end in debug mode with DBG_PHRASES constant set * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( !$this->Application->isAdmin && $this->Application->isDebugMode(false) && kUtil::constOn('DBG_PHRASES') ) { - $allow_events = Array ('OnCreate', 'OnUpdate'); + $allow_events = Array ('OnCreate', 'OnCreateAjax', 'OnUpdate', 'OnUpdateAjax'); if ( in_array($event->Name, $allow_events) ) { return true; } } return parent::CheckPermission($event); } /** * Prepares phrase for translation * * @param kEvent $event */ function OnPreparePhrase($event) { $label = $this->Application->GetVar($event->getPrefixSpecial() . '_label'); if (!$label) { return ; } // we got label, try to get it's ID then if any $phrase_id = $this->_getPhraseId($label); if ($phrase_id) { $event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $phrase_id); $event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial()); $next_template = $this->Application->GetVar('next_template'); if ($next_template) { $event->SetRedirectParam('next_template', $next_template); } } else { $event->CallSubEvent('OnNew'); } if ($this->Application->GetVar('simple_mode')) { $event->SetRedirectParam('simple_mode', 1); } } function _getPhraseId($phrase) { $config = $this->getUnitConfig(); $sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName() . ' WHERE PhraseKey = ' . $this->Conn->qstr( mb_strtoupper($phrase) ); return $this->Conn->GetOne($sql); } /** * Sets phrase type based on place where it's created (to display on form only) * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $this->_setPhraseModule($object); } /** * Forces new label in case if issued from get link * * @param kEvent $event * @return void * @access protected */ protected function OnNew(kEvent $event) { parent::OnNew($event); $object = $event->getObject(); /* @var $object kDBItem */ $label = $this->Application->GetVar($event->getPrefixSpecial() . '_label'); if ( $label ) { // phrase is created in language, used to display phrases $object->SetDBField('Phrase', $label); $object->SetDBField('PhraseType', $this->_getPhraseType($label)); // to show on form $object->SetDBField('PrimaryTranslation', $this->_getPrimaryTranslation($label)); } $this->_setPhraseModule($object); if ( $event->Special == 'export' || $event->Special == 'import' ) { $object->SetDBField('PhraseType', '|0|1|2|'); $object->SetDBField('Module', '|' . implode('|', array_keys($this->Application->ModuleInfo)) . '|'); $export_mode = $this->Application->GetVar('export_mode'); if ( $export_mode != 'lang' ) { $object->SetDBField('ExportDataTypes', '|' . $export_mode . '|'); } } } /** * Returns given phrase translation on primary language * * @param string $phrase * @return string * @access protected */ protected function _getPrimaryTranslation($phrase) { $sql = 'SELECT l' . $this->Application->GetDefaultLanguageId() . '_Translation FROM ' . $this->getUnitConfig()->getTableName() . ' WHERE PhraseKey = ' . $this->Conn->qstr( mb_strtoupper($phrase) ); return $this->Conn->GetOne($sql); } /** * Sets new phrase module * * @param kDBItem $object * @return void * @access protected */ protected function _setPhraseModule(&$object) { $last_module = $this->Application->GetVar('last_module'); if ( $last_module ) { $object->SetDBField('Module', $last_module); } } /** * Forces create to use live table * * @param kEvent $event * @return void * @access protected */ protected function OnCreate(kEvent $event) { if ( $this->Application->GetVar($event->Prefix . '_label') ) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ if ( $this->Application->GetVar('m_lang') != $this->Application->GetVar('lang_id') ) { $object->SwitchToLive(); } $this->returnToOriginalTemplate($event); } parent::OnCreate($event); } /** + * Processes items create from ajax request + * + * @param kEvent $event + * @return void + * @access protected + */ + protected function OnCreateAjax(kEvent $event) + { + $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); + /* @var $ajax_form_helper AjaxFormHelper */ + + $ajax_form_helper->transitEvent($event, 'OnCreate'); + } + + /** * Redirects to original template after phrase is being update * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { if ( $this->Application->GetVar($event->Prefix . '_label') ) { $this->returnToOriginalTemplate($event); } parent::OnUpdate($event); } /** + * Processes items update from ajax request + * + * @param kEvent $event + * @return void + * @access protected + */ + protected function OnUpdateAjax(kEvent $event) + { + $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper'); + /* @var $ajax_form_helper AjaxFormHelper */ + + $ajax_form_helper->transitEvent($event, 'OnUpdate'); + } + + /** * Returns to original template after phrase adding/editing * * @param kEvent $event * @return void * @access protected */ protected function returnToOriginalTemplate(kEvent $event) { $next_template = $this->Application->GetVar('next_template'); if ( $next_template ) { $event->redirect = $next_template; $event->SetRedirectParam('opener', 's'); } } /** * Set last change info, when phrase is created * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $primary_language_id = $this->Application->GetDefaultLanguageId(); if ( !$object->GetDBField('l' . $primary_language_id . '_Translation') ) { // no translation on primary language -> try to copy from other language $src_languages = Array ('lang_id', 'm_lang'); // editable language, theme language foreach ($src_languages as $src_language) { $src_language = $this->Application->GetVar($src_language); $src_value = $src_language ? $object->GetDBField('l' . $src_language . '_Translation') : false; if ( $src_value ) { $object->SetDBField('l' . $primary_language_id . '_Translation', $src_value); break; } } } $this->_phraseChanged($event); } /** * Update last change info, when phrase is updated * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_phraseChanged($event); } /** * Set's phrase key and last change info, used for phrase updating and loading * * @param kEvent $event */ function _phraseChanged($event) { $object = $event->getObject(); /* @var $object kDBItem */ $label = $object->GetDBField('Phrase'); $object->SetDBField('PhraseKey', mb_strtoupper($label)); $object->SetDBField('PhraseType', $this->_getPhraseType($label)); if ( $this->translationChanged($object) ) { $object->SetDBField('LastChanged_date', time() ); $object->SetDBField('LastChanged_time', time() ); $object->SetDBField('LastChangeIP', $this->Application->getClientIp()); } $this->Application->Session->SetCookie('last_module', $object->GetDBField('Module')); } /** * Returns phrase type, that corresponds given phrase label * * @param string $label * @return int * @access protected */ protected function _getPhraseType($label) { $phrase_type_map = Array ( 'LU' => Language::PHRASE_TYPE_FRONT, 'LA' => Language::PHRASE_TYPE_ADMIN, 'LC' => Language::PHRASE_TYPE_COMMON ); $label = mb_strtoupper($label); $label_prefix = substr($label, 0, 2); return isset($phrase_type_map[$label_prefix]) ? $phrase_type_map[$label_prefix] : Language::PHRASE_TYPE_COMMON; } /** * Checks, that at least one of phrase's translations was changed * * @param kDBItem $object * @return bool */ function translationChanged(&$object) { $translation_fields = $this->getTranslationFields(); $changed_fields = array_keys( $object->GetChangedFields() ); foreach ($changed_fields as $changed_field) { $changed_field = preg_replace('/^l[\d]+_/', '', $changed_field); if ( in_array($changed_field, $translation_fields) ) { return true; } } return false; } /** * Returns fields, that can be translated * * @return Array * @access protected */ protected function getTranslationFields() { return Array ('Translation', 'HintTranslation', 'ColumnTranslation'); } /** * Put correct translation to source language fields * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $translation_fields = $this->getTranslationFields(); $source_language = $ml_helper->getSourceLanguage($object->GetDBField('TranslateFromLanguage')); foreach ($translation_fields as $translation_field) { $object->SetDBField('Source' . $translation_field, $object->GetDBField('l' . $source_language . '_' . $translation_field)); } } /** * Changes default module to custom (when available) * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); if ( $this->Application->findModule('Name', 'Custom') ) { $config = $event->getUnitConfig(); $fields = $config->getFields(); $fields['Module']['default'] = 'Custom'; $config->setFields($fields); } $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->replaceMLCalculatedFields($event); if ( $this->Application->GetVar('regional') ) { $event->getUnitConfig()->setPopulateMlFields(true); } } /** * Saves changes & changes language * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndChangeLanguage(kEvent $event) { $label = $this->Application->GetVar($event->getPrefixSpecial() . '_label'); if ( $label && !$this->UseTempTables($event) ) { $phrase_id = $this->_getPhraseId($label); if ( $phrase_id ) { $event->CallSubEvent('OnUpdate'); $event->SetRedirectParam('opener', 's'); } else { $event->CallSubEvent('OnCreate'); $event->SetRedirectParam('opener', 's'); } if ( $event->status != kEvent::erSUCCESS ) { return; } $event->SetRedirectParam($event->getPrefixSpecial() . '_event', 'OnPreparePhrase'); $event->SetRedirectParam('pass_events', true); } if ( $this->Application->GetVar('simple_mode') ) { $event->SetRedirectParam('simple_mode', 1); } parent::OnPreSaveAndChangeLanguage($event); } /** * Prepare temp tables and populate it * with items selected in the grid * * @param kEvent $event * @return void * @access protected */ protected function OnEdit(kEvent $event) { parent::OnEdit($event); // use language from grid, instead of primary language used by default $event->SetRedirectParam('m_lang', $this->Application->GetVar('m_lang')); } /** * Stores ids of selected phrases and redirects to export language step 1 * * @param kEvent $event * @return void * @access protected */ protected function OnExportPhrases(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $this->Application->getUnitConfig('phrases')->setAutoLoad(false); $this->StoreSelectedIDs($event); $this->Application->StoreVar('export_language_ids', $this->Application->GetVar('m_lang')); $event->setRedirectParams( Array ( 'phrases.export_event' => 'OnNew', 'pass' => 'all,phrases.export', 'export_mode' => $event->Prefix, ) ); } /** * Updates translation state for all saved phrases * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeCopyToLive(kEvent $event) { parent::OnBeforeCopyToLive($event); $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->updateTranslationState($event); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/languages/languages_event_handler.php =================================================================== --- branches/5.3.x/core/units/languages/languages_event_handler.php (revision 16110) +++ branches/5.3.x/core/units/languages/languages_event_handler.php (revision 16111) @@ -1,794 +1,796 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class LanguagesEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnChangeLanguage' => Array ('self' => true), 'OnSetPrimary' => Array ('self' => 'advanced:set_primary|add|edit'), 'OnImportLanguage' => Array ('self' => 'advanced:import'), 'OnExportLanguage' => Array ('self' => 'advanced:export'), 'OnExportProgress' => Array ('self' => 'advanced:export'), 'OnReflectMultiLingualFields' => Array ('self' => 'view'), 'OnSynchronizeLanguages' => Array ('self' => 'edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { if ( $event->Name == 'OnItemBuild' ) { // check permission without using $event->getSection(), // so first cache rebuild won't lead to "ldefault_Name" field being used return true; } return parent::CheckPermission($event); } /** * Allows to get primary language object * * @param kEvent $event * @return int * @access public */ public function getPassedID(kEvent $event) { if ( $event->Special == 'primary' ) { return $this->Application->GetDefaultLanguageId(); } return parent::getPassedID($event); } /** * [HOOK] Updates table structure on new language adding/removing language * * @param kEvent $event */ function OnReflectMultiLingualFields($event) { if ($this->Application->GetVar('ajax') == 'yes') { $event->status = kEvent::erSTOP; } if (is_object($event->MasterEvent)) { if ($event->MasterEvent->status != kEvent::erSUCCESS) { // only rebuild when all fields are validated return ; } if (($event->MasterEvent->Name == 'OnSave') && !$this->Application->GetVar('new_language')) { // only rebuild during new language adding return ; } } $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->massCreateFields(); $event->SetRedirectParam('action_completed', 1); } /** * Allows to set selected language as primary * * @param kEvent $event */ function OnSetPrimary($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $this->StoreSelectedIDs($event); $ids = $this->getSelectedIDs($event); if ($ids) { $id = array_shift($ids); $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object LanguagesItem */ $object->Load($id); $object->copyMissingData( $object->setPrimary() ); } } /** * [HOOK] Reset primary status of other languages if we are saving primary language * * @param kEvent $event */ function OnUpdatePrimary($event) { if ($event->MasterEvent->status != kEvent::erSUCCESS) { return ; } $object = $event->getObject( Array('skip_autoload' => true) ); /* @var $object LanguagesItem */ $object->SwitchToLive(); // set primary for each languages, that have this checkbox checked $ids = explode(',', $event->MasterEvent->getEventParam('ids')); foreach ($ids as $id) { $object->Load($id); if ($object->GetDBField('PrimaryLang')) { $object->copyMissingData( $object->setPrimary(true, false) ); } if ($object->GetDBField('AdminInterfaceLang')) { $object->setPrimary(true, true); } } // if no primary language left, then set primary last language (not to load again) from edited list $sql = 'SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE PrimaryLang = 1'; $primary_language = $this->Conn->GetOne($sql); if (!$primary_language) { $object->setPrimary(false, false); // set primary language } $sql = 'SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE AdminInterfaceLang = 1'; $primary_language = $this->Conn->GetOne($sql); if (!$primary_language) { $object->setPrimary(false, true); // set admin interface language } } /** * Prefills options with dynamic values * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); $this->_evaluateFieldFormats($event, 'InputDateFormat'); $this->_evaluateFieldFormats($event, 'InputTimeFormat'); } /** * Set dynamic hints for options in date format fields * * @param kEvent $event * @param string $field * @return void * @access protected */ protected function _evaluateFieldFormats(kEvent $event, $field) { $config = $event->getUnitConfig(); $field_options = $config->getFieldByName($field); foreach ($field_options['options'] as $option_key => $option_title) { $field_options['options'][$option_key] .= ' (' . date($option_key) . ')'; } $config->addFields($field_options, $field); } /** * Occurs before creating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->_itemChanged($event); } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $object = $event->getObject(); /* @var $object kDBItem */ $status_field = $event->getUnitConfig()->getStatusField(true); if ( $object->GetDBField('PrimaryLang') == 1 && $object->GetDBField($status_field) == 0 ) { $object->SetDBField($status_field, 1); } $this->_itemChanged($event); } /** * Dynamically changes required fields * * @param kEvent $event * @return void * @access protected */ protected function _itemChanged(kEvent $event) { $this->setRequired($event); } /** * Dynamically changes required fields * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemValidate(kEvent $event) { parent::OnBeforeItemValidate($event); $object = $event->getObject(); /* @var $object kDBItem */ $email_template_helper = $this->Application->recallObject('kEmailTemplateHelper'); /* @var $email_template_helper kEmailTemplateHelper */ $email_template_helper->parseField($object, 'HtmlEmailTemplate'); $email_template_helper->parseField($object, 'TextEmailTemplate'); $check_field = $object->GetDBField('TextEmailTemplate') ? 'TextEmailTemplate' : 'HtmlEmailTemplate'; $check_value = $object->GetDBField($check_field); if ( $check_value && strpos($check_value, '$body') === false ) { $object->SetError($check_field, 'body_missing'); } } /** * Dynamically changes required fields * * @param kEvent $event * @return void * @access protected */ protected function setRequired(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $object->setRequired('HtmlEmailTemplate', !$object->GetDBField('TextEmailTemplate')); $object->setRequired('TextEmailTemplate', !$object->GetDBField('HtmlEmailTemplate')); } /** * Shows only enabled languages on front * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ if ( in_array($event->Special, Array ('enabled', 'selected', 'available')) ) { $object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE); } // site domain language picker if ( $event->Special == 'selected' || $event->Special == 'available' ) { $edit_picker_helper = $this->Application->recallObject('EditPickerHelper'); /* @var $edit_picker_helper EditPickerHelper */ $edit_picker_helper->applyFilter($event, 'Languages'); } // apply domain-based language filtering $languages = $this->Application->siteDomainField('Languages'); if ( strlen($languages) ) { $languages = explode('|', substr($languages, 1, -1)); $object->addFilter('domain_filter', '%1$s.LanguageId IN (' . implode(',', $languages) . ')'); } } /** * Copy labels from another language * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $src_language = $object->GetDBField('CopyFromLanguage'); if ( $object->GetDBField('CopyLabels') && $src_language ) { $dst_language = $object->GetID(); // 1. schedule data copy after OnSave event is executed $var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid'); $pending_actions = $this->Application->RecallVar($var_name, Array ()); if ( $pending_actions ) { $pending_actions = unserialize($pending_actions); } $pending_actions[$src_language] = $dst_language; $this->Application->StoreVar($var_name, serialize($pending_actions)); $object->SetDBField('CopyLabels', 0); } } /** * Saves language from temp table to live * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { parent::OnSave($event); if ( $event->status != kEvent::erSUCCESS ) { return; } $var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid'); $pending_actions = $this->Application->RecallVar($var_name, Array ()); if ( $pending_actions ) { $pending_actions = unserialize($pending_actions); } // create multilingual columns for phrases & email events table first (actual for 6+ language) $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->createFields('phrases'); $ml_helper->createFields('email-template'); foreach ($pending_actions as $src_language => $dst_language) { // phrases import $sql = 'UPDATE ' . $this->Application->getUnitConfig('phrases')->getTableName() . ' SET l' . $dst_language . '_Translation = l' . $src_language . '_Translation'; $this->Conn->Query($sql); // events import $sql = 'UPDATE ' . $this->Application->getUnitConfig('email-template')->getTableName() . ' SET l' . $dst_language . '_Subject = l' . $src_language . '_Subject, l' . $dst_language . '_HtmlBody = l' . $src_language . '_HtmlBody, l' . $dst_language . '_PlainTextBody = l' . $src_language . '_PlainTextBody'; $this->Conn->Query($sql); } $this->Application->RemoveVar($var_name); $event->CallSubEvent('OnReflectMultiLingualFields'); $event->CallSubEvent('OnUpdatePrimary'); } /** * Prepare temp tables for creating new item * but does not create it. Actual create is * done in OnPreSaveCreated * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('CopyLabels', 1); $sql = 'SELECT ' . $object->IDField . ' FROM ' . $event->getUnitConfig()->getTableName() . ' WHERE PrimaryLang = 1'; $primary_lang_id = $this->Conn->GetOne($sql); $object->SetDBField('CopyFromLanguage', $primary_lang_id); $object->SetDBField('SynchronizationModes', Language::SYNCHRONIZE_DEFAULT); $this->setRequired($event); } /** * Sets dynamic required fields * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ $this->setRequired($event); } /** * Sets new language mark * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { parent::OnBeforeDeleteFromLive($event); $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $sql = 'SELECT ' . $id_field . ' FROM ' . $config->getTableName() . ' WHERE ' . $id_field . ' = ' . $event->getEventParam('id'); $id = $this->Conn->GetOne($sql); if ( !$id ) { $this->Application->SetVar('new_language', 1); } } function OnChangeLanguage($event) { $language_id = $this->Application->GetVar('language'); $language_field = $this->Application->isAdmin ? 'AdminLanguage' : 'FrontLanguage'; $this->Application->SetVar('m_lang', $language_id); // set new language for this session $this->Application->Session->SetField('Language', $language_id); // remember last user language if ($this->Application->RecallVar('user_id') == USER_ROOT) { $this->Application->StorePersistentVar($language_field, $language_id); } else { $object = $this->Application->recallObject('u.current'); /* @var $object kDBItem */ $object->SetDBField($language_field, $language_id); $object->Update(); } // without this language change in admin will cause erase of last remembered tree section $this->Application->SetVar('skip_last_template', 1); } /** * Parse language XML file into temp tables and redirect to progress bar screen * * @param kEvent $event */ function OnImportLanguage($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $items_info = $this->Application->GetVar('phrases_import'); if ($items_info) { list ($id, $field_values) = each($items_info); $object = $this->Application->recallObject('phrases.import', 'phrases', Array('skip_autoload' => true)); /* @var $object kDBItem */ $object->setID($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); if (!$object->Validate()) { $event->status = kEvent::erFAIL; return ; } $filename = $object->GetField('LangFile', 'full_path'); if (!filesize($filename)) { $object->SetError('LangFile', 'la_empty_file', 'la_EmptyFile'); $event->status = kEvent::erFAIL; } $language_import_helper = $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ if ( $object->GetDBField('ImportOverwrite') ) { $language_import_helper->setOption(LanguageImportHelper::OVERWRITE_EXISTING); } if ( $object->GetDBField('ImportSynced') ) { $language_import_helper->setOption(LanguageImportHelper::SYNC_ADDED); } $language_import_helper->performImport($filename, $object->GetDBField('PhraseType'), $object->GetDBField('Module')); // delete uploaded language pack after import is finished unlink($filename); $event->SetRedirectParam('opener', 'u'); } } /** * Stores ids of selected languages and redirects to export language step 1 * * @param kEvent $event */ function OnExportLanguage($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $this->Application->getUnitConfig('phrases')->setAutoLoad(false); $this->StoreSelectedIDs($event); $this->Application->StoreVar('export_language_ids', implode(',', $this->getSelectedIDs($event))); $event->setRedirectParams( Array ( 'phrases.export_event' => 'OnNew', 'pass' => 'all,phrases.export', 'export_mode' => $event->Prefix, ) ); } /** * Saves selected languages to xml file passed * * @param kEvent $event */ function OnExportProgress($event) { $items_info = $this->Application->GetVar('phrases_export'); if ( $items_info ) { list($id, $field_values) = each($items_info); $object = $this->Application->recallObject('phrases.export', null, Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->setID($id); - $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); + $object->SetFieldsFromHash($field_values); + $event->setEventParam('form_data', $field_values); if ( !$object->Validate() ) { $event->status = kEvent::erFAIL; return; } $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $file_helper->CheckFolder(EXPORT_PATH); if ( !is_writable(EXPORT_PATH) ) { $event->status = kEvent::erFAIL; $object->SetError('LangFile', 'write_error', 'la_ExportFolderNotWritable'); return; } if ( substr($field_values['LangFile'], -5) != '.lang' ) { $field_values['LangFile'] .= '.lang'; } $filename = EXPORT_PATH . '/' . $field_values['LangFile']; $language_import_helper = $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ if ( $object->GetDBField('DoNotEncode') ) { $language_import_helper->setExportEncoding('plain'); } $data_types = Array ( 'phrases' => 'ExportPhrases', 'email-template' => 'ExportEmailTemplates', 'country-state' => 'ExportCountries' ); $export_mode = $this->Application->GetVar('export_mode'); $allowed_data_types = explode('|', substr($field_values['ExportDataTypes'], 1, -1)); if ( $export_mode == $event->Prefix ) { foreach ($data_types as $prefix => $export_limit_field) { $export_limit = in_array($prefix, $allowed_data_types) ? $field_values[$export_limit_field] : '-'; $language_import_helper->setExportLimit($prefix, $export_limit); } } else { foreach ($data_types as $prefix => $export_limit_field) { $export_limit = in_array($prefix, $allowed_data_types) ? null : '-'; $language_import_helper->setExportLimit($prefix, $export_limit); } } $lang_ids = explode(',', $this->Application->RecallVar('export_language_ids')); $language_import_helper->performExport($filename, $field_values['PhraseType'], $lang_ids, $field_values['Module']); } $event->redirect = 'regional/languages_export_step2'; $event->SetRedirectParam('export_file', $field_values['LangFile']); } /** * Returns to previous template in opener stack * * @param kEvent $event * @return void * @access protected */ protected function OnGoBack(kEvent $event) { $event->SetRedirectParam('opener', 'u'); } function OnScheduleTopFrameReload($event) { $this->Application->StoreVar('RefreshTopFrame',1); } /** * Do now allow deleting current language * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemDelete(kEvent $event) { parent::OnBeforeItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ if ( $object->GetDBField('PrimaryLang') || $object->GetDBField('AdminInterfaceLang') || $object->GetID() == $this->Application->GetVar('m_lang') ) { $event->status = kEvent::erFAIL; } } /** * Deletes phrases and email events on given language * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemDelete(kEvent $event) { parent::OnAfterItemDelete($event); $object = $event->getObject(); /* @var $object kDBItem */ // clean EmailTemplates table $fields_hash = Array ( 'l' . $object->GetID() . '_Subject' => NULL, 'l' . $object->GetID() . '_HtmlBody' => NULL, 'l' . $object->GetID() . '_PlainTextBody' => NULL, ); $this->Conn->doUpdate($fields_hash, $this->Application->getUnitConfig('email-template')->getTableName(), 1); // clean Phrases table $fields_hash = Array ( 'l' . $object->GetID() . '_Translation' => NULL, 'l' . $object->GetID() . '_HintTranslation' => NULL, 'l' . $object->GetID() . '_ColumnTranslation' => NULL, ); $this->Conn->doUpdate($fields_hash, $this->Application->getUnitConfig('phrases')->getTableName(), 1); } /** * Copy missing phrases across all system languages (starting from primary) * * @param kEvent $event * @return void * @access protected */ protected function OnSynchronizeLanguages($event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $source_languages = $target_languages = Array (); // get language list with primary language first $sql = 'SELECT SynchronizationModes, LanguageId FROM ' . TABLE_PREFIX . 'Languages WHERE SynchronizationModes <> "" ORDER BY PrimaryLang DESC'; $languages = $this->Conn->GetCol($sql, 'LanguageId'); foreach ($languages as $language_id => $synchronization_modes) { $synchronization_modes = explode('|', substr($synchronization_modes, 1, -1)); if ( in_array(Language::SYNCHRONIZE_TO_OTHERS, $synchronization_modes) ) { $source_languages[] = $language_id; } if ( in_array(Language::SYNCHRONIZE_FROM_OTHERS, $synchronization_modes) ) { $target_languages[] = $language_id; } } foreach ($source_languages as $source_id) { foreach ($target_languages as $target_id) { if ( $source_id == $target_id ) { continue; } $sql = 'UPDATE ' . TABLE_PREFIX . 'LanguageLabels SET l' . $target_id . '_Translation = l' . $source_id . '_Translation WHERE COALESCE(l' . $target_id . '_Translation, "") = "" AND COALESCE(l' . $source_id . '_Translation, "") <> ""'; $this->Conn->Query($sql); } } } - } \ No newline at end of file + } Index: branches/5.3.x/core/admin_templates/export/export_progress.tpl =================================================================== --- branches/5.3.x/core/admin_templates/export/export_progress.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/export/export_progress.tpl (revision 16111) @@ -1,35 +1,35 @@ <inp2:adm_SetPopupSize width="515" height="194"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get var="PrefixSpecial" result_to_var="exportprefix" /> +<inp2:m_Get var="PrefixSpecial" no_html_escape="1" result_to_var="exportprefix" /> <inp2:{$exportprefix}_PermSection section="main" result_to_var="permsection" /> <inp2:m_RenderElement name="combined_header" section="$permsection" prefix="adm" title_preset="csv_export"/> <inp2:m_RenderElement name="ajax_progress_bar" cancel_action="cancel_action();"/> <script type="text/javascript"> function cancel_action() { redirect('<inp2:m_Link template="export/export_complete" no_amp="1" js_escape="1"/>'); } function rand(n) { return (Math.floor(Math.random() * n + 1)); } var PrefixSpecial = '<inp2:m_Get var="PrefixSpecial" js_escape="1" />'; var grid = '<inp2:m_Get var="grid" js_escape="1" />'; var export_rand = ''; for(var i=0; i<10; i++) { export_rand += (rand(10) - 1); } var action_url = '<inp2:m_t template="index" adm_event="OnExportCSV" type="return_progress" finish_template="export/export_complete" PrefixSpecial="#PrefixSpecial#" grid="#grid#" export_rand="#export_rand#" pass="m,adm" no_amp="1" js_escape="1"/>'; action_url = action_url.replace('#PrefixSpecial#', PrefixSpecial); action_url = action_url.replace('#grid#', grid); action_url = action_url.replace('#export_rand#', export_rand); // window.open(action_url); $QueueProcessor = new AjaxProgressBar(action_url); </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/categories/category_path.tpl =================================================================== --- branches/5.3.x/core/admin_templates/categories/category_path.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/categories/category_path.tpl (revision 16111) @@ -1,15 +1,15 @@ <inp2:m_DefaultParam category_id=""/> <inp2:m_if check="m_Get" name="ajax" equals_to="yes"> <inp2:m_NoDebug/> - <inp2:m_Get name="category_id" result_to_var="category_id"/> + <inp2:m_Get name="category_id" no_html_escape="1" result_to_var="category_id"/> </inp2:m_if> <inp2:m_DefineElement name="category_caption"> <inp2:m_ifnot check="m_Param" name="is_first"> <inp2:m_param name="separator"/> </inp2:m_ifnot> <inp2:m_param name="cat_name"/> </inp2:m_DefineElement> -<inp2:c_CategoryPath cat_id="$category_id" separator=" > " render_as="category_caption"/> \ No newline at end of file +<inp2:c_CategoryPath cat_id="$category_id" separator=" > " render_as="category_caption"/> Index: branches/5.3.x/core/admin_templates/categories/permissions_tab.tpl =================================================================== --- branches/5.3.x/core/admin_templates/categories/permissions_tab.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/categories/permissions_tab.tpl (revision 16111) @@ -1,77 +1,77 @@ <inp2:m_RequireLogin permissions="in-portal:browse.view" system="1"/> <inp2:m_if check="m_ParamEquals" name="tab_init" value="1"> <div id="<inp2:m_param name="item_prefix"/>_div" prefix="<inp2:m_param name="item_prefix"/>" group_id="-1" class="catalog-tab"><!-- IE minimal height problem fix --></div> <script type="text/javascript">$PermManager.registerTab('<inp2:m_param name="item_prefix"/>');</script> <inp2:m_else/> <inp2:lang.current_SetContentType content_type="text/plain"/> if ($request_visible) { - document.getElementById('<inp2:m_get name="item_prefix"/>_div').setAttribute('group_id', <inp2:m_get name="group_id"/>); - maximizeElement( jq('#<inp2:m_get name="item_prefix"/>_div') ); + document.getElementById('<inp2:m_get name="item_prefix" no_html_escape="1" js_escape="1"/>_div').setAttribute('group_id', <inp2:m_get name="group_id" no_html_escape="1" js_escape="1"/>); + maximizeElement( jq('#<inp2:m_get name="item_prefix" no_html_escape="1" js_escape="1"/>_div') ); } <inp2:m_if check="c_SaveWarning"> document.getElementById('save_warning').style.display = 'block'; $edit_mode = true; </inp2:m_if> #separator# <inp2:m_DefineElement name="permission_element"> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>"> <td> <inp2:m_phrase name="$Description"/> [<inp2:m_param name="PermissionName"/>] </td> <td> <!-- Inherited checkbox --> <input type="hidden" id="<inp2:PermInputName sub_key="inherited"/>" name="<inp2:PermInputName sub_key="inherited"/>" value="<inp2:m_if check="m_ParamEquals" name="Inherited" value="1">1<inp2:m_else/>0</inp2:m_if>" /> <input type="checkbox" id="_cb_<inp2:PermInputName sub_key="inherited"/>" <inp2:m_if check="m_ParamEquals" name="Inherited" value="1">checked</inp2:m_if> onchange="update_checkbox(this, document.getElementById('<inp2:PermInputName sub_key="inherited"/>'));" onclick="inherited_click('<inp2:m_param name="PermissionName"/>', <inp2:m_param name="InheritedValue"/>, this.checked, '_cb_<inp2:PermInputName sub_key="value"/>')" /> </td> <td> <inp2:CategoryPath cat_id="$InheritedFrom"/> </td> <td> <!-- Access checkbox --> <input type="hidden" id="<inp2:PermInputName sub_key="value"/>" name="<inp2:PermInputName sub_key="value"/>" value="<inp2:m_if check="m_ParamEquals" name="Value" value="1">1<inp2:m_else/>0</inp2:m_if>" /> <input type="checkbox" id="_cb_<inp2:PermInputName sub_key="value"/>" <inp2:m_if check="m_ParamEquals" name="Inherited" value="1">disabled="disabled"</inp2:m_if> <inp2:m_if check="m_ParamEquals" name="Value" value="1">checked</inp2:m_if> onchange="update_checkbox(this, document.getElementById('<inp2:PermInputName sub_key="value"/>'));" onclick="update_light('<inp2:m_param name="PermissionName"/>', this.checked)" /> </td> <td> <img id="light_<inp2:m_param name="PermissionName"/>" src="img/perm_<inp2:m_if check="m_ParamEquals" name="Value" value="1">green<inp2:m_else/>red</inp2:m_if>.gif"/> </td> </tr> </inp2:m_DefineElement> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="tableborder_full"> <inp2:m_set odd_even="table-color1"/> <thead class="subsectiontitle"> <td><inp2:m_phrase name="column:la_fld_Description"/></td> <td><inp2:m_phrase name="la_col_Inherited"/></td> <td><inp2:m_phrase name="la_col_InheritedFrom"/></td> <td><inp2:m_phrase name="la_col_Access"/></td> <td><inp2:m_phrase name="la_col_Effective"/></td> </thead> <inp2:c-perm_PrintPermissions render_as="permission_element"/> </table> -</inp2:m_if> \ No newline at end of file +</inp2:m_if> Index: branches/5.3.x/core/admin_templates/catalog_tab.tpl =================================================================== --- branches/5.3.x/core/admin_templates/catalog_tab.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/catalog_tab.tpl (revision 16111) @@ -1,95 +1,95 @@ <inp2:m_RequireLogin permissions="in-portal:browse.view" system="1"/> <inp2:m_DefineElement name="catalog_tab"> <inp2:m_if check="m_ParamEquals" name="tab_init" value="" inverse="inverse"> <inp2:m_if check="m_ParamEquals" name="tab_init" value="1"> a_toolbar.AddButton( new ToolBarButton( 'new_cat', '<inp2:m_phrase label="la_ToolTip_New_Category" escape="1"/>', add_item, true ) ); </inp2:m_if> <inp2:m_if check="m_Param" name="tab_init" equals_to="2"> <div id="categories_div" prefix="<inp2:m_param name='prefix'/>" view_template="catalog_tab" edit_template="categories/categories_edit" category_id="-1" dep_buttons="new_cat" class="catalog-tab"><!-- IE minimal height problem fix --></div> <script type="text/javascript">$Catalog.registerTab('categories');</script> </inp2:m_if> <inp2:m_if check="m_ParamEquals" name="tab_init" value="3"> $Catalog.setItemCount('<inp2:m_Param name="prefix"/>', '<inp2:{$prefix}_CatalogItemCount grid="$grid_name"/>'); </inp2:m_if> <inp2:m_else/> <inp2:lang.current_SetContentType content_type="text/plain"/> <inp2:m_include t="incs/blocks"/> <inp2:m_include t="incs/in-portal"/> <inp2:m_include t="categories/ci_blocks"/> <inp2:m_if check="m_Param" name="prefix" equals_to="c.showall"> <inp2:$prefix_InitList grid="$grid_name" parent_cat_id="any"/> <inp2:m_else/> <inp2:$prefix_InitList grid="$grid_name"/> </inp2:m_if> // substitute form action, like from was created from here document.getElementById('categories_form').action = '<inp2:m_t pass="all" no_amp="1" js_escape="1"/>'; $Catalog.setItemCount('<inp2:m_param name="prefix"/>', '<inp2:$prefix_CatalogItemCount/>'); - $Catalog.setCurrentCategory('<inp2:m_param name="prefix"/>', <inp2:m_get name="m_cat_id"/>); + $Catalog.setCurrentCategory('<inp2:m_param name="prefix"/>', <inp2:m_get name="m_cat_id" no_html_escape="1" js_escape="1"/>); $Catalog.saveSearch('<inp2:m_Param name="prefix"/>', '<inp2:$prefix_SearchKeyword js_escape="1"/>', '<inp2:m_Param name="grid_name"/>'); <inp2:m_RenderElement name="structure_reload_element"/> <inp2:m_DefineElement name="grid_parent_category_td" format=""> <inp2:Field name="ParentId" result_to_var="item_category" db="db"/> <inp2:m_if check="m_Get" name="type" equals_to="item_selector"> <inp2:CategoryName cat_id="$item_category"/> <inp2:m_else/> <a href="<inp2:m_Link template='catalog/catalog' m_cat_id='$item_category' no_pass_through='1'/>"><inp2:CategoryName cat_id="$item_category"/></a> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="page_browse_td" format=""> <inp2:m_if check="m_Get" name="type" equals_to="item_selector"> <a href="javascript:$Catalog.go_to_cat(<inp2:m_get name="c_id"/>, '<inp2:GetModulePrefix/>');" title="<inp2:m_Phrase name='la_alt_GoInside' html_escape='1'/>"><inp2:Field field="$field" grid="$grid" format="$format"/></a> <inp2:m_else/> <a href="<inp2:ItemEditLink/>" title="<inp2:m_Phrase name='la_Text_Edit' no_editing='1'/>" onclick="return direct_edit('<inp2:m_param name="PrefixSpecial"/>', this.href);"><inp2:Field field="$field" grid="$grid" format="$format"/></a> </inp2:m_if> <!--##<span class="small-statistics">(<inp2:SubCatCount/> / <inp2:ItemCount/>)</span>##--> <inp2:m_if check="BrowseModeAvailable" pass_params="1"> <a href="<inp2:PageBrowseLink/>" title="<inp2:m_Phrase name='la_alt_Browse' no_editing='1'/>"><img src="<inp2:m_TemplatesBase/>/img/ic_browse_mode.gif" width="8" height="7" alt="<inp2:m_Phrase name='la_alt_Browse' html_escape='1'/>" border="0"/></a> </inp2:m_if> <inp2:m_if check="Field" field="Type" equals_to="2" db="db"> <span class="field-required" title="<inp2:m_Phrase name='la_System' no_editing='1'/>"> *</span> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid_js" PrefixSpecial="$prefix" IdField="CategoryId" grid="$grid_name" menu_filters="yes"/> <inp2:m_RenderElement name="grid_search_buttons" PrefixSpecial="$prefix" grid="$grid_name" ajax="1"/> Grids['<inp2:m_param name="prefix"/>'].SetDependantToolbarButtons( new Array('edit','delete','approve','decline','sep3','cut','copy','move_up','move_down','sep6')); <inp2:m_RenderElement name="reflect_catalog_buttons"/> <inp2:m_if check="m_Recall" name="root_delete_error"> alert('<inp2:m_Phrase name="la_error_RootCategoriesDelete"/>'); <inp2:m_RemoveVar name="root_delete_error"/> </inp2:m_if> <inp2:m_if check="adm_CheckPermCache"> Application.SetVar('continue', 1); openSelector('c', '<inp2:m_t t="categories/cache_updater" pass="m" no_amp="1" js_escape="1"/>'); </inp2:m_if> #separator# <!-- categories tab: begin --> <inp2:m_RenderElement name="kernel_form" form_name="categories_form"/> <inp2:m_RenderElement name="grid" ajax="1" PrefixSpecial="$prefix" IdField="CategoryId" grid="$grid_name" menu_filters="yes"/> <inp2:m_RenderElement name="kernel_form_end"/> <!-- categories tab: end --> </inp2:m_if> </inp2:m_DefineElement> -<inp2:c_InitCatalogTab render_as="catalog_tab" default_grid="Default" radio_grid="Radio"/> \ No newline at end of file +<inp2:c_InitCatalogTab render_as="catalog_tab" default_grid="Default" radio_grid="Radio"/> Index: branches/5.3.x/core/admin_templates/custom_fields/custom_fields_edit.tpl =================================================================== --- branches/5.3.x/core/admin_templates/custom_fields/custom_fields_edit.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/custom_fields/custom_fields_edit.tpl (revision 16111) @@ -1,158 +1,158 @@ <inp2:adm_SetPopupSize width="750" height="620"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="cf" section="$section" perm_event="cf:OnLoad" title_preset="custom_fields_edit"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('cf','<inp2:cf_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('cf','OnCancelEdit'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() { go_to_id('cf', '<inp2:cf_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('cf', '<inp2:cf_NextId/>'); } ) ); a_toolbar.Render(); <inp2:m_if check="cf_IsSingle" > a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); <inp2:m_else/> <inp2:m_if check="cf_IsLast" > a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="cf_IsFirst" > a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> </tr> </tbody> </table> <inp2:m_RenderElement name="inp_edit_hidden" prefix="cf" field="Type" db="db"/> <inp2:cf_SaveWarning name="grid_save_warning"/> <inp2:cf_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="subsection" title="la_section_General"/> <inp2:m_RenderElement name="inp_id_label" prefix="cf" field="CustomFieldId" title="la_prompt_FieldId"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="FieldName" title="la_prompt_FieldName" size="40"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="FieldLabel" title="la_prompt_FieldLabel" size="40"/> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="cf" field="MultiLingual" title="la_fld_MultiLingual"/> <inp2:m_RenderElement name="subsection" title="la_tab_AdminUI"/> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="cf" field="OnGeneralTab" title="la_prompt_showgeneraltab"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="Heading" title="la_prompt_heading" size="40"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="Prompt" title="la_prompt_FieldPrompt" size="40"/> <inp2:m_RenderElement name="inp_edit_options" prefix="cf" field="ElementType" title="la_prompt_InputType" size="20" has_empty="1" onchange="update_layout();"/> <inp2:m_if check="cf_Field" name="ElementType" equals_to="select|multiselect|radio" db="db" inverse="inverse"> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="ValueList" title="la_prompt_valuelist" size="40"/> </inp2:m_if> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="cf" field="IsRequired" title="la_fld_IsRequired"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="DefaultValue" title="la_prompt_Default"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="DisplayOrder" title="la_field_displayorder" size="10"/> <inp2:m_if check="m_IsDebugMode"> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="cf" field="IsSystem" title="la_fld_IsSystem"/> </inp2:m_if> <inp2:m_if check="cf_Field" name="ElementType" equals_to="select|multiselect|radio" db="db"> <inp2:m_RenderElement name="subsection" title="la_section_Values"/> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="cf" field="SortValues" title="la_fld_SortValues"/> <inp2:m_RenderElement name="inp_edit_hidden" prefix="cf" field="OptionKey"/> <inp2:m_RenderElement name="inp_edit_box" prefix="cf" field="OptionTitle" title="la_fld_OptionTitle" size="60"/> <inp2:m_RenderElement name="inp_edit_minput" prefix="cf" field="Options" title="la_fld_Options" format="#OptionTitle# (#OptionKey#)" style="width: 400px; height: 200px;"/> </inp2:m_if> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> <inp2:m_if check="cf_Field" name="ElementType" equals_to="select|multiselect|radio" db="db"> <script type="text/javascript"> Options.registerControl('OptionKey', 'text', false); Options.registerControl('OptionTitle', 'text', true); Options.LoadValues(); Options.getControlValue = function ($field) { var $value = this.getControl($field).value; if ($field == 'OptionKey' && !$value) { $options = this.getControl(this.FieldName, 'minput').options; if ($options.length) { var $i = 0; var $max_option_key = 0; while ($i < $options.length) { if (parseInt(this.Records[ $options[$i].value ]['OptionKey']) > $max_option_key) { $max_option_key = parseInt(this.Records[ $options[$i].value ]['OptionKey']); } $i++; } return $max_option_key + 1; } // when no this will be 1st record in list use 1 return 1; } return $value; } Options.compareRecords = function($record_a, $record_b) { // compare by option title only, it's id doesn't matter return $record_a['OptionTitle'].toLowerCase() == $record_b['OptionTitle'].toLowerCase(); } </script> </inp2:m_if> <script type="text/javascript"> function update_layout() { var $last_element_type = '<inp2:cf_Field name="ElementType" db="db" js_escape="1"/>'; var $element_type = document.getElementById('<inp2:cf_InputName name="ElementType" js_escape="1"/>').value; // value is changed from ml supported to normal or otherwise if (canHaveMultipleValues($last_element_type) != canHaveMultipleValues($element_type)) { submit_event('cf', 'OnPreSave'); } } function canHaveMultipleValues($element_type) { var $element_types = ['select', 'multiselect', 'radio']; return $element_types.indexOf($element_type) != -1; } </script> <inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/custom_fields/custom_fields_list.tpl =================================================================== --- branches/5.3.x/core/admin_templates/custom_fields/custom_fields_list.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/custom_fields/custom_fields_list.tpl (revision 16111) @@ -1,92 +1,92 @@ <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="cf" section="$section" perm_event="cf:OnLoad" title_preset="custom_fields_list" pagination="1"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> //do not rename - this function is used in default grid for double click! function edit() { std_edit_item('cf', 'custom_fields/custom_fields_edit'); } var a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('new_item', '<inp2:m_phrase label="la_ToolTip_New_CustomField" escape="1"/>::<inp2:m_phrase label="la_Add" escape="1"/>', function() { std_precreate_item('cf', 'custom_fields/custom_fields_edit') } ) ); a_toolbar.AddButton( new ToolBarButton('edit', '<inp2:m_phrase label="la_ToolTip_Edit" escape="1"/>', edit) ); a_toolbar.AddButton( new ToolBarButton('delete', '<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>', function() { std_delete_items('cf') } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); <inp2:m_if check="m_isDebugMode"> a_toolbar.AddButton( new ToolBarButton('clone', '<inp2:m_phrase label="la_ToolTip_Clone" escape="1"/>', function() { submit_event('cf', 'OnMassClone') } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep2') ); </inp2:m_if> a_toolbar.AddButton( new ToolBarButton('move_up', '<inp2:m_phrase label="la_ToolTip_MoveUp" escape="1"/>', function() { submit_event('cf','OnMassMoveUp'); } ) ); a_toolbar.AddButton( new ToolBarButton('move_down', '<inp2:m_phrase label="la_ToolTip_MoveDown" escape="1"/>', function() { submit_event('cf','OnMassMoveDown'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep3') ); a_toolbar.AddButton( new ToolBarButton('view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function() { show_viewmenu(a_toolbar,'view'); } ) ); a_toolbar.Render(); </script> </td> <inp2:m_RenderElement name="search_main_toolbar" prefix="cf" grid="Default"/> </tr> </tbody> </table> <inp2:m_DefineElement name="cf_grid_data_td"> <inp2:Field field="$field" grid="$grid" no_special="no_special" as_label="1" /> </inp2:m_DefineElement> <style type="text/css"> tr.row-disabled td { background-color: #FFDFE0; } tr.grid-data-row-even.row-disabled td { background-color: #F4D4D5; } </style> <inp2:m_DefineElement name="row_class"> <inp2:m_if check="Field" field="IsSystem" db="db"> row-disabled </inp2:m_if> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid" PrefixSpecial="cf" IdField="CustomFieldId" grid="Default" row_class_render_as="row_class"/> <script type="text/javascript"> Grids['cf'].SetDependantToolbarButtons( new Array('edit','delete', 'clone', 'move_down', 'move_up') ); </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/users/user_selector.tpl =================================================================== --- branches/5.3.x/core/admin_templates/users/user_selector.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/users/user_selector.tpl (revision 16111) @@ -1,52 +1,54 @@ <inp2:adm_SetPopupSize width="582" height="504"/> <inp2:m_include t="incs/header"/> <inp2:m_RenderElement name="combined_header" prefix="u.group" section="in-portal:user_list" grid="UserSelector" title_preset="group_user_select" pagination="1"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', edit) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { window_close(); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep3') ); a_toolbar.AddButton( new ToolBarButton('view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function() { show_viewmenu(a_toolbar,'view'); } ) ); a_toolbar.Render(); function edit() { set_hidden_field('remove_specials[u.group]', 1); submit_event('<inp2:m_recall name="main_prefix"/>', 'OnProcessSelected'); } + + var $user_logins = {}; </script> </td> </tr> </tbody> </table> <inp2:m_DefineElement name="grid_login_td" format="" no_special="1" nl2br="" first_chars="" td_style=""> <inp2:Field field="$field" first_chars="$first_chars" nl2br="$nl2br" grid="$grid" no_special="$no_special" format="$format"/> <script type="text/javascript"> $user_logins[ <inp2:Field name="$IdField"/> ] = '<inp2:Field name="Username" js_escape="1"/>'; </script> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid" PrefixSpecial="u.group" IdField="PortalUserId" grid="UserSelector"/> <script type="text/javascript"> Grids['u.group'].SetDependantToolbarButtons( new Array('select') ); // Grids['u'].DblClick = function() {return false}; </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/promo_block_groups/promo_block_group_list.tpl =================================================================== --- branches/5.3.x/core/admin_templates/promo_block_groups/promo_block_group_list.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/promo_block_groups/promo_block_group_list.tpl (revision 16111) @@ -1,69 +1,69 @@ <inp2:m_include t="incs/header"/> <inp2:m_RenderElement name="combined_header" section="in-portal:promo_block_groups" prefix="promo-block-group" title_preset="promo_block_group_list" /> <!-- ToolBar ---> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> <inp2:m_include t="promo_block_groups/section_reload"/> //do not rename - this function is used in default grid for double click! function edit() { std_edit_item('promo-block-group', 'promo_block_groups/promo_block_group_edit'); } var a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('new_item', '<inp2:m_phrase label="la_ToolTip_Add" escape="1"/>', function() { std_precreate_item('promo-block-group', 'promo_block_groups/promo_block_group_edit'); } ) ); a_toolbar.AddButton( new ToolBarButton('edit', '<inp2:m_phrase label="la_ToolTip_Edit" escape="1"/>::<inp2:m_phrase label="la_ShortToolTip_Edit" escape="1"/>', edit) ); a_toolbar.AddButton( new ToolBarButton('delete', '<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>', function() { std_delete_items('promo-block-group') } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton('approve', '<inp2:m_phrase label="la_ToolTip_Approve" escape="1"/>', function() { submit_event('promo-block-group', 'OnMassApprove'); } ) ); a_toolbar.AddButton( new ToolBarButton('decline', '<inp2:m_phrase label="la_ToolTip_Decline" escape="1"/>', function() { submit_event('promo-block-group', 'OnMassDecline'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep2') ); a_toolbar.AddButton( new ToolBarButton('view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function() { show_viewmenu(a_toolbar,'view'); } ) ); a_toolbar.Render(); </script> </td> <inp2:m_RenderElement name="search_main_toolbar" prefix="promo-block-group" grid="Default"/> </tr> </tbody> </table> <inp2:m_if check="m_Get" var="grid_error"> <inp2:m_RenderElement design="form_message" pass_params="1"> - <inp2:m_Get name="grid_error" result_to_var="grid_error"/> + <inp2:m_Get name="grid_error" no_html_escape="1" result_to_var="grid_error"/> <inp2:m_Phrase name="$grid_error"/> </inp2:m_RenderElement> </inp2:m_if> <inp2:m_RenderElement name="grid" PrefixSpecial="promo-block-group" IdField="PromoBlockGroupId" grid="Default" grid_filters="1"/> <script type="text/javascript"> Grids['promo-block-group'].SetDependantToolbarButtons( new Array('edit','delete','approve','decline') ); </script> <inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/reviews/reviews_tab.tpl =================================================================== --- branches/5.3.x/core/admin_templates/reviews/reviews_tab.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/reviews/reviews_tab.tpl (revision 16111) @@ -1,43 +1,43 @@ <inp2:m_RequireLogin permissions="in-portal:reviews.view" system="1"/> <inp2:m_DefineElement name="catalog_tab"> <inp2:m_if check="m_ParamEquals" name="tab_init" value=""> <inp2:lang.current_SetContentType content_type="text/plain"/> <inp2:m_include t="incs/blocks"/> <inp2:m_include t="incs/in-portal"/> <inp2:$prefix_InitList grid="$grid_name"/> $Catalog.setItemCount('<inp2:m_param name="prefix"/>', '<inp2:{$prefix}_CatalogItemCount/>'); $Catalog.setCurrentCategory('<inp2:m_param name="prefix"/>', 0); $Catalog.saveSearch('<inp2:m_Param name="prefix"/>', '<inp2:$prefix_SearchKeyword js_escape="1"/>', '<inp2:m_Param name="grid_name"/>'); <inp2:m_DefineElement name="grid_reviewtext_td"> <inp2:Field field="$field" cut_first="100"/> <br /> <span class="small-statistics"> <a href="<inp2:ItemEditLink/>" onclick="return direct_edit('<inp2:m_param name="prefix"/>', this.href);"><inp2:Field field="CatalogItemName"/></a> </span> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid_js" PrefixSpecial="$prefix" IdField="ReviewId" grid="$grid_name"/> <inp2:m_if check="m_ParamEquals" name="tab_dependant" value="yes"> Grids['<inp2:m_param name="prefix"/>'].AddAlternativeGrid('<inp2:m_param name="cat_prefix"/>', true); </inp2:m_if> Grids['<inp2:m_param name="prefix"/>'].SetDependantToolbarButtons( new Array('edit','delete','approve','decline','sep3','cut','copy','move_up','move_down','sep6')); $Catalog.reflectPasteButton(<inp2:c_HasClipboard/>); $Catalog.setViewMenu('<inp2:m_param name="prefix"/>'); <inp2:m_if check="m_ParamEquals" name="tab_mode" value="single"> Grids['<inp2:m_param name="prefix"/>'].DblClick = function() {return false}; </inp2:m_if> #separator# <!-- products tab: begin --> <inp2:m_RenderElement name="kernel_form" form_name="{$tab_name}_form"/> <inp2:m_RenderElement name="grid" ajax="1" PrefixSpecial="$prefix" IdField="ReviewId" grid="$grid_name"/> <inp2:m_RenderElement name="kernel_form_end"/> <!-- products tab: end --> </inp2:m_if> </inp2:m_DefineElement> -<inp2:m_Get name="item_prefix" result_to_var="prefix"/> -<inp2:{$prefix}_InitCatalogTab render_as="catalog_tab" default_grid="ReviewsSection" radio_grid="Radio"/> \ No newline at end of file +<inp2:m_Get name="item_prefix" no_html_escape="1" result_to_var="prefix"/> +<inp2:{$prefix}_InitCatalogTab render_as="catalog_tab" default_grid="ReviewsSection" radio_grid="Radio"/> Index: branches/5.3.x/core/admin_templates/reviews/review_direct_edit.tpl =================================================================== --- branches/5.3.x/core/admin_templates/reviews/review_direct_edit.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/reviews/review_direct_edit.tpl (revision 16111) @@ -1,51 +1,51 @@ <inp2:adm_SetPopupSize width="750" height="400"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get name="item_prefix" result_to_var="prefix"/> +<inp2:m_Get name="item_prefix" no_html_escape="1" result_to_var="prefix"/> <inp2:m_RenderElement name="combined_header" prefix="c" section="in-portal:reviews" title_preset="review_edit"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('<inp2:m_Param name="prefix"/>','<inp2:{$prefix}_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('<inp2:m_Param name="prefix"/>','OnCancel'); } ) ); a_toolbar.Render(); </script> </td> </tr> </tbody> </table> <inp2:$prefix_SaveWarning name="grid_save_warning"/> <inp2:$prefix_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="subsection" prefix="$prefix" fields="ItemId,TextFormat,ReviewId,CreatedById,ReviewText,Rating" title="la_Text_Review"/> <inp2:m_RenderElement name="inp_edit_hidden" prefix="$prefix" field="ItemId"/> <inp2:m_RenderElement name="inp_edit_checkbox_allow_html" prefix="$prefix" field="TextFormat"/> <inp2:m_RenderElement name="inp_id_label" prefix="$prefix" field="ReviewId" title="la_fld_Id"/> <inp2:m_RenderElement name="inp_edit_user" prefix="$prefix" field="CreatedById" title="la_fld_CreatedById" class="text"/> <inp2:m_RenderElement name="inp_edit_textarea" prefix="$prefix" field="ReviewText" title="la_fld_ReviewText" control_options="{min_height: 100}" cols="70" rows="8"/> <inp2:m_RenderElement name="subsection" prefix="$prefix" fields="Status,Priority,CreatedOn" title="la_Text_General"/> <inp2:m_RenderElement name="inp_edit_radio" prefix="$prefix" field="Status" title="la_fld_Status"/> <inp2:m_RenderElement name="inp_edit_box" prefix="$prefix" field="Priority" title="la_fld_Priority" size="3" class="text"/> <inp2:m_RenderElement name="inp_edit_date_time" prefix="$prefix" field="CreatedOn" title="la_fld_CreatedOn" size="20" class="text"/> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/head.tpl =================================================================== --- branches/5.3.x/core/admin_templates/head.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/head.tpl (revision 16111) @@ -1,125 +1,125 @@ <inp2:m_Set skip_last_template="1"/> <inp2:m_include t="incs/header" nobody="yes"/> <inp2:m_NoDebug/> <inp2:m_Set skip_last_template="1"/> <script type="text/javascript"> $popup_manager = new AjaxPopupManager('<inp2:m_Link template="index" no_amp="1" js_escape="1"/>'); grid_widths_cache = new Array(); </script> <inp2:m_DefineElement name="root_node"><inp2:m_param name="section_url"/></inp2:m_DefineElement> <body style="overflow: hidden; background-color: white;"> <style type="text/css"> div.layout { position: absolute; } </style> <div id="site_logo"> <table class="head-table" style="width: 100%;" cellpadding="0" cellspacing="0"> <td style="height: 95px; text-align: left;"> <div class="layout" style="top: 0px; left: 0px; vertical-align: top;"> <a href="<inp2:adm_PrintSection render_as='root_node' section_name='in-portal:browse_site' html_escape='1'/>" target="main"> <img src="<inp2:adm_AdminSkin type='logo'/>" alt="" border="0"/> </a> </div> <div class="layout" style="top: 5px; right: 8px;"> <inp2:m_if check="lang.enabled_IsMultiLanguage"> <select name="language" onchange="change_language();" style="color: white; background-color: transparent; border-width: 0px;"> <inp2:m_DefineElement name="lang_elem"> <option style="background-color: #1DAAF2;" value="<inp2:Field name="LanguageId"/>" <inp2:m_if check="SelectedLanguage">selected="selected"</inp2:m_if> ><inp2:Field name="LocalName"/></option> </inp2:m_DefineElement> <inp2:lang.enabled_PrintList render_as="lang_elem"/> </select> </inp2:m_if> <inp2:m_if check="lang.current_Field" name="UserDocsUrl"> <a href="<inp2:lang.current_Field name='UserDocsUrl' js_escape='1'/>" id="help_link" target="_blank"> <img src="<inp2:m_TemplatesBase/>/img/top_frame/help_icon.gif" width="15" height="15" border="0"/><br /> </a> </inp2:m_if> </div> <div class="layout" style="bottom:35px; right:8px; font-size:30px;"> <!--##<a href="http://www.intechnic.com" target="_new"> <img src="<inp2:adm_ModulePath module="core"/>img/logo_intechnic.gif" alt="" width="115" height="49" alt="Intechnic Corporation" border="0"/> </a>##--> <inp2:m_if check="adm_AdminSkin" type="DisplaySiteNameInHeader"> <a href="<inp2:adm_PrintSection render_as='root_node' section_name='in-portal:browse_site' html_escape='1'/>" target="main"> <inp2:m_GetConfig var="Site_Name"/> </a> </inp2:m_if> </div> </td> </table> </div> <div style="background: url(<inp2:m_TemplatesBase/>/img/top_frame/toolbar_background.gif) repeat-x top left;"> <div style="<inp2:m_if check='adm_AdminSkin' type='LogoBottom'>background: url(<inp2:adm_AdminSkin type='LogoBottom'/>) no-repeat bottom left; </inp2:m_if>text-align: left;"> <table cellpadding="0" cellspacing="0" style="width: 100%;"> <tr> <td style="vertical-align: bottom; height: 22px; font-size: 9px; width: <inp2:adm_MenuFrameWidth/>px;"> <inp2:m_if check="m_RecallEquals" name="ShowAdminMenu" value="0" persistent="1"> <a id="menu_toggle_link" href="#"><img src="img/list_arrow_desc.gif" id="menu_toggle_img" width="15" height="15" alt=""/><span id="menu_toggle_text"><inp2:m_Phrase name="la_ToolTip_HideMenu"/></span></a> <inp2:m_else/> <a id="menu_toggle_link" href="#"><img src="img/list_arrow_desc.gif" id="menu_toggle_img" width="15" height="15" alt=""/><span id="menu_toggle_text"><inp2:m_Phrase name="la_ToolTip_ShowMenu"/></span></a> </inp2:m_if> </td> <td> <div id="extra_toolbar" style="height: 22px;"></div> <inp2:m_Include template="themes/extra_toolbar"/> </td> <td class="kx-block-header" align="right" nowrap valign="middle"> <table cellpadding="0" cellspacing="0" border="0" > <tr> <td class="kx-block-header" style="background-image: none;"> <inp2:m_phrase name="la_Logged_in_as"/> <strong><a href="javascript:change_password();" class="kx-header-link"><inp2:u.current_LoginName/></strong></a> | <a href="<inp2:m_t t='index' u_event='OnLogout' pass='m,u'/>" target="_parent" class="kx-header-link"><strong><inp2:m_Phrase label="la_Logout"/></strong></a> </td> <td> <a href="<inp2:m_t t='index' u_event='OnLogout' pass='m,u'/>" target="_parent"><img src="<inp2:adm_ModulePath module='core'/>img/x.gif" alt="" width="15" height="15" /></a> </td> </tr> </table> </td> </tr> </table> </div> </div> <inp2:m_include t="incs/footer"/> <script type="text/javascript" src="js/frame_resizer.js"></script> <script type="text/javascript"> var $skip_refresh = true; function change_language() { // when changing language submit is made to frameset, not the current frame var $kf = document.getElementById($form_name); $kf.target = 'main_frame'; submit_event('lang', 'OnChangeLanguage', 'index'); } function change_password() { - getFrame('main').set_hidden_field('u_id', <inp2:m_get name="u.current_id"/>); + getFrame('main').set_hidden_field('u_id', <inp2:m_get name="u.current_id" no_html_escape="1" js_escape="1"/>); <inp2:m_if check="m_GetEquals" name="u.current_id" value="-1"> open_popup('u', '', 'users/root_edit_password'); <inp2:m_else/> open_popup('u', '', 'users/user_edit_password'); </inp2:m_if> } $FrameResizer = new FrameResizer('<inp2:m_Phrase name="la_ToolTip_ShowMenu" js_escape="1"/>', '<inp2:m_Phrase name="la_ToolTip_HideMenu" js_escape="1"/>', window.parent, '<inp2:m_Link pass="m,adm" adm_event="OnSaveSetting" var_name="#NAME#" var_value="#VALUE#" no_amp="1" js_escape="1"/>', <inp2:adm_MenuFrameWidth/>); $FrameResizer.InitControls($FrameResizer); $FrameResizer.SetStatus(<inp2:m_if check="m_RecallEquals" name="ShowAdminMenu" value="0" persistent="1">0<inp2:m_else/>1</inp2:m_if>); -</script> \ No newline at end of file +</script> Index: branches/5.3.x/core/admin_templates/config/config_general.tpl =================================================================== --- branches/5.3.x/core/admin_templates/config/config_general.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/config/config_general.tpl (revision 16111) @@ -1,180 +1,180 @@ <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="conf" section="$section" perm_event="conf:OnLoad" title_preset="config_list_general"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> function validate_password_fields() { var $validated = true; $("input[primarytype='password']", '#' + $form_name).each( function ($e) { if ( !validate_password_field(this.id) ) { $validated = false; } } ); return $validated; } function validate_password_field($field_id) { var password_field = document.getElementById($field_id), password_verify_field = document.getElementById('verify_' + $field_id); if ( password_field && password_verify_field && password_field.value == password_verify_field.value ) { return true; } else { var password_error_cell = document.getElementById('error_' + $field_id); if ( password_error_cell ) { $(window).scrollTop($(password_field).position().top - 15); password_error_cell.innerHTML = '<inp2:m_Phrase name="la_error_PasswordMatch"/>'; } return false; } } function toggle_section($label) { var $table = document.getElementById('config_table'); var $row = null; var $is_visible = false; for (var $i = 0; $i < $table.rows.length; $i++) { $row = $table.rows[$i]; if ( $row.getAttribute('header_label') != $label ) { continue; } if ( !$row.style.display ) { $row.style.display = document.all ? 'block' : 'table-row'; } $is_visible = !($row.style.display == 'none'); $row.style.display = $is_visible ? 'none' : (document.all ? 'block' : 'table-row'); document.getElementById('toggle_mark[' + $label + ']').innerHTML = '[' + ($is_visible ? '+' : '-') + ']'; } } var a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton( 'select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('conf','<inp2:conf_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton( 'cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('conf','OnCancel'); } ) ); <inp2:m_if check="m_IsDebugMode"> a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton( 'new_item', '<inp2:m_phrase label="la_ToolTip_NewSystemSetting" escape="1"/>::<inp2:m_phrase label="la_ToolTip_Add" escape="1"/>', function() { std_precreate_item('conf', 'config/config_edit'); } ) ); </inp2:m_if> <inp2:m_ModuleInclude template = "config/custom_toolbar"/> a_toolbar.Render(); </script> </td> </tr> </tbody> </table> <inp2:m_include t="incs/config_blocks"/> <inp2:m_RenderElement name="config_updated_notice"/> <inp2:conf_SaveWarning name="grid_save_warning"/> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="bordered" id="config_table"> <!-- module root category selector: begin --> <tr class="subsectiontitle"> <td colspan="2"> <inp2:m_phrase name="la_Text_RootCategory" /> </td> <td align="right"> <a class="config-header" href="javascript:toggle_section('la_Text_RootCategory');" id="toggle_mark[la_Text_RootCategory]" title="Collapse/Expand Section">[-]</a> </td> </tr> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>" header_label="la_Text_RootCategory"> <td> <span class="text"><inp2:m_phrase name="la_prompt_RootCategory" /></span> </td> <td> <inp2:m_DefineElement name="category_caption"> <inp2:m_ifnot check="m_Param" name="is_first"> <inp2:m_param name="separator"/> </inp2:m_ifnot> <inp2:m_param name="cat_name"/> </inp2:m_DefineElement> <b><inp2:conf_CategoryPath separator=" > " render_as="category_caption" /></b> <input type="hidden" name="conf[ModuleRootCategory][VariableValue]" value="<inp2:conf_ModuleRootCategory/>"/> <a href="<inp2:adm_SelectorLink prefix='conf' selection_mode='single' tab_prefixes='none'/>" onclick="openSelector('conf', this.href, 'ModuleRootCategory', '950x600'); return false;"><img src="img/icons/icon24_cat.gif" border="0" align="absmiddle" /></a> </td> <td class="error"> </td> </tr> <!-- module root category selector: end --> <inp2:conf_PrintList block="config_block" per_page="-1" full_block="config_block" half_block1="config_block1" half_block2="config_block2" value_render_as="cf_default_value"/> </table> <script type="text/javascript"> <inp2:m_if check="m_Get" name="refresh_tree"> getFrame('menu').location.reload(); </inp2:m_if> <inp2:m_if check="m_Get" name="refresh_all"> var $menu_frame = getFrame('menu'); $menu_frame.parent.location.href = $menu_frame.parent.location.href; </inp2:m_if> Application.setHook( 'conf:*', function ($event) { $event.status = $event.Name == 'OnCancel' ? true : validate_password_fields(); } ); <inp2:m_if check="m_Get" name="first_error"> $(document).ready(function () { var $error_cell = $('.field-<inp2:m_Get name="first_error"/>.error'); if ( $error_cell.length ) { $(window).scrollTop($error_cell.position().top - 15); } }); </inp2:m_if> </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/config/config_edit.tpl =================================================================== --- branches/5.3.x/core/admin_templates/config/config_edit.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/config/config_edit.tpl (revision 16111) @@ -1,100 +1,100 @@ <inp2:adm_SetPopupSize width="650" height="790"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="conf" section="$section" title_preset="config_edit"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('conf', '<inp2:conf_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { cancel_edit('conf', 'OnCancelEdit','<inp2:conf_SaveEvent/>','<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('reset_edit', '<inp2:m_phrase label="la_ToolTip_Reset" escape="1"/>', function() { reset_form('conf', 'OnReset', '<inp2:m_Phrase label="la_FormResetConfirmation" escape="1"/>'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() { go_to_id('conf', '<inp2:conf_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('conf', '<inp2:conf_NextId/>'); } ) ); a_toolbar.Render(); <inp2:m_if check="conf_IsSingle" > a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); <inp2:m_else/> <inp2:m_if check="conf_IsLast" > a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="conf_IsFirst" > a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> </tr> </tbody> </table> <inp2:conf_SaveWarning name="grid_save_warning"/> <inp2:conf_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="inp_id_label" prefix="conf" field="VariableId" title="la_fld_Id"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="VariableName" title="la_fld_SystemSettingName"/> <inp2:m_RenderElement name="inp_label" prefix="conf" field="VariableValue" title="la_fld_SystemSettingValue"/> <inp2:m_RenderElement name="inp_edit_options" prefix="conf" field="ModuleOwner" title="la_fld_Module" has_empty="1"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="Section" title="la_fld_SystemSettingSection"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="Heading" title="la_prompt_heading"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="Prompt" title="la_prompt_FieldPrompt"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="HintLabel" title="la_fld_SystemSettingHint"/> <inp2:m_RenderElement name="inp_edit_options" prefix="conf" field="ElementType" title="la_prompt_InputType" has_empty="1"/> <inp2:m_RenderElement name="inp_edit_textarea" prefix="conf" field="Validation" title="la_fld_SystemSettingValidation"/> <inp2:m_RenderElement name="inp_edit_textarea" prefix="conf" field="ValueList" title="la_prompt_valuelist"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="DisplayOrder" title="la_field_displayorder"/> <inp2:m_RenderElement name="inp_edit_box" prefix="conf" field="GroupDisplayOrder"/> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> <script type="text/javascript"> $(document).ready(function () { var $suggest_url = '<inp2:m_Link template="dummy" pass="m,conf" conf_event="OnSuggestValuesJSON" field="#FIELD_NAME#" no_amp="1" js_escape="1"/>'; $.each(['Section', 'Heading'], function ($i, $field) { var $selector = '<inp2:conf_InputName name="#FIELD_NAME#" js_escape="1"/>'.replace('#FIELD_NAME#', $field); $('#' + jq($selector)) .autocomplete({ source: $suggest_url.replace('#FIELD_NAME#', $field) }) .data('ui-autocomplete')._renderItem = function (ul, item) { // allow HTML in items return $('<li>').append($('<a>').html(item.label)).appendTo(ul); }; }); }); </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/config/config_search.tpl =================================================================== --- branches/5.3.x/core/admin_templates/config/config_search.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/config/config_search.tpl (revision 16111) @@ -1,142 +1,142 @@ <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="confs" section="$section" perm_event="confs:OnLoad" title_preset="config_list_search"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> var a_toolbar = new ToolBar(); <inp2:m_if check="m_IsDebugMode"> a_toolbar.AddButton( new ToolBarButton('new_item', '<inp2:m_phrase label="la_ToolTip_NewSearchConfig" escape="1"/>::<inp2:m_phrase label="la_ToolTip_Add" escape="1"/>', function() { std_new_item('confs', 'config/config_search_edit'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep2') ); </inp2:m_if> a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('confs','<inp2:confs_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('confs','OnCancel'); } ) ); a_toolbar.Render(); </script> </td> </tr> </tbody> </table> <inp2:m_DefineElement name="confs_checkbox_td"> <td valign="top" class="text"> <inp2:m_if check="m_ParamEquals" name="nolabel" value="true"> <inp2:m_else /> <label for="_cb_<inp2:InputName field="$Field"/>"><inp2:m_Phrase label="$Label" /></label> </inp2:m_if> <input type="checkbox" name="_cb_<inp2:InputName field="$Field"/>" <inp2:Field field="$Field" checked="checked" db="db"/> id="_cb_<inp2:InputName field="$Field"/>" onclick="update_checkbox(this, document.getElementById('<inp2:InputName field="$Field"/>'))" > <input type="hidden" id="<inp2:InputName field="$Field"/>" name="<inp2:InputName field="$Field"/>" value="<inp2:Field field="$Field" db="db"/>"> </td> </inp2:m_DefineElement> <inp2:m_DefineElement name="confs_edit_text"> <td valign="top" class="text"> <label for="<inp2:InputName field="$Field"/>"><inp2:m_Phrase label="$Label" /></label> <input type="text" name="<inp2:InputName field="$Field"/>" value="<inp2:Field field="$Field"/>" size="3" /> </td> </inp2:m_DefineElement> <inp2:m_DefineElement name="confs_detail_row"> <inp2:m_if check="m_ParamEquals" name="show_heading" value="1"> <tr class="subsectiontitle"> <td colspan="4"> <inp2:Field name="ConfigHeader" as_label="1"/> </td> </tr> </inp2:m_if> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>"> <td class="text"> <inp2:Field field="DisplayName" as_label="true" /> <inp2:m_if check="m_IsDebugMode"> <br /><small style="color: grey;">[ID: <b><inp2:Field name="SearchConfigId"/></b>; DisplayOrder: <b><inp2:Field name="DisplayOrder"/></b>; Field: <b><inp2:Field name="FieldName"/></b>]</small> </inp2:m_if> </td> <inp2:m_if check="Field" name="FieldType" equals_to="text|range|select|multiselect" db="db"> <inp2:m_RenderElement name="confs_checkbox_td" pass_params="true" IdField="SearchConfigId" Label="la_prompt_SimpleSearch" Field="SimpleSearch" /> <inp2:m_else/> <td class="text">   </td> </inp2:m_if> <inp2:m_RenderElement name="confs_edit_text" pass_params="true" IdField="SearchConfigId" Label="la_prompt_weight" Field="Priority" /> <inp2:m_RenderElement name="confs_checkbox_td" pass_params="true" IdField="SearchConfigId" Label="la_prompt_AdvancedSearch" Field="AdvancedSearch" /> </tr> </inp2:m_DefineElement> <inp2:m_DefineElement name="config_values"> <tr class="subsectiontitle"> <td colspan="4"> <inp2:m_phrase name="$module_item" /> <inp2:m_phrase name="la_prompt_relevence_settings" /> </td> </tr> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>"> <td colspan="4"> <inp2:m_phrase name="la_prompt_required_field_increase"/> <input type="text" size="3" name="conf[<inp2:conf_GetVariableID name="SearchRel_Increase_{$module_key}"/>][VariableValue]" VALUE="<inp2:Field field="SearchRel_Increase_{$module_key}" />">% </td> </tr> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>"> <td colspan="4"> <inp2:m_phrase name="la_prompt_relevence_percent"/> <input type="text" size="3" name="conf[<inp2:conf_GetVariableID name="SearchRel_Keyword_{$module_key}"/>][VariableValue]" value="<inp2:Field field="SearchRel_Keyword_{$module_key}" />">% <inp2:Field field="SearchRel_Keyword_{$module_key}_prompt" as_label="1" />     <input type="text" size="3" name="conf[<inp2:conf_GetVariableID name="SearchRel_Pop_{$module_key}"/>][VariableValue]" value="<inp2:Field field="SearchRel_Pop_{$module_key}" />">% <inp2:Field field="SearchRel_Pop_{$module_key}_prompt" as_label="1" />    <input type="text" size="3" name="conf[<inp2:conf_GetVariableID name="SearchRel_Rating_{$module_key}"/>][VariableValue]" value="<inp2:Field field="SearchRel_Rating_{$module_key}" />">% <inp2:Field field="SearchRel_Rating_{$module_key}_prompt" as_label="1" /> </td> </tr> <inp2:m_if check="m_GetEquals" name="module" value="In-Portal" inverse="inverse"> <tr class="subsectiontitle"> <td colspan="4"> <inp2:m_phrase name="$module_item" /> <inp2:m_phrase name="la_prompt_multipleshow" /> </td> </tr> <tr class="<inp2:m_odd_even odd="table-color1" even="table-color2"/>"> <td class="text" width="120"><inp2:Field field="Search_ShowMultiple_{$module_key}_prompt" as_label="1"/></td> <td class="text" colspan="3"> <input type="checkbox" name="_cb_conf[<inp2:conf_GetVariableID name="Search_ShowMultiple_{$module_key}"/>][VariableValue]" <inp2:Field field="Search_ShowMultiple_{$module_key}" checked="checked" db="db"/> id="_cb_conf[<inp2:conf_GetVariableID name="Search_ShowMultiple_{$module_key}"/>][VariableValue]" onclick="update_checkbox(this, document.getElementById('conf[<inp2:conf_GetVariableID name="Search_ShowMultiple_{$module_key}"/>][VariableValue]'))" > <input type="hidden" id="conf[<inp2:conf_GetVariableID name="Search_ShowMultiple_{$module_key}"/>][VariableValue]" name="conf[<inp2:conf_GetVariableID name="Search_ShowMultiple_{$module_key}"/>][VariableValue]" value="<inp2:Field field="Search_ShowMultiple_{$module_key}" db="db"/>"> </td> </tr> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_include t="incs/config_blocks"/> <inp2:m_RenderElement name="config_updated_notice"/> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="bordered"<inp2:m_if check="conf_ShowRelevance"> style="border-bottom-width: 0px;"</inp2:m_if>> <inp2:confs_PrintList render_as="confs_detail_row" /> </table> <inp2:m_if check="conf_ShowRelevance"> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="bordered"> <inp2:conf_PrintConfList block="config_values" per_page="-1"/> </table> </inp2:m_if> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/config/config_universal.tpl =================================================================== --- branches/5.3.x/core/admin_templates/config/config_universal.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/config/config_universal.tpl (revision 16111) @@ -1,151 +1,151 @@ <inp2:m_include t="incs/header"/> <inp2:conf_InitList name="Default" per_page="-1"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="conf" section="$section" title_preset="section_label" perm_event="conf:OnLoad"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> function validate_password_fields() { var $validated = true; $("input[primarytype='password']", '#' + $form_name).each( function ($e) { if ( !validate_password_field(this.id) ) { $validated = false; } } ); return $validated; } function validate_password_field($field_id) { var password_field = document.getElementById($field_id), password_verify_field = document.getElementById('verify_' + $field_id); if ( password_field && password_verify_field && password_field.value == password_verify_field.value ) { return true; } else { var password_error_cell = document.getElementById('error_' + $field_id); if ( password_error_cell ) { $(window).scrollTop($(password_field).position().top - 15); password_error_cell.innerHTML = '<inp2:m_Phrase name="la_error_PasswordMatch"/>'; } return false; } } function toggle_section($label) { var $row = null, $is_visible = false, $table = document.getElementById('config_table'); for (var $i = 0; $i < $table.rows.length; $i++) { $row = $table.rows[$i]; if ( $row.getAttribute('header_label') != $label ) { continue; } if ( !$row.style.display ) { $row.style.display = document.all ? 'block' : 'table-row'; } $is_visible = !($row.style.display == 'none'); $row.style.display = $is_visible ? 'none' : (document.all ? 'block' : 'table-row'); document.getElementById('toggle_mark[' + $label + ']').innerHTML = '[' + ($is_visible ? '+' : '-') + ']'; } } var a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton( 'select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function () { submit_event('conf', '<inp2:conf_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton( 'cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function () { submit_event('conf', 'OnCancel'); } ) ); <inp2:m_if check="m_IsDebugMode"> a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton( 'new_item', '<inp2:m_phrase label="la_ToolTip_NewSystemSetting" escape="1"/>::<inp2:m_phrase label="la_ToolTip_Add" escape="1"/>', function() { std_precreate_item('conf', 'config/config_edit'); } ) ); </inp2:m_if> <inp2:m_ModuleInclude template = "config/custom_toolbar"/> a_toolbar.Render(); </script> </td> </tr> </tbody> </table> <inp2:m_include t="incs/config_blocks"/> <inp2:m_RenderElement name="config_updated_notice"/> <inp2:conf_SaveWarning name="grid_save_warning"/> <table width="100%" border="0" cellspacing="0" cellpadding="4" class="bordered" id="config_table"> <inp2:conf_PrintList list_name="default" block="config_block" full_block="config_block" half_block1="config_block1" half_block2="config_block2" value_render_as="cf_default_value"/> </table> <script type="text/javascript"> <inp2:m_if check="m_Get" name="refresh_tree"> getFrame('menu').location.reload(); </inp2:m_if> <inp2:m_if check="m_Get" name="refresh_all"> var $menu_frame = getFrame('menu'); $menu_frame.parent.location.href = $menu_frame.parent.location.href; </inp2:m_if> Application.setHook( 'conf:*', function ($event) { $event.status = $event.Name == 'OnCancel' ? true : validate_password_fields(); } ); <inp2:m_if check="m_Get" name="first_error"> $(document).ready(function () { var $error_cell = $('.field-<inp2:m_Get name="first_error"/>.error'); if ( $error_cell.length ) { $(window).scrollTop($error_cell.position().top - 15); } }); </inp2:m_if> </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/config/config_search_edit.tpl =================================================================== --- branches/5.3.x/core/admin_templates/config/config_search_edit.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/config/config_search_edit.tpl (revision 16111) @@ -1,76 +1,76 @@ <inp2:adm_SetPopupSize width="750" height="400"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get name="section" result_to_var="section"/> +<inp2:m_Get name="section" no_html_escape="1" result_to_var="section"/> <inp2:m_RenderElement name="combined_header" prefix="confs" section="$section" perm_event="confs:OnLoad" title_preset="configsearch_edit"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('confs','<inp2:confs_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('confs','OnGoBack'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() { go_to_id('confs', '<inp2:confs_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('confs', '<inp2:confs_NextId/>'); } ) ); a_toolbar.Render(); <inp2:m_if check="confs_IsSingle" > a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); <inp2:m_else/> <inp2:m_if check="confs_IsLast" > a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="confs_IsFirst" > a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> </tr> </tbody> </table> <inp2:confs_SaveWarning name="grid_save_warning"/> <inp2:confs_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="subsection" title="la_section_General"/> <inp2:m_RenderElement name="inp_id_label" prefix="confs" field="SearchConfigId" title="la_fld_Id"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="TableName" title="la_fld_TableName"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="FieldName" title="la_fld_FieldName"/> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="confs" field="SimpleSearch" title="la_fld_SimpleSearch"/> <inp2:m_RenderElement name="inp_edit_checkbox" prefix="confs" field="AdvancedSearch" title="la_fld_AdvancedSearch"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="Description" title="la_fld_Description"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="DisplayName" title="la_fld_DisplayName"/> <inp2:m_RenderElement name="inp_edit_options" prefix="confs" field="ModuleName" title="la_fld_ModuleName"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="ConfigHeader" title="la_fld_ConfigHeader"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="DisplayOrder" title="la_fld_Priority"/> <inp2:m_RenderElement name="inp_edit_box" prefix="confs" field="Priority" title="la_fld_Weight"/> <inp2:m_RenderElement name="inp_edit_options" prefix="confs" field="FieldType" title="la_fld_FieldType"/> </table> </div> <inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/import/import_start.tpl =================================================================== --- branches/5.3.x/core/admin_templates/import/import_start.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/import/import_start.tpl (revision 16111) @@ -1,52 +1,52 @@ <inp2:adm_SetPopupSize width="500" height="270"/> <inp2:m_include t="incs/header"/> <inp2:m_set adm_id="0" /> -<inp2:m_Get var="PrefixSpecial" result_to_var="importprefix" /> +<inp2:m_Get var="PrefixSpecial" no_html_escape="1" result_to_var="importprefix" /> <inp2:{$importprefix}_PermSection section="main" result_to_var="permsection" /> <inp2:m_RenderElement name="combined_header" section="$permsection" prefix="adm" title_preset="csv_import"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('import', '<inp2:m_phrase label="la_ToolTip_Import" escape="1"/>', function() { submit_event('adm','OnCSVImportBegin'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { window_close(); } ) ); a_toolbar.Render(); </script> <script src="js/swfobject.js" type="text/javascript"></script> <script type="text/javascript" src="<inp2:m_Compress files='js/uploader/upload_manager.js|js/uploader/uploader.js'/>"></script> </td> </tr> </tbody> </table> <inp2:adm_SaveWarning name="grid_save_warning"/> <inp2:adm_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="subsection" title="la_section_General"/> <inp2:m_RenderElement name="inp_edit_swf_upload" prefix="adm" field="ImportFile" title="la_fld_ImportFile"/> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> <input type="hidden" name="next_template" value="import/import_progress" /> <input type="hidden" name="PrefixSpecial" value="<inp2:m_Get var="PrefixSpecial" />" /> <input type="hidden" name="grid" value="<inp2:m_Get var="grid" />" /> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/catalog/catalog_counters.tpl =================================================================== --- branches/5.3.x/core/admin_templates/catalog/catalog_counters.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/catalog/catalog_counters.tpl (revision 16111) @@ -1,29 +1,29 @@ // counters <inp2:m_ModuleInclude template="catalog_tab" tab_init="3"/> // category related statistics $Catalog.ParentCategoryID = <inp2:c_GetParentCategory/>; <inp2:m_DefineElement name="category_caption"> <inp2:m_ifnot check="m_Param" name="is_first"> <inp2:m_param name="separator"/> </inp2:m_ifnot> <inp2:m_if check="m_ParamEquals" name="current" value="1" inverse="1"> <a class="control_link" href="javascript:$Catalog.go_to_cat(<inp2:m_param name="cat_id"/>);"><inp2:m_param name="cat_name"/></a> <inp2:m_else/> <inp2:m_param name="cat_name"/> </inp2:m_if> </inp2:m_DefineElement> setInnerHTML('category_path', '<inp2:c_CategoryPath separator=" / " render_as="category_caption" js_escape="1" strip_nl="2"/>'); set_window_title( RemoveTranslationLink(document.getElementById('blue_bar').innerHTML, false).replace(/(<[^<]+>)/g, '').replace(/\s+/g, ' ').trim() ); <inp2:m_if check="m_Get" name="last_template"> - <inp2:m_Get name="last_template" result_to_var="last_template"/> + <inp2:m_Get name="last_template" no_html_escape="1" result_to_var="last_template"/> <inp2:c_UpdateLastTemplate template="$last_template"/> <inp2:m_else/> <inp2:c_UpdateLastTemplate template="catalog/catalog"/> </inp2:m_if> <inp2:m_include t="categories/ci_blocks"/> -<inp2:m_RenderElement name="structure_reload_element"/> \ No newline at end of file +<inp2:m_RenderElement name="structure_reload_element"/> Index: branches/5.3.x/core/admin_templates/submissions/submission_log_edit.tpl =================================================================== --- branches/5.3.x/core/admin_templates/submissions/submission_log_edit.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/submissions/submission_log_edit.tpl (revision 16111) @@ -1,134 +1,134 @@ <inp2:adm_SetPopupSize width="820" height="570"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get var="form_id" result_to_var="form_id"/> +<inp2:m_Get var="form_id" no_html_escape="1" result_to_var="form_id"/> <inp2:m_RenderElement name="combined_header" prefix="formsubs" section="in-portal:submissions:$form_id" title_preset="submission_log_edit"/> <!-- ToolBar ---> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); <inp2:m_if check="submission-log_IsNewItem"> a_toolbar.AddButton( new ToolBarButton( 'select', '<inp2:m_phrase label="la_ToolTip_Send" escape="1"/>', function() { submit_event('submission-log', '<inp2:submission-log_SaveEvent/>'); } ) ); </inp2:m_if> a_toolbar.AddButton( new ToolBarButton( 'cancel', '<inp2:m_phrase label="la_ToolTip_Close" escape="1"/>', function() { cancel_edit('submission-log', 'OnGoBack', '<inp2:submission-log_SaveEvent/>', '<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>'); } ) ); <inp2:m_if check="submission-log_IsUserReply" inverse="inverse"> a_toolbar.AddButton( new ToolBarButton( 'resend', '<inp2:m_phrase label="la_ToolTip_Resend" escape="1"/>', function() { submit_event('submission-log', 'OnResendReply'); } ) ); </inp2:m_if> <inp2:m_if check="submission-log_IsNewItem"> a_toolbar.AddButton( new ToolBarButton( 'reset_to_user', '<inp2:m_phrase label="la_ToolTip_SaveAsDraft" escape="1"/>', function() { submit_event('submission-log', 'OnSaveDraft'); } ) ); </inp2:m_if> a_toolbar.Render(); </script> <script type="text/javascript" src="js/swfobject.js"></script> <script type="text/javascript" src="<inp2:m_Compress files='js/uploader/upload_manager.js|js/uploader/uploader.js'/>"></script> </td> </tr> </table> <inp2:m_DefineElement name="file_element"> <a href="<inp2:Field field='$field' format='full_url'/>" target="_blank"><inp2:Field field="$field"/></a><br/> </inp2:m_DefineElement> <input type="hidden" name="client_mode" value="<inp2:m_Get name='client_mode'/>"/> <inp2:submission-log_SaveWarning name="grid_save_warning"/> <inp2:submission-log_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="inp_edit_hidden" prefix="submission-log" field="FormSubmissionId"/> <inp2:m_RenderElement name="inp_edit_hidden" prefix="submission-log" field="DraftId"/> <inp2:m_RenderElement name="inp_id_label" prefix="submission-log" field="SubmissionLogId" title="la_fld_Id"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="FromEmail" title="la_fld_FromEmail"/> <inp2:m_RenderElement name="inp_edit_box" prefix="submission-log" field="ToEmail" size="60" title="la_fld_ToEmail"/> <inp2:m_RenderElement name="inp_edit_box" prefix="submission-log" field="Cc" size="60" title="la_fld_Cc"/> <inp2:m_RenderElement name="inp_edit_box" prefix="submission-log" field="Bcc" size="60" title="la_fld_Bcc"/> <inp2:m_if check="submission-log_IsNewItem"> <inp2:m_RenderElement name="inp_edit_box" prefix="submission-log" field="Subject" size="80" title="la_fld_Subject"/> <inp2:m_if check="submission-log_HasDraft"> <inp2:m_RenderElement design="form_row" prefix="submission-log" field="DraftId" title="la_DraftAvailableFrom"> <td class="control-cell"> <inp2:draft.related_Field name="CreatedOn"/> [<a href="#" onclick="submit_event('<inp2:m_Param name='prefix'/>', 'OnUseDraft'); return false;"><inp2:m_Phrase name="la_btn_UseDraft"/></a>]  [<a href="#" onclick="submit_event('<inp2:m_Param name='prefix'/>', 'OnDeleteDraft'); return false;"><inp2:m_Phrase name="la_btn_DeleteDraft"/></a>] </td> </inp2:m_RenderElement> </inp2:m_if> <inp2:m_RenderElement name="inp_edit_textarea" prefix="submission-log" field="Message" rows="30" cols="100" title="la_fld_Message"/> <inp2:m_RenderElement name="inp_edit_swf_upload" prefix="submission-log" field="Attachment" title="la_fld_Attachment"/> <inp2:m_else/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="Subject" title="la_fld_Subject"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="Message" title="la_fld_Message" nl2br="1"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="ReplyStatus" title="la_fld_ReplyStatus"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="RepliedOn" title="la_fld_RepliedOn"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="SentStatus" title="la_fld_SentStatus"/> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="SentOn" title="la_fld_SentOn"/> <inp2:m_RenderElement design="form_row" prefix="submission-log" field="Attachment" title="la_fld_Attachment"> <td class="control-cell"> <inp2:$prefix_IterateFiles render_as="file_element" field="$field"/> </td> </inp2:m_RenderElement> <inp2:m_if check="submission-log_Field" name="BounceInfo"> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="BounceInfo" title="la_fld_BounceInfo" nl2br="1"/> </inp2:m_if> <inp2:m_if check="submission-log_Field" name="BounceDate" db="db"> <inp2:m_RenderElement name="inp_label" prefix="submission-log" field="BounceDate" title="la_fld_BounceDate"/> </inp2:m_if> </inp2:m_if> <inp2:m_RenderElement name="inp_edit_hidden" prefix="submission-log" field="FromEmail"/> <inp2:m_RenderElement name="inp_edit_hidden" prefix="submission-log" field="ReplyTo"/> <inp2:m_RenderElement name="inp_edit_filler" /> </table> </div> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/submissions/submission_edit_logs.tpl =================================================================== --- branches/5.3.x/core/admin_templates/submissions/submission_edit_logs.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/submissions/submission_edit_logs.tpl (revision 16111) @@ -1,149 +1,149 @@ <inp2:adm_SetPopupSize width="800" height="640"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get var="form_id" result_to_var="form_id"/> +<inp2:m_Get var="form_id" no_html_escape="1" result_to_var="form_id"/> <inp2:m_RenderElement name="combined_header" prefix="formsubs" section="in-portal:submissions:$form_id" title_preset="submission_edit_logs" tab_preset="Default" /> <!-- ToolBar ---> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tr> <td> <table width="100%" cellpadding="0" cellspacing="0"> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('formsubs', '<inp2:formsubs_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton( 'cancel', '<inp2:m_phrase label="la_ToolTip_Close" escape="1"/>', function() { submit_event('formsubs', 'OnGoBack'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton( 'prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() { go_to_id('formsubs', '<inp2:formsubs_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton( 'next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('formsubs', '<inp2:formsubs_NextId/>'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep2') ); function edit() { std_edit_temp_item('submission-log', 'submissions/submission_log_edit'); } a_toolbar.AddButton( new ToolBarButton( 'edit', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', edit ) ); a_toolbar.AddButton( new ToolBarButton( 'reply', '<inp2:m_phrase label="la_ToolTip_Reply" escape="1"/>', function() { Application.SetVar('client_mode', 0); std_new_item('submission-log', 'submissions/submission_log_edit'); } ) ); a_toolbar.AddButton( new ToolBarButton( 'delete', '<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>', function() { std_delete_items('submission-log') } ) ); a_toolbar.AddButton( new ToolBarButton( 'resend', '<inp2:m_phrase label="la_ToolTip_Resend" escape="1"/>', function() { Application.SetVar('from_list', 1); submit_event('submission-log', 'OnResendReply'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep3') ); a_toolbar.AddButton( new ToolBarButton( 'view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function(id) { show_viewmenu(a_toolbar, 'view'); } ) ); a_toolbar.Render(); <inp2:m_if check="formsubs_IsSingle"> a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); <inp2:m_else/> <inp2:m_if check="formsubs_IsLast"> a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="formsubs_IsFirst"> a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> </tr> </table> </td> </tr> </table> <inp2:m_DefineElement name="grid_subject_td" format="" nl2br="" first_chars="" td_style="" currency=""> <inp2:m_if check="IsNewUserReply"> <strong><inp2:Field field="$field" first_chars="$first_chars" currency="$currency" nl2br="$nl2br" grid="$grid" format="$format"/></strong> <inp2:m_else/> <inp2:Field field="$field" first_chars="$first_chars" currency="$currency" nl2br="$nl2br" grid="$grid" format="$format"/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid" PrefixSpecial="submission-log" IdField="SubmissionLogId" grid="Default"/> <script type="text/javascript"> Grids['submission-log'].SetDependantToolbarButtons( new Array('edit','delete', 'resend') ); </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/submissions/submission_view.tpl =================================================================== --- branches/5.3.x/core/admin_templates/submissions/submission_view.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/submissions/submission_view.tpl (revision 16111) @@ -1,223 +1,223 @@ <inp2:adm_SetPopupSize width="800" height="640"/> <inp2:m_include t="incs/header"/> -<inp2:m_Get var="form_id" result_to_var="form_id"/> +<inp2:m_Get var="form_id" no_html_escape="1" result_to_var="form_id"/> <inp2:m_if check="form_Field" name="EnableEmailCommunication" db="db"> <inp2:m_RenderElement name="combined_header" prefix="formsubs" section="in-portal:submissions:$form_id" title_preset="formsubs_view" tab_preset="Default"/> <inp2:m_else/> <inp2:m_RenderElement name="combined_header" prefix="formsubs" section="in-portal:submissions:$form_id" title_preset="formsubs_view"/> </inp2:m_if> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript"> a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('formsubs', '<inp2:formsubs_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Close" escape="1"/>', function() { submit_event('formsubs', 'OnGoBack'); } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep1') ); a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() { go_to_id('formsubs', '<inp2:formsubs_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('formsubs', '<inp2:formsubs_NextId/>'); } ) ); //a_toolbar.AddButton( new ToolBarSeparator('sep2') ); a_toolbar.Render(); <inp2:m_if check="formsubs_IsSingle" > a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); //a_toolbar.HideButton('sep2'); <inp2:m_else/> <inp2:m_if check="formsubs_IsLast" > a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="formsubs_IsFirst" > a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> </tr> </tbody> </table> <!--##<inp2:m_DefineElement name="form_field_text"> <inp2:m_if check="FieldEquals" field="Validation" value="1"> <a href="mailto:<inp2:SubmissionTag tag='Field'/>"><inp2:SubmissionTag tag="Field"/></a> <inp2:m_else/> <inp2:SubmissionTag tag="Field"/> </inp2:m_if> </inp2:m_DefineElement>##--> <inp2:m_DefineElement name="form_field_text"> <input type="text" name="<inp2:CustomInputName/>" id="<inp2:CustomInputName/>" value="<inp2:SubmissionTag tag="Field"/>" <inp2:m_param name="field_params" />/> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_password"> <input type="password" primarytype="password" name="<inp2:CustomInputName/>" id="<inp2:CustomInputName/>" value="" /> <input type="password" name="<inp2:CustomInputName verify='1'/>" id="verify_<inp2:CustomInputName verify='1'/>" value="" /> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_option"> <option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_param name="option"/></option> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_select"> <select name="<inp2:CustomInputName/>" id="<inp2:CustomInputName/>"> <inp2:SubmissionTag tag="PredefinedOptions" field="$field" block="form_field_option" selected="selected"/> </select> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_checkbox"> <input type="hidden" id="<inp2:CustomInputName/>" name="<inp2:CustomInputName/>" value="<inp2:SubmissionTag tag='Field' field='$field' db='db'/>"> <input type="checkbox" id="_cb_<inp2:m_param name='field'/>" <inp2:SubmissionTag tag="Field" checked="checked" db="db"/> onchange="update_checkbox(this, document.getElementById('<inp2:CustomInputName/>'));"> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_textarea"> <textarea name="<inp2:CustomInputName/>" id="<inp2:CustomInputName/>" style="width: 100%;" <inp2:m_param name="field_params" />><inp2:SubmissionTag tag="Field" field="$field" /></textarea> <script type="text/javascript"> Form.addControl('<inp2:CustomInputName/>', false); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_radio_item"> <input type="radio" <inp2:m_param name="checked"/> name="<inp2:m_param name="field_name"/>" id="<inp2:m_param name="field_name"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>"><label for="<inp2:m_param name="field_name"/>_<inp2:m_param name="key"/>"><inp2:m_param name="option"/></label>  </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_radio"> <inp2:SubmissionTag tag="PredefinedOptions" field="$field" block="form_radio_item" selected="checked"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_field_upload"> <input type="file" name="<inp2:CustomInputName/>" id="<inp2:CustomInputName/>" <inp2:m_param name="field_params" />/> <inp2:m_ifnot check="SubmissionTag" tag='Field' equals_to="" db="db"> (<a href="<inp2:SubmissionTag tag='Field' format='full_url'/>" target="_blank"><inp2:SubmissionTag tag="Field"/></a>) </inp2:m_ifnot> <input type="hidden" name="<inp2:CustomInputName/>[upload]" id="<inp2:CustomInputName/>[upload]" value="<inp2:SubmissionTag tag='Field'/>"/> </inp2:m_DefineElement> <inp2:formsubs_SaveWarning name="grid_save_warning"/> <inp2:formsubs_ErrorWarning name="form_error_warning"/> <div id="scroll_container"> <table class="edit-form"> <inp2:m_RenderElement name="subsection" prefix="formsubs" fields="FormSubmissionId,SubmissionTime,IPAddress,ReferrerURL,LogStatus,LastUpdatedOn" title="la_section_General"/> <inp2:m_RenderElement name="inp_id_label" prefix="formsubs" field="FormSubmissionId" title="la_fld_Id"/> <inp2:m_RenderElement name="inp_label" prefix="formsubs" field="SubmissionTime" title="la_fld_SubmissionTime" /> <inp2:m_RenderElement name="inp_label" prefix="formsubs" field="IPAddress" title="la_fld_IPAddress" /> <inp2:m_RenderElement name="inp_label" prefix="formsubs" field="ReferrerURL" title="la_fld_ReferrerURL" /> <inp2:m_if check="form_Field" name="EnableEmailCommunication" db="db"> <inp2:m_RenderElement name="inp_label" prefix="formsubs" field="LogStatus" title="la_fld_Status" /> <inp2:m_else/> <inp2:m_RenderElement name="inp_edit_options" prefix="formsubs" field="LogStatus" title="la_fld_Status" has_empty="1"/> </inp2:m_if> <inp2:m_if check="formsubs_Field" name="LastUpdatedOn" db="db"> <inp2:m_RenderElement name="inp_label" prefix="formsubs" field="LastUpdatedOn" title="la_fld_LastUpdatedOn" /> </inp2:m_if> <inp2:m_if check="form_Field" name="EnableEmailCommunication" db="db"> <inp2:m_ifnot check="submission-log_TotalRecords"> <inp2:m_RenderElement design="form_row" prefix="formsubs" field="MergeToSubmission" title="la_fld_MergeToSubmission"> <td class="control-cell"> <input type="hidden" id="<inp2:{$prefix}_InputName field='Is{$field}'/>" name="<inp2:{$prefix}_InputName field='Is{$field}'/>" value="<inp2:{$prefix}_Field field='Is{$field}' db='db'/>"> <input tabindex="<inp2:m_get param='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='Is{$field}'/>" name="_cb_<inp2:{$prefix}_InputName field='Is{$field}'/>" <inp2:{$prefix}_Field field="Is{$field}" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='Is{$field}'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>"> <select tabindex="<inp2:m_get param='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="width: 400px;"> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="1"/> </select> </td> </inp2:m_RenderElement> </inp2:m_ifnot> </inp2:m_if> <inp2:m_RenderElement name="subsection" title="la_section_Data"/> <inp2:m_DefineElement name="form_field" prefix="formsubs"> <tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>"> <inp2:m_inc param="tab_index" by="1"/> <td class="label-cell" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', 'fld_<inp2:Field name='FormFieldId'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')"> <inp2:Field field="Prompt" result_to_var="title"/> <inp2:Field field="Prompt" plus_or_as_label="1" no_special="no_special"/><inp2:m_if check="Field" name="Required" db="db"><span class="field-required"> *</span></inp2:m_if>:<inp2:m_if check="FieldHintLabel" title_label="$title"><span> <img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:FieldHintLabel title_label='$title' html_escape='1'/>" alt="<inp2:FieldHintLabel title_label='$title' html_escape='1'/>"/></inp2:m_if> </td> <td class="control-mid"> </td> <script type="text/javascript"> if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') { fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object(); } fields['<inp2:m_Param name="prefix" js_escape="1"/>']['fld_<inp2:Field name="FormFieldId"/>'] = '<inp2:Field field="Prompt" plus_or_as_label="1" no_special="no_special" js_escape="1"/>' </script> <td class="control-cell"> <inp2:ConfigFormElement field="Value" blocks_prefix="form_field_" element_type_field="ElementType" value_list_field="ValueList" /><br/> </td> </tr> <script type="text/javascript"> add_form_error('<inp2:m_Param name="prefix" js_escape="1"/>', 'fld_<inp2:Field name="FormFieldId"/>', '<inp2:CustomInputName/>', '<inp2:SubmissionTag tag="Error" js_escape="1"/>') </script> </inp2:m_DefineElement> <inp2:formflds_PrintList render_as="form_field" SourcePrefix="formsubs" per_page="-1"/> <inp2:m_RenderElement name="subsection" prefix="formsubs" fields="Notes" title="la_section_SubmissionNotes"/> <inp2:m_RenderElement name="inp_edit_textarea" prefix="formsubs" field="Notes" title="la_fld_Notes" /> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> <inp2:m_if check="form_Field" name="EnableEmailCommunication" db="db"> <inp2:m_ifnot check="submission-log_TotalRecords"> <script language="javascript" type="text/javascript"> var $field_mask = '<inp2:formsubs_InputName name="#FIELD_NAME#" js_escape="1"/>'; $(document).ready( function () { processMergeToSubmission(); $( get_control($field_mask, 'IsMergeToSubmission', undefined, '_cb') ).click(processMergeToSubmission); } ); function processMergeToSubmission() { var $do_merge = get_control($field_mask, 'IsMergeToSubmission', undefined, '_cb').checked; var $merge_to_submission = get_control($field_mask, 'MergeToSubmission'); if (!$do_merge) { $merge_to_submission.selectedIndex = 0; } $merge_to_submission.disabled = !$do_merge; if ($do_merge) { $('#merge_submission').removeClass('button-disabled').addClass('button').prop('disabled', false); } else { $('#merge_submission').removeClass('button').addClass('button-disabled').prop('disabled', true); } } </script> </inp2:m_ifnot> </inp2:m_if> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/admin_templates/submissions/submissions_list.tpl =================================================================== --- branches/5.3.x/core/admin_templates/submissions/submissions_list.tpl (revision 16110) +++ branches/5.3.x/core/admin_templates/submissions/submissions_list.tpl (revision 16111) @@ -1,67 +1,67 @@ <inp2:m_include t="incs/header" /> -<inp2:m_Get var="form_id" result_to_var="form_id"/> +<inp2:m_Get var="form_id" no_html_escape="1" result_to_var="form_id"/> <inp2:m_RenderElement name="combined_header" prefix="formsubs" section="in-portal:submissions:$form_id" pagination="1" title_preset="formsubs_list" /> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td> <script type="text/javascript"> //do not rename - this function is used in default grid for double click! function edit() { // don't use temp tables, since we can receive user replies while reviewing form submission std_edit_temp_item('formsubs', 'submissions/submission_view'); } var a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('edit', '<inp2:m_phrase label="la_ToolTip_Edit" escape="1"/>', edit) ); a_toolbar.AddButton( new ToolBarSeparator('sep3') ); a_toolbar.AddButton( new ToolBarButton('delete', '<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>', function() { std_delete_items('formsubs') } ) ); a_toolbar.AddButton( new ToolBarSeparator('sep2') ); a_toolbar.AddButton( new ToolBarButton( 'view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function(id) { show_viewmenu(a_toolbar,'view'); } ) ); a_toolbar.Render(); </script> </td> <inp2:m_RenderElement name="search_main_toolbar" prefix="formsubs" grid="Default"/> </tr> </tbody> </table> <inp2:m_DefineElement name="grid_email_td"> <a href="mailto:<inp2:Field field="$field" />"><inp2:Field field="$field"/></a> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_upload_td"> <inp2:m_if check="Field" name="$field" db="db"> <a href="<inp2:Field field='$field' format='full_url'/>" target="_blank"><inp2:Field field="$field"/></a> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid" PrefixSpecial="formsubs" IdField="FormSubmissionId" grid="Default"/> <script type="text/javascript"> Grids['formsubs'].SetDependantToolbarButtons( new Array('edit','delete') ); </script> -<inp2:m_include t="incs/footer"/> \ No newline at end of file +<inp2:m_include t="incs/footer"/> Index: branches/5.3.x/core/install/upgrades.sql =================================================================== --- branches/5.3.x/core/install/upgrades.sql (revision 16110) +++ branches/5.3.x/core/install/upgrades.sql (revision 16111) @@ -1,3006 +1,3008 @@ # ===== v 4.0.1 ===== ALTER TABLE EmailLog ADD EventParams TEXT NOT NULL; INSERT INTO ConfigurationAdmin VALUES ('MailFunctionHeaderSeparator', 'la_Text_smtp_server', 'la_config_MailFunctionHeaderSeparator', 'radio', NULL, '1=la_Linux,2=la_Windows', 30.08, 0, 0); INSERT INTO ConfigurationValues VALUES (0, 'MailFunctionHeaderSeparator', 1, 'In-Portal', 'in-portal:configure_general'); ALTER TABLE PersistantSessionData DROP PRIMARY KEY ; ALTER TABLE PersistantSessionData ADD INDEX ( `PortalUserId` ) ; # ===== v 4.1.0 ===== ALTER TABLE EmailMessage ADD ReplacementTags TEXT AFTER Template; ALTER TABLE Phrase CHANGE Translation Translation TEXT NOT NULL, CHANGE Module Module VARCHAR(30) NOT NULL DEFAULT 'In-Portal'; ALTER TABLE Category CHANGE Description Description TEXT, CHANGE l1_Description l1_Description TEXT, CHANGE l2_Description l2_Description TEXT, CHANGE l3_Description l3_Description TEXT, CHANGE l4_Description l4_Description TEXT, CHANGE l5_Description l5_Description TEXT, CHANGE CachedNavbar CachedNavbar text, CHANGE l1_CachedNavbar l1_CachedNavbar text, CHANGE l2_CachedNavbar l2_CachedNavbar text, CHANGE l3_CachedNavbar l3_CachedNavbar text, CHANGE l4_CachedNavbar l4_CachedNavbar text, CHANGE l5_CachedNavbar l5_CachedNavbar text, CHANGE ParentPath ParentPath TEXT NULL DEFAULT NULL, CHANGE NamedParentPath NamedParentPath TEXT NULL DEFAULT NULL; ALTER TABLE ConfigurationAdmin CHANGE ValueList ValueList TEXT; ALTER TABLE EmailQueue CHANGE `Subject` `Subject` TEXT, CHANGE toaddr toaddr TEXT, CHANGE fromaddr fromaddr TEXT; ALTER TABLE Category DROP Pop; ALTER TABLE PortalUser CHANGE CreatedOn CreatedOn INT DEFAULT NULL, CHANGE dob dob INT(11) NULL DEFAULT NULL, CHANGE PassResetTime PassResetTime INT(11) UNSIGNED NULL DEFAULT NULL, CHANGE PwRequestTime PwRequestTime INT(11) UNSIGNED NULL DEFAULT NULL, CHANGE `Password` `Password` VARCHAR(255) NULL DEFAULT 'd41d8cd98f00b204e9800998ecf8427e'; ALTER TABLE Modules CHANGE BuildDate BuildDate INT UNSIGNED NULL DEFAULT NULL, CHANGE Version Version VARCHAR(10) NOT NULL DEFAULT '0.0.0', CHANGE `Var` `Var` VARCHAR(100) NOT NULL DEFAULT ''; ALTER TABLE Language CHANGE Enabled Enabled INT(11) NOT NULL DEFAULT '1', CHANGE InputDateFormat InputDateFormat VARCHAR(50) NOT NULL DEFAULT 'm/d/Y', CHANGE InputTimeFormat InputTimeFormat VARCHAR(50) NOT NULL DEFAULT 'g:i:s A', CHANGE DecimalPoint DecimalPoint VARCHAR(10) NOT NULL DEFAULT '', CHANGE ThousandSep ThousandSep VARCHAR(10) NOT NULL DEFAULT ''; ALTER TABLE Events CHANGE FromUserId FromUserId INT(11) NOT NULL DEFAULT '-1'; ALTER TABLE StdDestinations CHANGE DestAbbr2 DestAbbr2 CHAR(2) NULL DEFAULT NULL; ALTER TABLE PermCache DROP DACL; ALTER TABLE PortalGroup CHANGE CreatedOn CreatedOn INT UNSIGNED NULL DEFAULT NULL; ALTER TABLE UserSession CHANGE SessionKey SessionKey INT UNSIGNED NULL DEFAULT NULL , CHANGE CurrentTempKey CurrentTempKey INT UNSIGNED NULL DEFAULT NULL , CHANGE PrevTempKey PrevTempKey INT UNSIGNED NULL DEFAULT NULL , CHANGE LastAccessed LastAccessed INT UNSIGNED NOT NULL DEFAULT '0', CHANGE PortalUserId PortalUserId INT(11) NOT NULL DEFAULT '-2', CHANGE Language Language INT(11) NOT NULL DEFAULT '1', CHANGE Theme Theme INT(11) NOT NULL DEFAULT '1'; CREATE TABLE Counters ( CounterId int(10) unsigned NOT NULL auto_increment, Name varchar(100) NOT NULL default '', CountQuery text, CountValue text, LastCounted int(10) unsigned default NULL, LifeTime int(10) unsigned NOT NULL default '3600', IsClone tinyint(3) unsigned NOT NULL default '0', TablesAffected text, PRIMARY KEY (CounterId), UNIQUE KEY Name (Name) ); CREATE TABLE Skins ( `SkinId` int(11) NOT NULL auto_increment, `Name` varchar(255) default NULL, `CSS` text, `Logo` varchar(255) default NULL, `Options` text, `LastCompiled` int(11) NOT NULL default '0', `IsPrimary` int(1) NOT NULL default '0', PRIMARY KEY (`SkinId`) ); INSERT INTO Skins VALUES (DEFAULT, 'Default', '/* General elements */\r\n\r\nhtml {\r\n height: 100%;\r\n}\r\n\r\nbody {\r\n font-family: verdana,arial,helvetica,sans-serif;\r\n font-size: 9pt;\r\n color: #000000;\r\n overflow-x: auto; overflow-y: auto;\r\n margin: 0px 0px 0px 0px;\r\n text-decoration: none;\r\n}\r\n\r\na {\r\n color: #006699;\r\n text-decoration: none;\r\n}\r\n\r\na:hover {\r\n color: #009ff0;\r\n text-decoration: none;\r\n}\r\n\r\nform {\r\n display: inline;\r\n}\r\n\r\nimg { border: 0px; }\r\n\r\nbody.height-100 {\r\n height: 100%;\r\n}\r\n\r\nbody.regular-body {\r\n margin: 0px 10px 5px 10px;\r\n color: #000000;\r\n background-color: @@SectionBgColor@@;\r\n}\r\n\r\nbody.edit-popup {\r\n margin: 0px 0px 0px 0px;\r\n}\r\n\r\ntable.collapsed {\r\n border-collapse: collapse;\r\n}\r\n\r\n.bordered, table.bordered, .bordered-no-bottom {\r\n border: 1px solid #000000;\r\n border-collapse: collapse;\r\n}\r\n\r\n.bordered-no-bottom {\r\n border-bottom: none;\r\n}\r\n\r\n.login-table td {\r\n padding: 1px;\r\n}\r\n\r\n.disabled {\r\n background-color: #ebebeb;\r\n}\r\n\r\n/* Head frame */\r\n.head-table tr td {\r\n background-color: @@HeadBgColor@@;\r\n color: @@HeadColor@@\r\n}\r\n\r\ntd.kx-block-header, .head-table tr td.kx-block-header{\r\n color: @@HeadBarColor@@;\r\n background-color: @@HeadBarBgColor@@;\r\n padding-left: 7px;\r\n padding-right: 7px;\r\n}\r\n\r\na.kx-header-link {\r\n text-decoration: underline;\r\n color: #FFFFFF;\r\n}\r\n\r\na.kx-header-link:hover {\r\n color: #FFCB05;\r\n text-decoration: none;\r\n}\r\n\r\n.kx-secondary-foreground {\r\n color: @@HeadBarColor@@;\r\n background-color: @@HeadBarBgColor@@;\r\n}\r\n\r\n.kx-login-button {\r\n background-color: #2D79D6;\r\n color: #FFFFFF;\r\n}\r\n\r\n/* General form button (yellow) */\r\n.button {\r\n font-size: 12px;\r\n font-weight: normal;\r\n color: #000000;\r\n background: url(@@base_url@@/proj-base/admin_templates/img/button_back.gif) #f9eeae repeat-x;\r\n text-decoration: none;\r\n}\r\n\r\n/* Disabled (grayed-out) form button */\r\n.button-disabled {\r\n font-size: 12px;\r\n font-weight: normal;\r\n color: #676767;\r\n background: url(@@base_url@@/proj-base/admin_templates/img/button_back_disabled.gif) #f9eeae repeat-x;\r\n text-decoration: none;\r\n}\r\n\r\n/* Tabs bar */\r\n\r\n.tab, .tab-active {\r\n background-color: #F0F1EB;\r\n padding: 3px 7px 2px 7px;\r\n border-top: 1px solid black;\r\n border-left: 1px solid black;\r\n border-right: 1px solid black;\r\n}\r\n\r\n.tab-active {\r\n background-color: #2D79D6;\r\n border-bottom: 1px solid #2D79D6;\r\n}\r\n\r\n.tab a {\r\n color: #00659C;\r\n font-weight: bold;\r\n}\r\n\r\n.tab-active a {\r\n color: #fff;\r\n font-weight: bold;\r\n}\r\n\r\n\r\n/* Toolbar */\r\n\r\n.toolbar {\r\n font-size: 8pt;\r\n border: 1px solid #000000;\r\n border-width: 0px 1px 1px 1px;\r\n background-color: @@ToolbarBgColor@@;\r\n border-collapse: collapse;\r\n}\r\n\r\n.toolbar td {\r\n height: 100%;\r\n}\r\n\r\n.toolbar-button, .toolbar-button-disabled, .toolbar-button-over {\r\n float: left;\r\n text-align: center;\r\n font-size: 8pt;\r\n padding: 5px 5px 5px 5px;\r\n vertical-align: middle;\r\n color: #006F99;\r\n}\r\n\r\n.toolbar-button-over {\r\n color: #000;\r\n}\r\n\r\n.toolbar-button-disabled {\r\n color: #444;\r\n}\r\n\r\n/* Scrollable Grids */\r\n\r\n\r\n/* Main Grid class */\r\n.grid-scrollable {\r\n padding: 0px;\r\n border: 1px solid black !important;\r\n border-top: none !important;\r\n}\r\n\r\n/* Div generated by js, which contains all the scrollable grid elements, affects the style of scrollable area without data (if there are too few rows) */\r\n.grid-container {\r\n background-color: #fff;\r\n}\r\n\r\n.grid-container table {\r\n border-collapse: collapse;\r\n}\r\n\r\n/* Inner div generated in each data-cell */\r\n.grid-cell-div {\r\n overflow: hidden;\r\n height: auto;\r\n}\r\n\r\n/* Main row definition */\r\n.grid-data-row td, .grid-data-row-selected td, .grid-data-row-even-selected td, .grid-data-row-mouseover td, .table-color1, .table-color2 {\r\n font-weight: normal;\r\n color: @@OddColor@@;\r\n background-color: @@OddBgColor@@;\r\n padding: 3px 5px 3px 5px;\r\n height: 30px;\r\n overflow: hidden;\r\n /* border-right: 1px solid black; */\r\n}\r\n.grid-data-row-even td, .table-color2 {\r\n background-color: @@EvenBgColor@@;\r\n color: @@EvenColor@@;\r\n}\r\n.grid-data-row td a, .grid-data-row-selected td a, .grid-data-row-mouseover td a {\r\n text-decoration: underline;\r\n}\r\n\r\n/* mouse-over rows */\r\n.grid-data-row-mouseover td {\r\n background: #FFFDF4;\r\n}\r\n\r\n/* Selected row, applies to both checkbox and data areas */\r\n.grid-data-row-selected td {\r\n background: #FEF2D6;\r\n}\r\n\r\n.grid-data-row-even-selected td {\r\n background: #FFF7E0;\r\n}\r\n\r\n/* General header cell definition */\r\n.grid-header-row td {\r\n font-weight: bold;\r\n background-color: @@ColumnTitlesBgColor@@;\r\n text-decoration: none;\r\n padding: 3px 5px 3px 5px;\r\n color: @@ColumnTitlesColor@@;\r\n border-right: none;\r\n text-align: left;\r\n vertical-align: middle !important;\r\n white-space: nowrap;\r\n /* border-right: 1px solid black; */\r\n}\r\n\r\n/* Filters row */\r\ntr.grid-header-row-0 td {\r\n background-color: @@FiltersBgColor@@;\r\n border-bottom: 1px solid black;\r\n}\r\n\r\n/* Grid Filters */\r\ntable.range-filter {\r\n width: 100%;\r\n}\r\n\r\n.range-filter td {\r\n padding: 0px 0px 2px 2px !important;\r\n border: none !important;\r\n font-size: 8pt !important;\r\n font-weight: normal !important;\r\n text-align: left;\r\n color: #000000 !important;\r\n}\r\n\r\ninput.filter, select.filter {\r\n margin-bottom: 0px;\r\n width: 85%;\r\n}\r\n\r\ninput.filter-active {\r\n background-color: #FFFF00;\r\n}\r\n\r\nselect.filter-active {\r\n background-color: #FFFF00;\r\n}\r\n\r\n/* Column titles row */\r\ntr.grid-header-row-1 td {\r\n height: 25px;\r\n font-weight: bold;\r\n background-color: @@ColumnTitlesBgColor@@;\r\n color: @@ColumnTitlesColor@@;\r\n}\r\n\r\ntr.grid-header-row-1 td a {\r\n color: @@ColumnTitlesColor@@;\r\n}\r\n\r\ntr.grid-header-row-1 td a:hover {\r\n color: #FFCC00;\r\n}\r\n\r\n\r\n.grid-footer-row td {\r\n background-color: #D7D7D7;\r\n font-weight: bold;\r\n border-right: none;\r\n padding: 3px 5px 3px 5px;\r\n}\r\n\r\ntd.grid-header-last-cell, td.grid-data-last-cell, td.grid-footer-last-cell {\r\n border-right: none !important;\r\n}\r\n\r\ntd.grid-data-col-0, td.grid-data-col-0 div {\r\n text-align: center;\r\n vertical-align: middle !important;\r\n}\r\n\r\ntr.grid-header-row-0 td.grid-header-col-0 {\r\n text-align: center;\r\n vertical-align: middle !important;\r\n}\r\n\r\ntr.grid-header-row-0 td.grid-header-col-0 div {\r\n display: table-cell;\r\n vertical-align: middle;\r\n}\r\n\r\n.grid-status-bar {\r\n border: 1px solid black;\r\n border-top: none;\r\n padding: 0px;\r\n width: 100%;\r\n border-collapse: collapse;\r\n height: 30px;\r\n}\r\n\r\n.grid-status-bar td {\r\n background-color: @@TitleBarBgColor@@;\r\n color: @@TitleBarColor@@;\r\n font-size: 11pt;\r\n font-weight: normal;\r\n padding: 2px 8px 2px 8px;\r\n}\r\n\r\n/* /Scrollable Grids */\r\n\r\n\r\n/* Forms */\r\ntable.edit-form {\r\n border: none;\r\n border-top-width: 0px;\r\n border-collapse: collapse;\r\n width: 100%;\r\n}\r\n\r\n.edit-form-odd, .edit-form-even {\r\n padding: 0px;\r\n}\r\n\r\n.subsectiontitle {\r\n font-size: 10pt;\r\n font-weight: bold;\r\n background-color: #4A92CE;\r\n color: #fff;\r\n height: 25px;\r\n border-top: 1px solid black;\r\n}\r\n\r\n.label-cell {\r\n background: #DEE7F6 url(@@base_url@@/proj-base/admin_templates/img/bgr_input_name_line.gif) no-repeat right bottom;\r\n font: 12px arial, sans-serif;\r\n padding: 4px 20px;\r\n width: 150px;\r\n}\r\n\r\n.control-mid {\r\n width: 13px;\r\n border-left: 1px solid #7A95C2;\r\n background: #fff url(@@base_url@@/proj-base/admin_templates/img/bgr_mid.gif) repeat-x left bottom;\r\n}\r\n\r\n.control-cell {\r\n font: 11px arial, sans-serif;\r\n padding: 4px 10px 5px 5px;\r\n background: #fff url(@@base_url@@/proj-base/admin_templates/img/bgr_input_line.gif) no-repeat left bottom;\r\n width: auto;\r\n vertical-align: middle;\r\n}\r\n\r\n.label-cell-filler {\r\n background: #DEE7F6 none;\r\n}\r\n.control-mid-filler {\r\n background: #fff none;\r\n border-left: 1px solid #7A95C2;\r\n}\r\n.control-cell-filler {\r\n background: #fff none;\r\n}\r\n\r\n\r\n.error-cell {\r\n background-color: #fff;\r\n color: red;\r\n}\r\n\r\n.form-warning {\r\n color: red;\r\n}\r\n\r\n.req-note {\r\n font-style: italic;\r\n color: #333;\r\n}\r\n\r\n#scroll_container table.tableborder {\r\n border-collapse: separate\r\n}\r\n\r\n\r\n/* Uploader */\r\n\r\n.uploader-main {\r\n position: absolute;\r\n display: none;\r\n z-index: 10;\r\n border: 1px solid #777;\r\n padding: 10px;\r\n width: 350px;\r\n height: 120px;\r\n overflow: hidden;\r\n background-color: #fff;\r\n}\r\n\r\n.uploader-percent {\r\n width: 100%;\r\n padding-top: 3px;\r\n text-align: center;\r\n position: relative;\r\n z-index: 20;\r\n float: left;\r\n font-weight: bold;\r\n}\r\n\r\n.uploader-left {\r\n width: 100%;\r\n border: 1px solid black;\r\n height: 20px;\r\n background: #fff url(@@base_url@@/core/admin_templates/img/progress_left.gif);\r\n}\r\n\r\n.uploader-done {\r\n width: 0%;\r\n background-color: green;\r\n height: 20px;\r\n background: #4A92CE url(@@base_url@@/core/admin_templates/img/progress_done.gif);\r\n}\r\n\r\n\r\n/* To be sorted */\r\n\r\n\r\n/* Section title, right to the big icon */\r\n.admintitle {\r\n font-size: 16pt;\r\n font-weight: bold;\r\n color: @@SectionColor@@;\r\n text-decoration: none;\r\n}\r\n\r\n/* Left sid of bluebar */\r\n.header_left_bg {\r\n background-color: @@TitleBarBgColor@@;\r\n background-image: none;\r\n padding-left: 5px;\r\n}\r\n\r\n/* Right side of bluebar */\r\n.tablenav, tablenav a {\r\n font-size: 11pt;\r\n font-weight: bold;\r\n color: @@TitleBarColor@@;\r\n\r\n text-decoration: none;\r\n background-color: @@TitleBarBgColor@@;\r\n background-image: none;\r\n}\r\n\r\n/* Section title in the bluebar * -- why ''link''? :S */\r\n.tablenav_link {\r\n font-size: 11pt;\r\n font-weight: bold;\r\n color: @@TitleBarColor@@;\r\n text-decoration: none;\r\n}\r\n\r\n/* Active page in top and bottom bluebars pagination */\r\n.current_page {\r\n font-size: 10pt;\r\n font-weight: bold;\r\n background-color: #fff;\r\n color: #2D79D6;\r\n padding: 3px 2px 3px 3px;\r\n}\r\n\r\n/* Other pages and arrows in pagination on blue */\r\n.nav_url {\r\n font-size: 10pt;\r\n font-weight: bold;\r\n color: #fff;\r\n padding: 3px 2px 3px 3px;\r\n}\r\n\r\n/* Tree */\r\n.tree-body {\r\n background-color: @@TreeBgColor@@;\r\n height: 100%\r\n}\r\n\r\n.tree_head.td, .tree_head, .tree_head:hover {\r\n font-weight: bold;\r\n font-size: 10px;\r\n color: #FFFFFF;\r\n font-family: Verdana, Arial;\r\n text-decoration: none;\r\n}\r\n\r\n.tree {\r\n padding: 0px;\r\n border: none;\r\n border-collapse: collapse;\r\n}\r\n\r\n.tree tr td {\r\n padding: 0px;\r\n margin: 0px;\r\n font-family: helvetica, arial, verdana,;\r\n font-size: 11px;\r\n white-space: nowrap;\r\n}\r\n\r\n.tree tr td a {\r\n font-size: 11px;\r\n color: @@TreeColor@@;\r\n font-family: Helvetica, Arial, Verdana;\r\n text-decoration: none;\r\n padding: 2px 0px 2px 2px;\r\n}\r\n\r\n.tree tr.highlighted td a {\r\n background-color: @@TreeHighBgColor@@;\r\n color: @@TreeHighColor@@;\r\n}\r\n\r\n.tree tr.highlighted td a:hover {\r\n color: #fff;\r\n}\r\n\r\n.tree tr td a:hover {\r\n color: #000000;\r\n}', 'just_logo.gif', 'a:20:{s:11:"HeadBgColor";a:2:{s:11:"Description";s:27:"Head frame background color";s:5:"Value";s:7:"#1961B8";}s:9:"HeadColor";a:2:{s:11:"Description";s:21:"Head frame text color";s:5:"Value";s:7:"#CCFF00";}s:14:"SectionBgColor";a:2:{s:11:"Description";s:28:"Section bar background color";s:5:"Value";s:7:"#FFFFFF";}s:12:"SectionColor";a:2:{s:11:"Description";s:22:"Section bar text color";s:5:"Value";s:7:"#2D79D6";}s:12:"HeadBarColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:14:"HeadBarBgColor";a:1:{s:5:"Value";s:7:"#1961B8";}s:13:"TitleBarColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:15:"TitleBarBgColor";a:1:{s:5:"Value";s:7:"#2D79D6";}s:14:"ToolbarBgColor";a:1:{s:5:"Value";s:7:"#F0F1EB";}s:14:"FiltersBgColor";a:1:{s:5:"Value";s:7:"#D7D7D7";}s:17:"ColumnTitlesColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:19:"ColumnTitlesBgColor";a:1:{s:5:"Value";s:7:"#999999";}s:8:"OddColor";a:1:{s:5:"Value";s:7:"#000000";}s:10:"OddBgColor";a:1:{s:5:"Value";s:7:"#F6F6F6";}s:9:"EvenColor";a:1:{s:5:"Value";s:7:"#000000";}s:11:"EvenBgColor";a:1:{s:5:"Value";s:7:"#EBEBEB";}s:9:"TreeColor";a:1:{s:5:"Value";s:7:"#006F99";}s:11:"TreeBgColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:13:"TreeHighColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:15:"TreeHighBgColor";a:1:{s:5:"Value";s:7:"#4A92CE";}}', 1178706881, 1); INSERT INTO Permissions VALUES (0, 'in-portal:skins.view', 11, 1, 1, 0), (0, 'in-portal:skins.add', 11, 1, 1, 0), (0, 'in-portal:skins.edit', 11, 1, 1, 0), (0, 'in-portal:skins.delete', 11, 1, 1, 0); # ===== v 4.1.1 ===== DROP TABLE EmailQueue; CREATE TABLE EmailQueue ( EmailQueueId int(10) unsigned NOT NULL auto_increment, ToEmail varchar(255) NOT NULL default '', `Subject` varchar(255) NOT NULL default '', MessageHeaders text, MessageBody longtext, Queued int(10) unsigned NOT NULL default '0', SendRetries int(10) unsigned NOT NULL default '0', LastSendRetry int(10) unsigned NOT NULL default '0', PRIMARY KEY (EmailQueueId), KEY LastSendRetry (LastSendRetry), KEY SendRetries (SendRetries) ); ALTER TABLE Events ADD ReplacementTags TEXT AFTER Event; # ===== v 4.2.0 ===== ALTER TABLE CustomField ADD MultiLingual TINYINT UNSIGNED NOT NULL DEFAULT '1' AFTER FieldLabel; ALTER TABLE Category ADD TreeLeft BIGINT NOT NULL AFTER ParentPath, ADD TreeRight BIGINT NOT NULL AFTER TreeLeft; ALTER TABLE Category ADD INDEX (TreeLeft); ALTER TABLE Category ADD INDEX (TreeRight); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CategoriesRebuildSerial', '0', 'In-Portal', ''); UPDATE ConfigurationAdmin SET `element_type` = 'textarea' WHERE `VariableName` IN ('Category_MetaKey', 'Category_MetaDesc'); ALTER TABLE PortalUser CHANGE FirstName FirstName VARCHAR(255) NOT NULL DEFAULT '', CHANGE LastName LastName VARCHAR(255) NOT NULL DEFAULT ''; # ===== v 4.2.1 ===== INSERT INTO ConfigurationAdmin VALUES ('UseSmallHeader', 'la_Text_Website', 'la_config_UseSmallHeader', 'checkbox', '', '', 10.21, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseSmallHeader', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('User_Default_Registration_Country', 'la_Text_General', 'la_config_DefaultRegistrationCountry', 'select', NULL , '=+,<SQL>SELECT DestName AS OptionName, DestId AS OptionValue FROM <PREFIX>StdDestinations WHERE DestParentId IS NULL Order BY OptionName</SQL>', 10.111, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'User_Default_Registration_Country', '', 'In-Portal:Users', 'in-portal:configure_users'); ALTER TABLE Category ADD SymLinkCategoryId INT UNSIGNED NULL DEFAULT NULL AFTER `Type`, ADD INDEX (SymLinkCategoryId); ALTER TABLE ConfigurationValues CHANGE VariableValue VariableValue TEXT NULL DEFAULT NULL; ALTER TABLE Language ADD AdminInterfaceLang TINYINT UNSIGNED NOT NULL AFTER PrimaryLang, ADD Priority INT NOT NULL AFTER AdminInterfaceLang; UPDATE Language SET AdminInterfaceLang = 1 WHERE PrimaryLang = 1; DELETE FROM PersistantSessionData WHERE VariableName = 'lang_columns_.'; ALTER TABLE SessionData CHANGE VariableValue VariableValue longtext NOT NULL; INSERT INTO ConfigurationAdmin VALUES ('CSVExportDelimiter', 'la_Text_CSV_Export', 'la_config_CSVExportDelimiter', 'select', NULL, '0=la_Tab,1=la_Comma,2=la_Semicolon,3=la_Space,4=la_Colon', 40.1, 0, 1); INSERT INTO ConfigurationAdmin VALUES ('CSVExportEnclosure', 'la_Text_CSV_Export', 'la_config_CSVExportEnclosure', 'radio', NULL, '0=la_Doublequotes,1=la_Quotes', 40.2, 0, 1); INSERT INTO ConfigurationAdmin VALUES ('CSVExportSeparator', 'la_Text_CSV_Export', 'la_config_CSVExportSeparator', 'radio', NULL, '0=la_Linux,1=la_Windows', 40.3, 0, 1); INSERT INTO ConfigurationAdmin VALUES ('CSVExportEncoding', 'la_Text_CSV_Export', 'la_config_CSVExportEncoding', 'radio', NULL, '0=la_Unicode,1=la_Regular', 40.4, 0, 1); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportDelimiter', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEnclosure', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportSeparator', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CSVExportEncoding', '0', 'In-Portal', 'in-portal:configure_general'); # ===== v 4.2.2 ===== INSERT INTO ConfigurationAdmin VALUES ('UseColumnFreezer', 'la_Text_Website', 'la_config_UseColumnFreezer', 'checkbox', '', '', 10.22, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseColumnFreezer', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('TrimRequiredFields', 'la_Text_Website', 'la_config_TrimRequiredFields', 'checkbox', '', '', 10.23, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'TrimRequiredFields', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('MenuFrameWidth', 'la_title_General', 'la_prompt_MenuFrameWidth', 'text', NULL, NULL, '11', '0', '0'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'MenuFrameWidth', 200, 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('DefaultSettingsUserId', 'la_title_General', 'la_prompt_DefaultUserId', 'text', NULL, NULL, '12', '0', '0'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'DefaultSettingsUserId', -1, 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('KeepSessionOnBrowserClose', 'la_title_General', 'la_prompt_KeepSessionOnBrowserClose', 'checkbox', NULL, NULL, '13', '0', '0'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'KeepSessionOnBrowserClose', 0, 'In-Portal', 'in-portal:configure_general'); ALTER TABLE PersistantSessionData ADD VariableId BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; # ===== v 4.3.0 ===== INSERT INTO ConfigurationAdmin VALUES ('u_MaxImageCount', 'la_section_ImageSettings', 'la_config_MaxImageCount', 'text', '', '', 30.01, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('u_ThumbnailImageWidth', 'la_section_ImageSettings', 'la_config_ThumbnailImageWidth', 'text', '', '', 30.02, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('u_ThumbnailImageHeight', 'la_section_ImageSettings', 'la_config_ThumbnailImageHeight', 'text', '', '', 30.03, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('u_FullImageWidth', 'la_section_ImageSettings', 'la_config_FullImageWidth', 'text', '', '', 30.04, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('u_FullImageHeight', 'la_section_ImageSettings', 'la_config_FullImageHeight', 'text', '', '', 30.05, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'u_MaxImageCount', 5, 'In-Portal:Users', 'in-portal:configure_users'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'u_ThumbnailImageWidth', 120, 'In-Portal:Users', 'in-portal:configure_users'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'u_ThumbnailImageHeight', 120, 'In-Portal:Users', 'in-portal:configure_users'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'u_FullImageWidth', 450, 'In-Portal:Users', 'in-portal:configure_users'); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'u_FullImageHeight', 450, 'In-Portal:Users', 'in-portal:configure_users'); CREATE TABLE ChangeLogs ( ChangeLogId bigint(20) NOT NULL auto_increment, PortalUserId int(11) NOT NULL default '0', SessionLogId int(11) NOT NULL default '0', `Action` tinyint(4) NOT NULL default '0', OccuredOn int(11) NOT NULL default '0', Prefix varchar(255) NOT NULL default '', ItemId bigint(20) NOT NULL default '0', Changes text NOT NULL, MasterPrefix varchar(255) NOT NULL default '', MasterId bigint(20) NOT NULL default '0', PRIMARY KEY (ChangeLogId), KEY PortalUserId (PortalUserId), KEY SessionLogId (SessionLogId), KEY `Action` (`Action`), KEY OccuredOn (OccuredOn), KEY Prefix (Prefix), KEY MasterPrefix (MasterPrefix) ); CREATE TABLE SessionLogs ( SessionLogId bigint(20) NOT NULL auto_increment, PortalUserId int(11) NOT NULL default '0', SessionId int(10) NOT NULL default '0', `Status` tinyint(4) NOT NULL default '1', SessionStart int(11) NOT NULL default '0', SessionEnd int(11) default NULL, IP varchar(15) NOT NULL default '', AffectedItems int(11) NOT NULL default '0', PRIMARY KEY (SessionLogId), KEY SessionId (SessionId), KEY `Status` (`Status`), KEY PortalUserId (PortalUserId) ); ALTER TABLE CustomField ADD INDEX (MultiLingual), ADD INDEX (DisplayOrder), ADD INDEX (OnGeneralTab), ADD INDEX (IsSystem); ALTER TABLE ConfigurationAdmin ADD INDEX (DisplayOrder), ADD INDEX (GroupDisplayOrder), ADD INDEX (Install); ALTER TABLE EmailSubscribers ADD INDEX (EmailMessageId), ADD INDEX (PortalUserId); ALTER TABLE Events ADD INDEX (`Type`), ADD INDEX (Enabled); ALTER TABLE Language ADD INDEX (Enabled), ADD INDEX (PrimaryLang), ADD INDEX (AdminInterfaceLang), ADD INDEX (Priority); ALTER TABLE Modules ADD INDEX (Loaded), ADD INDEX (LoadOrder); ALTER TABLE PhraseCache ADD INDEX (CacheDate), ADD INDEX (ThemeId), ADD INDEX (StylesheetId); ALTER TABLE PortalGroup ADD INDEX (CreatedOn); ALTER TABLE PortalUser ADD INDEX (Status), ADD INDEX (Modified), ADD INDEX (dob), ADD INDEX (IsBanned); ALTER TABLE Theme ADD INDEX (Enabled), ADD INDEX (StylesheetId), ADD INDEX (PrimaryTheme); ALTER TABLE UserGroup ADD INDEX (MembershipExpires), ADD INDEX (ExpirationReminderSent); ALTER TABLE EmailLog ADD INDEX (`timestamp`); ALTER TABLE StdDestinations ADD INDEX (DestType), ADD INDEX (DestParentId); ALTER TABLE Category ADD INDEX (Status), ADD INDEX (CreatedOn), ADD INDEX (EditorsPick); ALTER TABLE Stylesheets ADD INDEX (Enabled), ADD INDEX (LastCompiled); ALTER TABLE Counters ADD INDEX (IsClone), ADD INDEX (LifeTime), ADD INDEX (LastCounted); ALTER TABLE Skins ADD INDEX (IsPrimary), ADD INDEX (LastCompiled); INSERT INTO ConfigurationAdmin VALUES ('UseChangeLog', 'la_Text_Website', 'la_config_UseChangeLog', 'checkbox', '', '', 10.25, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseChangeLog', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('AutoRefreshIntervals', 'la_Text_Website', 'la_config_AutoRefreshIntervals', 'text', '', '', 10.26, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'AutoRefreshIntervals', '1,5,15,30,60,120,240', 'In-Portal', 'in-portal:configure_general'); DELETE FROM Cache WHERE SUBSTRING(VarName, 1, 7) = 'mod_rw_'; ALTER TABLE Category CHANGE `Status` `Status` TINYINT(4) NOT NULL DEFAULT '2'; # ===== v 4.3.1 ===== INSERT INTO ConfigurationAdmin VALUES ('RememberLastAdminTemplate', 'la_Text_General', 'la_config_RememberLastAdminTemplate', 'checkbox', '', '', 10.13, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'RememberLastAdminTemplate', '', 'In-Portal:Users', 'in-portal:configure_users'); INSERT INTO ConfigurationAdmin VALUES ('AllowSelectGroupOnFront', 'la_Text_General', 'la_config_AllowSelectGroupOnFront', 'checkbox', NULL, NULL, 10.13, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'AllowSelectGroupOnFront', '0', 'In-Portal:Users', 'in-portal:configure_users'); CREATE TABLE StatisticsCapture ( StatisticsId int(10) unsigned NOT NULL auto_increment, TemplateName varchar(255) NOT NULL default '', Hits int(10) unsigned NOT NULL default '0', LastHit int(11) NOT NULL default '0', ScriptTimeMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', ScriptTimeAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', ScriptTimeMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlTimeMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlTimeAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlTimeMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlCountMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlCountAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', SqlCountMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', PRIMARY KEY (StatisticsId), KEY TemplateName (TemplateName), KEY Hits (Hits), KEY LastHit (LastHit), KEY ScriptTimeMin (ScriptTimeMin), KEY ScriptTimeAvg (ScriptTimeAvg), KEY ScriptTimeMax (ScriptTimeMax), KEY SqlTimeMin (SqlTimeMin), KEY SqlTimeAvg (SqlTimeAvg), KEY SqlTimeMax (SqlTimeMax), KEY SqlCountMin (SqlCountMin), KEY SqlCountAvg (SqlCountAvg), KEY SqlCountMax (SqlCountMax) ); CREATE TABLE SlowSqlCapture ( CaptureId int(10) unsigned NOT NULL auto_increment, TemplateNames text, Hits int(10) unsigned NOT NULL default '0', LastHit int(11) NOT NULL default '0', SqlQuery text, TimeMin decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', TimeAvg decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', TimeMax decimal(40,20) unsigned NOT NULL default '0.00000000000000000000', QueryCrc int(11) NOT NULL default '0', PRIMARY KEY (CaptureId), KEY Hits (Hits), KEY LastHit (LastHit), KEY TimeMin (TimeMin), KEY TimeAvg (TimeAvg), KEY TimeMax (TimeMax), KEY QueryCrc (QueryCrc) ); ALTER TABLE PortalGroup ADD FrontRegistration TINYINT UNSIGNED NOT NULL; UPDATE PortalGroup SET FrontRegistration = 1 WHERE GroupId = 13; INSERT INTO ConfigurationAdmin VALUES ('ForceImageMagickResize', 'la_Text_Website', 'la_config_ForceImageMagickResize', 'checkbox', '', '', 10.28, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'ForceImageMagickResize', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('AdminSSL_URL', 'la_Text_Website', 'la_config_AdminSSL_URL', 'text', '', '', 10.091, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'AdminSSL_URL', '', 'In-Portal', 'in-portal:configure_general'); # ===== v 4.3.9 ===== ALTER TABLE CustomField CHANGE ValueList ValueList TEXT NULL DEFAULT NULL, ADD DefaultValue VARCHAR(255) NOT NULL AFTER ValueList, ADD INDEX (DefaultValue); UPDATE CustomField SET ValueList = REPLACE(ValueList, ',', '||'); CREATE TABLE Agents ( AgentId int(11) NOT NULL auto_increment, AgentName varchar(255) NOT NULL default '', AgentType tinyint(3) unsigned NOT NULL default '1', Status tinyint(3) unsigned NOT NULL default '1', Event varchar(255) NOT NULL default '', RunInterval int(10) unsigned NOT NULL default '0', RunMode tinyint(3) unsigned NOT NULL default '2', LastRunOn int(10) unsigned default NULL, LastRunStatus tinyint(3) unsigned NOT NULL default '1', NextRunOn int(11) default NULL, RunTime int(10) unsigned NOT NULL default '0', PRIMARY KEY (AgentId), KEY Status (Status), KEY RunInterval (RunInterval), KEY RunMode (RunMode), KEY AgentType (AgentType), KEY LastRunOn (LastRunOn), KEY LastRunStatus (LastRunStatus), KEY RunTime (RunTime), KEY NextRunOn (NextRunOn) ); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:agents.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:agents.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:agents.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:agents.view', 11, 1, 1, 0); INSERT INTO ConfigurationAdmin VALUES ('FilenameSpecialCharReplacement', 'la_Text_General', 'la_config_FilenameSpecialCharReplacement', 'select', NULL, '_=+_,-=+-', 10.16, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'FilenameSpecialCharReplacement', '_', 'In-Portal', 'in-portal:configure_categories'); CREATE TABLE SpellingDictionary ( SpellingDictionaryId int(11) NOT NULL auto_increment, MisspelledWord varchar(255) NOT NULL default '', SuggestedCorrection varchar(255) NOT NULL default '', PRIMARY KEY (SpellingDictionaryId), KEY MisspelledWord (MisspelledWord), KEY SuggestedCorrection (SuggestedCorrection) ); INSERT INTO ConfigurationValues VALUES(NULL, 'YahooApplicationId', '', 'In-Portal', 'in-portal:configure_categories'); INSERT INTO ConfigurationAdmin VALUES('YahooApplicationId', 'la_Text_General', 'la_config_YahooApplicationId', 'text', NULL, NULL, 10.15, 0, 0); CREATE TABLE Thesaurus ( ThesaurusId int(11) NOT NULL auto_increment, SearchTerm varchar(255) NOT NULL default '', ThesaurusTerm varchar(255) NOT NULL default '', ThesaurusType tinyint(3) unsigned NOT NULL default '0', PRIMARY KEY (ThesaurusId), KEY ThesaurusType (ThesaurusType), KEY SearchTerm (SearchTerm) ); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:ban_rulelist.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:ban_rulelist.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:ban_rulelist.add', 11, 1, 1, 0); ALTER TABLE Language ADD FilenameReplacements TEXT NULL AFTER UnitSystem; ALTER TABLE Language ADD Locale varchar(10) NOT NULL default 'en-US' AFTER FilenameReplacements; CREATE TABLE LocalesList ( LocaleId int(11) NOT NULL auto_increment, LocaleIdentifier varchar(6) NOT NULL default '', LocaleName varchar(255) NOT NULL default '', Locale varchar(20) NOT NULL default '', ScriptTag varchar(255) NOT NULL default '', ANSICodePage varchar(10) NOT NULL default '', PRIMARY KEY (LocaleId) ); INSERT INTO LocalesList VALUES (1, '0x0436', 'Afrikaans (South Africa)', 'af-ZA', 'Latn', '1252'), (2, '0x041c', 'Albanian (Albania)', 'sq-AL', 'Latn', '1252'), (3, '0x0484', 'Alsatian (France)', 'gsw-FR', '', ''), (4, '0x045e', 'Amharic (Ethiopia)', 'am-ET', '', 'UTF-8'), (5, '0x1401', 'Arabic (Algeria)', 'ar-DZ', 'Arab', '1256'), (6, '0x3c01', 'Arabic (Bahrain)', 'ar-BH', 'Arab', '1256'), (7, '0x0c01', 'Arabic (Egypt)', 'ar-EG', 'Arab', '1256'), (8, '0x0801', 'Arabic (Iraq)', 'ar-IQ', 'Arab', '1256'), (9, '0x2c01', 'Arabic (Jordan)', 'ar-JO', 'Arab', '1256'), (10, '0x3401', 'Arabic (Kuwait)', 'ar-KW', 'Arab', '1256'), (11, '0x3001', 'Arabic (Lebanon)', 'ar-LB', 'Arab', '1256'), (12, '0x1001', 'Arabic (Libya)', 'ar-LY', 'Arab', '1256'), (13, '0x1801', 'Arabic (Morocco)', 'ar-MA', 'Arab', '1256'), (14, '0x2001', 'Arabic (Oman)', 'ar-OM', 'Arab', '1256'), (15, '0x4001', 'Arabic (Qatar)', 'ar-QA', 'Arab', '1256'), (16, '0x0401', 'Arabic (Saudi Arabia)', 'ar-SA', 'Arab', '1256'), (17, '0x2801', 'Arabic (Syria)', 'ar-SY', 'Arab', '1256'), (18, '0x1c01', 'Arabic (Tunisia)', 'ar-TN', 'Arab', '1256'), (19, '0x3801', 'Arabic (U.A.E.)', 'ar-AE', 'Arab', '1256'), (20, '0x2401', 'Arabic (Yemen)', 'ar-YE', 'Arab', '1256'), (21, '0x042b', 'Armenian (Armenia)', 'hy-AM', 'Armn', 'UTF-8'), (22, '0x044d', 'Assamese (India)', 'as-IN', '', 'UTF-8'), (23, '0x082c', 'Azeri (Azerbaijan, Cyrillic)', 'az-Cyrl-AZ', 'Cyrl', '1251'), (24, '0x042c', 'Azeri (Azerbaijan, Latin)', 'az-Latn-AZ', 'Latn', '1254'), (25, '0x046d', 'Bashkir (Russia)', 'ba-RU', '', ''), (26, '0x042d', 'Basque (Basque)', 'eu-ES', 'Latn', '1252'), (27, '0x0423', 'Belarusian (Belarus)', 'be-BY', 'Cyrl', '1251'), (28, '0x0445', 'Bengali (India)', 'bn-IN', 'Beng', 'UTF-8'), (29, '0x201a', 'Bosnian (Bosnia and Herzegovina, Cyrillic)', 'bs-Cyrl-BA', 'Cyrl', '1251'), (30, '0x141a', 'Bosnian (Bosnia and Herzegovina, Latin)', 'bs-Latn-BA', 'Latn', '1250'), (31, '0x047e', 'Breton (France)', 'br-FR', 'Latn', '1252'), (32, '0x0402', 'Bulgarian (Bulgaria)', 'bg-BG', 'Cyrl', '1251'), (33, '0x0403', 'Catalan (Catalan)', 'ca-ES', 'Latn', '1252'), (34, '0x0c04', 'Chinese (Hong Kong SAR, PRC)', 'zh-HK', 'Hant', '950'), (35, '0x1404', 'Chinese (Macao SAR)', 'zh-MO', 'Hant', '950'), (36, '0x0804', 'Chinese (PRC)', 'zh-CN', 'Hans', '936'), (37, '0x1004', 'Chinese (Singapore)', 'zh-SG', 'Hans', '936'), (38, '0x0404', 'Chinese (Taiwan)', 'zh-TW', 'Hant', '950'), (39, '0x101a', 'Croatian (Bosnia and Herzegovina, Latin)', 'hr-BA', 'Latn', '1250'), (40, '0x041a', 'Croatian (Croatia)', 'hr-HR', 'Latn', '1250'), (41, '0x0405', 'Czech (Czech Republic)', 'cs-CZ', 'Latn', '1250'), (42, '0x0406', 'Danish (Denmark)', 'da-DK', 'Latn', '1252'), (43, '0x048c', 'Dari (Afghanistan)', 'prs-AF', 'Arab', '1256'), (44, '0x0465', 'Divehi (Maldives)', 'dv-MV', 'Thaa', 'UTF-8'), (45, '0x0813', 'Dutch (Belgium)', 'nl-BE', 'Latn', '1252'), (46, '0x0413', 'Dutch (Netherlands)', 'nl-NL', 'Latn', '1252'), (47, '0x0c09', 'English (Australia)', 'en-AU', 'Latn', '1252'), (48, '0x2809', 'English (Belize)', 'en-BZ', 'Latn', '1252'), (49, '0x1009', 'English (Canada)', 'en-CA', 'Latn', '1252'), (50, '0x2409', 'English (Caribbean)', 'en-029', 'Latn', '1252'), (51, '0x4009', 'English (India)', 'en-IN', 'Latn', '1252'), (52, '0x1809', 'English (Ireland)', 'en-IE', 'Latn', '1252'), (53, '0x2009', 'English (Jamaica)', 'en-JM', 'Latn', '1252'), (54, '0x4409', 'English (Malaysia)', 'en-MY', 'Latn', '1252'), (55, '0x1409', 'English (New Zealand)', 'en-NZ', 'Latn', '1252'), (56, '0x3409', 'English (Philippines)', 'en-PH', 'Latn', '1252'), (57, '0x4809', 'English (Singapore)', 'en-SG', 'Latn', '1252'), (58, '0x1c09', 'English (South Africa)', 'en-ZA', 'Latn', '1252'), (59, '0x2c09', 'English (Trinidad and Tobago)', 'en-TT', 'Latn', '1252'), (60, '0x0809', 'English (United Kingdom)', 'en-GB', 'Latn', '1252'), (61, '0x0409', 'English (United States)', 'en-US', 'Latn', '1252'), (62, '0x3009', 'English (Zimbabwe)', 'en-ZW', 'Latn', '1252'), (63, '0x0425', 'Estonian (Estonia)', 'et-EE', 'Latn', '1257'), (64, '0x0438', 'Faroese (Faroe Islands)', 'fo-FO', 'Latn', '1252'), (65, '0x0464', 'Filipino (Philippines)', 'fil-PH', 'Latn', '1252'), (66, '0x040b', 'Finnish (Finland)', 'fi-FI', 'Latn', '1252'), (67, '0x080c', 'French (Belgium)', 'fr-BE', 'Latn', '1252'), (68, '0x0c0c', 'French (Canada)', 'fr-CA', 'Latn', '1252'), (69, '0x040c', 'French (France)', 'fr-FR', 'Latn', '1252'), (70, '0x140c', 'French (Luxembourg)', 'fr-LU', 'Latn', '1252'), (71, '0x180c', 'French (Monaco)', 'fr-MC', 'Latn', '1252'), (72, '0x100c', 'French (Switzerland)', 'fr-CH', 'Latn', '1252'), (73, '0x0462', 'Frisian (Netherlands)', 'fy-NL', 'Latn', '1252'), (74, '0x0456', 'Galician (Spain)', 'gl-ES', 'Latn', '1252'), (75, '0x0437', 'Georgian (Georgia)', 'ka-GE', 'Geor', 'UTF-8'), (76, '0x0c07', 'German (Austria)', 'de-AT', 'Latn', '1252'), (77, '0x0407', 'German (Germany)', 'de-DE', 'Latn', '1252'), (78, '0x1407', 'German (Liechtenstein)', 'de-LI', 'Latn', '1252'), (79, '0x1007', 'German (Luxembourg)', 'de-LU', 'Latn', '1252'), (80, '0x0807', 'German (Switzerland)', 'de-CH', 'Latn', '1252'), (81, '0x0408', 'Greek (Greece)', 'el-GR', 'Grek', '1253'), (82, '0x046f', 'Greenlandic (Greenland)', 'kl-GL', 'Latn', '1252'), (83, '0x0447', 'Gujarati (India)', 'gu-IN', 'Gujr', 'UTF-8'), (84, '0x0468', 'Hausa (Nigeria, Latin)', 'ha-Latn-NG', 'Latn', '1252'), (85, '0x040d', 'Hebrew (Israel)', 'he-IL', 'Hebr', '1255'), (86, '0x0439', 'Hindi (India)', 'hi-IN', 'Deva', 'UTF-8'), (87, '0x040e', 'Hungarian (Hungary)', 'hu-HU', 'Latn', '1250'), (88, '0x040f', 'Icelandic (Iceland)', 'is-IS', 'Latn', '1252'), (89, '0x0470', 'Igbo (Nigeria)', 'ig-NG', '', ''), (90, '0x0421', 'Indonesian (Indonesia)', 'id-ID', 'Latn', '1252'), (91, '0x085d', 'Inuktitut (Canada, Latin)', 'iu-Latn-CA', 'Latn', '1252'), (92, '0x045d', 'Inuktitut (Canada, Syllabics)', 'iu-Cans-CA', 'Cans', 'UTF-8'), (93, '0x083c', 'Irish (Ireland)', 'ga-IE', 'Latn', '1252'), (94, '0x0410', 'Italian (Italy)', 'it-IT', 'Latn', '1252'), (95, '0x0810', 'Italian (Switzerland)', 'it-CH', 'Latn', '1252'), (96, '0x0411', 'Japanese (Japan)', 'ja-JP', 'Hani;Hira;Kana', '932'), (97, '0x044b', 'Kannada (India)', 'kn-IN', 'Knda', 'UTF-8'), (98, '0x043f', 'Kazakh (Kazakhstan)', 'kk-KZ', 'Cyrl', '1251'), (99, '0x0453', 'Khmer (Cambodia)', 'kh-KH', 'Khmr', 'UTF-8'), (100, '0x0486', 'K''iche (Guatemala)', 'qut-GT', 'Latn', '1252'), (101, '0x0487', 'Kinyarwanda (Rwanda)', 'rw-RW', 'Latn', '1252'), (102, '0x0457', 'Konkani (India)', 'kok-IN', 'Deva', 'UTF-8'), (103, '0x0812', 'Windows 95, Windows NT 4.0 only: Korean (Johab)', '', '', ''), (104, '0x0412', 'Korean (Korea)', 'ko-KR', 'Hang;Hani', '949'), (105, '0x0440', 'Kyrgyz (Kyrgyzstan)', 'ky-KG', 'Cyrl', '1251'), (106, '0x0454', 'Lao (Lao PDR)', 'lo-LA', 'Laoo', 'UTF-8'), (107, '0x0426', 'Latvian (Latvia)', 'lv-LV', 'Latn', '1257'), (108, '0x0427', 'Lithuanian (Lithuania)', 'lt-LT', 'Latn', '1257'), (109, '0x082e', 'Lower Sorbian (Germany)', 'dsb-DE', 'Latn', '1252'), (110, '0x046e', 'Luxembourgish (Luxembourg)', 'lb-LU', 'Latn', '1252'), (111, '0x042f', 'Macedonian (Macedonia, FYROM)', 'mk-MK', 'Cyrl', '1251'), (112, '0x083e', 'Malay (Brunei Darussalam)', 'ms-BN', 'Latn', '1252'), (113, '0x043e', 'Malay (Malaysia)', 'ms-MY', 'Latn', '1252'), (114, '0x044c', 'Malayalam (India)', 'ml-IN', 'Mlym', 'UTF-8'), (115, '0x043a', 'Maltese (Malta)', 'mt-MT', 'Latn', '1252'), (116, '0x0481', 'Maori (New Zealand)', 'mi-NZ', 'Latn', '1252'), (117, '0x047a', 'Mapudungun (Chile)', 'arn-CL', 'Latn', '1252'), (118, '0x044e', 'Marathi (India)', 'mr-IN', 'Deva', 'UTF-8'), (119, '0x047c', 'Mohawk (Canada)', 'moh-CA', 'Latn', '1252'), (120, '0x0450', 'Mongolian (Mongolia)', 'mn-Cyrl-MN', 'Cyrl', '1251'), (121, '0x0850', 'Mongolian (PRC)', 'mn-Mong-CN', 'Mong', 'UTF-8'), (122, '0x0850', 'Nepali (India)', 'ne-IN', '__', 'UTF-8'), (123, '0x0461', 'Nepali (Nepal)', 'ne-NP', 'Deva', 'UTF-8'), (124, '0x0414', 'Norwegian (BokmÃ¥l, Norway)', 'nb-NO', 'Latn', '1252'), (125, '0x0814', 'Norwegian (Nynorsk, Norway)', 'nn-NO', 'Latn', '1252'), (126, '0x0482', 'Occitan (France)', 'oc-FR', 'Latn', '1252'), (127, '0x0448', 'Oriya (India)', 'or-IN', 'Orya', 'UTF-8'), (128, '0x0463', 'Pashto (Afghanistan)', 'ps-AF', '', ''), (129, '0x0429', 'Persian (Iran)', 'fa-IR', 'Arab', '1256'), (130, '0x0415', 'Polish (Poland)', 'pl-PL', 'Latn', '1250'), (131, '0x0416', 'Portuguese (Brazil)', 'pt-BR', 'Latn', '1252'), (132, '0x0816', 'Portuguese (Portugal)', 'pt-PT', 'Latn', '1252'), (133, '0x0446', 'Punjabi (India)', 'pa-IN', 'Guru', 'UTF-8'), (134, '0x046b', 'Quechua (Bolivia)', 'quz-BO', 'Latn', '1252'), (135, '0x086b', 'Quechua (Ecuador)', 'quz-EC', 'Latn', '1252'), (136, '0x0c6b', 'Quechua (Peru)', 'quz-PE', 'Latn', '1252'), (137, '0x0418', 'Romanian (Romania)', 'ro-RO', 'Latn', '1250'), (138, '0x0417', 'Romansh (Switzerland)', 'rm-CH', 'Latn', '1252'), (139, '0x0419', 'Russian (Russia)', 'ru-RU', 'Cyrl', '1251'), (140, '0x243b', 'Sami (Inari, Finland)', 'smn-FI', 'Latn', '1252'), (141, '0x103b', 'Sami (Lule, Norway)', 'smj-NO', 'Latn', '1252'), (142, '0x143b', 'Sami (Lule, Sweden)', 'smj-SE', 'Latn', '1252'), (143, '0x0c3b', 'Sami (Northern, Finland)', 'se-FI', 'Latn', '1252'), (144, '0x043b', 'Sami (Northern, Norway)', 'se-NO', 'Latn', '1252'), (145, '0x083b', 'Sami (Northern, Sweden)', 'se-SE', 'Latn', '1252'), (146, '0x203b', 'Sami (Skolt, Finland)', 'sms-FI', 'Latn', '1252'), (147, '0x183b', 'Sami (Southern, Norway)', 'sma-NO', 'Latn', '1252'), (148, '0x1c3b', 'Sami (Southern, Sweden)', 'sma-SE', 'Latn', '1252'), (149, '0x044f', 'Sanskrit (India)', 'sa-IN', 'Deva', 'UTF-8'), (150, '0x1c1a', 'Serbian (Bosnia and Herzegovina, Cyrillic)', 'sr-Cyrl-BA', 'Cyrl', '1251'), (151, '0x181a', 'Serbian (Bosnia and Herzegovina, Latin)', 'sr-Latn-BA', 'Latn', '1250'), (152, '0x0c1a', 'Serbian (Serbia, Cyrillic)', 'sr-Cyrl-CS', 'Cyrl', '1251'), (153, '0x081a', 'Serbian (Serbia, Latin)', 'sr-Latn-CS', 'Latn', '1250'), (154, '0x046c', 'Sesotho sa Leboa/Northern Sotho (South Africa)', 'ns-ZA', 'Latn', '1252'), (155, '0x0432', 'Setswana/Tswana (South Africa)', 'tn-ZA', 'Latn', '1252'), (156, '0x045b', 'Sinhala (Sri Lanka)', 'si-LK', 'Sinh', 'UTF-8'), (157, '0x041b', 'Slovak (Slovakia)', 'sk-SK', 'Latn', '1250'), (158, '0x0424', 'Slovenian (Slovenia)', 'sl-SI', 'Latn', '1250'), (159, '0x2c0a', 'Spanish (Argentina)', 'es-AR', 'Latn', '1252'), (160, '0x400a', 'Spanish (Bolivia)', 'es-BO', 'Latn', '1252'), (161, '0x340a', 'Spanish (Chile)', 'es-CL', 'Latn', '1252'), (162, '0x240a', 'Spanish (Colombia)', 'es-CO', 'Latn', '1252'), (163, '0x140a', 'Spanish (Costa Rica)', 'es-CR', 'Latn', '1252'), (164, '0x1c0a', 'Spanish (Dominican Republic)', 'es-DO', 'Latn', '1252'), (165, '0x300a', 'Spanish (Ecuador)', 'es-EC', 'Latn', '1252'), (166, '0x440a', 'Spanish (El Salvador)', 'es-SV', 'Latn', '1252'), (167, '0x100a', 'Spanish (Guatemala)', 'es-GT', 'Latn', '1252'), (168, '0x480a', 'Spanish (Honduras)', 'es-HN', 'Latn', '1252'), (169, '0x080a', 'Spanish (Mexico)', 'es-MX', 'Latn', '1252'), (170, '0x4c0a', 'Spanish (Nicaragua)', 'es-NI', 'Latn', '1252'), (171, '0x180a', 'Spanish (Panama)', 'es-PA', 'Latn', '1252'), (172, '0x3c0a', 'Spanish (Paraguay)', 'es-PY', 'Latn', '1252'), (173, '0x280a', 'Spanish (Peru)', 'es-PE', 'Latn', '1252'), (174, '0x500a', 'Spanish (Puerto Rico)', 'es-PR', 'Latn', '1252'), (175, '0x0c0a', 'Spanish (Spain)', 'es-ES', 'Latn', '1252'), (176, '0x040a', 'Spanish (Spain, Traditional Sort)', 'es-ES_tradnl', 'Latn', '1252'), (177, '0x540a', 'Spanish (United States)', 'es-US', '', ''), (178, '0x380a', 'Spanish (Uruguay)', 'es-UY', 'Latn', '1252'), (179, '0x200a', 'Spanish (Venezuela)', 'es-VE', 'Latn', '1252'), (180, '0x0441', 'Swahili (Kenya)', 'sw-KE', 'Latn', '1252'), (181, '0x081d', 'Swedish (Finland)', 'sv-FI', 'Latn', '1252'), (182, '0x041d', 'Swedish (Sweden)', 'sv-SE', 'Latn', '1252'), (183, '0x045a', 'Syriac (Syria)', 'syr-SY', 'Syrc', 'UTF-8'), (184, '0x0428', 'Tajik (Tajikistan)', 'tg-Cyrl-TJ', 'Cyrl', '1251'), (185, '0x085f', 'Tamazight (Algeria, Latin)', 'tzm-Latn-DZ', 'Latn', '1252'), (186, '0x0449', 'Tamil (India)', 'ta-IN', 'Taml', 'UTF-8'), (187, '0x0444', 'Tatar (Russia)', 'tt-RU', 'Cyrl', '1251'), (188, '0x044a', 'Telugu (India)', 'te-IN', 'Telu', 'UTF-8'), (189, '0x041e', 'Thai (Thailand)', 'th-TH', 'Thai', '874'), (190, '0x0851', 'Tibetan (Bhutan)', 'bo-BT', 'Tibt', 'UTF-8'), (191, '0x0451', 'Tibetan (PRC)', 'bo-CN', 'Tibt', 'UTF-8'), (192, '0x041f', 'Turkish (Turkey)', 'tr-TR', 'Latn', '1254'), (193, '0x0442', 'Turkmen (Turkmenistan)', 'tk-TM', 'Cyrl', '1251'), (194, '0x0480', 'Uighur (PRC)', 'ug-CN', 'Arab', '1256'), (195, '0x0422', 'Ukrainian (Ukraine)', 'uk-UA', 'Cyrl', '1251'), (196, '0x042e', 'Upper Sorbian (Germany)', 'wen-DE', 'Latn', '1252'), (197, '0x0820', 'Urdu (India)', 'tr-IN', '', ''), (198, '0x0420', 'Urdu (Pakistan)', 'ur-PK', 'Arab', '1256'), (199, '0x0843', 'Uzbek (Uzbekistan, Cyrillic)', 'uz-Cyrl-UZ', 'Cyrl', '1251'), (200, '0x0443', 'Uzbek (Uzbekistan, Latin)', 'uz-Latn-UZ', 'Latn', '1254'), (201, '0x042a', 'Vietnamese (Vietnam)', 'vi-VN', 'Latn', '1258'), (202, '0x0452', 'Welsh (United Kingdom)', 'cy-GB', 'Latn', '1252'), (203, '0x0488', 'Wolof (Senegal)', 'wo-SN', 'Latn', '1252'), (204, '0x0434', 'Xhosa/isiXhosa (South Africa)', 'xh-ZA', 'Latn', '1252'), (205, '0x0485', 'Yakut (Russia)', 'sah-RU', 'Cyrl', '1251'), (206, '0x0478', 'Yi (PRC)', 'ii-CN', 'Yiii', 'UTF-8'), (207, '0x046a', 'Yoruba (Nigeria)', 'yo-NG', '', ''), (208, '0x0435', 'Zulu/isiZulu (South Africa)', 'zu-ZA', 'Latn', '1252'); UPDATE Phrase SET Module = 'Core' WHERE Module IN ('Proj-Base', 'In-Portal'); UPDATE Phrase SET Module = 'Core' WHERE Phrase IN ('la_fld_Phone', 'la_fld_City', 'la_fld_State', 'la_fld_Zip'); UPDATE Phrase SET Module = 'Core' WHERE Phrase IN ('la_col_Image', 'la_col_Username', 'la_fld_AddressLine1', 'la_fld_AddressLine2', 'la_fld_Comments', 'la_fld_Country', 'la_fld_Email', 'la_fld_Language', 'la_fld_Login', 'la_fld_MessageText', 'la_fld_MetaDescription', 'la_fld_MetaKeywords', 'la_fld_Password', 'la_fld_Username', 'la_fld_Type'); UPDATE Phrase SET Phrase = 'la_Add' WHERE Phrase = 'LA_ADD'; UPDATE Phrase SET Phrase = 'la_col_MembershipExpires' WHERE Phrase = 'la_col_membershipexpires'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_Clone' WHERE Phrase = 'la_shorttooltip_clone'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_Edit' WHERE Phrase = 'LA_SHORTTOOLTIP_EDIT'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_Export' WHERE Phrase = 'LA_SHORTTOOLTIP_EXPORT'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_GoUp' WHERE Phrase = 'LA_SHORTTOOLTIP_GOUP'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_Import' WHERE Phrase = 'LA_SHORTTOOLTIP_IMPORT'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_MoveUp' WHERE Phrase = 'la_shorttooltip_moveup'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_MoveDown' WHERE Phrase = 'la_shorttooltip_movedown'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_RescanThemes' WHERE Phrase = 'la_shorttooltip_rescanthemes'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_SetPrimary' WHERE Phrase = 'LA_SHORTTOOLTIP_SETPRIMARY'; UPDATE Phrase SET Phrase = 'la_ShortToolTip_Rebuild' WHERE Phrase = 'LA_SHORTTOOLTIP_REBUILD'; UPDATE Phrase SET Phrase = 'la_Tab_Service' WHERE Phrase = 'la_tab_service'; UPDATE Phrase SET Phrase = 'la_tab_Files' WHERE Phrase = 'la_tab_files'; UPDATE Phrase SET Phrase = 'la_ToolTipShort_Edit_Current_Category' WHERE Phrase = 'LA_TOOLTIPSHORT_EDIT_CURRENT_CATEGORY'; UPDATE Phrase SET Phrase = 'la_ToolTip_Add' WHERE Phrase = 'LA_TOOLTIP_ADD'; UPDATE Phrase SET Phrase = 'la_ToolTip_Add_Product' WHERE Phrase = 'LA_TOOLTIP_ADD_PRODUCT'; UPDATE Phrase SET Phrase = 'la_ToolTip_NewSearchConfig' WHERE Phrase = 'LA_TOOLTIP_NEWSEARCHCONFIG'; UPDATE Phrase SET Phrase = 'la_ToolTip_Prev' WHERE Phrase = 'la_tooltip_prev'; UPDATE Phrase SET Phrase = 'la_Invalid_Password' WHERE Phrase = 'la_invalid_password'; UPDATE Events SET Module = REPLACE(Module, 'In-Portal', 'Core'); DROP TABLE ImportScripts; CREATE TABLE BanRules ( RuleId int(11) NOT NULL auto_increment, RuleType tinyint(4) NOT NULL default '0', ItemField varchar(255) default NULL, ItemVerb tinyint(4) NOT NULL default '0', ItemValue varchar(255) NOT NULL default '', ItemType int(11) NOT NULL default '0', Priority int(11) NOT NULL default '0', Status tinyint(4) NOT NULL default '1', ErrorTag varchar(255) default NULL, PRIMARY KEY (RuleId), KEY Status (Status), KEY Priority (Priority), KEY ItemType (ItemType) ); CREATE TABLE CountCache ( ListType int(11) NOT NULL default '0', ItemType int(11) NOT NULL default '-1', Value int(11) NOT NULL default '0', CountCacheId int(11) NOT NULL auto_increment, LastUpdate int(11) NOT NULL default '0', ExtraId varchar(50) default NULL, TodayOnly tinyint(4) NOT NULL default '0', PRIMARY KEY (CountCacheId) ); CREATE TABLE Favorites ( FavoriteId int(11) NOT NULL auto_increment, PortalUserId int(11) NOT NULL default '0', ResourceId int(11) NOT NULL default '0', ItemTypeId int(11) NOT NULL default '0', Modified int(11) NOT NULL default '0', PRIMARY KEY (FavoriteId), UNIQUE KEY main (PortalUserId,ResourceId), KEY Modified (Modified), KEY ItemTypeId (ItemTypeId) ); CREATE TABLE Images ( ImageId int(11) NOT NULL auto_increment, ResourceId int(11) NOT NULL default '0', Url varchar(255) NOT NULL default '', Name varchar(255) NOT NULL default '', AltName VARCHAR(255) NOT NULL DEFAULT '', ImageIndex int(11) NOT NULL default '0', LocalImage tinyint(4) NOT NULL default '1', LocalPath varchar(240) NOT NULL default '', Enabled int(11) NOT NULL default '1', DefaultImg int(11) NOT NULL default '0', ThumbUrl varchar(255) default NULL, Priority int(11) NOT NULL default '0', ThumbPath varchar(255) default NULL, LocalThumb tinyint(4) NOT NULL default '1', SameImages tinyint(4) NOT NULL default '1', PRIMARY KEY (ImageId), KEY ResourceId (ResourceId), KEY Enabled (Enabled), KEY Priority (Priority) ); CREATE TABLE ItemRating ( RatingId int(11) NOT NULL auto_increment, IPAddress varchar(255) NOT NULL default '', CreatedOn INT UNSIGNED NULL DEFAULT NULL, RatingValue int(11) NOT NULL default '0', ItemId int(11) NOT NULL default '0', PRIMARY KEY (RatingId), KEY CreatedOn (CreatedOn), KEY ItemId (ItemId), KEY RatingValue (RatingValue) ); CREATE TABLE ItemReview ( ReviewId int(11) NOT NULL auto_increment, CreatedOn INT UNSIGNED NULL DEFAULT NULL, ReviewText longtext NOT NULL, Rating tinyint(3) unsigned default NULL, IPAddress varchar(255) NOT NULL default '', ItemId int(11) NOT NULL default '0', CreatedById int(11) NOT NULL default '-1', ItemType tinyint(4) NOT NULL default '0', Priority int(11) NOT NULL default '0', Status tinyint(4) NOT NULL default '2', TextFormat int(11) NOT NULL default '0', Module varchar(255) NOT NULL default '', PRIMARY KEY (ReviewId), KEY CreatedOn (CreatedOn), KEY ItemId (ItemId), KEY ItemType (ItemType), KEY Priority (Priority), KEY Status (Status) ); CREATE TABLE ItemTypes ( ItemType int(11) NOT NULL default '0', Module varchar(50) NOT NULL default '', Prefix varchar(20) NOT NULL default '', SourceTable varchar(100) NOT NULL default '', TitleField varchar(50) default NULL, CreatorField varchar(255) NOT NULL default '', PopField varchar(255) default NULL, RateField varchar(255) default NULL, LangVar varchar(255) NOT NULL default '', PrimaryItem int(11) NOT NULL default '0', EditUrl varchar(255) NOT NULL default '', ClassName varchar(40) NOT NULL default '', ItemName varchar(50) NOT NULL default '', PRIMARY KEY (ItemType), KEY Module (Module) ); CREATE TABLE ItemFiles ( FileId int(11) NOT NULL auto_increment, ResourceId int(11) unsigned NOT NULL default '0', FileName varchar(255) NOT NULL default '', FilePath varchar(255) NOT NULL default '', Size int(11) NOT NULL default '0', `Status` tinyint(4) NOT NULL default '1', CreatedOn int(11) unsigned NOT NULL default '0', CreatedById int(11) NOT NULL default '-1', MimeType varchar(255) NOT NULL default '', PRIMARY KEY (FileId), KEY ResourceId (ResourceId), KEY CreatedOn (CreatedOn), KEY Status (Status) ); CREATE TABLE Relationship ( RelationshipId int(11) NOT NULL auto_increment, SourceId int(11) default NULL, TargetId int(11) default NULL, SourceType tinyint(4) NOT NULL default '0', TargetType tinyint(4) NOT NULL default '0', Type int(11) NOT NULL default '0', Enabled int(11) NOT NULL default '1', Priority int(11) NOT NULL default '0', PRIMARY KEY (RelationshipId), KEY RelSource (SourceId), KEY RelTarget (TargetId), KEY `Type` (`Type`), KEY Enabled (Enabled), KEY Priority (Priority), KEY SourceType (SourceType), KEY TargetType (TargetType) ); CREATE TABLE SearchConfig ( TableName varchar(40) NOT NULL default '', FieldName varchar(40) NOT NULL default '', SimpleSearch tinyint(4) NOT NULL default '1', AdvancedSearch tinyint(4) NOT NULL default '1', Description varchar(255) default NULL, DisplayName varchar(80) default NULL, ModuleName VARCHAR(20) NOT NULL DEFAULT 'In-Portal', ConfigHeader varchar(255) default NULL, DisplayOrder int(11) NOT NULL default '0', SearchConfigId int(11) NOT NULL auto_increment, Priority int(11) NOT NULL default '0', FieldType varchar(20) NOT NULL default 'text', ForeignField TEXT, JoinClause TEXT, IsWhere text, IsNotWhere text, ContainsWhere text, NotContainsWhere text, CustomFieldId int(11) default NULL, PRIMARY KEY (SearchConfigId), KEY SimpleSearch (SimpleSearch), KEY AdvancedSearch (AdvancedSearch), KEY DisplayOrder (DisplayOrder), KEY Priority (Priority), KEY CustomFieldId (CustomFieldId) ); CREATE TABLE SearchLog ( SearchLogId int(11) NOT NULL auto_increment, Keyword varchar(255) NOT NULL default '', Indices bigint(20) NOT NULL default '0', SearchType int(11) NOT NULL default '0', PRIMARY KEY (SearchLogId), KEY SearchType (SearchType) ); CREATE TABLE IgnoreKeywords ( keyword varchar(20) NOT NULL default '', PRIMARY KEY (keyword) ); CREATE TABLE SpamControl ( ItemResourceId int(11) NOT NULL default '0', IPaddress varchar(20) NOT NULL default '', Expire INT UNSIGNED NULL DEFAULT NULL, PortalUserId int(11) NOT NULL default '0', DataType varchar(20) default NULL, KEY PortalUserId (PortalUserId), KEY Expire (Expire), KEY ItemResourceId (ItemResourceId) ); CREATE TABLE StatItem ( StatItemId int(11) NOT NULL auto_increment, Module varchar(20) NOT NULL default '', ValueSQL varchar(255) default NULL, ResetSQL varchar(255) default NULL, ListLabel varchar(255) NOT NULL default '', Priority int(11) NOT NULL default '0', AdminSummary int(11) NOT NULL default '0', PRIMARY KEY (StatItemId), KEY AdminSummary (AdminSummary), KEY Priority (Priority) ); CREATE TABLE SuggestMail ( email varchar(255) NOT NULL default '', sent INT UNSIGNED NULL DEFAULT NULL, PRIMARY KEY (email), KEY sent (sent) ); CREATE TABLE SysCache ( SysCacheId int(11) NOT NULL auto_increment, Name varchar(255) NOT NULL default '', Value mediumtext, Expire INT UNSIGNED NULL DEFAULT NULL, Module varchar(20) default NULL, Context varchar(255) default NULL, GroupList varchar(255) NOT NULL default '', PRIMARY KEY (SysCacheId), KEY Name (Name) ); CREATE TABLE TagLibrary ( TagId int(11) NOT NULL auto_increment, name varchar(255) NOT NULL default '', description text, example text, scope varchar(20) NOT NULL default 'global', PRIMARY KEY (TagId) ); CREATE TABLE TagAttributes ( AttrId int(11) NOT NULL auto_increment, TagId int(11) NOT NULL default '0', Name varchar(255) NOT NULL default '', AttrType varchar(20) default NULL, DefValue varchar(255) default NULL, Description TEXT, Required int(11) NOT NULL default '0', PRIMARY KEY (AttrId), KEY TagId (TagId) ); CREATE TABLE ImportScripts ( ImportId INT(11) NOT NULL auto_increment, Name VARCHAR(255) NOT NULL DEFAULT '', Description TEXT NOT NULL, Prefix VARCHAR(10) NOT NULL DEFAULT '', Module VARCHAR(50) NOT NULL DEFAULT '', ExtraFields VARCHAR(255) NOT NULL DEFAULT '', Type VARCHAR(10) NOT NULL DEFAULT '', Status TINYINT NOT NULL, PRIMARY KEY (ImportId), KEY Module (Module), KEY Status (Status) ); CREATE TABLE StylesheetSelectors ( SelectorId int(11) NOT NULL auto_increment, StylesheetId int(11) NOT NULL default '0', Name varchar(255) NOT NULL default '', SelectorName varchar(255) NOT NULL default '', SelectorData text NOT NULL, Description text NOT NULL, Type tinyint(4) NOT NULL default '0', AdvancedCSS text NOT NULL, ParentId int(11) NOT NULL default '0', PRIMARY KEY (SelectorId), KEY StylesheetId (StylesheetId), KEY ParentId (ParentId), KEY `Type` (`Type`) ); CREATE TABLE Visits ( VisitId int(11) NOT NULL auto_increment, VisitDate int(10) unsigned NOT NULL default '0', Referer varchar(255) NOT NULL default '', IPAddress varchar(15) NOT NULL default '', AffiliateId int(10) unsigned NOT NULL default '0', PortalUserId int(11) NOT NULL default '-2', PRIMARY KEY (VisitId), KEY PortalUserId (PortalUserId), KEY AffiliateId (AffiliateId), KEY VisitDate (VisitDate) ); CREATE TABLE ImportCache ( CacheId int(11) NOT NULL auto_increment, CacheName varchar(255) NOT NULL default '', VarName int(11) NOT NULL default '0', VarValue text NOT NULL, PRIMARY KEY (CacheId), KEY CacheName (CacheName), KEY VarName (VarName) ); CREATE TABLE RelatedSearches ( RelatedSearchId int(11) NOT NULL auto_increment, ResourceId int(11) NOT NULL default '0', Keyword varchar(255) NOT NULL default '', ItemType tinyint(4) NOT NULL default '0', Enabled tinyint(4) NOT NULL default '1', Priority int(11) NOT NULL default '0', PRIMARY KEY (RelatedSearchId), KEY Enabled (Enabled), KEY ItemType (ItemType), KEY ResourceId (ResourceId) ); UPDATE Modules SET Path = 'core/', Version='4.3.9' WHERE Name = 'In-Portal'; UPDATE Skins SET Logo = 'just_logo.gif' WHERE Logo = 'just_logo_1.gif'; UPDATE ConfigurationAdmin SET prompt = 'la_config_PathToWebsite' WHERE VariableName = 'Site_Path'; # ===== v 5.0.0 ===== CREATE TABLE StopWords ( StopWordId int(11) NOT NULL auto_increment, StopWord varchar(255) NOT NULL default '', PRIMARY KEY (StopWordId), KEY StopWord (StopWord) ); INSERT INTO StopWords VALUES (90, '~'),(152, 'on'),(157, 'see'),(156, 'put'),(128, 'and'),(154, 'or'),(155, 'other'),(153, 'one'),(126, 'as'),(127, 'at'),(125, 'are'),(91, '!'),(92, '@'),(93, '#'),(94, '$'),(95, '%'),(96, '^'),(97, '&'),(98, '*'),(99, '('),(100, ')'),(101, '-'),(102, '_'),(103, '='),(104, '+'),(105, '['),(106, '{'),(107, ']'),(108, '}'),(109, '\\'),(110, '|'),(111, ';'),(112, ':'),(113, ''''),(114, '"'),(115, '<'),(116, '.'),(117, '>'),(118, '/'),(119, '?'),(120, 'ah'),(121, 'all'),(122, 'also'),(123, 'am'),(124, 'an'),(151, 'of'),(150, 'note'),(149, 'not'),(148, 'no'),(147, 'may'),(146, 'its'),(145, 'it'),(144, 'is'),(143, 'into'),(142, 'in'),(141, 'had'),(140, 'has'),(139, 'have'),(138, 'from'),(137, 'form'),(136, 'for'),(135, 'end'),(134, 'each'),(133, 'can'),(132, 'by'),(130, 'be'),(131, 'but'),(129, 'any'),(158, 'that'),(159, 'the'),(160, 'their'),(161, 'there'),(162, 'these'),(163, 'they'),(164, 'this'),(165, 'through'),(166, 'thus'),(167, 'to'),(168, 'two'),(169, 'too'),(170, 'up'),(171, 'where'),(172, 'which'),(173, 'with'),(174, 'were'),(175, 'was'),(176, 'you'),(177, 'yet'); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:stop_words.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:stop_words.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:stop_words.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:stop_words.delete', 11, 1, 1, 0); INSERT INTO ConfigurationAdmin VALUES ('CheckStopWords', 'la_Text_Website', 'la_config_CheckStopWords', 'checkbox', '', '', 10.29, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CheckStopWords', '0', 'In-Portal', 'in-portal:configure_general'); ALTER TABLE SpamControl ADD INDEX (DataType); CREATE TABLE MailingLists ( MailingId int(10) unsigned NOT NULL auto_increment, PortalUserId int(11) NOT NULL default '-1', `To` longtext, ToParsed longtext, Attachments text, `Subject` varchar(255) NOT NULL, MessageText longtext, MessageHtml longtext, `Status` tinyint(3) unsigned NOT NULL default '1', EmailsQueued int(10) unsigned NOT NULL, EmailsSent int(10) unsigned NOT NULL, EmailsTotal int(10) unsigned NOT NULL, PRIMARY KEY (MailingId), KEY EmailsTotal (EmailsTotal), KEY EmailsSent (EmailsSent), KEY EmailsQueued (EmailsQueued), KEY `Status` (`Status`), KEY PortalUserId (PortalUserId) ); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mailing_lists.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mailing_lists.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mailing_lists.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mailing_lists.delete', 11, 1, 1, 0); ALTER TABLE EmailQueue ADD MailingId INT UNSIGNED NOT NULL, ADD INDEX (MailingId); INSERT INTO ConfigurationAdmin VALUES ('MailingListQueuePerStep', 'la_Text_smtp_server', 'la_config_MailingListQueuePerStep', 'text', NULL, NULL, 30.09, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'MailingListQueuePerStep', 10, 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('MailingListSendPerStep', 'la_Text_smtp_server', 'la_config_MailingListSendPerStep', 'text', NULL, NULL, 30.10, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'MailingListSendPerStep', 10, 'In-Portal', 'in-portal:configure_general'); ALTER TABLE Events ADD INDEX (Event); ALTER TABLE SearchLog ADD INDEX (Keyword); ALTER TABLE Skins ADD LogoBottom VARCHAR(255) NOT NULL AFTER Logo, ADD LogoLogin VARCHAR(255) NOT NULL AFTER LogoBottom; UPDATE Skins SET Logo = 'in-portal_logo_img.jpg', LogoBottom = 'in-portal_logo_img2.jpg', LogoLogin = 'in-portal_logo_login.gif' WHERE Logo = 'just_logo_1.gif' OR Logo = 'just_logo.gif'; INSERT INTO ConfigurationValues VALUES (DEFAULT, 'SiteNameSubTitle', '', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('SiteNameSubTitle', 'la_Text_Website', 'la_config_SiteNameSubTitle', 'text', '', '', 10.021, 0, 0); INSERT INTO ConfigurationAdmin VALUES ('ResizableFrames', 'la_Text_Website', 'la_config_ResizableFrames', 'checkbox', '', '', 10.30, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'ResizableFrames', '0', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('QuickCategoryPermissionRebuild', 'la_Text_General', 'la_config_QuickCategoryPermissionRebuild', 'checkbox', NULL , NULL , 10.12, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'QuickCategoryPermissionRebuild', '1', 'In-Portal', 'in-portal:configure_categories'); ALTER TABLE Language ADD UserDocsUrl VARCHAR(255) NOT NULL; UPDATE Category SET Template = CategoryTemplate WHERE CategoryTemplate <> ''; ALTER TABLE Category ADD ThemeId INT UNSIGNED NOT NULL, ADD INDEX (ThemeId), ADD COLUMN UseExternalUrl tinyint(3) unsigned NOT NULL default '0' AFTER Template, ADD COLUMN ExternalUrl varchar(255) NOT NULL default '' AFTER UseExternalUrl, ADD COLUMN UseMenuIconUrl tinyint(3) unsigned NOT NULL default '0' AFTER ExternalUrl, ADD COLUMN MenuIconUrl varchar(255) NOT NULL default '' AFTER UseMenuIconUrl, CHANGE MetaKeywords MetaKeywords TEXT, CHANGE MetaDescription MetaDescription TEXT, CHANGE CachedCategoryTemplate CachedTemplate VARCHAR(255) NOT NULL, DROP CategoryTemplate; UPDATE Category SET l1_MenuTitle = l1_Name WHERE l1_MenuTitle = '' OR l1_MenuTitle LIKE '_Auto: %'; UPDATE Category SET l2_MenuTitle = l2_Name WHERE l2_MenuTitle = '' OR l2_MenuTitle LIKE '_Auto: %'; UPDATE Category SET l3_MenuTitle = l3_Name WHERE l3_MenuTitle = '' OR l3_MenuTitle LIKE '_Auto: %'; UPDATE Category SET l4_MenuTitle = l4_Name WHERE l4_MenuTitle = '' OR l4_MenuTitle LIKE '_Auto: %'; UPDATE Category SET l5_MenuTitle = l5_Name WHERE l5_MenuTitle = '' OR l5_MenuTitle LIKE '_Auto: %'; UPDATE Category SET Template = '/platform/designs/general' WHERE Template = '/in-edit/designs/general'; UPDATE Category SET CachedTemplate = '/platform/designs/general' WHERE CachedTemplate = '/in-edit/designs/general'; UPDATE Category SET CachedTemplate = Template WHERE Template <> ''; CREATE TABLE PageContent ( PageContentId int(11) NOT NULL auto_increment, ContentNum int(11) NOT NULL default '0', PageId int(11) NOT NULL default '0', l1_Content text, l2_Content text, l3_Content text, l4_Content text, l5_Content text, l1_Translated tinyint(4) NOT NULL default '0', l2_Translated tinyint(4) NOT NULL default '0', l3_Translated tinyint(4) NOT NULL default '0', l4_Translated tinyint(4) NOT NULL default '0', l5_Translated tinyint(4) NOT NULL default '0', PRIMARY KEY (PageContentId), KEY ContentNum (ContentNum,PageId) ); CREATE TABLE FormFields ( FormFieldId int(11) NOT NULL auto_increment, FormId int(11) NOT NULL default '0', Type int(11) NOT NULL default '0', FieldName varchar(255) NOT NULL default '', FieldLabel varchar(255) default NULL, Heading varchar(255) default NULL, Prompt varchar(255) default NULL, ElementType varchar(50) NOT NULL default '', ValueList varchar(255) default NULL, Priority int(11) NOT NULL default '0', IsSystem tinyint(3) unsigned NOT NULL default '0', Required tinyint(1) NOT NULL default '0', DisplayInGrid tinyint(1) NOT NULL default '1', DefaultValue text NOT NULL, Validation TINYINT NOT NULL DEFAULT '0', PRIMARY KEY (FormFieldId), KEY `Type` (`Type`), KEY FormId (FormId), KEY Priority (Priority), KEY IsSystem (IsSystem), KEY DisplayInGrid (DisplayInGrid) ); CREATE TABLE FormSubmissions ( FormSubmissionId int(11) NOT NULL auto_increment, FormId int(11) NOT NULL default '0', SubmissionTime int(11) NOT NULL default '0', PRIMARY KEY (FormSubmissionId), KEY FormId (FormId), KEY SubmissionTime (SubmissionTime) ); CREATE TABLE Forms ( FormId int(11) NOT NULL auto_increment, Title VARCHAR(255) NOT NULL DEFAULT '', Description text, PRIMARY KEY (FormId) ); UPDATE Events SET Module = 'Core:Category', Description = 'la_event_FormSubmitted' WHERE Event = 'FORM.SUBMITTED'; DELETE FROM PersistantSessionData WHERE VariableName LIKE '%img%'; UPDATE Modules SET TemplatePath = Path WHERE TemplatePath <> ''; UPDATE ConfigurationValues SET VariableValue = '/platform/designs/general' WHERE VariableName = 'cms_DefaultDesign'; UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_categories' WHERE VariableName = 'cms_DefaultDesign'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.15 WHERE VariableName = 'cms_DefaultDesign'; UPDATE Phrase SET Phrase = 'la_Regular' WHERE Phrase = 'la_regular'; UPDATE Phrase SET Module = 'Core' WHERE Phrase IN ('la_Hide', 'la_Show', 'la_fld_Requied', 'la_col_Modified', 'la_col_Referer', 'la_Regular'); UPDATE Phrase SET Phrase = 'la_title_Editing_E-mail' WHERE Phrase = 'la_title_editing_e-mail'; ALTER TABLE Phrase ADD UNIQUE (LanguageId, Phrase); ALTER TABLE CustomField ADD IsRequired tinyint(3) unsigned NOT NULL default '0'; DELETE FROM Permissions WHERE (Permission LIKE 'proj-cms:structure%') OR (Permission LIKE 'proj-cms:submissions%') OR (Permission LIKE 'proj-base:users%') OR (Permission LIKE 'proj-base:system_variables%') OR (Permission LIKE 'proj-base:email_settings%') OR (Permission LIKE 'proj-base:other_settings%') OR (Permission LIKE 'proj-base:sysconfig%'); UPDATE Permissions SET Permission = REPLACE(Permission, 'proj-cms:browse', 'in-portal:browse_site'); UPDATE Permissions SET Permission = REPLACE(Permission, 'proj-cms:', 'in-portal:'); UPDATE Permissions SET Permission = REPLACE(Permission, 'proj-base:', 'in-portal:'); ALTER TABLE CategoryItems ADD INDEX (ItemResourceId); ALTER TABLE CategoryItems DROP INDEX Filename; ALTER TABLE CategoryItems ADD INDEX Filename(Filename); DROP TABLE Pages; DELETE FROM PermissionConfig WHERE PermissionName LIKE 'PAGE.%'; DELETE FROM Permissions WHERE Permission LIKE 'PAGE.%'; DELETE FROM SearchConfig WHERE TableName = 'Pages'; DELETE FROM ConfigurationAdmin WHERE VariableName LIKE '%_pages'; DELETE FROM ConfigurationValues WHERE VariableName LIKE '%_pages'; DELETE FROM ConfigurationAdmin WHERE VariableName LIKE 'PerPage_Pages%'; DELETE FROM ConfigurationValues WHERE VariableName LIKE 'PerPage_Pages%'; INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:website_setting_folder.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_setting_folder.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_advanced.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_advanced.edit', 11, 1, 1, 0); #INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spelling_dictionary.delete', 11, 1, 1, 0); #INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spelling_dictionary.edit', 11, 1, 1, 0); #INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spelling_dictionary.add', 11, 1, 1, 0); #INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spelling_dictionary.view', 11, 1, 1, 0); UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_general' WHERE ModuleOwner = 'Proj-Base' AND Section IN ('proj-base:system_variables', 'proj-base:email_settings'); UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_advanced' WHERE ModuleOwner = 'Proj-Base' AND Section IN ('proj-base:other_settings', 'proj-base:sysconfig'); UPDATE ConfigurationAdmin SET heading = 'la_Text_General' WHERE VariableName IN ('AdvancedUserManagement', 'RememberLastAdminTemplate', 'DefaultSettingsUserId'); UPDATE ConfigurationAdmin SET DisplayOrder = 10.011 WHERE VariableName = 'AdvancedUserManagement'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.14 WHERE VariableName = 'RememberLastAdminTemplate'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.15 WHERE VariableName = 'DefaultSettingsUserId'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.13 WHERE VariableName = 'FilenameSpecialCharReplacement'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.14 WHERE VariableName = 'YahooApplicationId'; UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsMailling', prompt = 'la_prompt_AdminMailFrom', ValueList = 'size="40"', DisplayOrder = 30.07 WHERE VariableName = 'Smtp_AdminMailFrom'; UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsWebsite' WHERE VariableName IN ('Site_Path','SiteNameSubTitle','UseModRewrite','Config_Server_Time','Config_Site_Time','ErrorTemplate','NoPermissionTemplate','UsePageHitCounter','ForceImageMagickResize','CheckStopWords','Site_Name'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsSession' WHERE VariableName IN ('CookieSessions','SessionCookieName','SessionTimeout','KeepSessionOnBrowserClose','SessionReferrerCheck','UseJSRedirect'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsSSL' WHERE VariableName IN ('SSL_URL','AdminSSL_URL','Require_SSL','Require_AdminSSL','Force_HTTP_When_SSL_Not_Required','UseModRewriteWithSSL'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsAdmin' WHERE VariableName IN ('UseToolbarLabels','UseSmallHeader','UseColumnFreezer','UsePopups','UseDoubleSorting','MenuFrameWidth','ResizableFrames','AutoRefreshIntervals'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsMailling' WHERE VariableName IN ('Smtp_Server','Smtp_Port','Smtp_Authenticate','Smtp_User','Smtp_Pass','Smtp_DefaultHeaders','MailFunctionHeaderSeparator','MailingListQueuePerStep','MailingListSendPerStep'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsSystem' WHERE VariableName IN ('UseOutputCompression','OutputCompressionLevel','TrimRequiredFields','UseCronForRegularEvent','UseChangeLog','Backup_Path','SystemTagCache','SocketBlockingMode'); UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsCSVExport' WHERE VariableName IN ('CSVExportDelimiter','CSVExportEnclosure','CSVExportSeparator','CSVExportEncoding'); UPDATE ConfigurationAdmin SET DisplayOrder = 10.01 WHERE VariableName = 'Site_Path'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.02 WHERE VariableName = 'SiteNameSubTitle'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.03 WHERE VariableName = 'UseModRewrite'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.04 WHERE VariableName = 'Config_Server_Time'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.05 WHERE VariableName = 'Config_Site_Time'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.06 WHERE VariableName = 'ErrorTemplate'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.07 WHERE VariableName = 'NoPermissionTemplate'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.08 WHERE VariableName = 'UsePageHitCounter'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.09 WHERE VariableName = 'ForceImageMagickResize'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.10 WHERE VariableName = 'CheckStopWords'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.01 WHERE VariableName = 'CookieSessions'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.02 WHERE VariableName = 'SessionCookieName'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.03 WHERE VariableName = 'SessionTimeout'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.04 WHERE VariableName = 'KeepSessionOnBrowserClose'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.05 WHERE VariableName = 'SessionReferrerCheck'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.06 WHERE VariableName = 'UseJSRedirect'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.01 WHERE VariableName = 'SSL_URL'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.02 WHERE VariableName = 'AdminSSL_URL'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.03 WHERE VariableName = 'Require_SSL'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.04 WHERE VariableName = 'Require_AdminSSL'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.05 WHERE VariableName = 'Force_HTTP_When_SSL_Not_Required'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.06 WHERE VariableName = 'UseModRewriteWithSSL'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.01 WHERE VariableName = 'UseToolbarLabels'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.02 WHERE VariableName = 'UseSmallHeader'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.03 WHERE VariableName = 'UseColumnFreezer'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.04 WHERE VariableName = 'UsePopups'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.05 WHERE VariableName = 'UseDoubleSorting'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.06 WHERE VariableName = 'MenuFrameWidth'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.07 WHERE VariableName = 'ResizableFrames'; UPDATE ConfigurationAdmin SET DisplayOrder = 40.08 WHERE VariableName = 'AutoRefreshIntervals'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.01 WHERE VariableName = 'Smtp_Server'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.02 WHERE VariableName = 'Smtp_Port'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.03 WHERE VariableName = 'Smtp_Authenticate'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.04 WHERE VariableName = 'Smtp_User'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.05 WHERE VariableName = 'Smtp_Pass'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.06 WHERE VariableName = 'Smtp_DefaultHeaders'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.07 WHERE VariableName = 'MailFunctionHeaderSeparator'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.08 WHERE VariableName = 'MailingListQueuePerStep'; UPDATE ConfigurationAdmin SET DisplayOrder = 50.09 WHERE VariableName = 'MailingListSendPerStep'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.01 WHERE VariableName = 'UseOutputCompression'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.02 WHERE VariableName = 'OutputCompressionLevel'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.03 WHERE VariableName = 'TrimRequiredFields'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.04 WHERE VariableName = 'UseCronForRegularEvent'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.05 WHERE VariableName = 'UseChangeLog'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.06 WHERE VariableName = 'Backup_Path'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.07 WHERE VariableName = 'SystemTagCache'; UPDATE ConfigurationAdmin SET DisplayOrder = 60.08 WHERE VariableName = 'SocketBlockingMode'; UPDATE ConfigurationAdmin SET DisplayOrder = 70.01 WHERE VariableName = 'CSVExportDelimiter'; UPDATE ConfigurationAdmin SET DisplayOrder = 70.02 WHERE VariableName = 'CSVExportEnclosure'; UPDATE ConfigurationAdmin SET DisplayOrder = 70.03 WHERE VariableName = 'CSVExportSeparator'; UPDATE ConfigurationAdmin SET DisplayOrder = 70.04 WHERE VariableName = 'CSVExportEncoding'; UPDATE Phrase SET Phrase = 'la_section_SettingsWebsite' WHERE Phrase = 'la_Text_Website'; UPDATE Phrase SET Phrase = 'la_section_SettingsMailling' WHERE Phrase = 'la_Text_smtp_server'; UPDATE Phrase SET Phrase = 'la_section_SettingsCSVExport' WHERE Phrase = 'la_Text_CSV_Export'; DELETE FROM Phrase WHERE Phrase IN ( 'la_Text_BackupPath', 'la_config_AllowManualFilenames', 'la_fld_cat_MenuLink', 'la_fld_UseCategoryTitle', 'la_In-Edit', 'la_ItemTab_Pages', 'la_Text_Pages', 'la_title_Pages', 'la_title_Page_Categories', 'lu_Pages', 'lu_page_HtmlTitle', 'lu_page_OnPageTitle', 'la_tab_AllPages', 'la_title_AllPages', 'la_title_ContentManagement', 'la_title_ContentManagment', 'lu_ViewSubPages', 'la_CMS_FormSubmitted' ); DELETE FROM Phrase WHERE (Phrase LIKE 'la_Description_In-Edit%') OR (Phrase LIKE 'la_Pages_PerPage%') OR (Phrase LIKE 'lu_PermName_Page.%'); UPDATE ConfigurationValues SET VariableValue = 1, ModuleOwner = 'In-Portal:Users', Section = 'in-portal:configure_users' WHERE VariableName = 'RememberLastAdminTemplate'; UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal:Users', Section = 'in-portal:configure_users' WHERE VariableName IN ('AdvancedUserManagement', 'DefaultSettingsUserId'); INSERT INTO ConfigurationAdmin VALUES ('Search_MinKeyword_Length', 'la_Text_General', 'la_config_Search_MinKeyword_Length', 'text', NULL, NULL, 10.19, 0, 0); UPDATE ConfigurationValues SET Section = 'in-portal:configure_categories' WHERE VariableName = 'Search_MinKeyword_Length'; UPDATE ConfigurationAdmin SET ValueList = '=+,<SQL>SELECT DestName AS OptionName, DestId AS OptionValue FROM <PREFIX>StdDestinations WHERE COALESCE(DestParentId, 0) = 0 ORDER BY OptionName</SQL>' WHERE VariableName = 'User_Default_Registration_Country'; UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_advanced' WHERE VariableName IN ( 'Site_Path', 'SiteNameSubTitle', 'CookieSessions', 'SessionCookieName', 'SessionTimeout', 'SessionReferrerCheck', 'SystemTagCache', 'SocketBlockingMode', 'SSL_URL', 'AdminSSL_URL', 'Require_SSL', 'Force_HTTP_When_SSL_Not_Required', 'UseModRewrite', 'UseModRewriteWithSSL', 'UseJSRedirect', 'UseCronForRegularEvent', 'ErrorTemplate', 'NoPermissionTemplate', 'UseOutputCompression', 'OutputCompressionLevel', 'UseToolbarLabels', 'UseSmallHeader', 'UseColumnFreezer', 'TrimRequiredFields', 'UsePageHitCounter', 'UseChangeLog', 'AutoRefreshIntervals', 'KeepSessionOnBrowserClose', 'ForceImageMagickResize', 'CheckStopWords', 'ResizableFrames', 'Config_Server_Time', 'Config_Site_Time', 'Smtp_Server', 'Smtp_Port', 'Smtp_Authenticate', 'Smtp_User', 'Smtp_Pass', 'Smtp_DefaultHeaders', 'MailFunctionHeaderSeparator', 'MailingListQueuePerStep', 'MailingListSendPerStep', 'Backup_Path', 'CSVExportDelimiter', 'CSVExportEnclosure', 'CSVExportSeparator', 'CSVExportEncoding' ); DELETE FROM ConfigurationValues WHERE VariableName IN ( 'Columns_Category', 'Perpage_Archive', 'debug', 'Perpage_User', 'Perpage_LangEmail', 'Default_FromAddr', 'email_replyto', 'email_footer', 'Default_Theme', 'Default_Language', 'User_SortField', 'User_SortOrder', 'Suggest_MinInterval', 'SubCat_ListCount', 'Timeout_Rating', 'Perpage_Relations', 'Group_SortField', 'Group_SortOrder', 'Default_FromName', 'Relation_LV_Sortfield', 'ampm_time', 'Perpage_Template', 'Perpage_Phrase', 'Perpage_Sessionlist', 'Perpage_Items', 'GuestSessions', 'Perpage_Email', 'LinksValidation_LV_Sortfield', 'CustomConfig_LV_Sortfield', 'Event_LV_SortField', 'Theme_LV_SortField', 'Template_LV_SortField', 'Lang_LV_SortField', 'Phrase_LV_SortField', 'LangEmail_LV_SortField', 'CustomData_LV_SortField', 'Summary_SortField', 'Session_SortField', 'SearchLog_SortField', 'Perpage_StatItem', 'Perpage_Groups', 'Perpage_Event', 'Perpage_BanRules', 'Perpage_SearchLog', 'Perpage_LV_lang', 'Perpage_LV_Themes', 'Perpage_LV_Catlist', 'Perpage_Reviews', 'Perpage_Modules', 'Perpage_Grouplist', 'Perpage_Images', 'EmailsL_SortField', 'Perpage_EmailsL', 'Perpage_CustomData', 'Perpage_Review', 'SearchRel_DefaultIncrease', 'SearchRel_DefaultKeyword', 'SearchRel_DefaultPop', 'SearchRel_DefaultRating', 'Category_Highlight_OpenTag', 'Category_Highlight_CloseTag', 'DomainSelect', 'MetaKeywords', 'MetaDescription', 'Config_Name', 'Config_Company', 'Config_Reg_Number', 'Config_Website_Name', 'Config_Web_Address', 'Smtp_SendHTML', 'ProjCMSAllowManualFilenames' ); DELETE FROM ConfigurationAdmin WHERE VariableName IN ('Domain_Detect', 'Server_Name', 'ProjCMSAllowManualFilenames'); DROP TABLE SuggestMail; ALTER TABLE ThemeFiles ADD FileMetaInfo TEXT NULL; UPDATE SearchConfig SET SimpleSearch = 0 WHERE FieldType NOT IN ('text', 'range') AND SimpleSearch = 1; DELETE FROM PersistantSessionData WHERE VariableName IN ('c_columns_.', 'c.showall_columns_.', 'emailevents_columns_.', 'emailmessages_columns_.'); INSERT INTO ConfigurationAdmin VALUES ('DebugOnlyFormConfigurator', 'la_section_SettingsAdmin', 'la_config_DebugOnlyFormConfigurator', 'checkbox', '', '', 40.09, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'DebugOnlyFormConfigurator', '0', 'In-Portal', 'in-portal:configure_advanced'); CREATE TABLE Semaphores ( SemaphoreId int(11) NOT NULL auto_increment, SessionKey int(10) unsigned NOT NULL, Timestamp int(10) unsigned NOT NULL, MainPrefix varchar(255) NOT NULL, PRIMARY KEY (SemaphoreId), KEY SessionKey (SessionKey), KEY Timestamp (Timestamp), KEY MainPrefix (MainPrefix) ); ALTER TABLE Language ADD IconDisabledURL VARCHAR(255) NULL DEFAULT NULL AFTER IconURL; UPDATE Phrase SET Translation = REPLACE(Translation, 'category', 'section') WHERE (Phrase IN ( 'la_confirm_maintenance', 'la_error_move_subcategory', 'la_error_RootCategoriesDelete', 'la_error_unknown_category', 'la_fld_IsBaseCategory', 'la_nextcategory', 'la_prevcategory', 'la_prompt_max_import_category_levels', 'la_prompt_root_name', 'la_SeparatedCategoryPath', 'la_title_category_select' ) OR Phrase LIKE 'la_Description_%') AND (PhraseType = 1); UPDATE Phrase SET Translation = REPLACE(Translation, 'Category', 'Section') WHERE PhraseType = 1; UPDATE Phrase SET Translation = REPLACE(Translation, 'categories', 'sections') WHERE (Phrase IN ( 'la_category_perpage_prompt', 'la_category_showpick_prompt', 'la_category_sortfield_prompt', 'la_Description_in-portal:advanced_view', 'la_Description_in-portal:browse', 'la_Description_in-portal:site', 'la_error_copy_subcategory', 'la_Msg_PropagateCategoryStatus', 'la_Text_DataType_1' )) AND (PhraseType = 1); UPDATE Phrase SET Translation = REPLACE(Translation, 'Categories', 'Sections') WHERE PhraseType = 1; UPDATE Phrase SET Translation = REPLACE(Translation, 'Page', 'Section') WHERE (Phrase IN ('la_col_PageTitle', 'la_col_System', 'la_fld_IsIndex', 'la_fld_PageTitle', 'la_section_Page')) AND (PhraseType = 1); DELETE FROM Phrase WHERE Phrase IN ('la_title_Adding_Page', 'la_title_Editing_Page', 'la_title_New_Page', 'la_fld_PageId'); INSERT INTO ConfigurationAdmin VALUES ('UseModalWindows', 'la_section_SettingsAdmin', 'la_config_UseModalWindows', 'checkbox', '', '', 40.10, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseModalWindows', '1', 'In-Portal', 'in-portal:configure_advanced'); UPDATE Language SET UserDocsUrl = 'http://docs.in-portal.org/eng/index.php'; DELETE FROM Modules WHERE Name = 'Proj-Base'; DELETE FROM Phrase WHERE Phrase IN ('la_fld_ImageId', 'la_fld_RelationshipId', 'la_fld_ReviewId', 'la_prompt_CensorhipId', 'my_account_title', 'Next Theme', 'Previous Theme', 'test 1', 'la_article_reviewed', 'la_configerror_review', 'la_link_reviewed', 'la_Prompt_ReviewedBy', 'la_prompt_ReviewId', 'la_prompt_ReviewText', 'la_reviewer', 'la_review_added', 'la_review_alreadyreviewed', 'la_review_error', 'la_tab_Editing_Review', 'la_tab_Review', 'la_ToolTip_New_Review', 'la_topic_reviewed', 'lu_add_review', 'lu_article_reviews', 'lu_ferror_review_duplicate', 'lu_link_addreview_confirm_pending_text', 'lu_link_reviews', 'lu_link_review_confirm', 'lu_link_review_confirm_pending', 'lu_link_addreview_confirm_text', 'lu_news_addreview_confirm_text', 'lu_news_addreview_confirm__pending_text', 'lu_news_review_confirm', 'lu_news_review_confirm_pending', 'lu_prompt_review', 'lu_reviews_updated', 'lu_review_access_denied', 'lu_review_article', 'lu_review_link', 'lu_review_news', 'lu_review_this_article', 'lu_fld_Review', 'lu_product_reviews', 'lu_ReviewProduct', ' lu_resetpw_confirm_text', 'lu_resetpw_confirm_text'); UPDATE Modules SET Version = '5.0.0', Loaded = 1 WHERE Name = 'In-Portal'; # ===== v 5.0.1 ===== UPDATE ConfigurationAdmin SET ValueList = '1=la_opt_UserInstantRegistration,2=la_opt_UserNotAllowedRegistration,3=la_opt_UserUponApprovalRegistration,4=la_opt_UserEmailActivation' WHERE VariableName = 'User_Allow_New'; UPDATE ConfigurationValues SET VariableValue = '1' WHERE VariableName = 'ResizableFrames'; UPDATE Phrase SET Translation = REPLACE(Translation, 'Page', 'Section') WHERE (Phrase IN ('la_col_PageTitle', 'la_col_System', 'la_fld_IsIndex', 'la_fld_PageTitle', 'la_section_Page')) AND (PhraseType = 1); DELETE FROM Phrase WHERE Phrase IN ('la_Tab', 'la_Colon', 'la_Semicolon', 'la_Space', 'la_Colon', 'la_User_Instant', 'la_User_Not_Allowed', 'la_User_Upon_Approval', 'lu_title_PrivacyPolicy'); UPDATE ConfigurationAdmin SET ValueList = '0=la_opt_Tab,1=la_opt_Comma,2=la_opt_Semicolon,3=la_opt_Space,4=la_opt_Colon' WHERE VariableName = 'CSVExportDelimiter'; UPDATE ConfigurationAdmin SET ValueList = '0=lu_opt_QueryString,1=lu_opt_Cookies,2=lu_opt_AutoDetect' WHERE VariableName = 'CookieSessions'; UPDATE ConfigurationAdmin SET ValueList = 'Name=la_opt_Title,Description=la_opt_Description,CreatedOn=la_opt_CreatedOn,EditorsPick=la_opt_EditorsPick,<SQL>SELECT Prompt AS OptionName, CONCAT("cust_", FieldName) AS OptionValue FROM <PREFIX>CustomField WHERE (Type = 1) AND (IsSystem = 0)</SQL>' WHERE VariableName = 'Category_Sortfield'; UPDATE ConfigurationAdmin SET ValueList = 'Name=la_opt_Title,Description=la_opt_Description,CreatedOn=la_opt_CreatedOn,EditorsPick=la_opt_EditorsPick,<SQL>SELECT Prompt AS OptionName, CONCAT("cust_", FieldName) AS OptionValue FROM <PREFIX>CustomField WHERE (Type = 1) AND (IsSystem = 0)</SQL>' WHERE VariableName = 'Category_Sortfield2'; UPDATE Category SET Template = '#inherit#' WHERE COALESCE(Template, '') = ''; ALTER TABLE Category CHANGE Template Template VARCHAR(255) NOT NULL DEFAULT '#inherit#'; UPDATE Phrase SET Phrase = 'la_config_DefaultDesignTemplate' WHERE Phrase = 'la_prompt_DefaultDesignTemplate'; UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsWebsite', prompt = 'la_config_DefaultDesignTemplate', DisplayOrder = 10.06 WHERE VariableName = 'cms_DefaultDesign'; UPDATE ConfigurationValues SET Section = 'in-portal:configure_advanced' WHERE VariableName = 'cms_DefaultDesign'; UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName IN ('ErrorTemplate', 'NoPermissionTemplate'); UPDATE ConfigurationAdmin SET DisplayOrder = 10.15 WHERE VariableName = 'Search_MinKeyword_Length'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.01 WHERE VariableName = 'Site_Name'; UPDATE ConfigurationAdmin SET DisplayOrder = 20.01 WHERE VariableName = 'FirstDayOfWeek'; UPDATE ConfigurationAdmin SET DisplayOrder = 30.01 WHERE VariableName = 'Smtp_AdminMailFrom'; UPDATE ConfigurationAdmin SET heading = 'la_Text_Date_Time_Settings', DisplayOrder = DisplayOrder + 9.98 WHERE VariableName IN ('Config_Server_Time', 'Config_Site_Time'); UPDATE ConfigurationValues SET Section = 'in-portal:configure_general' WHERE VariableName IN ('Config_Server_Time', 'Config_Site_Time'); UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder - 0.02 WHERE VariableName IN ('cms_DefaultDesign', 'ErrorTemplate', 'NoPermissionTemplate', 'UsePageHitCounter', 'ForceImageMagickResize', 'CheckStopWords'); UPDATE ConfigurationAdmin SET DisplayOrder = 40.01 WHERE VariableName = 'SessionTimeout'; UPDATE ConfigurationValues SET Section = 'in-portal:configure_general' WHERE VariableName = 'SessionTimeout'; UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder - 0.01 WHERE VariableName IN ('KeepSessionOnBrowserClose', 'SessionReferrerCheck', 'UseJSRedirect'); ALTER TABLE Events ADD FrontEndOnly TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER Enabled, ADD INDEX (FrontEndOnly); UPDATE Events SET FrontEndOnly = 1 WHERE Enabled = 2; UPDATE Events SET Enabled = 1 WHERE Enabled = 2; ALTER TABLE Events CHANGE FromUserId FromUserId INT(11) NULL DEFAULT NULL; UPDATE Events SET FromUserId = NULL WHERE FromUserId = 0; DELETE FROM ConfigurationAdmin WHERE VariableName = 'SiteNameSubTitle'; DELETE FROM ConfigurationValues WHERE VariableName = 'SiteNameSubTitle'; UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder - 0.01 WHERE VariableName IN ('UseModRewrite', 'cms_DefaultDesign', 'ErrorTemplate' 'NoPermissionTemplate', 'UsePageHitCounter', 'ForceImageMagickResize', 'CheckStopWords'); ALTER TABLE ConfigurationAdmin CHANGE validation Validation TEXT NULL DEFAULT NULL; UPDATE ConfigurationAdmin SET Validation = 'a:3:{s:4:"type";s:3:"int";s:13:"min_value_inc";i:1;s:8:"required";i:1;}' WHERE VariableName = 'SessionTimeout'; INSERT INTO ConfigurationAdmin VALUES ('AdminConsoleInterface', 'la_section_SettingsAdmin', 'la_config_AdminConsoleInterface', 'select', '', 'simple=+simple,advanced=+advanced,custom=+custom', 50.01, 0, 1); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'AdminConsoleInterface', 'simple', 'In-Portal', 'in-portal:configure_general'); INSERT INTO ConfigurationAdmin VALUES ('AllowAdminConsoleInterfaceChange', 'la_section_SettingsAdmin', 'la_config_AllowAdminConsoleInterfaceChange', 'checkbox', NULL , NULL , 40.01, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'AllowAdminConsoleInterfaceChange', '1', 'In-Portal', 'in-portal:configure_advanced'); UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName IN ('UseToolbarLabels', 'UseSmallHeader', 'UseColumnFreezer', 'UsePopups', 'UseDoubleSorting', 'MenuFrameWidth', 'ResizableFrames', 'AutoRefreshIntervals', 'DebugOnlyFormConfigurator', 'UseModalWindows'); INSERT INTO ConfigurationAdmin VALUES ('UseTemplateCompression', 'la_section_SettingsSystem', 'la_config_UseTemplateCompression', 'checkbox', '', '', 60.03, 0, 1); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseTemplateCompression', '0', 'In-Portal', 'in-portal:configure_advanced'); UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName IN ('TrimRequiredFields', 'UseCronForRegularEvent', 'UseChangeLog', 'Backup_Path', 'SystemTagCache', 'SocketBlockingMode'); DELETE FROM ConfigurationAdmin WHERE VariableName = 'UseModalWindows'; DELETE FROM ConfigurationValues WHERE VariableName = 'UseModalWindows'; DELETE FROM Phrase WHERE Phrase = 'la_config_UseModalWindows'; UPDATE ConfigurationAdmin SET element_type = 'select', ValueList = '0=la_opt_SameWindow,1=la_opt_PopupWindow,2=la_opt_ModalWindow' WHERE VariableName = 'UsePopups'; UPDATE Phrase SET Translation = 'Editing Window Style' WHERE Phrase = 'la_config_UsePopups'; INSERT INTO ConfigurationAdmin VALUES ('UseVisitorTracking', 'la_section_SettingsWebsite', 'la_config_UseVisitorTracking', 'checkbox', '', '', 10.09, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseVisitorTracking', '0', 'In-Portal', 'in-portal:configure_advanced'); DELETE FROM ConfigurationAdmin WHERE VariableName = 'SessionReferrerCheck'; DELETE FROM ConfigurationValues WHERE VariableName = 'SessionReferrerCheck'; DELETE FROM Phrase WHERE Phrase = 'la_promt_ReferrerCheck'; INSERT INTO ConfigurationAdmin VALUES ('SessionBrowserSignatureCheck', 'la_section_SettingsSession', 'la_config_SessionBrowserSignatureCheck', 'checkbox', NULL, NULL, 20.04, 0, 1); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'SessionBrowserSignatureCheck', '0', 'In-Portal', 'in-portal:configure_advanced'); INSERT INTO ConfigurationAdmin VALUES ('SessionIPAddressCheck', 'la_section_SettingsSession', 'la_config_SessionIPAddressCheck', 'checkbox', NULL, NULL, 20.05, 0, 1); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'SessionIPAddressCheck', '0', 'In-Portal', 'in-portal:configure_advanced'); UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName = 'UseJSRedirect'; ALTER TABLE UserSession DROP CurrentTempKey, DROP PrevTempKey, ADD BrowserSignature VARCHAR(32) NOT NULL, ADD INDEX (BrowserSignature); UPDATE ConfigurationAdmin SET DisplayOrder = DisplayOrder + 0.01 WHERE heading = 'la_section_SettingsAdmin' AND DisplayOrder > 40 AND DisplayOrder < 50; UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsAdmin', DisplayOrder = 40.01 WHERE VariableName = 'RootPass'; UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_advanced' WHERE VariableName = 'RootPass'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.12 WHERE VariableName = 'User_Default_Registration_Country'; UPDATE ConfigurationAdmin SET heading = 'la_section_SettingsAdmin', DisplayOrder = 40.12 WHERE VariableName = 'RememberLastAdminTemplate'; UPDATE ConfigurationValues SET ModuleOwner = 'In-Portal', Section = 'in-portal:configure_advanced' WHERE VariableName = 'RememberLastAdminTemplate'; UPDATE ConfigurationAdmin SET DisplayOrder = 10.14 WHERE VariableName = 'DefaultSettingsUserId'; INSERT INTO ConfigurationAdmin VALUES ('UseHTTPAuth', 'la_section_SettingsAdmin', 'la_config_UseHTTPAuth', 'checkbox', '', '', 40.13, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UseHTTPAuth', '0', 'In-Portal', 'in-portal:configure_advanced'); INSERT INTO ConfigurationAdmin VALUES ('HTTPAuthUsername', 'la_section_SettingsAdmin', 'la_config_HTTPAuthUsername', 'text', '', '', 40.14, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'HTTPAuthUsername', '', 'In-Portal', 'in-portal:configure_advanced'); INSERT INTO ConfigurationAdmin VALUES ('HTTPAuthPassword', 'la_section_SettingsAdmin', 'la_config_HTTPAuthPassword', 'password', NULL, NULL, 40.15, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'HTTPAuthPassword', '', 'In-Portal', 'in-portal:configure_advanced'); INSERT INTO ConfigurationAdmin VALUES ('HTTPAuthBypassIPs', 'la_section_SettingsAdmin', 'la_config_HTTPAuthBypassIPs', 'text', '', '', 40.15, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'HTTPAuthBypassIPs', '', 'In-Portal', 'in-portal:configure_advanced'); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:service.edit', 11, 1, 1, 0); UPDATE Phrase SET Phrase = 'la_col_Rating' WHERE Phrase = 'la_col_rating'; UPDATE Phrase SET Phrase = 'la_text_Review' WHERE Phrase = 'la_text_review'; UPDATE Phrase SET Phrase = 'la_title_Reviews' WHERE Phrase = 'la_title_reviews'; UPDATE Phrase SET Phrase = 'la_ToolTip_cancel' WHERE Phrase = 'la_tooltip_cancel'; ALTER TABLE Phrase ADD PhraseKey VARCHAR(255) NOT NULL AFTER Phrase, ADD INDEX (PhraseKey); UPDATE Phrase SET PhraseKey = UPPER(Phrase); UPDATE Modules SET Loaded = 1 WHERE `Name` = 'In-Portal'; # ===== v 5.0.2-B1 ===== ALTER TABLE PortalGroup DROP ResourceId; ALTER TABLE Category DROP l1_Translated, DROP l2_Translated, DROP l3_Translated, DROP l4_Translated, DROP l5_Translated; ALTER TABLE PageContent DROP l1_Translated, DROP l2_Translated, DROP l3_Translated, DROP l4_Translated, DROP l5_Translated; ALTER TABLE Category CHANGE CachedTemplate CachedTemplate varchar(255) NOT NULL DEFAULT '', CHANGE ThemeId ThemeId int(10) unsigned NOT NULL DEFAULT '0'; ALTER TABLE UserSession CHANGE BrowserSignature BrowserSignature varchar(32) NOT NULL DEFAULT ''; ALTER TABLE ChangeLogs CHANGE Changes Changes text NULL, CHANGE OccuredOn OccuredOn INT(11) NULL DEFAULT NULL; ALTER TABLE EmailLog CHANGE EventParams EventParams text NULL; ALTER TABLE FormFields CHANGE DefaultValue DefaultValue text NULL; ALTER TABLE ImportCache CHANGE VarValue VarValue text NULL; ALTER TABLE ImportScripts CHANGE Description Description text NULL; ALTER TABLE PersistantSessionData CHANGE VariableValue VariableValue text NULL; ALTER TABLE Phrase CHANGE `Translation` `Translation` text NULL, CHANGE PhraseKey PhraseKey VARCHAR(255) NOT NULL DEFAULT '', CHANGE LastChanged LastChanged INT(10) UNSIGNED NULL DEFAULT NULL; ALTER TABLE PhraseCache CHANGE PhraseList PhraseList text NULL; ALTER TABLE Stylesheets CHANGE AdvancedCSS AdvancedCSS text NULL, CHANGE LastCompiled LastCompiled INT(10) UNSIGNED NULL DEFAULT NULL; ALTER TABLE StylesheetSelectors CHANGE SelectorData SelectorData text NULL, CHANGE Description Description text NULL, CHANGE AdvancedCSS AdvancedCSS text NULL; ALTER TABLE Category CHANGE `Status` `Status` TINYINT(4) NOT NULL DEFAULT '1', CHANGE CreatedOn CreatedOn INT(11) NULL DEFAULT NULL, CHANGE Modified Modified INT(11) NULL DEFAULT NULL; ALTER TABLE Language CHANGE UserDocsUrl UserDocsUrl VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE MailingLists CHANGE Subject Subject VARCHAR(255) NOT NULL DEFAULT '', CHANGE EmailsQueued EmailsQueued INT(10) UNSIGNED NOT NULL DEFAULT '0', CHANGE EmailsSent EmailsSent INT(10) UNSIGNED NOT NULL DEFAULT '0', CHANGE EmailsTotal EmailsTotal INT(10) UNSIGNED NOT NULL DEFAULT '0'; ALTER TABLE EmailQueue CHANGE MailingId MailingId INT(10) UNSIGNED NOT NULL DEFAULT '0', CHANGE Queued Queued INT(10) UNSIGNED NULL DEFAULT NULL, CHANGE LastSendRetry LastSendRetry INT(10) UNSIGNED NULL DEFAULT NULL; ALTER TABLE ImportScripts CHANGE `Status` `Status` TINYINT(4) NOT NULL DEFAULT '1'; ALTER TABLE Semaphores CHANGE SessionKey SessionKey INT(10) UNSIGNED NOT NULL DEFAULT '0', CHANGE `Timestamp` `Timestamp` INT(10) UNSIGNED NOT NULL DEFAULT '0', CHANGE MainPrefix MainPrefix VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE Skins CHANGE LogoBottom LogoBottom VARCHAR(255) NOT NULL DEFAULT '', CHANGE LogoLogin LogoLogin VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE ItemReview CHANGE ReviewText ReviewText LONGTEXT NULL; ALTER TABLE SessionData CHANGE VariableValue VariableValue LONGTEXT NULL; ALTER TABLE PortalUser CHANGE `Status` `Status` TINYINT(4) NOT NULL DEFAULT '1', CHANGE Modified Modified INT(11) NULL DEFAULT NULL; ALTER TABLE ItemFiles CHANGE CreatedOn CreatedOn INT(11) UNSIGNED NULL DEFAULT NULL; ALTER TABLE FormSubmissions CHANGE SubmissionTime SubmissionTime INT(11) NULL DEFAULT NULL; ALTER TABLE SessionLogs CHANGE SessionStart SessionStart INT(11) NULL DEFAULT NULL; ALTER TABLE Visits CHANGE VisitDate VisitDate INT(10) UNSIGNED NULL DEFAULT NULL; # ===== v 5.0.2-B2 ===== ALTER TABLE Theme ADD LanguagePackInstalled TINYINT UNSIGNED NOT NULL DEFAULT '0', ADD TemplateAliases TEXT, ADD INDEX (LanguagePackInstalled); ALTER TABLE ThemeFiles ADD TemplateAlias VARCHAR(255) NOT NULL DEFAULT '' AFTER FilePath, ADD INDEX (TemplateAlias); UPDATE Phrase SET PhraseType = 1 WHERE Phrase IN ('la_ToolTip_MoveUp', 'la_ToolTip_MoveDown', 'la_invalid_state', 'la_Pending', 'la_text_sess_expired', 'la_ToolTip_Export'); DELETE FROM Phrase WHERE Phrase IN ('la_ToolTip_Move_Up', 'la_ToolTip_Move_Down'); UPDATE Phrase SET Phrase = 'lu_btn_SendPassword' WHERE Phrase = 'LU_BTN_SENDPASSWORD'; ALTER TABLE Category DROP IsIndex; DELETE FROM Phrase WHERE Phrase IN ('la_CategoryIndex', 'la_Container', 'la_fld_IsIndex', 'lu_text_Redirecting', 'lu_title_Redirecting', 'lu_zip_code'); ALTER TABLE PortalUser ADD AdminLanguage INT(11) NULL DEFAULT NULL, ADD INDEX (AdminLanguage); # ===== v 5.0.2-RC1 ===== # ===== v 5.0.2 ===== # ===== v 5.0.3-B1 ===== ALTER TABLE PermCache ADD INDEX (ACL); INSERT INTO ConfigurationAdmin VALUES ('cms_DefaultTrackingCode', 'la_section_SettingsWebsite', 'la_config_DefaultTrackingCode', 'textarea', NULL, 'COLS=40 ROWS=5', 10.10, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'cms_DefaultTrackingCode', '', 'In-Portal', 'in-portal:configure_advanced'); UPDATE Phrase SET Module = 'Core' WHERE Phrase IN ('la_fld_Image', 'la_fld_Qty'); # ===== v 5.0.3-B2 ===== UPDATE CustomField SET ValueList = REPLACE(ValueList, '=+||', '') WHERE ElementType = 'radio'; # ===== v 5.0.3-RC1 ===== # ===== v 5.0.3 ===== # ===== v 5.0.4-B1 ===== # ===== v 5.0.4-B2 ===== # ===== v 5.0.4 ===== # ===== v 5.1.0-B1 ===== DROP TABLE EmailMessage; DELETE FROM PersistantSessionData WHERE VariableName = 'emailevents_columns_.'; INSERT INTO Permissions (Permission, GroupId, PermissionValue, Type, CatId) SELECT 'in-portal:configemail.add' AS Permission, GroupId, PermissionValue, Type, CatId FROM <%TABLE_PREFIX%>Permissions WHERE Permission = 'in-portal:configemail.edit'; INSERT INTO Permissions (Permission, GroupId, PermissionValue, Type, CatId) SELECT 'in-portal:configemail.delete' AS Permission, GroupId, PermissionValue, Type, CatId FROM <%TABLE_PREFIX%>Permissions WHERE Permission = 'in-portal:configemail.edit'; ALTER TABLE Events ADD l1_Description text; UPDATE Events e SET e.l1_Description = ( SELECT p.l<%PRIMARY_LANGUAGE%>_Translation FROM <%TABLE_PREFIX%>Phrase p WHERE p.Phrase = e.Description ); UPDATE Events SET Description = l1_Description; ALTER TABLE Events DROP l1_Description, CHANGE Description Description TEXT NULL; DELETE FROM Phrase WHERE Phrase LIKE 'la_event_%'; DELETE FROM PersistantSessionData WHERE VariableName = 'phrases_columns_.'; UPDATE Category SET FormId = NULL WHERE FormId = 0; INSERT INTO ConfigurationAdmin VALUES ('MemcacheServers', 'la_section_SettingsCaching', 'la_config_MemcacheServers', 'text', '', '', 80.02, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'MemcacheServers', 'localhost:11211', 'In-Portal', 'in-portal:configure_advanced'); ALTER TABLE Category ADD EnablePageCache TINYINT NOT NULL DEFAULT '0', ADD OverridePageCacheKey TINYINT NOT NULL DEFAULT '0', ADD PageCacheKey VARCHAR(255) NOT NULL DEFAULT '', ADD PageExpiration INT NULL DEFAULT NULL , ADD INDEX (EnablePageCache), ADD INDEX (OverridePageCacheKey), ADD INDEX (PageExpiration); DELETE FROM Cache WHERE VarName LIKE 'mod_rw_%'; CREATE TABLE CachedUrls ( UrlId int(11) NOT NULL AUTO_INCREMENT, Url varchar(255) NOT NULL DEFAULT '', DomainId int(11) NOT NULL DEFAULT '0', `Hash` int(11) NOT NULL DEFAULT '0', Prefixes varchar(255) NOT NULL DEFAULT '', ParsedVars text NOT NULL, Cached int(10) unsigned DEFAULT NULL, LifeTime int(11) NOT NULL DEFAULT '-1', PRIMARY KEY (UrlId), KEY Url (Url), KEY `Hash` (`Hash`), KEY Prefixes (Prefixes), KEY Cached (Cached), KEY LifeTime (LifeTime), KEY DomainId (DomainId) ); INSERT INTO ConfigurationAdmin VALUES ('CacheHandler', 'la_section_SettingsCaching', 'la_config_CacheHandler', 'select', NULL, 'Fake=la_None||Memcache=+Memcached||Apc=+Alternative PHP Cache||XCache=+XCache', 80.01, 0, 0); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'CacheHandler', 'Fake', 'In-Portal', 'in-portal:configure_advanced'); ALTER TABLE ConfigurationValues ADD Heading varchar(255) NOT NULL DEFAULT '', ADD Prompt varchar(255) NOT NULL DEFAULT '', ADD ElementType varchar(255) NOT NULL DEFAULT '', ADD Validation text, ADD ValueList text, ADD DisplayOrder double NOT NULL DEFAULT '0', ADD GroupDisplayOrder double NOT NULL DEFAULT '0', ADD Install int(11) NOT NULL DEFAULT '1', ADD INDEX (DisplayOrder), ADD INDEX (GroupDisplayOrder), ADD INDEX (Install); UPDATE ConfigurationValues cv SET cv.Heading = (SELECT ca1.heading FROM <%TABLE_PREFIX%>ConfigurationAdmin ca1 WHERE ca1.VariableName = cv.VariableName), cv.Prompt = (SELECT ca2.prompt FROM <%TABLE_PREFIX%>ConfigurationAdmin ca2 WHERE ca2.VariableName = cv.VariableName), cv.ElementType = (SELECT ca3.element_type FROM <%TABLE_PREFIX%>ConfigurationAdmin ca3 WHERE ca3.VariableName = cv.VariableName), cv.Validation = (SELECT ca4.Validation FROM <%TABLE_PREFIX%>ConfigurationAdmin ca4 WHERE ca4.VariableName = cv.VariableName), cv.ValueList = (SELECT ca5.ValueList FROM <%TABLE_PREFIX%>ConfigurationAdmin ca5 WHERE ca5.VariableName = cv.VariableName), cv.DisplayOrder = (SELECT ca6.DisplayOrder FROM <%TABLE_PREFIX%>ConfigurationAdmin ca6 WHERE ca6.VariableName = cv.VariableName), cv.GroupDisplayOrder = (SELECT ca7.GroupDisplayOrder FROM <%TABLE_PREFIX%>ConfigurationAdmin ca7 WHERE ca7.VariableName = cv.VariableName), cv.`Install` = (SELECT ca8.`Install` FROM <%TABLE_PREFIX%>ConfigurationAdmin ca8 WHERE ca8.VariableName = cv.VariableName); DROP TABLE ConfigurationAdmin; UPDATE ConfigurationValues SET ValueList = '=+||<SQL+>SELECT l%3$s_Name AS OptionName, CountryStateId AS OptionValue FROM <PREFIX>CountryStates WHERE Type = 1 ORDER BY OptionName</SQL>' WHERE ValueList = '=+||<SQL>SELECT DestName AS OptionName, DestId AS OptionValue FROM <PREFIX>StdDestinations WHERE COALESCE(DestParentId, 0) = 0 ORDER BY OptionName</SQL>'; ALTER TABLE Forms ADD RequireLogin TINYINT NOT NULL DEFAULT '0', ADD INDEX (RequireLogin), ADD UseSecurityImage TINYINT NOT NULL DEFAULT '0', ADD INDEX (UseSecurityImage), ADD EnableEmailCommunication TINYINT NOT NULL DEFAULT '0', ADD INDEX (EnableEmailCommunication), ADD ReplyFromName VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyFromEmail VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyCc VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyBcc VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyMessageSignature TEXT, ADD ReplyServer VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyPort INT(10) NOT NULL DEFAULT '110', ADD ReplyUsername VARCHAR(255) NOT NULL DEFAULT '', ADD ReplyPassword VARCHAR(255) NOT NULL DEFAULT '', ADD BounceEmail VARCHAR(255) NOT NULL DEFAULT '', ADD BounceServer VARCHAR(255) NOT NULL DEFAULT '', ADD BouncePort INT(10) NOT NULL DEFAULT '110', ADD BounceUsername VARCHAR(255) NOT NULL DEFAULT '', ADD BouncePassword VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE FormFields ADD Visibility TINYINT NOT NULL DEFAULT '1', ADD INDEX (Visibility), ADD EmailCommunicationRole TINYINT NOT NULL DEFAULT '0', ADD INDEX (EmailCommunicationRole); ALTER TABLE FormSubmissions ADD IPAddress VARCHAR(15) NOT NULL DEFAULT '' AFTER SubmissionTime, ADD ReferrerURL VARCHAR(255) NOT NULL DEFAULT '' AFTER IPAddress, ADD LogStatus TINYINT UNSIGNED NOT NULL DEFAULT '2' AFTER ReferrerURL, ADD LastUpdatedOn INT UNSIGNED NULL AFTER LogStatus, ADD Notes TEXT NULL AFTER LastUpdatedOn, ADD INDEX (LogStatus), ADD INDEX (LastUpdatedOn); CREATE TABLE SubmissionLog ( SubmissionLogId int(11) NOT NULL AUTO_INCREMENT, FormSubmissionId int(10) unsigned NOT NULL, FromEmail varchar(255) NOT NULL DEFAULT '', ToEmail varchar(255) NOT NULL DEFAULT '', Cc text, Bcc text, `Subject` varchar(255) NOT NULL DEFAULT '', Message text, Attachment text, ReplyStatus tinyint(3) unsigned NOT NULL DEFAULT '0', SentStatus tinyint(3) unsigned NOT NULL DEFAULT '0', SentOn int(10) unsigned DEFAULT NULL, RepliedOn int(10) unsigned DEFAULT NULL, VerifyCode varchar(32) NOT NULL DEFAULT '', DraftId int(10) unsigned NOT NULL DEFAULT '0', MessageId varchar(255) NOT NULL DEFAULT '', BounceInfo text, BounceDate int(11) DEFAULT NULL, PRIMARY KEY (SubmissionLogId), KEY FormSubmissionId (FormSubmissionId), KEY ReplyStatus (ReplyStatus), KEY SentStatus (SentStatus), KEY SentOn (SentOn), KEY RepliedOn (RepliedOn), KEY VerifyCode (VerifyCode), KEY DraftId (DraftId), KEY BounceDate (BounceDate), KEY MessageId (MessageId) ); CREATE TABLE Drafts ( DraftId int(11) NOT NULL AUTO_INCREMENT, FormSubmissionId int(10) unsigned NOT NULL DEFAULT '0', CreatedOn int(10) unsigned DEFAULT NULL, CreatedById int(11) NOT NULL, Message text, PRIMARY KEY (DraftId), KEY FormSubmissionId (FormSubmissionId), KEY CreatedOn (CreatedOn), KEY CreatedById (CreatedById) ); INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, FromUserId, Module, Description, Type) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.TO.USER', NULL, 1, 0, NULL, 'Core:Category', 'Admin Reply to User Form Submission', 1); INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, FromUserId, Module, Description, Type) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.FROM.USER', NULL, 1, 0, NULL, 'Core:Category', 'User Replied to It\'s Form Submission', 1); INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, FromUserId, Module, Description, Type) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.FROM.USER.BOUNCED', NULL, 1, 0, NULL, 'Core:Category', 'Form Submission Admin Reply Delivery Failure', 1); ALTER TABLE ConfigurationValues ADD HintLabel VARCHAR(255) NULL DEFAULT NULL, ADD INDEX (HintLabel); UPDATE ConfigurationValues SET HintLabel = 'la_hint_MemcacheServers' WHERE VariableName = 'MemcacheServers'; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'ModRewriteUrlEnding', '.html', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ModRewriteUrlEnding', 'select', '', '=+||/=+/||.html=+.html', 10.021, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'ForceModRewriteUrlEnding', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ForceModRewriteUrlEnding', 'checkbox', '', NULL, 10.022, 0, 0, 'la_hint_ForceModRewriteUrlEnding'); UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = 'Enable SEO-friendly URLs mode (MOD-REWRITE)' WHERE Phrase = 'la_config_use_modrewrite' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Use MOD REWRITE'; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'UseContentLanguageNegotiation', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_UseContentLanguageNegotiation', 'checkbox', '', '', 10.023, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SessionCookieDomains', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_SessionCookieDomains', 'textarea', '', 'rows="5" cols="40"', 20.021, 0, 0, NULL); CREATE TABLE SiteDomains ( DomainId int(11) NOT NULL AUTO_INCREMENT, DomainName varchar(255) NOT NULL DEFAULT '', DomainNameUsesRegExp tinyint(4) NOT NULL DEFAULT '0', SSLUrl varchar(255) NOT NULL DEFAULT '', SSLUrlUsesRegExp tinyint(4) NOT NULL DEFAULT '0', AdminEmail varchar(255) NOT NULL DEFAULT '', Country varchar(3) NOT NULL DEFAULT '', PrimaryLanguageId int(11) NOT NULL DEFAULT '0', Languages varchar(255) NOT NULL DEFAULT '', PrimaryThemeId int(11) NOT NULL DEFAULT '0', Themes varchar(255) NOT NULL DEFAULT '', DomainIPRange text, ExternalUrl varchar(255) NOT NULL DEFAULT '', RedirectOnIPMatch tinyint(4) NOT NULL DEFAULT '0', Priority int(11) NOT NULL DEFAULT '0', PRIMARY KEY (DomainId), KEY DomainName (DomainName), KEY DomainNameUsesRegExp (DomainNameUsesRegExp), KEY SSLUrl (SSLUrl), KEY SSLUrlUsesRegExp (SSLUrlUsesRegExp), KEY AdminEmail (AdminEmail), KEY Country (Country), KEY PrimaryLanguageId (PrimaryLanguageId), KEY Languages (Languages), KEY PrimaryThemeId (PrimaryThemeId), KEY Themes (Themes), KEY ExternalUrl (ExternalUrl), KEY RedirectOnIPMatch (RedirectOnIPMatch), KEY Priority (Priority) ); DELETE FROM Phrase WHERE Phrase = 'la_config_time_server'; DELETE FROM ConfigurationValues WHERE VariableName = 'Config_Server_Time'; UPDATE ConfigurationValues SET ValueList = NULL, DisplayOrder = 20.02 WHERE VariableName = 'Config_Site_Time'; UPDATE ConfigurationValues SET VariableValue = '' WHERE VariableName = 'Config_Site_Time' AND VariableValue = 14; UPDATE Events SET AllowChangingSender = 1, AllowChangingRecipient = 1; UPDATE Events SET Module = 'Core' WHERE Module LIKE 'Core:%'; DELETE FROM Permissions WHERE Permission LIKE 'in-portal:configuration_email%'; DELETE FROM Permissions WHERE Permission LIKE 'in-portal:user_email%'; DELETE FROM Phrase WHERE Phrase IN ('la_fld_FromToUser', 'la_col_FromToUser'); # ===== v 5.1.0-B2 ===== # ===== v 5.1.0-RC1 ===== UPDATE Phrase SET Module = 'Core' WHERE Phrase = 'la_fld_Group'; UPDATE PermissionConfig SET Description = REPLACE(Description, 'lu_PermName_', 'la_PermName_'), ErrorMessage = REPLACE(ErrorMessage, 'lu_PermName_', 'la_PermName_'); UPDATE Phrase SET Phrase = REPLACE(Phrase, 'lu_PermName_', 'la_PermName_'), PhraseKey = REPLACE(PhraseKey, 'LU_PERMNAME_', 'LA_PERMNAME_'), PhraseType = 1 WHERE PhraseKey LIKE 'LU_PERMNAME_%'; UPDATE Phrase SET Phrase = 'la_no_permissions', PhraseKey = 'LA_NO_PERMISSIONS', PhraseType = 1 WHERE PhraseKey = 'LU_NO_PERMISSIONS'; UPDATE Phrase SET PhraseType = 0 WHERE PhraseKey IN ( 'LU_FERROR_FORGOTPW_NODATA', 'LU_FERROR_UNKNOWN_USERNAME', 'LU_FERROR_UNKNOWN_EMAIL' ); DELETE FROM ConfigurationValues WHERE VariableName = 'Root_Name'; DELETE FROM Phrase WHERE PhraseKey = 'LA_PROMPT_ROOT_NAME'; UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder - 0.01 WHERE ModuleOwner = 'In-Portal' AND `Section` = 'in-portal:configure_categories' AND DisplayOrder > 10.07; # ===== v 5.1.0 ===== UPDATE Events SET Headers = NULL WHERE Headers = ''; UPDATE Events SET MessageType = 'text' WHERE Event = 'FORM.SUBMISSION.REPLY.TO.USER'; ALTER TABLE Forms ADD ProcessUnmatchedEmails TINYINT NOT NULL DEFAULT '0' AFTER EnableEmailCommunication, ADD INDEX (ProcessUnmatchedEmails); ALTER TABLE FormSubmissions ADD MessageId VARCHAR(255) NULL DEFAULT NULL AFTER Notes, ADD INDEX (MessageId); # ===== v 5.1.1-B1 ===== ALTER TABLE PortalUser ADD DisplayToPublic TEXT NULL; UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = 'Comments' WHERE PhraseKey = 'LA_FLD_COMMENTS'; ALTER TABLE Category CHANGE `Type` `Type` INT(11) NOT NULL DEFAULT '1', CHANGE `IsSystem` `Protected` TINYINT( 4 ) NOT NULL DEFAULT '0', ADD INDEX ( `Protected` ); UPDATE Category SET `Type` = IF(`Protected` = 1, 2, 1); UPDATE Category SET `Protected` = 1 WHERE ThemeId > 0; ALTER TABLE Category CHANGE CachedDescendantCatsQty CachedDescendantCatsQty INT(11) NOT NULL DEFAULT '0'; ALTER TABLE Events CHANGE `Module` `Module` VARCHAR(40) NOT NULL DEFAULT 'Core'; ALTER TABLE Language CHANGE DateFormat DateFormat VARCHAR(50) NOT NULL DEFAULT 'm/d/Y', CHANGE TimeFormat TimeFormat VARCHAR(50) NOT NULL DEFAULT 'g:i:s A', CHANGE DecimalPoint DecimalPoint VARCHAR(10) NOT NULL DEFAULT '.', CHANGE Charset Charset VARCHAR(20) NOT NULL DEFAULT 'utf-8'; ALTER TABLE ItemReview CHANGE Rating Rating TINYINT(3) UNSIGNED NOT NULL DEFAULT '0'; UPDATE PortalUser SET tz = NULL; ALTER TABLE Category CHANGE CreatedById CreatedById INT(11) NULL DEFAULT NULL, CHANGE ModifiedById ModifiedById INT(11) NULL DEFAULT NULL; UPDATE Category SET CreatedById = NULL WHERE CreatedById = 0; UPDATE Category SET ModifiedById = NULL WHERE ModifiedById = 0; ALTER TABLE ItemFiles CHANGE CreatedById CreatedById INT(11) NULL DEFAULT NULL; ALTER TABLE Drafts CHANGE CreatedById CreatedById INT(11) NULL DEFAULT NULL; UPDATE Drafts SET CreatedById = NULL WHERE CreatedById = 0; ALTER TABLE ItemReview CHANGE CreatedById CreatedById INT(11) NULL DEFAULT NULL; # ===== v 5.1.1-B2 ===== UPDATE Phrase SET `Module` = 'Core' WHERE PhraseKey = 'LU_SECTION_FILES'; # ===== v 5.1.1-RC1 ===== ALTER TABLE PortalUser CHANGE Phone Phone VARCHAR(255) NOT NULL DEFAULT '', CHANGE City City VARCHAR(255) NOT NULL DEFAULT '', CHANGE Street Street VARCHAR(255) NOT NULL DEFAULT '', CHANGE Zip Zip VARCHAR(20) NOT NULL DEFAULT '', CHANGE ip ip VARCHAR(20) NOT NULL DEFAULT ''; UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = 'Use Cron to run Agents' WHERE PhraseKey = 'LA_USECRONFORREGULAREVENT' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Use Cron for Running Regular Events'; # ===== v 5.1.1 ===== # ===== v 5.1.2-B1 ===== DROP TABLE EmailSubscribers; DROP TABLE IgnoreKeywords; DROP TABLE IgnoreKeywords; ALTER TABLE PermissionConfig DROP ErrorMessage; # ===== v 5.1.2-B2 ===== # ===== v 5.1.2-RC1 ===== DROP TABLE Stylesheets; DROP TABLE StylesheetSelectors; DROP TABLE SysCache; DROP TABLE TagAttributes; DROP TABLE TagLibrary; DELETE FROM Phrase WHERE PhraseKey IN ( 'LA_FLD_STYLESHEETID', 'LA_PROMPT_STYLESHEET', 'LA_TAB_STYLESHEETS', 'LA_TITLE_ADDING_STYLESHEET', 'LA_TITLE_EDITING_STYLESHEET', 'LA_TITLE_NEW_STYLESHEET', 'LA_TITLE_STYLESHEETS', 'LA_TOOLTIP_NEWSTYLESHEET', 'LA_COL_SELECTORNAME', 'LA_COL_BASEDON', 'LA_FLD_SELECTORBASE', 'LA_FLD_SELECTORDATA', 'LA_FLD_SELECTORID', 'LA_FLD_SELECTORNAME' ); # ===== v 5.1.2 ===== # ===== v 5.1.3-B1 ===== ALTER TABLE FormSubmissions CHANGE ReferrerURL ReferrerURL TEXT NULL; INSERT INTO ConfigurationValues VALUES (DEFAULT, 'UserEmailActivationTimeout', '', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_config_UserEmailActivationTimeout', 'text', NULL, NULL, 10.051, 0, 0, NULL); # ===== v 5.1.3-B2 ===== ALTER TABLE Modules ADD AppliedDBRevisions TEXT NULL; # ===== v 5.1.3-RC1 ===== # ===== v 5.1.3-RC2 ===== UPDATE Events SET l<%PRIMARY_LANGUAGE%>_Subject = 'New User Registration (<inp2:u_Field name="Login"/><inp2:m_if check="m_GetConfig" name="User_Allow_New" equals_to="4"> - Activation Email</inp2:m_if>)' WHERE Event = 'USER.ADD.PENDING' AND `Type` = 0 AND l<%PRIMARY_LANGUAGE%>_Subject LIKE '%<inp2:m_if check="m_GetConfig" name="User_Allow_New" equals_to="4"> - Activation Email</inp2:m_if>)%'; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaxUserName', '', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_text_min_username', 'text', '', 'style="width: 50px;"', 10.03, 2, 0, NULL); UPDATE ConfigurationValues SET GroupDisplayOrder = 1, ValueList = 'style="width: 50px;"' WHERE VariableName = 'Min_UserName'; UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = 'User name length (min - max)' WHERE PhraseKey = 'LA_TEXT_MIN_USERNAME' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Minimum user name length'; # ===== v 5.1.3 ===== UPDATE PortalUser SET Modified = NULL; INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:site_domains.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:site_domains.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:site_domains.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:site_domains.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:country_states.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:country_states.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:country_states.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:country_states.view', 11, 1, 1, 0); # ===== v 5.2.0-B1 ===== ALTER TABLE PortalUser ADD UserType TINYINT NOT NULL, ADD PrimaryGroupId INT NULL, ADD INDEX (UserType); UPDATE PortalUser u SET u.PrimaryGroupId = (SELECT ug.GroupId FROM <%TABLE_PREFIX%>UserGroup ug WHERE ug.PortalUserId = u.PortalUserId AND ug.PrimaryGroup = 1); UPDATE PortalUser u SET u.UserType = IF(u.PrimaryGroupId = 11, 1, 0); ALTER TABLE UserGroup DROP PrimaryGroup; UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder + 0.01 WHERE `ModuleOwner` = 'In-Portal:Users' AND `Section` = 'in-portal:configure_users' AND DisplayOrder BETWEEN 10.12 AND 20.00; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'User_AdminGroup', '11', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_admin_group', 'select', NULL, '0=lu_none||<SQL+>SELECT GroupId as OptionValue, Name as OptionName FROM <PREFIX>PortalGroup WHERE Enabled=1 AND Personal=0</SQL>', 10.12, 0, 1, NULL); ALTER TABLE PortalUser DROP INDEX Login, ADD INDEX Login (Login); ALTER TABLE PortalUser CHANGE Login Login VARCHAR(255) NOT NULL; ALTER TABLE PortalUser ADD OldStyleLogin TINYINT NOT NULL; UPDATE PortalUser SET OldStyleLogin = 1 WHERE (Login <> '') AND (Login NOT REGEXP '^[A-Z0-9_\\-\\.]+$'); DELETE FROM Events WHERE Event = 'USER.PSWD'; UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = 'Your password has been reset.' WHERE PhraseKey = 'LU_TEXT_FORGOTPASSHASBEENRESET' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Your password has been reset. The new password has been sent to your e-mail address. You may now login with the new password.'; ALTER TABLE PortalUser DROP MinPwResetDelay, DROP PassResetTime, CHANGE PwResetConfirm PwResetConfirm VARCHAR(255) NOT NULL; UPDATE PortalUser SET PwRequestTime = NULL WHERE PwRequestTime = 0; ALTER TABLE Category ADD DirectLinkEnabled TINYINT NOT NULL DEFAULT '1', ADD DirectLinkAuthKey VARCHAR(20) NOT NULL; UPDATE Category SET DirectLinkAuthKey = SUBSTRING( MD5( CONCAT(CategoryId, ':', ParentId, ':', l<%PRIMARY_LANGUAGE%>_Name, ':b38') ), 1, 20) WHERE DirectLinkAuthKey = ''; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'ExcludeTemplateSectionsFromSearch', '0', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_ExcludeTemplateSectionsFromSearch', 'checkbox', '', '', 10.15, 0, 0, NULL); ALTER TABLE Agents ADD SiteDomainLimitation VARCHAR(255) NOT NULL, ADD INDEX (SiteDomainLimitation); UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName = 'HTTPAuthBypassIPs'; UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder + 0.01 WHERE ModuleOwner = 'In-Portal' AND `Section` = 'in-portal:configure_advanced' AND Heading = 'la_section_SettingsAdmin' AND DisplayOrder > 40.06; INSERT INTO ConfigurationValues VALUES (DEFAULT, 'StickyGridSelection', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_StickyGridSelection', 'radio', '', '1=la_Yes||0=la_No', 40.07, 0, 0, NULL); ALTER TABLE Forms ADD SubmitNotifyEmail VARCHAR(255) NOT NULL DEFAULT '' AFTER UseSecurityImage; ALTER TABLE FormFields ADD UploadExtensions VARCHAR(255) NOT NULL DEFAULT '' AFTER Validation, ADD UploadMaxSize INT NULL AFTER UploadExtensions; ALTER TABLE Language ADD SynchronizationModes VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE PortalUser CHANGE ip IPAddress VARCHAR(15) NOT NULL, ADD IPRestrictions TEXT NULL; ALTER TABLE PortalGroup ADD IPRestrictions TEXT NULL; INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'ROOT.RESET.PASSWORD', NULL, 1, 0, 'Core', 'Root Reset Password', 1, 1, 0); ALTER TABLE Skins ADD DisplaySiteNameInHeader TINYINT(1) NOT NULL DEFAULT '1'; DELETE FROM PersistantSessionData WHERE VariableName LIKE 'formsubs_Sort%' AND VariableValue = 'FormFieldId'; ALTER TABLE ItemReview ADD HelpfulCount INT NOT NULL , ADD NotHelpfulCount INT NOT NULL; ALTER TABLE PermissionConfig ADD IsSystem TINYINT(1) NOT NULL DEFAULT '0'; UPDATE PermissionConfig SET IsSystem = 1; INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:permission_types.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:permission_types.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:permission_types.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:permission_types.delete', 11, 1, 1, 0); ALTER TABLE Agents ADD Timeout INT(10) UNSIGNED NULL AFTER RunTime, ADD LastTimeoutOn int(10) unsigned default NULL AFTER Timeout, ADD INDEX (Timeout); CREATE TABLE CurlLog ( LogId int(11) NOT NULL AUTO_INCREMENT, Message varchar(255) NOT NULL, PageUrl varchar(255) NOT NULL, RequestUrl varchar(255) NOT NULL, PortalUserId int(11) NOT NULL, SessionKey int(11) NOT NULL, IsAdmin tinyint(4) NOT NULL, PageData text, RequestData text, ResponseData text, RequestDate int(11) DEFAULT NULL, ResponseDate int(11) DEFAULT NULL, ResponseHttpCode int(11) NOT NULL, CurlError varchar(255) NOT NULL, PRIMARY KEY (LogId), KEY Message (Message), KEY PageUrl (PageUrl), KEY RequestUrl (RequestUrl), KEY PortalUserId (PortalUserId), KEY SessionKey (SessionKey), KEY IsAdmin (IsAdmin), KEY RequestDate (RequestDate), KEY ResponseDate (ResponseDate), KEY ResponseHttpCode (ResponseHttpCode), KEY CurlError (CurlError) ); DELETE FROM ConfigurationValues WHERE VariableName = 'Site_Path'; UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder + 0.01 WHERE `Section` = 'in-portal:configure_advanced' AND Heading = 'la_section_SettingsWebsite'; UPDATE ItemTypes SET TitleField = 'Username' WHERE SourceTable = 'PortalUser' AND TitleField = 'Login'; UPDATE SearchConfig SET FieldName = 'Username' WHERE TableName = 'PortalUser' AND FieldName = 'Login'; ALTER TABLE PortalUser DROP INDEX Login; ALTER TABLE PortalUser CHANGE Login Username VARCHAR(255) NOT NULL; ALTER TABLE PortalUser ADD INDEX Username (Username); UPDATE Events SET l<%PRIMARY_LANGUAGE%>_Subject = REPLACE(l<%PRIMARY_LANGUAGE%>_Subject, 'name="Login"', 'name="Username"'), l<%PRIMARY_LANGUAGE%>_Body = REPLACE(l<%PRIMARY_LANGUAGE%>_Body, 'name="Login"', 'name="Username"'); DELETE FROM PersistantSessionData WHERE (VariableName LIKE 'u%]columns_.') OR (VariableName LIKE 'u%_sort%'); DELETE FROM Phrase WHERE Phrase = 'LU_FLD_LOGIN'; UPDATE BanRules SET ItemField = 'Username' WHERE ItemField = 'Login'; DELETE FROM Phrase WHERE PhraseKey IN ( 'LU_USERNAME', 'LU_EMAIL', 'LU_PASSWORD', 'LA_TEXT_LOGIN', 'LA_PROMPT_PASSWORD', 'LA_USE_EMAILS_AS_LOGIN', 'LU_USER_AND_EMAIL_ALREADY_EXIST', 'LU_ENTERFORGOTEMAIL' ); UPDATE ConfigurationValues SET VariableName = 'RegistrationUsernameRequired', Prompt = 'la_config_RegistrationUsernameRequired' WHERE VariableName = 'Email_As_Login'; UPDATE ConfigurationValues SET VariableValue = IF(VariableValue = 1, 0, 1) WHERE VariableName = 'RegistrationUsernameRequired'; INSERT INTO ConfigurationValues VALUES (DEFAULT, 'PerformExactSearch', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_PerformExactSearch', 'checkbox', '', '', '10.10', 0, 0, 'la_hint_PerformExactSearch'); UPDATE Phrase SET PhraseType = 1 WHERE PhraseKey IN ( 'LA_USERS_SUBSCRIBER_GROUP', 'LA_PROMPT_DUPREVIEWS', 'LA_PROMPT_DUPREVIEWS', 'LA_PROMPT_DUPRATING', 'LA_PROMPT_OVERWRITEPHRASES', 'LA_TEXT_BACKUP_ACCESS', 'LA_PHRASETYPE_BOTH', 'LA_TOOLTIP_NEWLISTING' ); UPDATE Phrase SET PhraseType = 0 WHERE PhraseKey IN ('LU_TITLE_SHIPPINGINFORMATION', 'LU_COMM_LASTQUATER'); UPDATE Phrase SET Phrase = REPLACE(Phrase, 'lu_', 'la_'), PhraseKey = UPPER(Phrase) WHERE PhraseKey IN ('LU_OPT_AUTODETECT', 'LU_OPT_COOKIES', 'LU_OPT_QUERYSTRING'); UPDATE ConfigurationValues SET ValueList = REPLACE(ValueList, 'lu_', 'la_') WHERE VariableName = 'CookieSessions'; DELETE FROM Phrase WHERE PhraseKey IN ('LU_INVALID_PASSWORD', 'LA_OF', 'LU_TITLE_REVIEWPRODUCT'); UPDATE Phrase SET PhraseType = 2 WHERE PhraseType = 1 AND (PhraseKey LIKE 'lu_field_%' OR PhraseKey = 'LA_TEXT_VALID'); UPDATE Phrase SET Phrase = REPLACE(Phrase, 'la_', 'lc_'), PhraseKey = UPPER(Phrase) WHERE PhraseType = 2; UPDATE Phrase SET Phrase = REPLACE(Phrase, 'lu_', 'lc_'), PhraseKey = UPPER(Phrase) WHERE PhraseType = 2; UPDATE SearchConfig SET DisplayName = REPLACE(DisplayName, 'lu_', 'lc_') WHERE DisplayName IN ( 'lu_field_newitem', 'lu_field_popitem', 'lu_field_hotitem', 'lu_field_resourceid', 'lu_field_createdbyid', 'lu_field_priority', 'lu_field_status', 'lu_field_createdon', 'lu_field_description', 'lu_field_name', 'lu_field_modified', 'lu_field_modifiedbyid', 'lu_field_ParentPath', 'lu_field_ParentId', 'lu_field_MetaKeywords', 'lu_field_MetaDescription', 'lu_field_EditorsPick', 'lu_field_CategoryId', 'lu_field_CachedNavBar', 'lu_field_CachedDescendantCatsQty', 'lu_field_hits', 'lu_field_cachedrating', 'lu_field_cachedvotesqty', 'lu_field_cachedreviewsqty', 'lu_field_orgid' ); CREATE TABLE SpamReports ( ReportId int(11) NOT NULL AUTO_INCREMENT, ItemPrefix varchar(255) NOT NULL, ItemId int(11) NOT NULL, MessageText text, ReportedOn int(11) DEFAULT NULL, ReportedById int(11) DEFAULT NULL, PRIMARY KEY (ReportId), KEY ItemPrefix (ItemPrefix), KEY ItemId (ItemId), KEY ReportedById (ReportedById) ); DELETE FROM Phrase WHERE PhraseKey IN ( 'LA_SECTION_SETTINGSCACHING', 'LA_CONFIG_CACHEHANDLER', 'LA_CONFIG_MEMCACHESERVERS', 'LA_HINT_MEMCACHESERVERS' ); DELETE FROM ConfigurationValues WHERE VariableName IN ('CacheHandler', 'MemcacheServers'); CREATE TABLE PromoBlocks ( BlockId int(11) NOT NULL AUTO_INCREMENT, Title varchar(50) NOT NULL DEFAULT '', Priority int(11) NOT NULL DEFAULT '0', Status tinyint(1) NOT NULL DEFAULT '0', l1_Image varchar(255) NOT NULL DEFAULT '', l2_Image varchar(255) NOT NULL DEFAULT '', l3_Image varchar(255) NOT NULL DEFAULT '', l4_Image varchar(255) NOT NULL DEFAULT '', l5_Image varchar(255) NOT NULL DEFAULT '', CSSClassName varchar(255) NOT NULL DEFAULT '', LinkType tinyint(1) NOT NULL DEFAULT '1', CategoryId int(11) NOT NULL DEFAULT '0', ExternalLink varchar(255) NOT NULL DEFAULT '', OpenInNewWindow tinyint(3) unsigned NOT NULL DEFAULT '0', ScheduleFromDate int(11) DEFAULT NULL, ScheduleToDate int(11) DEFAULT NULL, NumberOfClicks int(11) NOT NULL DEFAULT '0', NumberOfViews int(11) NOT NULL DEFAULT '0', Sticky tinyint(1) NOT NULL DEFAULT '0', Html text, l1_Html text, l2_Html text, l3_Html text, l4_Html text, l5_Html text, PRIMARY KEY (BlockId), KEY OpenInNewWindow (OpenInNewWindow) ); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'PromoRotationDelay', '7', 'In-Portal', 'in-portal:configure_promo_blocks', 'la_Text_PromoSettings', 'la_config_PromoRotationDelay', 'text', '', '', 10.01, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'PromoTransitionTime', '0.6', 'In-Portal', 'in-portal:configure_promo_blocks', 'la_Text_PromoSettings', 'la_config_PromoTransitionTime', 'text', '', '', 10.02, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'PromoTransitionControls', '1', 'In-Portal', 'in-portal:configure_promo_blocks', 'la_Text_PromoSettings', 'la_config_PromoTransitionControls', 'select', '', '1=la_Enabled||0=la_Disabled', 10.03, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES (DEFAULT, 'PromoTransitionEffect', 'fade', 'In-Portal', 'in-portal:configure_promo_blocks', 'la_Text_PromoSettings', 'la_config_PromoTransitionEffect', 'select', '', 'fade=la_opt_AnimationFade||slide=la_opt_AnimationSlide', 10.04, 0, 0, NULL); UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_ColumnTranslation = l<%PRIMARY_LANGUAGE%>_Translation WHERE PhraseKey IN ('LA_FLD_CATEGORY', 'LA_FLD_ORDER'); CREATE TABLE PageRevisions ( RevisionId int(11) NOT NULL AUTO_INCREMENT, PageId int(11) NOT NULL, RevisionNumber int(11) NOT NULL, IsDraft tinyint(4) NOT NULL, FromRevisionId int(11) NOT NULL, CreatedById int(11) DEFAULT NULL, CreatedOn int(11) DEFAULT NULL, AutoSavedOn int(11) DEFAULT NULL, `Status` tinyint(4) NOT NULL DEFAULT '2', PRIMARY KEY (RevisionId), KEY PageId (PageId), KEY RevisionNumber (RevisionNumber), KEY IsDraft (IsDraft), KEY `Status` (`Status`) ); ALTER TABLE Category ADD LiveRevisionNumber INT NOT NULL DEFAULT '1' AFTER PageExpiration, ADD INDEX (LiveRevisionNumber); ALTER TABLE PageContent ADD RevisionId INT NOT NULL AFTER PageId, ADD INDEX (RevisionId); ALTER TABLE PermissionConfig CHANGE PermissionName PermissionName VARCHAR(255) NOT NULL DEFAULT ''; INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD', 'la_PermName_Category.Revision.Add_desc', 'In-Portal', 1); INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal', 1); INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal', 1); INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal', 1); INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal', 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.ADD', 11, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 11, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 11, 1, 0, 1); ALTER TABLE EmailQueue ADD `LogData` TEXT; UPDATE Permissions SET Permission = REPLACE(Permission, 'agents', 'scheduled_tasks') WHERE Permission LIKE 'in-portal:agents%'; DELETE FROM Phrase WHERE PhraseKey IN ( 'LA_TITLE_ADDINGAGENT', 'LA_TITLE_EDITINGAGENT', 'LA_TITLE_NEWAGENT', 'LA_TITLE_AGENTS', 'LA_TOOLTIP_NEWAGENT' ); UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_Translation = REPLACE(l<%PRIMARY_LANGUAGE%>_Translation, 'Agents', 'Scheduled Tasks') WHERE PhraseKey IN ( 'LA_USECRONFORREGULAREVENT', 'LA_HINT_SYSTEMTOOLSRESETPARSEDCACHEDDATA', 'LA_HINT_SYSTEMTOOLSRESETCONFIGSANDPARSEDDATA' ); DELETE FROM PersistantSessionData WHERE VariableName LIKE 'agent%'; RENAME TABLE <%TABLE_PREFIX%>Agents TO <%TABLE_PREFIX%>ScheduledTasks; ALTER TABLE ScheduledTasks CHANGE AgentId ScheduledTaskId INT(11) NOT NULL AUTO_INCREMENT, CHANGE AgentName Name VARCHAR(255) NOT NULL DEFAULT '', CHANGE AgentType `Type` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1'; ALTER TABLE ScheduledTasks DROP INDEX AgentType, ADD INDEX `Type` (`Type`); UPDATE ConfigurationValues SET VariableName = 'RunScheduledTasksFromCron' WHERE VariableName = 'UseCronForRegularEvent'; CREATE TABLE ItemFilters ( FilterId int(11) NOT NULL AUTO_INCREMENT, ItemPrefix varchar(255) NOT NULL, FilterField varchar(255) NOT NULL, FilterType varchar(100) NOT NULL, Enabled tinyint(4) NOT NULL DEFAULT '1', RangeCount int(11) DEFAULT NULL, PRIMARY KEY (FilterId), KEY ItemPrefix (ItemPrefix), KEY Enabled (Enabled) ); UPDATE ConfigurationValues SET HintLabel = CONCAT('hint:', Prompt) WHERE VariableName IN ('ForceModRewriteUrlEnding', 'PerformExactSearch'); DELETE FROM Phrase WHERE PhraseKey IN ( 'LA_TEXT_PROMOSETTINGS', 'LA_CONFIG_PROMOROTATIONDELAY', 'LA_CONFIG_PROMOTRANSITIONTIME', 'LA_CONFIG_PROMOTRANSITIONCONTROLS', 'LA_CONFIG_PROMOTRANSITIONEFFECT' ); DELETE FROM ConfigurationValues WHERE VariableName IN ('PromoRotationDelay', 'PromoTransitionTime', 'PromoTransitionControls', 'PromoTransitionEffect'); DELETE FROM Permissions WHERE Permission LIKE 'in-portal:promo_blocks.%'; CREATE TABLE PromoBlockGroups ( PromoBlockGroupId int(11) NOT NULL AUTO_INCREMENT, Title varchar(255) NOT NULL DEFAULT '', CreatedOn int(10) unsigned DEFAULT NULL, `Status` tinyint(1) NOT NULL DEFAULT '1', RotationDelay decimal(9,2) DEFAULT NULL, TransitionTime decimal(9,2) DEFAULT NULL, TransitionControls tinyint(1) NOT NULL DEFAULT '1', TransitionEffect varchar(255) NOT NULL DEFAULT '', TransitionEffectCustom varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (PromoBlockGroupId) ); ALTER TABLE Category ADD PromoBlockGroupId int(10) unsigned NOT NULL DEFAULT '0', ADD INDEX (PromoBlockGroupId); ALTER TABLE PromoBlocks ADD PromoBlockGroupId int(10) unsigned NOT NULL DEFAULT '0', ADD INDEX (PromoBlockGroupId); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'DebugOnlyPromoBlockGroupConfigurator', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_DebugOnlyPromoBlockGroupConfigurator', 'checkbox', '', '', 40.13, 0, 0, NULL); UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder + 0.01 WHERE VariableName IN ('RememberLastAdminTemplate', 'UseHTTPAuth', 'HTTPAuthUsername', 'HTTPAuthPassword', 'HTTPAuthBypassIPs'); INSERT INTO PromoBlockGroups VALUES (DEFAULT, 'Default Group', UNIX_TIMESTAMP(), '1', '7.00', '0.60', '1', 'fade', ''); UPDATE PromoBlocks SET PromoBlockGroupId = 1; INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.delete', 11, 1, 1, 0); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageFront', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageFront', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.01', 0, 0, 'hint:la_config_MaintenanceMessageFront'); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageAdmin', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageAdmin', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.02', 0, 0, 'hint:la_config_MaintenanceMessageAdmin'); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SoftMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_SoftMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.03', 0, 0, 'hint:la_config_SoftMaintenanceTemplate'); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'HardMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_HardMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.04', 0, 0, 'hint:la_config_HardMaintenanceTemplate'); UPDATE ConfigurationValues SET VariableName = 'DefaultEmailSender' WHERE VariableName = 'Smtp_AdminMailFrom'; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'DefaultEmailRecipients', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_DefaultEmailRecipients', 'text', NULL, NULL, 50.10, 0, 0, NULL); ALTER TABLE SiteDomains ADD DefaultEmailRecipients TEXT NULL AFTER AdminEmail; UPDATE ConfigurationValues SET Section = 'in-portal:configure_advanced', Heading = 'la_section_Settings3rdPartyAPI', DisplayOrder = 80.01 WHERE VariableName = 'YahooApplicationId'; UPDATE ConfigurationValues SET DisplayOrder = DisplayOrder - 0.01 WHERE VariableName IN ('Search_MinKeyword_Length', 'ExcludeTemplateSectionsFromSearch'); UPDATE Phrase SET l<%PRIMARY_LANGUAGE%>_ColumnTranslation = l<%PRIMARY_LANGUAGE%>_Translation WHERE PhraseKey IN ('LA_FLD_ADDRESSLINE1', 'LA_FLD_ADDRESSLINE2', 'LA_FLD_CITY', 'LA_FLD_COMPANY', 'LA_FLD_FAX', 'LA_FLD_STATE', 'LA_FLD_ZIP'); DELETE FROM Phrase WHERE PhraseKey IN ('LA_TEXT_RESTRICTIONS', 'LA_USERS_REVIEW_DENY', 'LA_USERS_VOTES_DENY'); DELETE FROM ConfigurationValues WHERE VariableName IN ('User_Review_Deny', 'User_Votes_Deny'); ALTER TABLE PortalUser ADD FrontLanguage INT(11) NULL AFTER PwRequestTime; ALTER TABLE PortalUser DROP INDEX AdminLanguage; UPDATE PortalUser SET FrontLanguage = 1 WHERE UserType = 0; ALTER TABLE PortalUser ADD PrevEmails TEXT NULL AFTER Email, ADD EmailVerified TINYINT NOT NULL AFTER `Status`; UPDATE PortalUser SET EmailVerified = 1; INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.VERIFY', NULL, 1, 0, 'Core', 'Changed E-mail Verification', 0, 1, 1); INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.UNDO', NULL, 1, 0, 'Core', 'Changed E-mail Rollback', 0, 1, 1); ALTER TABLE Category ADD RequireSSL TINYINT NOT NULL DEFAULT '0', ADD RequireLogin TINYINT NOT NULL DEFAULT '0'; INSERT INTO ConfigurationValues VALUES(DEFAULT, 'UpdateCountersOnFilterChange', '1', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_UpdateCountersOnFilterChange', 'checkbox', '', '', 10.15, 0, 0, NULL); # use new table name (see /core/install.php:390)! ALTER TABLE UserSessions DROP `tz`; ALTER TABLE UserSessions ADD `TimeZone` VARCHAR(255) NOT NULL AFTER `GroupList`; ALTER TABLE PortalUser DROP `tz`; ALTER TABLE PortalUser ADD `TimeZone` VARCHAR(255) NOT NULL AFTER `dob`; UPDATE SearchConfig SET FieldName = 'TimeZone' WHERE FieldName = 'tz' AND TableName = 'PortalUser'; RENAME TABLE <%TABLE_PREFIX%>BanRules TO <%TABLE_PREFIX%>UserBanRules; RENAME TABLE <%TABLE_PREFIX%>Cache TO <%TABLE_PREFIX%>SystemCache; RENAME TABLE <%TABLE_PREFIX%>ConfigurationValues TO <%TABLE_PREFIX%>SystemSettings; RENAME TABLE <%TABLE_PREFIX%>Category TO <%TABLE_PREFIX%>Categories; UPDATE ItemTypes SET SourceTable = 'Categories' WHERE ItemType = 1; UPDATE ItemTypes SET SourceTable = 'Users' WHERE ItemType = 6; UPDATE SearchConfig SET TableName = 'Categories' WHERE TableName = 'Category'; UPDATE SearchConfig SET TableName = 'CustomFields' WHERE TableName = 'CustomField'; UPDATE SearchConfig SET TableName = 'Users' WHERE TableName = 'PortalUser'; UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>Category', '<%prefix%>Categories'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>ItemReview', '<%prefix%>CatalogReviews'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>Language', '<%prefix%>Languages'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>PortalGroup', '<%prefix%>UserGroups'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>PortalUser', '<%prefix%>Users'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>Theme', '<%prefix%>Themes'); UPDATE StatItem SET ValueSQL = REPLACE(ValueSQL, '<%prefix%>UserSession', '<%prefix%>UserSessions'); UPDATE SystemSettings SET ValueList = REPLACE(ValueList, '<PREFIX>CustomField', '<PREFIX>CustomFields'); UPDATE SystemSettings SET ValueList = REPLACE(ValueList, '<PREFIX>PortalGroup', '<PREFIX>UserGroups'); UPDATE Counters SET CountQuery = 'SELECT COUNT(*) FROM <%PREFIX%>Users WHERE Status = 1', TablesAffected = '|Users|' WHERE `Name` = 'members_count'; UPDATE Counters SET CountQuery = REPLACE(CountQuery, '<%PREFIX%>UserSession', '<%PREFIX%>UserSessions'), TablesAffected = REPLACE(TablesAffected, '|UserSession|', '|UserSessions|'); RENAME TABLE <%TABLE_PREFIX%>CustomField TO <%TABLE_PREFIX%>CustomFields; RENAME TABLE <%TABLE_PREFIX%>Drafts TO <%TABLE_PREFIX%>FormSubmissionReplyDrafts; RENAME TABLE <%TABLE_PREFIX%>Events TO <%TABLE_PREFIX%>EmailEvents; DELETE FROM PersistantSessionData WHERE VariableName LIKE '%custom_filter%'; RENAME TABLE <%TABLE_PREFIX%>Favorites TO <%TABLE_PREFIX%>UserFavorites; RENAME TABLE <%TABLE_PREFIX%>Images TO <%TABLE_PREFIX%>CatalogImages; RENAME TABLE <%TABLE_PREFIX%>ItemFiles TO <%TABLE_PREFIX%>CatalogFiles; RENAME TABLE <%TABLE_PREFIX%>ItemRating TO <%TABLE_PREFIX%>CatalogRatings; RENAME TABLE <%TABLE_PREFIX%>ItemReview TO <%TABLE_PREFIX%>CatalogReviews; RENAME TABLE <%TABLE_PREFIX%>Language TO <%TABLE_PREFIX%>Languages; RENAME TABLE <%TABLE_PREFIX%>PermCache TO <%TABLE_PREFIX%>CategoryPermissionsCache; RENAME TABLE <%TABLE_PREFIX%>PermissionConfig TO <%TABLE_PREFIX%>CategoryPermissionsConfig; RENAME TABLE <%TABLE_PREFIX%>Phrase TO <%TABLE_PREFIX%>LanguageLabels; RENAME TABLE <%TABLE_PREFIX%>PortalGroup TO <%TABLE_PREFIX%>UserGroups; RENAME TABLE <%TABLE_PREFIX%>PersistantSessionData TO <%TABLE_PREFIX%>UserPersistentSessionData; RENAME TABLE <%TABLE_PREFIX%>PortalUser TO <%TABLE_PREFIX%>Users; RENAME TABLE <%TABLE_PREFIX%>PortalUserCustomData TO <%TABLE_PREFIX%>UserCustomData; RENAME TABLE <%TABLE_PREFIX%>RelatedSearches TO <%TABLE_PREFIX%>CategoryRelatedSearches; RENAME TABLE <%TABLE_PREFIX%>Relationship TO <%TABLE_PREFIX%>CatalogRelationships; RENAME TABLE <%TABLE_PREFIX%>SearchLog TO <%TABLE_PREFIX%>SearchLogs; RENAME TABLE <%TABLE_PREFIX%>Skins TO <%TABLE_PREFIX%>AdminSkins; RENAME TABLE <%TABLE_PREFIX%>SubmissionLog TO <%TABLE_PREFIX%>FormSubmissionReplies; RENAME TABLE <%TABLE_PREFIX%>Theme TO <%TABLE_PREFIX%>Themes; RENAME TABLE <%TABLE_PREFIX%>UserGroup TO <%TABLE_PREFIX%>UserGroupRelations; RENAME TABLE <%TABLE_PREFIX%>Visits TO <%TABLE_PREFIX%>UserVisits; RENAME TABLE <%TABLE_PREFIX%>SessionLogs TO <%TABLE_PREFIX%>UserSessionLogs; DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_FLD_RUNMODE'; ALTER TABLE ScheduledTasks DROP RunMode; INSERT INTO SystemSettings VALUES(DEFAULT, 'CKFinderLicenseName', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_CKFinderLicenseName', 'text', NULL, NULL, 80.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CKFinderLicenseKey', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_CKFinderLicenseKey', 'text', NULL, NULL, 80.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'EnablePageContentRevisionControl', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_EnablePageContentRevisionControl', 'checkbox', '', '', 40.19, 0, 0, NULL); # ===== v 5.2.0-B2 ===== ALTER TABLE Users CHANGE Username Username varchar(255) NOT NULL DEFAULT '', CHANGE IPAddress IPAddress varchar(15) NOT NULL DEFAULT '', CHANGE PwResetConfirm PwResetConfirm varchar(255) NOT NULL DEFAULT ''; ALTER TABLE UserSessions CHANGE TimeZone TimeZone varchar(255) NOT NULL DEFAULT ''; ALTER TABLE CountryStates CHANGE l1_Name l1_Name varchar(255) NOT NULL DEFAULT '', CHANGE l2_Name l2_Name varchar(255) NOT NULL DEFAULT '', CHANGE l3_Name l3_Name varchar(255) NOT NULL DEFAULT '', CHANGE l4_Name l4_Name varchar(255) NOT NULL DEFAULT '', CHANGE l5_Name l5_Name varchar(255) NOT NULL DEFAULT ''; ALTER TABLE Categories CHANGE DirectLinkAuthKey DirectLinkAuthKey varchar(20) NOT NULL DEFAULT ''; ALTER TABLE ScheduledTasks CHANGE SiteDomainLimitation SiteDomainLimitation varchar(255) NOT NULL DEFAULT ''; ALTER TABLE ItemFilters CHANGE ItemPrefix ItemPrefix varchar(255) NOT NULL DEFAULT '', CHANGE FilterField FilterField varchar(255) NOT NULL DEFAULT '', CHANGE FilterType FilterType varchar(100) NOT NULL DEFAULT ''; ALTER TABLE SpamReports CHANGE ItemPrefix ItemPrefix varchar(255) NOT NULL DEFAULT ''; ALTER TABLE CachedUrls CHANGE ParsedVars ParsedVars text; ALTER TABLE CurlLog CHANGE Message Message varchar(255) NOT NULL DEFAULT '', CHANGE PageUrl PageUrl varchar(255) NOT NULL DEFAULT '', CHANGE RequestUrl RequestUrl varchar(255) NOT NULL DEFAULT '', CHANGE CurlError CurlError varchar(255) NOT NULL DEFAULT ''; UPDATE SystemSettings SET DisplayOrder = DisplayOrder + 0.01 WHERE ModuleOwner = 'In-Portal' AND Section = 'in-portal:configure_advanced' AND Heading = 'la_section_SettingsAdmin' AND DisplayOrder > 40.11; INSERT INTO SystemSettings VALUES(DEFAULT, 'DefaultGridPerPage', '20', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_DefaultGridPerPage', 'select', '', '10=+10||20=+20||50=+50||100=+100||500=+500', 40.12, 0, 0, NULL); ALTER TABLE EmailEvents ADD LastChanged INT UNSIGNED NULL; ALTER TABLE PromoBlocks DROP Html, CHANGE Status Status TINYINT(1) NOT NULL DEFAULT '1', CHANGE CategoryId CategoryId INT(11) NULL; # ===== v 5.2.0-B3 ===== ALTER TABLE Languages ADD HtmlEmailTemplate TEXT NULL, ADD TextEmailTemplate TEXT NULL; ALTER TABLE EmailLog CHANGE fromuser `From` VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE EmailLog CHANGE addressto `To` VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE EmailLog CHANGE subject `Subject` VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE EmailLog CHANGE `timestamp` SentOn INT(11) NULL; ALTER TABLE EmailLog CHANGE `event` EventName VARCHAR(255) NOT NULL DEFAULT ''; ALTER TABLE EmailLog ADD OtherRecipients TEXT NULL AFTER `To`; ALTER TABLE EmailLog ADD HtmlBody LONGTEXT NULL AFTER `Subject`, ADD TextBody LONGTEXT NULL AFTER HtmlBody; ALTER TABLE EmailLog ADD AccessKey VARCHAR(32) NOT NULL DEFAULT ''; INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:emaillog.edit', 11, 1, 1, 0); DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_PROMPT_FROMUSERNAME'; INSERT INTO SystemSettings VALUES(DEFAULT, 'EmailLogRotationInterval', '-1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_EmailLogRotationInterval', 'select', NULL, '=la_opt_EmailLogKeepNever||86400=la_opt_OneDay||604800=la_opt_OneWeek||1209600=la_opt_TwoWeeks||2419200=la_opt_OneMonth||7257600=la_opt_ThreeMonths||29030400=la_opt_OneYear||-1=la_opt_EmailLogKeepForever', 50.11, 0, 0, 'hint:la_config_EmailLogRotationInterval'); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spam_reports.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spam_reports.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:spam_reports.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:item_filters.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:item_filters.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:item_filters.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:item_filters.delete', 11, 1, 1, 0); ALTER TABLE SlowSqlCapture CHANGE QueryCrc QueryCrc BIGINT(11) NOT NULL DEFAULT '0'; UPDATE SlowSqlCapture SET QueryCrc = CAST((QueryCrc & 0xFFFFFFFF) AS UNSIGNED INTEGER) WHERE QueryCrc < 0; ALTER TABLE ImportCache CHANGE VarName VarName BIGINT(11) NOT NULL DEFAULT '0'; UPDATE ImportCache SET VarName = CAST((VarName & 0xFFFFFFFF) AS UNSIGNED INTEGER) WHERE VarName < 0; ALTER TABLE PageContent CHANGE ContentNum ContentNum BIGINT(11) NOT NULL DEFAULT '0'; UPDATE PageContent SET ContentNum = CAST((ContentNum & 0xFFFFFFFF) AS UNSIGNED INTEGER) WHERE ContentNum < 0; ALTER TABLE CachedUrls CHANGE Hash Hash BIGINT(11) NOT NULL DEFAULT '0'; UPDATE CachedUrls SET Hash = CAST((Hash & 0xFFFFFFFF) AS UNSIGNED INTEGER) WHERE Hash < 0; ALTER TABLE EmailEvents ADD BindToSystemEvent VARCHAR(255) NOT NULL DEFAULT ''; CREATE TABLE SystemEventSubscriptions ( SubscriptionId int(11) NOT NULL AUTO_INCREMENT, EmailEventId int(11) DEFAULT NULL, SubscriberEmail varchar(255) NOT NULL DEFAULT '', UserId int(11) DEFAULT NULL, CategoryId int(11) DEFAULT NULL, IncludeSublevels tinyint(4) NOT NULL DEFAULT '1', ItemId int(11) DEFAULT NULL, ParentItemId int(11) DEFAULT NULL, SubscribedOn int(11) DEFAULT NULL, PRIMARY KEY (SubscriptionId), KEY EmailEventId (EmailEventId) ); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_event_subscriptions.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_event_subscriptions.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_event_subscriptions.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_event_subscriptions.delete', 11, 1, 1, 0); UPDATE LanguageLabels SET l1_ColumnTranslation = l1_Translation, l2_ColumnTranslation = l2_Translation, l3_ColumnTranslation = l3_Translation, l4_ColumnTranslation = l4_Translation, l5_ColumnTranslation = l5_Translation WHERE PhraseKey IN ('LA_FLD_BINDTOSYSTEMEVENT', 'LA_FLD_CATEGORYID'); UPDATE Categories SET l1_MenuTitle = l1_Name WHERE l1_Name = 'Content'; UPDATE SystemSettings SET ValueList = '0=la_opt_QueryString||1=la_opt_Cookies||2=la_opt_AutoDetect' WHERE VariableName = 'CookieSessions'; # ===== v 5.2.0-RC1 ===== UPDATE LanguageLabels SET l<%PRIMARY_LANGUAGE%>_Translation = '<TITLE> Tag' WHERE PhraseKey = 'LA_FLD_PAGECONTENTTITLE'; ALTER TABLE EmailLog ADD EventType TINYINT(4) NULL AFTER EventName; DELETE FROM UserPersistentSessionData WHERE VariableName IN ('email-log[Default]columns_.', 'promo-block[Default]columns_.'); ALTER TABLE Categories ADD NamedParentPathHash INT UNSIGNED NOT NULL DEFAULT '0' AFTER NamedParentPath, ADD CachedTemplateHash INT UNSIGNED NOT NULL DEFAULT '0' AFTER CachedTemplate, ADD INDEX (NamedParentPathHash), ADD INDEX (CachedTemplateHash); # ===== v 5.2.0 ===== INSERT INTO SystemSettings VALUES(DEFAULT, 'CategoryPermissionRebuildMode', '3', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_CategoryPermissionRebuildMode', 'select', NULL, '1=la_opt_Manual||2=la_opt_Silent||3=la_opt_Automatic', 10.11, 0, 0, 'hint:la_config_CategoryPermissionRebuildMode'); DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_CONFIG_QUICKCATEGORYPERMISSIONREBUILD'; ALTER TABLE ScheduledTasks ADD RunSchedule VARCHAR(255) NOT NULL DEFAULT '* * * * *' AFTER Event; DELETE FROM UserPersistentSessionData WHERE VariableName = 'scheduled-task[Default]columns_.'; DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_FLD_RUNINTERVAL'; ALTER TABLE Languages ADD ShortDateFormat VARCHAR(255) NOT NULL DEFAULT 'm/d' AFTER DateFormat, ADD ShortTimeFormat VARCHAR(255) NOT NULL DEFAULT 'g:i A' AFTER TimeFormat; UPDATE Languages SET ShortDateFormat = REPLACE(REPLACE(DateFormat, '/Y', ''), '/y', ''), ShortTimeFormat = REPLACE(TimeFormat, ':s', ''); UPDATE SystemSettings SET GroupDisplayOrder = 1 WHERE VariableName = 'AdminConsoleInterface'; UPDATE SystemSettings SET Section = 'in-portal:configure_general', Prompt = 'la_config_AdminConsoleInterface', DisplayOrder = 50.01, GroupDisplayOrder = 2 WHERE VariableName = 'AllowAdminConsoleInterfaceChange'; DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_CONFIG_ALLOWADMINCONSOLEINTERFACECHANGE'; UPDATE SystemSettings SET DisplayOrder = DisplayOrder - 0.01 WHERE ModuleOwner = 'In-Portal' AND Section = 'in-portal:configure_advanced' AND DisplayOrder > 40.02 AND DisplayOrder < 50; UPDATE SystemSettings SET VariableValue = 1 WHERE VariableName = 'UseOutputCompression'; ALTER TABLE EmailQueue CHANGE LogData LogData LONGTEXT NULL DEFAULT NULL; DELETE FROM UserPersistentSessionData WHERE VariableName = 'mailing-list[Default]columns_.'; INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:configure_general.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:configure_advanced.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:configure_categories.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'in-portal:configure_users.add', 11, 1, 1, 0); # ===== v 5.2.1-B1 ===== UPDATE SystemSettings SET DisplayOrder = 30.05 WHERE VariableName = 'Force_HTTP_When_SSL_Not_Required'; INSERT INTO EmailEvents (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.NEW.PASSWORD', NULL, 1, 0, 'Core', 'Sends new password to an existing user', 0, 1, 0); INSERT INTO EmailEvents (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD.BYADMIN', NULL, 1, 0, 'Core', 'Sends password to a new user', 0, 1, 0); CREATE TABLE SystemLog ( LogId int(11) NOT NULL AUTO_INCREMENT, LogUniqueId int(11) DEFAULT NULL, LogLevel tinyint(4) NOT NULL DEFAULT '7', LogType tinyint(4) NOT NULL DEFAULT '3', LogCode int(11) DEFAULT NULL, LogMessage longtext, LogTimestamp int(11) DEFAULT NULL, LogDate datetime DEFAULT NULL, LogEventName varchar(100) NOT NULL DEFAULT '', LogHostname varchar(255) NOT NULL DEFAULT '', LogRequestSource tinyint(4) DEFAULT NULL, LogRequestURI varchar(255) NOT NULL DEFAULT '', LogRequestData longtext, LogUserId int(11) DEFAULT NULL, LogInterface tinyint(4) DEFAULT NULL, IpAddress varchar(15) NOT NULL DEFAULT '', LogSessionKey int(11) DEFAULT NULL, LogSessionData longtext, LogBacktrace longtext, LogSourceFilename varchar(255) NOT NULL DEFAULT '', LogSourceFileLine int(11) DEFAULT NULL, LogProcessId bigint(20) unsigned DEFAULT NULL, LogMemoryUsed bigint(20) unsigned NOT NULL, LogUserData longtext NOT NULL, LogNotificationStatus tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (LogId), KEY LogLevel (LogLevel), KEY LogType (LogType), KEY LogNotificationStatus (LogNotificationStatus) ); INSERT INTO SystemSettings VALUES(DEFAULT, 'EnableEmailLog', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_EnableEmailLog', 'radio', NULL, '1=la_Yes||0=la_No', 65.01, 0, 1, 'hint:la_config_EnableEmailLog'); UPDATE SystemSettings SET DisplayOrder = 65.02, Heading = 'la_section_SettingsLogs', ValueList = '86400=la_opt_OneDay||604800=la_opt_OneWeek||1209600=la_opt_TwoWeeks||2419200=la_opt_OneMonth||7257600=la_opt_ThreeMonths||29030400=la_opt_OneYear||-1=la_opt_EmailLogKeepForever' WHERE VariableName = 'EmailLogRotationInterval'; UPDATE LanguageLabels SET l<%PRIMARY_LANGUAGE%>_Translation = 'Keep "E-mail Log" for', l<%PRIMARY_LANGUAGE%>_HintTranslation = 'This setting allows you to control for how long "E-mail Log" messages will be stored in the log and then automatically deleted. Use option "Forever" with caution since it will completely disable automatic log cleanup and can lead to large size of database table that stores e-mail messages.' WHERE PhraseKey = 'LA_CONFIG_EMAILLOGROTATIONINTERVAL' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Keep Email Log for'; DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_OPT_EMAILLOGKEEPNEVER'; INSERT INTO SystemSettings VALUES(DEFAULT, 'SystemLogRotationInterval', '2419200', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_SystemLogRotationInterval', 'select', NULL, '86400=la_opt_OneDay||604800=la_opt_OneWeek||1209600=la_opt_TwoWeeks||2419200=la_opt_OneMonth||7257600=la_opt_ThreeMonths||29030400=la_opt_OneYear||-1=la_opt_SystemLogKeepForever', 65.03, 0, 1, 'hint:la_config_SystemLogRotationInterval'); INSERT INTO SystemSettings VALUES(DEFAULT, 'SystemLogNotificationEmail', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_SystemLogNotificationEmail', 'text', 'a:5:{s:4:"type";s:6:"string";s:9:"formatter";s:10:"kFormatter";s:6:"regexp";s:85:"/^([-a-zA-Z0-9!\\#$%&*+\\/=?^_`{|}~.]+@[a-zA-Z0-9]{1}[-.a-zA-Z0-9_]*\\.[a-zA-Z]{2,6})$/i";s:10:"error_msgs";a:1:{s:14:"invalid_format";s:18:"!la_invalid_email!";}s:7:"default";s:0:"";}', NULL, 65.04, 0, 1, 'hint:la_config_SystemLogNotificationEmail'); INSERT INTO EmailEvents (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'SYSTEM.LOG.NOTIFY', NULL, 1, 0, 'Core', 'Notification about message added to System Log', 1, 1, 1); ALTER TABLE Users ADD PasswordHashingMethod TINYINT NOT NULL DEFAULT '3' AFTER Password; UPDATE Users SET PasswordHashingMethod = 1; INSERT INTO SystemSettings VALUES(DEFAULT, 'TypeKitId', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_TypeKitId', 'text', NULL, NULL, 80.05, 0, 1, NULL); ALTER TABLE MailingLists CHANGE EmailsQueued EmailsQueuedTotal INT(10) UNSIGNED NOT NULL DEFAULT '0'; RENAME TABLE <%TABLE_PREFIX%>EmailEvents TO <%TABLE_PREFIX%>EmailTemplates; ALTER TABLE EmailTemplates CHANGE `Event` TemplateName VARCHAR(40) NOT NULL DEFAULT ''; ALTER TABLE EmailTemplates CHANGE EventId TemplateId INT(11) NOT NULL AUTO_INCREMENT; ALTER TABLE SystemEventSubscriptions CHANGE EmailEventId EmailTemplateId INT(11) NULL DEFAULT NULL; DELETE FROM LanguageLabels WHERE PhraseKey IN ( 'LA_FLD_EXPORTEMAILEVENTS', 'LA_FLD_EVENT', 'LA_TITLE_EMAILMESSAGES', 'LA_TAB_E-MAILS', 'LA_COL_EMAILEVENTS', 'LA_OPT_EMAILEVENTS', 'LA_FLD_EMAILEVENT', 'LA_TITLE_EMAILEVENTS', 'LA_TITLE_ADDING_E-MAIL', 'LA_TITLE_EDITING_E-MAIL', 'LA_TITLE_EDITINGEMAILEVENT', 'LA_TITLE_NEWEMAILEVENT', 'LA_TAB_EMAILEVENTS' ); DELETE FROM UserPersistentSessionData WHERE VariableName IN ('system-event-subscription[Default]columns_.', 'email-log[Default]columns_.'); ALTER TABLE EmailLog CHANGE EventName TemplateName VARCHAR(255) NOT NULL DEFAULT ''; # ===== v 5.2.1-B2 ===== DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_TAB_REPORTS'; ALTER TABLE Modules ADD ClassNamespace VARCHAR(255) NOT NULL DEFAULT '' AFTER Path; UPDATE Modules SET ClassNamespace = 'Intechnic\\InPortal\\Core' WHERE `Name` IN ('Core', 'In-Portal'); UPDATE SystemSettings SET DisplayOrder = DisplayOrder + 0.01 WHERE ModuleOwner = 'In-Portal' AND Section = 'in-portal:configure_categories' AND DisplayOrder > 10.10 AND DisplayOrder < 20; INSERT INTO SystemSettings VALUES(DEFAULT, 'CheckViewPermissionsInCatalog', '1', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_CheckViewPermissionsInCatalog', 'radio', NULL, '1=la_Yes||0=la_No', 10.11, 0, 1, 'hint:la_config_CheckViewPermissionsInCatalog'); # ===== v 5.2.1-RC1 ===== UPDATE LanguageLabels SET l1_Translation = REPLACE(l1_Translation, '<br />', '\n') WHERE PhraseKey = 'LA_EDITINGINPROGRESS'; UPDATE LanguageLabels SET l1_ColumnTranslation = 'Helpful' WHERE PhraseKey = 'LA_FLD_HELPFULCOUNT'; UPDATE LanguageLabels SET l1_ColumnTranslation = 'Not Helpful' WHERE PhraseKey = 'LA_FLD_NOTHELPFULCOUNT'; UPDATE LanguageLabels SET Module = 'Core' WHERE PhraseKey = 'LA_SECTION_FILE'; +# ===== v 5.2.1 ===== + # ===== v 5.3.0-B1 ===== ALTER TABLE ScheduledTasks ADD Settings TEXT NULL; ALTER TABLE Themes ADD ImageResizeRules TEXT NULL; DELETE FROM UserPersistentSessionData WHERE VariableName = 'emailevents[Emails]columns_.'; INSERT INTO <%TABLE_PREFIX%>SystemCache (VarName, Data) SELECT 'tmp_translation' AS VarName, l<%PRIMARY_LANGUAGE%>_Translation AS Data FROM <%TABLE_PREFIX%>LanguageLabels WHERE PhraseKey = 'LC_IMPORTLANG_PHRASEWARNING'; UPDATE <%TABLE_PREFIX%>LanguageLabels SET Phrase = 'la_fld_ImportOverwrite', PhraseKey = 'LA_FLD_IMPORTOVERWRITE', l<%PRIMARY_LANGUAGE%>_HintTranslation = (SELECT Data FROM <%TABLE_PREFIX%>SystemCache WHERE VarName = 'tmp_translation' LIMIT 1) WHERE PhraseKey = 'LA_PROMPT_OVERWRITEPHRASES'; DELETE FROM LanguageLabels WHERE PhraseKey = 'LC_IMPORTLANG_PHRASEWARNING'; DELETE FROM LanguageLabels WHERE PhraseKey = 'LA_CONFIG_USETEMPLATECOMPRESSION'; DELETE FROM SystemSettings WHERE VariableName = 'UseTemplateCompression'; UPDATE SystemSettings SET DisplayOrder = ROUND(DisplayOrder - 0.01, 2) WHERE (DisplayOrder BETWEEN 60.04 AND 60.10) AND (ModuleOwner = 'In-Portal') AND (Section = 'in-portal:configure_advanced'); INSERT INTO SystemSettings VALUES(DEFAULT, 'RandomString', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_RandomString', 'text', '', '', 60.09, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'PlainTextCookies', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_PlainTextCookies', 'text', '', '', 60.10, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ForceCanonicalUrls', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ForceCanonicalUrls', 'checkbox', '', '', 10.0125, 0, 0, NULL); UPDATE LanguageLabels SET l1_HintTranslation = '<ul>\r\n<li>This deploy script will apply all Database Changes stored in <b><u>[module]/project_upgrades.sql</u></b> to the current website and save applied Revisions in AppliedDBRevisions.</li>\r\n<li>This deploy script will create all new language phrases by re-importing <b><u>[module]/install/english.lang</u></b> file.</li>\r\n<li>This deploy script will reset all caches at once.</li>\r\n</ul>' WHERE Phrase = 'la_title_SystemToolsDeploy'; INSERT INTO SystemSettings VALUES(DEFAULT, 'EmailDelivery', '2', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_EmailDelivery', 'radio', NULL, '1=la_opt_EmailDeliveryQueue||2=la_opt_EmailDeliveryImmediate', 50.11, 0, 1, NULL); DELETE FROM UserPersistentSessionData WHERE VariableName = 'email-queue[Default]columns_.'; ALTER TABLE EmailLog ADD ToUserId INT(11) DEFAULT NULL, ADD ItemPrefix VARCHAR(50) NOT NULL DEFAULT '', ADD ItemId INT(11) DEFAULT NULL; DELETE FROM UserPersistentSessionData WHERE VariableName = 'email-log[Default]columns_.'; ALTER TABLE EmailLog ADD Status TINYINT NOT NULL DEFAULT '1' AFTER TextBody, ADD ErrorMessage VARCHAR(255) NOT NULL DEFAULT '' AFTER Status; ALTER TABLE ScheduledTasks ADD Module varchar(30) NOT NULL DEFAULT 'Core'; CREATE TABLE ModuleDeploymentLog ( Id int(11) NOT NULL AUTO_INCREMENT, Module varchar(30) NOT NULL DEFAULT 'In-Portal', RevisionNumber int(11) NOT NULL DEFAULT '0', RevisionTitle varchar(255) NOT NULL DEFAULT '', CreatedOn int(10) unsigned DEFAULT NULL, IPAddress varchar(15) NOT NULL DEFAULT '', Output text, ErrorMessage varchar(255) NOT NULL DEFAULT '', Mode tinyint(1) NOT NULL DEFAULT '1', Status tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (Id), KEY CreatedOn (CreatedOn), KEY Mode (Mode), KEY Status (Status) ); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:module_deployment_log.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:module_deployment_log.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:module_deployment_log.delete', 11, 1, 1, 0); UPDATE EmailTemplates SET l<%PRIMARY_LANGUAGE%>_Subject = REPLACE(l<%PRIMARY_LANGUAGE%>_Subject, '<inp2:u_Field name="Email"/>', '<inp2:Field name="Email"/>'), l<%PRIMARY_LANGUAGE%>_PlainTextBody = REPLACE(l<%PRIMARY_LANGUAGE%>_PlainTextBody, '<inp2:u_Field name="Email"/>', '<inp2:Field name="Email"/>'), l<%PRIMARY_LANGUAGE%>_HtmlBody = REPLACE(l<%PRIMARY_LANGUAGE%>_HtmlBody, '<inp2:u_Field name="Email"/>', '<inp2:Field name="Email"/>') WHERE TemplateName IN ('USER.SUBSCRIBE', 'USER.UNSUBSCRIBE'); ALTER TABLE CategoryItems ADD Id int(11) NOT NULL auto_increment FIRST, ADD PRIMARY KEY (Id); ALTER TABLE UserGroupRelations DROP PRIMARY KEY; ALTER TABLE UserGroupRelations ADD Id int(11) NOT NULL auto_increment FIRST, ADD PRIMARY KEY (Id), ADD UNIQUE KEY UserGroup (PortalUserId, GroupId); DELETE FROM UserPersistentSessionData WHERE VariableName IN ('u-ug[Default]columns_.', 'g-ug[Default]columns_.'); ALTER TABLE SpamControl ADD Id int(11) NOT NULL auto_increment FIRST, ADD PRIMARY KEY (Id); INSERT INTO SystemSettings VALUES(DEFAULT, 'SSLDomain', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_SSLDomain', 'text', '', '', 30.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AdminSSLDomain', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_AdminSSLDomain', 'text', '', '', 30.02, 0, 0, NULL); DELETE FROM LanguageLabels WHERE PhraseKey IN ('LA_CONFIG_SSL_URL', 'LA_CONFIG_ADMINSSL_URL', 'LA_FLD_SSLURL'); ALTER TABLE SiteDomains CHANGE SSLUrl SSLDomainName VARCHAR(255) NOT NULL DEFAULT '', CHANGE SSLUrlUsesRegExp SSLDomainNameUsesRegExp TINYINT(4) NOT NULL DEFAULT '0'; DELETE FROM UserPersistentSessionData WHERE VariableName = 'site-domain[Default]columns_.'; Property changes on: branches/5.3.x/CREDITS ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.2.x/CREDITS:r15907-16066 Merged /in-portal/releases/5.2.1/CREDITS:r16067-16068 Property changes on: branches/5.3.x/README ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.2.x/README:r15907-16066 Merged /in-portal/releases/5.2.1/README:r16067-16068 Property changes on: branches/5.3.x/index.php ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.2.x/index.php:r15907-16066 Merged /in-portal/releases/5.2.1/index.php:r16067-16068 Property changes on: branches/5.3.x/LICENSES ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/releases/5.2.1/LICENSES:r16067-16068 Merged /in-portal/branches/5.2.x/LICENSES:r15907-16066 Property changes on: branches/5.3.x/INSTALL ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/releases/5.2.1/INSTALL:r16067-16068 Merged /in-portal/branches/5.2.x/INSTALL:r15907-16066 Property changes on: branches/5.3.x/COPYRIGHT ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/releases/5.2.1/COPYRIGHT:r16067-16068 Merged /in-portal/branches/5.2.x/COPYRIGHT:r15907-16066 Property changes on: branches/5.3.x/.htaccess ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/releases/5.2.1/.htaccess:r16067-16068 Merged /in-portal/branches/5.2.x/.htaccess:r15907-16066 Property changes on: branches/5.3.x ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.2.x:r15907-16066 Merged /in-portal/releases/5.2.1:r16067-16068